diff --git a/known-software.json b/known-software.json index eba5902..828ad18 100644 --- a/known-software.json +++ b/known-software.json @@ -32,7 +32,6 @@ "name": "Akkoma", "nodeinfoName": "akkoma", "aliases": [ - "akkoma", "akko" ], "groups": [ @@ -94,7 +93,6 @@ "nodeinfoName": "mastodon", "buildMetadata": "glitch", "aliases": [ - "glitch-soc", "glitch" ], "groups": [ @@ -157,8 +155,7 @@ "name": "Iceshrimp.NET", "nodeinfoName": "Iceshrimp.NET", "aliases": [ - "iceshrimp-dotnet", - "iceshrimp.net" + "iceshrimp-dotnet" ], "groups": [ "misskey-compliant", diff --git a/static/main.css b/static/crossroad.css similarity index 73% rename from static/main.css rename to static/crossroad.css index 86c25f2..92bcd3c 100644 --- a/static/main.css +++ b/static/crossroad.css @@ -2,7 +2,6 @@ --red: #cb0b0b; --blue: #2081c3; --transparent-black: #0008; - --xl: 4em; --large: 2em; --medium: 1em; } @@ -54,19 +53,6 @@ abbr[title] { text-decoration-color: var(--blue); } -.align-start { - text-align: start; -} - -.inline-block { - display: inline-block; -} - -.align-content-center { - justify-content: center; - align-items: center; -} - .flex-vcenter { display: flex; flex-direction: column; @@ -75,14 +61,6 @@ abbr[title] { height: 100%; } -.flex-hcenter { - display: flex; - flex-direction: row; - justify-content: center; - width: 100%; - height: 100%; -} - .flex-row { display: flex; flex-direction: row; @@ -93,27 +71,11 @@ abbr[title] { flex-direction: row-reverse; } -.flex-column { - display: flex; - flex-direction: column; -} - .flex-column-reverse { display: flex; flex-direction: column-reverse; } -.flex-vevenly { - display: flex; - flex-direction: column; - justify-content: space-evenly; - height: 100%; -} - -.wfit-content { - width: fit-content; -} - .half-width { min-width: 50%; } @@ -126,10 +88,6 @@ abbr[title] { min-height: 100%; } -.medium-height { - height: var(--medium); -} - .separator-bottom { border-bottom: solid 1px var(--transparent-black); } @@ -161,16 +119,4 @@ abbr[title] { top: 50%; left: 50%; translate: -50% -50%; -} - -.logo { - height: 4em; -} - -.inlineIcon { - vertical-align: text-top; -} - -.buttonPanel>* { - margin-top: min(var(--xl), 6vh); } \ No newline at end of file diff --git a/static/crossroad.html b/static/crossroad.html index 4ed6abc..0ab8ba3 100644 --- a/static/crossroad.html +++ b/static/crossroad.html @@ -5,7 +5,7 @@ FeDirect - + @@ -20,29 +20,20 @@

FeDirect

  By Nekomata

- + Nekomata Logo -
-
-
- You're about to go to -
.
- -

You currently don't have any instances. You should add one!

-
-
- -
-
+
-
-
- - - Manage instances -
-
+
+ + +
+
+
+
diff --git a/static/crossroad.mts b/static/crossroad.mts index 2e6ee32..8d212c4 100644 --- a/static/crossroad.mts +++ b/static/crossroad.mts @@ -1,144 +1,14 @@ import { initializeAddInstanceFlow } from "./add_instance_flow.mjs"; -import { findButtonOrFail, findDialogOrFail, findFormOrFail, findInputOrFail, findParagraphOrFail, findPreOrFail } from "./dom.mjs"; -import knownSoftware from "./known_software.mjs"; -import storageManager from "./storage_manager.mjs"; +import { findDialogOrFail } from "./dom.mjs"; -const radioButtonName = "instanceSelect"; +export function getMainDialog(): HTMLDialogElement { + return document.getElementById('mainDialog') as HTMLDialogElement; +} const detailsDialog = findDialogOrFail(document.body, "#instanceDetails"); const addDialog = findDialogOrFail(document.body, "#addInstance"); -const instanceSelectForm = findFormOrFail(document.body, "#instanceSelectForm"); -const redirectButton = findButtonOrFail(document.body, "#redirect"); -const redirectAlwaysButton = findButtonOrFail(document.body, "#redirectAlways"); - -redirectButton.addEventListener("click", e => { - // Can be assumed to not fail because the button is disabled if there are no options and the first one is selected by default - redirect(getSelectedOption()!); -}); - -redirectAlwaysButton.addEventListener("click", e => { - // Can be assumed to not fail because the button is disabled if there are no options and the first one is selected by default - const option = getSelectedOption()!; - setAutoRedirect(option); - redirect(option); -}); - -export const getMainDialog = () => findDialogOrFail(document.body, "#mainDialog"); export const { showAddInstanceDialog, hideAddInstanceDialog -} = ((): { - showAddInstanceDialog: () => void, - hideAddInstanceDialog: () => void -} => { - // Don't bother initializing if we're performing autoredirect - if (autoRedirect()) return { - showAddInstanceDialog: () => { }, - hideAddInstanceDialog: () => { } - } - createInstanceSelectOptions(); - storageManager.addSaveCallback(createInstanceSelectOptions); - updateNoInstanceHint(); - storageManager.addSaveCallback(updateNoInstanceHint); - - findPreOrFail(document.body, "#path").innerText = getTargetPath(); - - return initializeAddInstanceFlow(detailsDialog, addDialog) -})(); - -function updateNoInstanceHint() { - findParagraphOrFail(document.body, "#no-instance").style.display = - storageManager.storage.instances.length > 0 - ? "none" - : ""; -} - -function createInstanceSelectOptions() { - instanceSelectForm.replaceChildren(); // Erase all child nodes - for (const instance of storageManager.storage.instances) { - const div = document.createElement("div"); - div.setAttribute("x-option", instance.origin); - const radio = document.createElement("input"); - radio.id = instance.origin; - radio.value = instance.origin; - radio.type = "radio"; - radio.name = radioButtonName; - const label = document.createElement("label"); - label.htmlFor = instance.origin; - label.innerText = instance.name + " "; - if (instance.iconURL) { - const img = new Image(); - img.src = instance.iconURL; - img.alt = `${instance.name} icon`; - img.className = "inlineIcon medium-height"; - label.append(img, " "); - } - const small = document.createElement("small"); - const softwareName = knownSoftware.software[instance.software].name; - small.innerText = `(${softwareName})`; - label.appendChild(small); - div.appendChild(radio); - div.appendChild(label); - instanceSelectForm.appendChild(div); - } - const firstInput = instanceSelectForm.querySelector("input"); - if (firstInput) firstInput.checked = true; - setRedirectButtonState(firstInput !== null); -} - -function setRedirectButtonState(enabled: boolean) { - redirectButton.disabled = !enabled; - redirectAlwaysButton.disabled = !enabled; -} - -function getTargetSoftwareOrGroup(): string { - const currentURL = URL.parse(location.href)!; - const target = currentURL.pathname.match(/\/+([^\/]*)\/?/)?.[1]; - if (target == null) throw new Error("Crossroad was served on an invalid path (likely a backend routing mistake)"); - const softwareName = Object.entries(knownSoftware.software).find(([name, software]) => software.aliases.includes(target))?.[0]; - if (softwareName) return softwareName; - const groupName = Object.entries(knownSoftware.groups).find(([name, group]) => group.aliases.includes(target))?.[0]; - if (groupName) return groupName; - throw new Error("Could not identify target software or group"); -} - -function getTargetPath(): string { - const currentURL = URL.parse(location.href)!; - return currentURL.pathname.replace(/\/+[^\/]*\/?/, "/"); -} - -function getSelectedOption(): string | null { - try { - return findInputOrFail(instanceSelectForm, `input[name="${radioButtonName}"]:checked`).value; - } catch { - return null; - } -} - -function autoRedirect(): boolean { - const targetSoftware = getTargetSoftwareOrGroup(); - const preferredFor = storageManager.storage.instances.find(instance => instance.preferredFor?.includes(targetSoftware)); - if (preferredFor) { - redirect(preferredFor.origin); - return true; - } - return false; -} - -function setAutoRedirect(option: string) { - const instance = storageManager.storage.instances.find(e => e.origin === option); - if (!instance) throw new Error("Invalid argument"); - instance.preferredFor ??= []; - instance.preferredFor.push(getTargetSoftwareOrGroup()); - storageManager.save(); -} - -function redirect(to: string) { - const url = URL.parse(to); - if (url === null) throw new Error("Couldn't parse destination"); - url.pathname = getTargetPath(); - location.href = url.toString(); -} - -export { storageManager }; \ No newline at end of file +} = initializeAddInstanceFlow(detailsDialog, addDialog); diff --git a/static/dom.mts b/static/dom.mts index 53d4c36..7a7aff5 100644 --- a/static/dom.mts +++ b/static/dom.mts @@ -1,20 +1,6 @@ // I would've LOVED to use generics for this but unfortunately that's not possible. // Type safety, but at what cost... >~< thanks TypeScript -export function findPreOrFail(on: Element, selector: string): HTMLPreElement { - const element = on.querySelector(selector); - if (!(element instanceof HTMLPreElement)) - throw new Error(`${selector} isn't a pre`); - return element; -} - -export function findParagraphOrFail(on: Element, selector: string): HTMLParagraphElement { - const element = on.querySelector(selector); - if (!(element instanceof HTMLParagraphElement)) - throw new Error(`${selector} isn't a paragraph`); - return element; -} - export function findDialogOrFail(on: Element, selector: string): HTMLDialogElement { const element = on.querySelector(selector); if (!(element instanceof HTMLDialogElement)) diff --git a/static/down_arrow.svg b/static/down_arrow.svg deleted file mode 100644 index 875e948..0000000 --- a/static/down_arrow.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/static/storage_manager.mts b/static/storage_manager.mts index 7ab3957..da88774 100644 --- a/static/storage_manager.mts +++ b/static/storage_manager.mts @@ -25,11 +25,6 @@ export type Instance = { * Make sure to sanitize this! Could lead to XSS */ iconURL?: string, - /** - * The list of software names and groups the user prefers to autoredirect to this instance - * @example ["sharkey", "misskey-compliant"] - */ - preferredFor?: string[], } type LocalStorage = { @@ -38,7 +33,6 @@ type LocalStorage = { export default new class StorageManager { storage: LocalStorage; - saveCallbacks: (() => void)[] = []; constructor() { this.load(); @@ -56,10 +50,5 @@ export default new class StorageManager { save() { window.localStorage.setItem("storage", JSON.stringify(this.storage)); - this.saveCallbacks.forEach(c => c()); - } - - addSaveCallback(callback: () => void) { - this.saveCallbacks.push(callback); } }(); \ No newline at end of file