refactor: page loadingOptions rework (@miodec, @fehmer) (#6949)

Co-authored-by: Miodec <jack@monkeytype.com>
This commit is contained in:
Christian Fehmer 2025-09-11 15:56:14 +02:00 committed by GitHub
parent 31a7a2de08
commit d1e118a01d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 51 additions and 29 deletions

View file

@ -170,10 +170,14 @@ export async function onAuthStateChanged(
await navigate(undefined, {
force: true,
loadingOptions: {
shouldLoad: () => {
return user !== null;
loadingMode: () => {
if (user !== null) {
return "sync";
} else {
return "none";
}
},
waitFor: async () => {
loadingPromise: async () => {
await userPromise;
},
style: "bar",

View file

@ -50,7 +50,7 @@ function updateTitle(nextPage: { id: string; display?: string }): void {
}
}
async function showLoading({
async function showSyncLoading({
loadingOptions,
totalDuration,
easingMethod,
@ -97,7 +97,7 @@ async function showLoading({
void PageLoading.updateBar(100, 125);
PageLoading.updateText("Done");
} else {
await options.waitFor();
await options.loadingPromise();
}
}
@ -123,7 +123,7 @@ async function getLoadingPromiseWithBarKeyframes(
fillOffset: number
): Promise<void> {
let aborted = false;
let loadingPromise = loadingOptions.waitFor();
let loadingPromise = loadingOptions.loadingPromise();
// Animate bar keyframes, but allow aborting if loading.promise finishes first
const keyframePromise = (async () => {
@ -214,28 +214,25 @@ export async function change(
previousPage.element.addClass("hidden");
await previousPage?.afterHide();
// we need to evaluate and store next page loading mode in case options.loadingOptions.loadingMode is sync
const nextPageLoadingMode = nextPage.loadingOptions?.loadingMode();
//show loading page if needed
try {
let loadingOptions: LoadingOptions[] = [];
if (options.loadingOptions) {
loadingOptions.push(options.loadingOptions);
let syncLoadingOptions: LoadingOptions[] = [];
if (options.loadingOptions?.loadingMode() === "sync") {
syncLoadingOptions.push(options.loadingOptions);
}
if (nextPage.loadingOptions) {
loadingOptions.push(nextPage.loadingOptions);
if (nextPage.loadingOptions?.loadingMode() === "sync") {
syncLoadingOptions.push(nextPage.loadingOptions);
}
if (loadingOptions.length > 0) {
const shouldShowLoading =
options.loadingOptions?.shouldLoad() ||
nextPage.loadingOptions?.shouldLoad();
if (shouldShowLoading === true) {
await showLoading({
loadingOptions,
totalDuration,
easingMethod,
});
}
if (syncLoadingOptions.length > 0) {
await showSyncLoading({
loadingOptions: syncLoadingOptions,
totalDuration,
easingMethod,
});
}
} catch (error) {
pages.loading.element.addClass("active");
@ -263,6 +260,17 @@ export async function change(
// @ts-expect-error for the future (i think)
data: options.data,
});
if (
typeof nextPageLoadingMode === "object" &&
nextPageLoadingMode.mode === "async"
) {
nextPageLoadingMode.beforeLoading();
void nextPage?.loadingOptions?.loadingPromise().then(() => {
nextPageLoadingMode.afterLoading();
});
}
nextPage.element.removeClass("hidden").css("opacity", 0);
await Misc.promiseAnimation(
nextPage.element,

View file

@ -1323,10 +1323,14 @@ export const page = new Page({
element: $(".page.pageAccount"),
path: "/account",
loadingOptions: {
shouldLoad: () => {
return DB.getSnapshot()?.results === undefined;
loadingMode: () => {
if (DB.getSnapshot()?.results === undefined) {
return "sync";
} else {
return "none";
}
},
waitFor: async () => {
loadingPromise: async () => {
if (DB.getSnapshot() === null) {
throw new Error(
"Looks like your account data didn't download correctly. Please refresh the page.<br>If this error persists, please contact support."

View file

@ -24,13 +24,19 @@ type Options<T> = {
export type LoadingOptions = {
/**
* Should the loading screen be shown?
* Get the loading mode for this page.
* "none" - No loading screen will be shown.
* "sync" - A loading spinner or bar (depending on style) will be shown until the page is ready.
* { mode: "async", beforeLoading, afterLoading } - The loadingPromise will be executed in the background and afterLoading called after it resolves.
*/
shouldLoad: () => boolean;
loadingMode: () =>
| "none"
| "sync"
| { mode: "async"; beforeLoading: () => void; afterLoading: () => void };
/**
* When this promise resolves, the loading screen will be hidden.
*/
waitFor: () => Promise<void>;
loadingPromise: () => Promise<void>;
} & (
| {
style: "spinner";