diff options
Diffstat (limited to 'kittybox-rs/companion-lite')
-rw-r--r-- | kittybox-rs/companion-lite/index.html | 77 | ||||
-rw-r--r-- | kittybox-rs/companion-lite/main.js | 70 | ||||
-rw-r--r-- | kittybox-rs/companion-lite/micropub_api.js | 43 | ||||
-rw-r--r-- | kittybox-rs/companion-lite/style.css | 47 |
4 files changed, 237 insertions, 0 deletions
diff --git a/kittybox-rs/companion-lite/index.html b/kittybox-rs/companion-lite/index.html new file mode 100644 index 0000000..b643ba2 --- /dev/null +++ b/kittybox-rs/companion-lite/index.html @@ -0,0 +1,77 @@ +<html> + <head> + <meta charset="utf-8"> + <title>Kittybox-Micropub debug client</title> + <link rel="stylesheet" href="./style.css"> + <script type="module" src="./main.js"></script> + </head> + <body> + <noscript> + <h1 class="header">Kittybox Companion (Lite)</h1> + <p>I'm sorry, Kittybox Companion requires JavaScript to work.</p> + + <p>This is a requirement due to multiple interactive features present in Kittybox, such as support for multiple-entry form fields, interactive login sequence and more.</p> + + <p>However, the Micropub standard is extremely flexible, and if you happen to have a token, you can publish articles, notes, likes, follows and more by sending requests directly to the Micropub endpoint.</p> + + <p><a href="https://micropub.spec.indieweb.org/">The Micropub spec is defined here.</a> Good luck!</p> + </noscript> + + <div class="view" id="unauthorized" style="display:none"> + + </div> + + <div class="view" id="authorized" style="display:none"> + <form action="/.kittybox/micropub" method="POST" id="micropub"> + <fieldset> + <legend>Authorization details</legend> + <section> + <label for="access_token">Access token:</label> + <input id="access_token" name="access_token" type="password"> + + <p><a href="https://gimme-a-token.5eb.nl/" target="_blank">Get an access token (will open in a new tab)</a></p> + </section> + </fieldset> + <fieldset> + <legend>Post details:</legend> + <section> + <label for="name">Name (leave blank for an unnamed post):</label> + <input id="name" type="text"> + </section> + <section> + <label for="content">Content:</label> + <textarea id="content" placeholder="Your post's text goes here"></textarea> + </section> + <section> + <label for="category">Categories (separeted by commas):</label> + <input id="category" type="text"> + </section> + <fieldset> + <legend>Channels</legend> + <section> + <input type="radio" id="no_channel" name="channel_select" checked value=""> + <label for="no_channel">Default channel only</label> + </section> + + <section> + <input type="radio" id="select_channels" name="channel_select" value="on"> + <label for="select_channels">Select channels manually</label> + </section> + + <fieldset id="channels" style="display: none"> + <legend>Available channels:</legend> + <template id="channel_selector"> + <section> + <input type="checkbox" name="channel" id="" value=""> + <label for=""></label> + </section> + </template> + <div id="channels_target"></div> + </fieldset> + </fieldset> + </fieldset> + <input type="submit"> + </div> + </main> + </body> +</html> diff --git a/kittybox-rs/companion-lite/main.js b/kittybox-rs/companion-lite/main.js new file mode 100644 index 0000000..da7e6e1 --- /dev/null +++ b/kittybox-rs/companion-lite/main.js @@ -0,0 +1,70 @@ +import { query_channels, submit } from "./micropub_api.js"; + +function get_token() { + return form.elements.access_token.value +} + +const form = document.getElementById("micropub"); +const channel_select_radio = document.getElementById("select_channels"); + +channel_select_radio.onclick = async () => { + const channels = await query_channels(form.action, get_token()) + if (channels !== undefined) { + populate_channel_list(channels) + } +} + +const no_channel_radio = document.getElementById("no_channel"); +no_channel_radio.onclick = () => { + document.getElementById("channels").style.display = "none"; + const channel_list = document.getElementById("channels_target") + channel_list.innerHTML = ""; +} + +function construct_body(form) { + return { + type: ["h-entry"], + properties: { + content: [form.elements.content.value], + name: form.elements.name.value ? [form.elements.name.value] : undefined, + category: form.elements.category.value ? form.elements.category.value.split(",").map(val => val.trim()) : undefined, + channel: form.elements.channel_select.value ? Array.from(form.elements.channel).map(i => i.checked ? i.value : false).filter(i => i) : undefined + } + } +} + +function populate_channel_list(channels) { + document.getElementById("channels").style.display = "block"; + const channel_list = document.getElementById("channels_target") + channel_list.innerHTML = ""; + channels.forEach((channel) => { + const template = document.getElementById("channel_selector").content.cloneNode(true) + const input = template.querySelector("input") + const label = template.querySelector("label") + input.id = `channel_selector_option_${channel.uid}` + input.value = channel.uid + label.for = input.id + label.innerHTML = `<a href="${channel.uid}">${channel.name}</a>` + + channel_list.appendChild(template) + }) +} + +form.onsubmit = async (event) => { + event.preventDefault() + const mf2 = construct_body(form); + console.log(JSON.stringify(mf2)); + try { + submit(form.action, get_token(), mf2) + } catch (e) { + // TODO show errors to user + + return + } + form.clear() +} + +document.getElementById("authorized").style.display = "block"; +// Local Variables: +// js-indent-level: 4 +// End: diff --git a/kittybox-rs/companion-lite/micropub_api.js b/kittybox-rs/companion-lite/micropub_api.js new file mode 100644 index 0000000..402c075 --- /dev/null +++ b/kittybox-rs/companion-lite/micropub_api.js @@ -0,0 +1,43 @@ +export async function query_channels(endpoint, token) { + const response = await fetch(endpoint + "?q=config", { + headers: { + "Authorization": `Bearer ${get_token()}` + } + }) + + const config = await response.json(); + + return config["channels"] +} + +export async function submit(endpoint, token, mf2) { + try { + const response = await fetch(endpoint, { + method: "POST", + headers: { + "Authorization": `Bearer ${token}`, + "Content-Type": "application/json" + }, + body: JSON.stringify(mf2) + }) + + + if (response.status != 201 || response.status != 202) { + let err = await response.json(); + console.error("Micropub error!", err); + + return err; + } else { + return { + "location": response.headers.get("Location") + } + } + } catch (e) { + console.error("Network error!", e) + throw e + } +} + +// Local Variables: +// js-indent-level: 4 +// End: diff --git a/kittybox-rs/companion-lite/style.css b/kittybox-rs/companion-lite/style.css new file mode 100644 index 0000000..09ed398 --- /dev/null +++ b/kittybox-rs/companion-lite/style.css @@ -0,0 +1,47 @@ +* { + box-sizing: border-box; +} + +:root { + font-family: sans-serif; +} + +body { + margin: 0; +} + +body > main { + margin: auto; + max-width: 1024px; +} + +h1.header { + margin-top: 0.75em; + text-align: center; +} + +fieldset + fieldset, +fieldset + input, +section + section, +section + fieldset +{ + margin-top: 0.75em; +} + +input[type="submit"] { + margin-left: auto; + display: block; +} + +form > fieldset > section > label { + width: 100%; + display: block; +} + +form > fieldset > section > input, form > fieldset > section > textarea { + width: 100%; +} + +textarea { + min-height: 10em; +} |