diff --git a/src/webpage/localuser.ts b/src/webpage/localuser.ts index 8bad04b..10354be 100644 --- a/src/webpage/localuser.ts +++ b/src/webpage/localuser.ts @@ -2731,9 +2731,9 @@ class Localuser { const genSecurity = () => { security.removeAll(); if (this.mfa_enabled) { - security.addButtonInput("", I18n.localuser["2faDisable"](), () => { - const form = security.addSubForm( - I18n.localuser["2faDisable"](), + security.addSubButtonInput(I18n.localuser["2faDisable"](), (sub) => { + const form = sub.addForm( + "", (_: any) => { if (_.message) { switch (_.code) { @@ -2755,13 +2755,13 @@ class Localuser { form.addTextInput(I18n.localuser["2faCode:"](), "code", {required: true}); }); } else { - security.addButtonInput("", I18n.localuser["2faEnable"](), async () => { + security.addSubButtonInput(I18n.localuser["2faEnable"](), async (sub) => { let secret = ""; for (let i = 0; i < 18; i++) { secret += "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"[Math.floor(Math.random() * 32)]; } - const form = security.addSubForm( - I18n.localuser.setUp2fa(), + const form = sub.addForm( + "", (_: any) => { if (_.message) { switch (_.code) { @@ -2794,8 +2794,7 @@ class Localuser { }); } { - security.addButtonInput("", I18n.webauth.manage(), () => { - const keyMenu = security.addSubOptions("Manage Keys"); + security.addSubButtonInput(I18n.webauth.manage(), (keyMenu) => { const addKey = (key: {name: string; id: string}) => { keyMenu.addButtonInput("", key.name, () => { const opt = keyMenu.addSubOptions(key.name); @@ -2882,9 +2881,9 @@ class Localuser { }); }); } - security.addButtonInput("", I18n.localuser.changeDiscriminator(), () => { - const form = security.addSubForm( - I18n.localuser.changeDiscriminator(), + security.addSubButtonInput(I18n.localuser.changeDiscriminator(), (sub) => { + const form = sub.addForm( + "", (_) => { security.returnFromSub(); }, @@ -2896,9 +2895,9 @@ class Localuser { ); form.addTextInput(I18n.localuser.newDiscriminator(), "discriminator"); }); - security.addButtonInput("", I18n.localuser.changeEmail(), () => { - const form = security.addSubForm( - I18n.localuser.changeEmail(), + security.addSubButtonInput(I18n.localuser.changeEmail(), (sub) => { + const form = sub.addForm( + "", (_) => { security.returnFromSub(); }, @@ -2916,9 +2915,9 @@ class Localuser { } form.addTextInput(I18n.localuser["newEmail:"](), "email"); }); - security.addButtonInput("", I18n.localuser.changeUsername(), () => { - const form = security.addSubForm( - I18n.localuser.changeUsername(), + security.addSubButtonInput(I18n.localuser.changeUsername(), (sub) => { + const form = sub.addForm( + "", (_) => { security.returnFromSub(); }, @@ -2936,9 +2935,9 @@ class Localuser { } form.addTextInput(I18n.localuser.newUsername(), "username"); }); - security.addButtonInput("", I18n.localuser.changePassword(), () => { - const form = security.addSubForm( - I18n.localuser.changePassword(), + security.addSubButtonInput(I18n.localuser.changePassword(), (sub) => { + const form = sub.addForm( + "", (_) => { security.returnFromSub(); }, @@ -3196,9 +3195,9 @@ class Localuser { }).then(async (teamsRes) => { const teams = await teamsRes.json(); - const button = devPortal.addButtonInput("", I18n.localuser.createApp(), () => { - const form = devPortal.addSubForm( - I18n.localuser.createApp(), + const button = devPortal.addSubButtonInput(I18n.localuser.createApp(), (sub) => { + const form = sub.addForm( + "", (json: any) => { if (json.message) form.error("name", json.message); else { @@ -3412,111 +3411,114 @@ class Localuser { }); } if (this.rights.hasPermission("CREATE_REGISTRATION_TOKENS")) { - manageInstance.addButtonInput("", I18n.manageInstance.createTokens(), () => { - const tokens = manageInstance.addSubOptions(I18n.manageInstance.createTokens(), { - noSubmit: true, - }); - const count = tokens.addTextInput(I18n.manageInstance.count(), () => {}, { - initText: "1", - }); - const length = tokens.addTextInput(I18n.manageInstance.length(), () => {}, { - initText: "32", - }); - const format = tokens.addSelect( - I18n.manageInstance.format(), - () => {}, - [ - I18n.manageInstance.TokenFormats.JSON(), - I18n.manageInstance.TokenFormats.plain(), - I18n.manageInstance.TokenFormats.URLs(), - ], - { - defaultIndex: 2, - }, - ); - format.watchForChange((e) => { - if (e !== 2) { - urlOption.removeAll(); - } else { - makeURLMenu(); - } - }); - const urlOption = tokens.addOptions(""); - const urlOptionsJSON = { - url: window.location.origin, - type: "Jank", - }; - function makeURLMenu() { - urlOption - .addTextInput(I18n.manageInstance.clientURL(), () => {}, { - initText: urlOptionsJSON.url, - }) - .watchForChange((str) => { - urlOptionsJSON.url = str; - }); - urlOption - .addSelect( - I18n.manageInstance.regType(), - () => {}, - ["Jank", I18n.manageInstance.genericType()], - { - defaultIndex: ["Jank", "generic"].indexOf(urlOptionsJSON.type), - }, - ) - .watchForChange((i) => { - urlOptionsJSON.type = ["Jank", "generic"][i]; - }); - } - makeURLMenu(); - tokens.addButtonInput("", I18n.manageInstance.create(), async () => { - const params = new URLSearchParams(); - params.set("count", count.value); - params.set("length", length.value); - const json = (await ( - await fetch( - this.info.api + "/auth/generate-registration-tokens?" + params.toString(), - { - headers: this.headers, - }, - ) - ).json()) as {tokens: string[]}; - if (format.index === 0) { - pre.textContent = JSON.stringify(json.tokens); - } else if (format.index === 1) { - pre.textContent = json.tokens.join("\n"); - } else if (format.index === 2) { - if (urlOptionsJSON.type === "Jank") { - const options = new URLSearchParams(); - options.set("instance", this.info.wellknown); - pre.textContent = json.tokens - .map((token) => { - options.set("token", token); - return `${urlOptionsJSON.url}/register?` + options.toString(); - }) - .join("\n"); + manageInstance.addSubButtonInput( + I18n.manageInstance.createTokens(), + (tokens) => { + const count = tokens.addTextInput(I18n.manageInstance.count(), () => {}, { + initText: "1", + }); + const length = tokens.addTextInput(I18n.manageInstance.length(), () => {}, { + initText: "32", + }); + const format = tokens.addSelect( + I18n.manageInstance.format(), + () => {}, + [ + I18n.manageInstance.TokenFormats.JSON(), + I18n.manageInstance.TokenFormats.plain(), + I18n.manageInstance.TokenFormats.URLs(), + ], + { + defaultIndex: 2, + }, + ); + format.watchForChange((e) => { + if (e !== 2) { + urlOption.removeAll(); } else { - const options = new URLSearchParams(); - pre.textContent = json.tokens - .map((token) => { - options.set("token", token); - return `${urlOptionsJSON.url}/register?` + options.toString(); - }) - .join("\n"); + makeURLMenu(); } + }); + const urlOption = tokens.addOptions(""); + const urlOptionsJSON = { + url: window.location.origin, + type: "Jank", + }; + function makeURLMenu() { + urlOption + .addTextInput(I18n.manageInstance.clientURL(), () => {}, { + initText: urlOptionsJSON.url, + }) + .watchForChange((str) => { + urlOptionsJSON.url = str; + }); + urlOption + .addSelect( + I18n.manageInstance.regType(), + () => {}, + ["Jank", I18n.manageInstance.genericType()], + { + defaultIndex: ["Jank", "generic"].indexOf(urlOptionsJSON.type), + }, + ) + .watchForChange((i) => { + urlOptionsJSON.type = ["Jank", "generic"][i]; + }); } - }); - tokens.addButtonInput("", I18n.manageInstance.copy(), async () => { - try { - if (pre.textContent) { - await navigator.clipboard.writeText(pre.textContent); + makeURLMenu(); + tokens.addButtonInput("", I18n.manageInstance.create(), async () => { + const params = new URLSearchParams(); + params.set("count", count.value); + params.set("length", length.value); + const json = (await ( + await fetch( + this.info.api + "/auth/generate-registration-tokens?" + params.toString(), + { + headers: this.headers, + }, + ) + ).json()) as {tokens: string[]}; + if (format.index === 0) { + pre.textContent = JSON.stringify(json.tokens); + } else if (format.index === 1) { + pre.textContent = json.tokens.join("\n"); + } else if (format.index === 2) { + if (urlOptionsJSON.type === "Jank") { + const options = new URLSearchParams(); + options.set("instance", this.info.wellknown); + pre.textContent = json.tokens + .map((token) => { + options.set("token", token); + return `${urlOptionsJSON.url}/register?` + options.toString(); + }) + .join("\n"); + } else { + const options = new URLSearchParams(); + pre.textContent = json.tokens + .map((token) => { + options.set("token", token); + return `${urlOptionsJSON.url}/register?` + options.toString(); + }) + .join("\n"); + } } - } catch (err) { - console.error(err); - } - }); - const pre = document.createElement("pre"); - tokens.addHTMLArea(pre); - }); + }); + tokens.addButtonInput("", I18n.manageInstance.copy(), async () => { + try { + if (pre.textContent) { + await navigator.clipboard.writeText(pre.textContent); + } + } catch (err) { + console.error(err); + } + }); + const pre = document.createElement("pre"); + tokens.addHTMLArea(pre); + }, + { + noSubmit: true, + }, + ); } } (async () => { diff --git a/src/webpage/settings.ts b/src/webpage/settings.ts index 66aa0f5..1aae585 100644 --- a/src/webpage/settings.ts +++ b/src/webpage/settings.ts @@ -354,6 +354,42 @@ class CheckboxInput implements OptionsElement { } } +class SubButtonInput implements OptionsElement { + readonly owner: Options; + readonly onClick: (opt: Options) => void; + textContent: string; + value!: void; + opts?: {noSubmit?: boolean; ltr?: boolean}; + + constructor( + textContent: string, + onClick: (opt: Options) => void, + owner: Options, + {} = {}, + opts?: SubButtonInput["opts"], + ) { + this.owner = owner; + this.onClick = onClick; + this.textContent = textContent; + this.opts = opts; + } + buttonHtml?: HTMLButtonElement; + generateHTML(): HTMLDivElement { + const div = document.createElement("div"); + const button = document.createElement("button"); + button.classList.add("subButton"); + button.textContent = this.textContent; + button.onclick = () => { + this.onClick(this.owner.addSubOptions(this.textContent, this.opts)); + }; + this.buttonHtml = button; + div.append(button); + return div; + } + watchForChange() {} + submit() {} +} + class ButtonInput implements OptionsElement { readonly label: string; readonly owner: Options; @@ -1516,6 +1552,16 @@ class Options implements OptionsElement { this.generate(button); return button; } + addSubButtonInput( + textContent: string, + onSubmit: (opt: Options) => void, + {ltr = false, noSubmit = false} = {}, + ) { + const button = new SubButtonInput(textContent, onSubmit, this, {ltr, noSubmit}); + this.options.push(button); + this.generate(button); + return button; + } addCheckboxInput(label: string, onSubmit: (str: boolean) => void, {initState = false} = {}) { const box = new CheckboxInput(label, onSubmit, this, {initState}); this.options.push(box); diff --git a/src/webpage/style.css b/src/webpage/style.css index 5c8d192..64bcdef 100644 --- a/src/webpage/style.css +++ b/src/webpage/style.css @@ -755,6 +755,23 @@ select { cursor: pointer; transition: background 0.1s ease-in-out; } +.subButton { + min-width: 350px; + padding: 10px; + position: relative; + &::after { + content: ""; + mask: url(./icons/intoMenu.svg); + mask-size: contain !important; + width: 24px; + height: 24px; + background: var(--primary-text-soft); + display: block; + position: absolute; + right: 4px; + top: 8px; + } +} input::file-selector-button { margin-right: 6px; } @@ -2896,11 +2913,13 @@ input[type="search"]:read-only { background: var(--spoiler-bg); color: var(--spoiler-bg); cursor: pointer; - samp, a { + samp, + a { background: transparent; color: inherit; } - pre, .quote { + pre, + .quote { background: var(--spoiler-bg); color: var(--spoiler-bg); } @@ -2908,7 +2927,8 @@ input[type="search"]:read-only { .spoiler:hover { background: var(--spoiler-hover); color: var(--spoiler-hover); - pre, .quote { + pre, + .quote { background: var(--spoiler-hover); color: var(--spoiler-hover); }