diff options
Diffstat (limited to 'kittybox-rs/companion-lite/src')
-rw-r--r-- | kittybox-rs/companion-lite/src/main.ts | 85 | ||||
-rw-r--r-- | kittybox-rs/companion-lite/src/micropub_api.ts | 58 |
2 files changed, 143 insertions, 0 deletions
diff --git a/kittybox-rs/companion-lite/src/main.ts b/kittybox-rs/companion-lite/src/main.ts new file mode 100644 index 0000000..d593188 --- /dev/null +++ b/kittybox-rs/companion-lite/src/main.ts @@ -0,0 +1,85 @@ +import { query_channels, submit, MicropubChannel, MF2 } from "./micropub_api.js"; + +function get_token() { + return (form.elements.namedItem("access_token") as HTMLInputElement).value +} + +const form = document.getElementById("micropub") as HTMLFormElement; +const channel_select_radio = document.getElementById("select_channels") as HTMLInputElement; + +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") as HTMLInputElement; +no_channel_radio.onclick = () => { + (document.getElementById("channels") as HTMLElement).style.display = "none"; + const channel_list = document.getElementById("channels_target") as HTMLElement + channel_list.innerHTML = ""; +} + +function construct_body(form: HTMLFormElement): MF2 { + let content = (form.elements.namedItem("content") as HTMLInputElement).value; + let name: string | undefined = (form.elements.namedItem("name") as HTMLInputElement).value || undefined; + let category: string[] = (form.elements.namedItem("category") as HTMLInputElement).value + .split(",") + .map(val => val.trim()); + + let channel: string[] | undefined = undefined; + let channel_select = (form.elements.namedItem("channel_select") as HTMLInputElement).value; + if (channel_select) { + let channel_selector = form.elements.namedItem("channel"); + if (channel_selector instanceof RadioNodeList) { + channel = (Array.from(channel_selector) as HTMLInputElement[]) + .map(i => i.checked ? i.value : false) + .filter(i => i) as string[]; + } else if (channel_selector instanceof HTMLInputElement) { + channel = [channel_selector.value] + } + } + return { + type: ["h-entry"], + properties: { + content: [content], + name: name ? [name] : undefined, + category: category.length ? category : undefined, + channel: channel ? channel : undefined + } + } +} + +function populate_channel_list(channels: MicropubChannel[]) { + (document.getElementById("channels") as HTMLElement).style.display = "block"; + const channel_list = document.getElementById("channels_target") as HTMLElement; + channel_list.innerHTML = ""; + channels.forEach((channel) => { + const template = (document.getElementById("channel_selector") as HTMLTemplateElement).content.cloneNode(true) as HTMLElement; + const input = template.querySelector("input") as HTMLInputElement; + const label = template.querySelector("label") as HTMLLabelElement; + input.id = `channel_selector_option_${channel.uid}` + input.value = channel.uid + label.htmlFor = 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") as HTMLElement).style.display = "block"; diff --git a/kittybox-rs/companion-lite/src/micropub_api.ts b/kittybox-rs/companion-lite/src/micropub_api.ts new file mode 100644 index 0000000..9eb65a2 --- /dev/null +++ b/kittybox-rs/companion-lite/src/micropub_api.ts @@ -0,0 +1,58 @@ +export interface MicropubChannel { + uid: string, + name: string +} + +export interface MF2 { + type: string[], + properties: { [key:string]: (string | MF2 | {[key:string]: string})[] | undefined } +} + +export interface MicropubConfig { + channels: MicropubChannel[], + "media-endpoint": string +} + +export async function query_channels(endpoint: string, token: string): Promise<MicropubChannel[]> { + const response = await fetch(endpoint + "?q=config", { + headers: { + "Authorization": `Bearer ${token}` + } + }) + + if (response.ok) { + const config = await response.json() as MicropubConfig; + + return config["channels"] + } else { + throw new Error(`Micropub endpoint returned ${response.status}: ${await response.json()}`) + } + +} + +export async function submit(endpoint: string, token: string, mf2: 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 + } +} |