From c1de80e61b2989a4cf1e6c014e0bbc0fe2896749 Mon Sep 17 00:00:00 2001 From: Mathium05 Date: Wed, 26 Aug 2026 14:02:23 -0500 Subject: [PATCH] use promise lock and add an await --- src/webpage/settings.ts | 277 ++++++++++++++++--------------- src/webpage/utils/promiseLock.ts | 9 +- 2 files changed, 149 insertions(+), 137 deletions(-) diff --git a/src/webpage/settings.ts b/src/webpage/settings.ts index d6de7f2..e09c2fc 100644 --- a/src/webpage/settings.ts +++ b/src/webpage/settings.ts @@ -10,6 +10,7 @@ import {Emoji} from "./emoji.js"; import {I18n} from "./i18n.js"; import {Localuser} from "./localuser.js"; import {MarkDown} from "./markdown.js"; +import {PromiseLock} from "./utils/promiseLock.js"; interface OptionsElement { // @@ -2350,161 +2351,165 @@ class Form implements OptionsElement { this.preprocessor = func; } onFormError = (_: FormError) => {}; - subbmitting = false; + submitLock = new PromiseLock(); async submit() { + if (this.submitLock.locked) return; + const lock = await this.submitLock.acquireLock(); + try { + this.submitInt(); + } finally { + lock(); + } + } + private async submitInt() { if (this.options.subOptions) { this.options.subOptions.submit(); return; } - if (this.subbmitting) return; - this.subbmitting = true; - try { - console.log("start"); - const build = {}; - for (const key of Object.keys(this.values)) { - const thing = this.values[key]; - if (thing instanceof Function) { - try { - (build as any)[key] = thing(); - } catch (e: any) { - if (e instanceof FormError) { - this.handleError(e); - } - return; + + console.log("start"); + const build = {}; + for (const key of Object.keys(this.values)) { + const thing = this.values[key]; + if (thing instanceof Function) { + try { + (build as any)[key] = thing(); + } catch (e: any) { + if (e instanceof FormError) { + this.handleError(e); + } + return; + } + } else { + (build as any)[key] = thing; + } + } + console.log("middle"); + const promises: Promise[] = []; + for (const thing of this.names.keys()) { + if (thing === "") continue; + const input = this.names.get(thing) as OptionsElement; + if (input instanceof SelectInput) { + (build as any)[thing] = (this.selectMap.get(input) as string[])[input.value]; + continue; + } else if (input instanceof FileInput) { + const options = this.fileOptions.get(input); + if (!options) { + throw new Error( + "FileInput without its options is in this form, this should never happen.", + ); + } + if (options.files === "one") { + console.log(input.value); + if (input.value) { + const reader = new FileReader(); + const promise = new Promise((res) => { + reader.onload = () => { + (build as any)[thing] = reader.result; + res(); + }; + }); + reader.readAsDataURL(input.value[0]); + promises.push(promise); + continue; } } else { - (build as any)[key] = thing; + console.error(options.files + " is not currently implemented"); } + } else if (input instanceof EmojiInput) { + if (!input.value) { + (build as any)[thing] = input.value; + } else if (input.value.id) { + (build as any)[thing] = input.value.id; + } else if (input.value.emoji) { + (build as any)[thing] = input.value.emoji; + } + continue; } - console.log("middle"); - const promises: Promise[] = []; - for (const thing of this.names.keys()) { - if (thing === "") continue; - const input = this.names.get(thing) as OptionsElement; - if (input instanceof SelectInput) { - (build as any)[thing] = (this.selectMap.get(input) as string[])[input.value]; - continue; - } else if (input instanceof FileInput) { - const options = this.fileOptions.get(input); - if (!options) { - throw new Error( - "FileInput without its options is in this form, this should never happen.", - ); + (build as any)[thing] = input.value; + } + console.log("middle2"); + await Promise.allSettled(promises); + try { + this.preprocessor(build); + } catch (e) { + if (e instanceof FormError) { + this.handleError(e); + } + return; + } + if (this.fetchURL !== "") { + const onSubmit = async (json: any) => { + try { + await this.onSubmit(json, build); + } catch (e) { + console.error(e); + if (e instanceof FormError) { + this.handleError(e); } - if (options.files === "one") { - console.log(input.value); - if (input.value) { - const reader = new FileReader(); - const promise = new Promise((res) => { - reader.onload = () => { - (build as any)[thing] = reader.result; - res(); - }; - }); - reader.readAsDataURL(input.value[0]); - promises.push(promise); - continue; + return; + } + }; + const doFetch = async () => { + fetch(this.fetchURL, { + method: this.method, + body: JSON.stringify(build), + headers: this.headers, + }) + .then(async (_) => { + return [await _.text(), _.status] as const; + }) + .then(([_, status]) => { + if (_ === "") return [null, status]; + return [JSON.parse(_), status] as const; + }) + .then(async ([json, status]) => { + if (await handleCaptcha(json, build, this.captcha)) { + return await doFetch(); } - } else { - console.error(options.files + " is not currently implemented"); - } - } else if (input instanceof EmojiInput) { - if (!input.value) { - (build as any)[thing] = input.value; - } else if (input.value.id) { - (build as any)[thing] = input.value.id; - } else if (input.value.emoji) { - (build as any)[thing] = input.value.emoji; - } - continue; - } - (build as any)[thing] = input.value; - } - console.log("middle2"); - await Promise.allSettled(promises); + const match = this.fetchURL.match(/https?:\/\/[^\/]*\/api/gm); + if (match && this.tfaCheck) { + const tried = await handle2fa(json, match[0]); + if (tried) { + return await onSubmit(tried); + } + } + if (json.ticket) { + } + if (json.errors) { + if (this.errors(json)) { + return; + } + } else if (status === 500) { + this.error( + [...this.names].at(-1)?.[0] ?? "", + json.message ?? "internal server error", + ); + return; + } + if ( + Math.floor(json.code / 100) === 4 && + json.message && + typeof json.message === "string" + ) { + this.showPrimError(json.message); + return; + } + onSubmit(json); + }); + }; + await doFetch(); + } else { try { - this.preprocessor(build); + await this.onSubmit(build, build); } catch (e) { if (e instanceof FormError) { this.handleError(e); } return; } - if (this.fetchURL !== "") { - const onSubmit = async (json: any) => { - try { - await this.onSubmit(json, build); - } catch (e) { - console.error(e); - if (e instanceof FormError) { - this.handleError(e); - } - return; - } - }; - const doFetch = async () => { - fetch(this.fetchURL, { - method: this.method, - body: JSON.stringify(build), - headers: this.headers, - }) - .then(async (_) => { - return [await _.text(), _.status] as const; - }) - .then(([_, status]) => { - if (_ === "") return [null, status]; - return [JSON.parse(_), status] as const; - }) - .then(async ([json, status]) => { - if (await handleCaptcha(json, build, this.captcha)) { - return await doFetch(); - } - const match = this.fetchURL.match(/https?:\/\/[^\/]*\/api/gm); - if (match && this.tfaCheck) { - const tried = await handle2fa(json, match[0]); - if (tried) { - return await onSubmit(tried); - } - } - if (json.ticket) { - } - if (json.errors) { - if (this.errors(json)) { - return; - } - } else if (status === 500) { - this.error( - [...this.names].at(-1)?.[0] ?? "", - json.message ?? "internal server error", - ); - return; - } - if ( - Math.floor(json.code / 100) === 4 && - json.message && - typeof json.message === "string" - ) { - this.showPrimError(json.message); - return; - } - onSubmit(json); - }); - }; - doFetch(); - } else { - try { - await this.onSubmit(build, build); - } catch (e) { - if (e instanceof FormError) { - this.handleError(e); - } - return; - } - } - console.warn("needs to be implemented"); - } finally { - this.subbmitting = false; } + console.warn("needs to be implemented"); } showPrimError(error: string) { const pop = new PopUp(error, {goAbove: true, buttons: popUpButtonTypes.dismiss}); diff --git a/src/webpage/utils/promiseLock.ts b/src/webpage/utils/promiseLock.ts index 1cf6e75..8dce997 100644 --- a/src/webpage/utils/promiseLock.ts +++ b/src/webpage/utils/promiseLock.ts @@ -1,10 +1,17 @@ export class PromiseLock { lastLock = Promise.resolve(); + private unlockedLock = this.lastLock; async acquireLock() { const {promise, resolve: res} = Promise.withResolvers(); const last = this.lastLock; this.lastLock = promise; await last; - return res; + return () => { + this.unlockedLock = promise; + res(); + }; + } + get locked() { + return this.lastLock !== this.unlockedLock; } }