diff --git a/scripts/ui.ts b/scripts/ui.ts index ec517d77..97fa1250 100644 --- a/scripts/ui.ts +++ b/scripts/ui.ts @@ -19,6 +19,7 @@ import { getCurrentAndReferrerDomains, INITIAL_PORTAL, PORTAL_LOGIN_COMPLETE_SENTINEL_KEY, + PORTAL_LOGIN_COMPLETE_SUCCESS_VALUE, SEED_STORAGE_KEY, } from "../src/mysky"; import { @@ -465,35 +466,58 @@ async function setupAndRunDisplay(displayUrl: string, methodName: string, ... } /** - * Resolves when portal login on Main MySky completes. + * Resolves when portal login on Main MySky completes successfully. + * + * We register a storage event listener inside a promise that resolves the + * promise when we detect a successful portal login. The successful login is + * signaled via local storage. Any value other than "1" is considered to be an + * error message. If a successful login is not detected within a given timeout, + * then we reject the promise. * * @returns - An empty promise. */ async function resolveOnMySkyPortalLogin(): Promise { log("Entered resolveOnMySkyPortalLogin"); - return Promise.race([ - new Promise((resolve, reject) => - window.addEventListener("storage", async ({ key, newValue }: StorageEvent) => { - if (key !== PORTAL_LOGIN_COMPLETE_SENTINEL_KEY) { - return; - } - - if (!newValue) { - // Key was removed. - return; - } - - // Check for errors from Main MySky. - if (newValue !== "1") { - reject(newValue); - } - - resolve(); - }) - ), - new Promise((_, reject) => setTimeout(reject, MYSKY_PORTAL_LOGIN_TIMEOUT)), - ]); + const abortController = new AbortController(); + + // Set up a promise that succeeds on successful login in main MySky, and fails + // when the login attempt returns an error. + const promise1 = new Promise((resolve, reject) => { + const handleEvent = async ({ key, newValue }: StorageEvent) => { + if (key !== PORTAL_LOGIN_COMPLETE_SENTINEL_KEY) { + return; + } + if (!newValue) { + // Key was removed. + return; + } + + // Check for errors from Main MySky. + if (newValue !== PORTAL_LOGIN_COMPLETE_SUCCESS_VALUE) { + reject(newValue); + } + + // We got the value signaling a successful login, resolve the promise. + resolve(); + }; + + // Set up a storage event listener. + window.addEventListener("storage", handleEvent, { + signal: abortController.signal, + }); + }); + + // Set up promise that rejects on timeout. + const promise2 = new Promise((_, reject) => setTimeout(reject, MYSKY_PORTAL_LOGIN_TIMEOUT)); + + // Return when either promise finishes. Promise 1 returns when a login either + // fails or succeeds. Promise 2 returns when the execution time surpasses the + // timeout window. + return Promise.race([promise1, promise2]).finally(() => { + // Unregister the event listener. + abortController.abort(); + }); } // ======= diff --git a/src/mysky.ts b/src/mysky.ts index 3e4df29d..4f63b3c5 100644 --- a/src/mysky.ts +++ b/src/mysky.ts @@ -26,7 +26,7 @@ export const PORTAL_STORAGE_KEY = "portal"; export const SEED_STORAGE_KEY = "seed"; export const PORTAL_LOGIN_COMPLETE_SENTINEL_KEY = "portal-login-complete"; -const PORTAL_LOGIN_COMPLETE_SUCCESS_VALUE = "1"; +export const PORTAL_LOGIN_COMPLETE_SUCCESS_VALUE = "1"; export const INITIAL_PORTAL = "https://siasky.net";