diff --git a/frontend/src/html/pages/loading.html b/frontend/src/html/pages/loading.html
index 1ac190570..60ed48da9 100644
--- a/frontend/src/html/pages/loading.html
+++ b/frontend/src/html/pages/loading.html
@@ -1,13 +1,12 @@
-
-
-
-
-
+
+
+
+
+
+
+
Loading...
diff --git a/frontend/src/styles/account.scss b/frontend/src/styles/account.scss
index c34cfb742..189fb9091 100644
--- a/frontend/src/styles/account.scss
+++ b/frontend/src/styles/account.scss
@@ -1,21 +1,6 @@
.pageAccount {
height: 100%;
- .error {
- display: grid;
- place-items: center;
- align-content: center;
- height: 100%;
- .icon {
- font-size: 2rem;
- color: var(--error-color);
- }
- .text {
- font-size: 1rem;
- text-align: center;
- }
- }
-
.accountVerificatinNotice {
background: var(--bg-color);
border-radius: var(--roundness);
diff --git a/frontend/src/styles/loading.scss b/frontend/src/styles/loading.scss
index d80c7fdbe..fbad0bed0 100644
--- a/frontend/src/styles/loading.scss
+++ b/frontend/src/styles/loading.scss
@@ -1,38 +1,40 @@
.pageLoading {
- .preloader {
- text-align: center;
+ text-align: center;
+ place-self: center;
+ align-content: center;
+ display: grid;
+ gap: 1rem;
+ width: 100%;
+
+ .spinner,
+ .error {
+ font-size: 2rem;
+ }
+
+ .spinner {
+ color: var(--main-color);
+ }
+
+ .error {
+ color: var(--error-color);
+ }
+
+ .text {
+ height: 1.25em;
+ }
+
+ .bar {
+ max-width: 20rem;
+ width: 100%;
+ height: 0.5rem;
+ background: var(--sub-alt-color);
+ border-radius: var(--roundness);
justify-self: center;
- display: grid;
- .barWrapper {
- justify-content: center;
- display: grid;
- gap: 1rem;
- grid-row: 1;
- grid-column: 1;
- .text {
- height: 1.25em;
- }
- .bar {
- width: 20rem;
- height: 0.5rem;
- background: var(--sub-alt-color);
- border-radius: var(--roundness);
- justify-self: center;
- .fill {
- height: 100%;
- width: 0%;
- background: var(--main-color);
- border-radius: var(--roundness);
- // transition: 1s;
- }
- }
- }
- .icon {
- grid-row: 1;
- grid-column: 1;
- font-size: 2rem;
- color: var(--main-color);
- margin-bottom: 1rem;
+ .fill {
+ height: 100%;
+ width: 50%;
+ background: var(--main-color);
+ border-radius: var(--roundness);
}
}
}
diff --git a/frontend/src/ts/controllers/account-controller.ts b/frontend/src/ts/controllers/account-controller.ts
index 59b7223f1..c74da7d49 100644
--- a/frontend/src/ts/controllers/account-controller.ts
+++ b/frontend/src/ts/controllers/account-controller.ts
@@ -6,7 +6,6 @@ import * as DB from "../db";
import * as Loader from "../elements/loader";
import * as LoginPage from "../pages/login";
import * as RegisterCaptchaModal from "../modals/register-captcha";
-import * as Account from "../pages/account";
import {
GoogleAuthProvider,
GithubAuthProvider,
@@ -97,9 +96,6 @@ async function getDataAndInit(): Promise
{
fb.functions.applyGlobalCSS();
}
}
- if (window.location.pathname === "/account") {
- await Account.downloadResults();
- }
return true;
} catch (error) {
console.error(error);
@@ -170,28 +166,10 @@ export async function onAuthStateChanged(
},
];
- if (
- window.location.pathname === "/account" ||
- window.location.pathname === "/login"
- ) {
- keyframes = [
- {
- percentage: 40,
- durationMs: 1000,
- text: "Downloading user data...",
- },
- {
- percentage: 90,
- durationMs: 1000,
- text: "Downloading results...",
- },
- ];
- }
-
//undefined means navigate to whatever the current window.location.pathname is
await navigate(undefined, {
force: true,
- overrideLoadingOptions: {
+ loadingOptions: {
shouldLoad: () => {
return user !== null;
},
diff --git a/frontend/src/ts/controllers/page-controller.ts b/frontend/src/ts/controllers/page-controller.ts
index 810a2bedc..8fcf84fed 100644
--- a/frontend/src/ts/controllers/page-controller.ts
+++ b/frontend/src/ts/controllers/page-controller.ts
@@ -21,7 +21,7 @@ type ChangeOptions = {
force?: boolean;
params?: Record;
data?: unknown;
- overrideLoadingOptions?: LoadingOptions;
+ loadingOptions?: LoadingOptions;
};
function updateOpenGraphUrl(): void {
@@ -50,11 +50,77 @@ function updateTitle(nextPage: { id: string; display?: string }): void {
}
}
+async function showLoading({
+ loadingOptions,
+ totalDuration,
+ easingMethod,
+}: {
+ loadingOptions: LoadingOptions[];
+ totalDuration: number;
+ easingMethod: Misc.JQueryEasing;
+}): Promise {
+ PageLoading.page.element.removeClass("hidden").css("opacity", 0);
+ await PageLoading.page.beforeShow({});
+
+ const fillDivider = loadingOptions.length;
+ const fillOffset = 100 / fillDivider;
+
+ //void here to run the loading promise as soon as possible
+ void Misc.promiseAnimation(
+ PageLoading.page.element,
+ {
+ opacity: "1",
+ },
+ totalDuration / 2,
+ easingMethod
+ );
+
+ for (let i = 0; i < loadingOptions.length; i++) {
+ const currentOffset = fillOffset * i;
+ const options = loadingOptions[i] as LoadingOptions;
+ if (options.style === "bar") {
+ await PageLoading.showBar();
+ if (i === 0) {
+ await PageLoading.updateBar(0, 0);
+ PageLoading.updateText("");
+ }
+ } else {
+ PageLoading.showSpinner();
+ }
+
+ if (options.style === "bar") {
+ await getLoadingPromiseWithBarKeyframes(
+ options,
+ fillDivider,
+ currentOffset
+ );
+ void PageLoading.updateBar(100, 125);
+ PageLoading.updateText("Done");
+ } else {
+ await options.waitFor();
+ }
+ }
+
+ await Misc.promiseAnimation(
+ PageLoading.page.element,
+ {
+ opacity: "0",
+ },
+ totalDuration / 2,
+ easingMethod
+ );
+
+ await PageLoading.page.afterHide();
+ PageLoading.page.element.addClass("hidden");
+}
+
async function getLoadingPromiseWithBarKeyframes(
loadingOptions: Extract<
NonNullable["loadingOptions"]>,
{ style: "bar" }
- >
+ >,
+ fillDivider: number,
+ fillOffset: number
): Promise {
let aborted = false;
let loadingPromise = loadingOptions.waitFor();
@@ -66,7 +132,10 @@ async function getLoadingPromiseWithBarKeyframes(
if (keyframe.text !== undefined) {
PageLoading.updateText(keyframe.text);
}
- await PageLoading.updateBar(keyframe.percentage, keyframe.durationMs);
+ await PageLoading.updateBar(
+ fillOffset + keyframe.percentage / fillDivider,
+ keyframe.durationMs
+ );
}
})();
@@ -145,58 +214,47 @@ export async function change(
previousPage.element.addClass("hidden");
await previousPage?.afterHide();
+ //show loading page if needed
+ try {
+ let loadingOptions: LoadingOptions[] = [];
+ if (options.loadingOptions) {
+ loadingOptions.push(options.loadingOptions);
+ }
+ if (nextPage.loadingOptions) {
+ loadingOptions.push(nextPage.loadingOptions);
+ }
+
+ if (loadingOptions.length > 0) {
+ const shouldShowLoading =
+ options.loadingOptions?.shouldLoad() ||
+ nextPage.loadingOptions?.shouldLoad();
+
+ if (shouldShowLoading === true) {
+ await showLoading({
+ loadingOptions,
+ totalDuration,
+ easingMethod,
+ });
+ }
+ }
+ } catch (error) {
+ pages.loading.element.addClass("active");
+ ActivePage.set(pages.loading.id);
+ Focus.set(false);
+ PageLoading.showError();
+ PageLoading.updateText(
+ `Failed to load the ${nextPage.id} page: ${
+ error instanceof Error ? error.message : String(error)
+ }`
+ );
+ PageTransition.set(false);
+ return false;
+ }
+
//between
updateTitle(nextPage);
ActivePage.set(nextPage.id);
updateOpenGraphUrl();
-
- const loadingOptions =
- options.overrideLoadingOptions ?? nextPage.loadingOptions;
-
- //show loading page if needed
- if (loadingOptions && loadingOptions.shouldLoad()) {
- pages.loading.element.removeClass("hidden").css("opacity", 0);
- await pages.loading.beforeShow({});
-
- if (loadingOptions.style === "bar") {
- await PageLoading.showBar();
- await PageLoading.updateBar(0, 0);
- PageLoading.updateText("");
- } else {
- PageLoading.showSpinner();
- }
-
- //void here to run the loading promise as soon as possible
- void Misc.promiseAnimation(
- pages.loading.element,
- {
- opacity: "1",
- },
- totalDuration / 2,
- easingMethod
- );
-
- if (loadingOptions.style === "bar") {
- await getLoadingPromiseWithBarKeyframes(loadingOptions);
- void PageLoading.updateBar(100, 125);
- PageLoading.updateText("Done");
- } else {
- await loadingOptions.waitFor();
- }
-
- await Misc.promiseAnimation(
- pages.loading.element,
- {
- opacity: "0",
- },
- totalDuration / 2,
- easingMethod
- );
-
- await pages.loading.afterHide();
- pages.loading.element.addClass("hidden");
- }
-
Focus.set(false);
//next page
diff --git a/frontend/src/ts/controllers/route-controller.ts b/frontend/src/ts/controllers/route-controller.ts
index d36372aa2..3a7f6e7ad 100644
--- a/frontend/src/ts/controllers/route-controller.ts
+++ b/frontend/src/ts/controllers/route-controller.ts
@@ -15,7 +15,7 @@ type NavigateOptions = {
force?: boolean;
empty?: boolean;
data?: unknown;
- overrideLoadingOptions?: LoadingOptions;
+ loadingOptions?: LoadingOptions;
};
function pathToRegex(path: string): RegExp {
diff --git a/frontend/src/ts/pages/account.ts b/frontend/src/ts/pages/account.ts
index cc5988dfb..f985e790d 100644
--- a/frontend/src/ts/pages/account.ts
+++ b/frontend/src/ts/pages/account.ts
@@ -987,26 +987,14 @@ export async function downloadResults(offset?: number): Promise {
}
}
-function showError(message: string): void {
- $(".pageAccount .error .text").html(message);
- $(".pageAccount .error").removeClass("hidden");
- $(".pageAccount .content").remove();
-}
-
async function update(): Promise {
- if (DB.getSnapshot() === null) {
- showError(
- "Looks like your account data didn't download correctly. Please refresh the page.
If this error persists, please contact support."
- );
- } else {
- await downloadResults();
- try {
- await Misc.sleep(0);
- await fillContent();
- } catch (e) {
- console.error(e);
- Notifications.add(`Something went wrong: ${e}`, -1);
- }
+ await downloadResults();
+ try {
+ await Misc.sleep(0);
+ await fillContent();
+ } catch (e) {
+ console.error(e);
+ Notifications.add(`Something went wrong: ${e}`, -1);
}
}
@@ -1344,12 +1332,19 @@ export const page = new Page({
shouldLoad: () => {
return DB.getSnapshot()?.results === undefined;
},
- waitFor: downloadResults,
+ waitFor: async () => {
+ if (DB.getSnapshot() === null) {
+ throw new Error(
+ "Looks like your account data didn't download correctly. Please refresh the page.
If this error persists, please contact support."
+ );
+ }
+ return downloadResults();
+ },
style: "bar",
keyframes: [
{
- percentage: 100,
- durationMs: 3000,
+ percentage: 90,
+ durationMs: 2000,
text: "Downloading results...",
},
],
diff --git a/frontend/src/ts/pages/loading.ts b/frontend/src/ts/pages/loading.ts
index 89eb3743c..18137583e 100644
--- a/frontend/src/ts/pages/loading.ts
+++ b/frontend/src/ts/pages/loading.ts
@@ -1,12 +1,19 @@
import Page from "./page";
import * as Skeleton from "../utils/skeleton";
+const pageEl = $(".page.pageLoading");
+const barEl = pageEl.find(".bar");
+const errorEl = pageEl.find(".error");
+const spinnerEl = pageEl.find(".spinner");
+const textEl = pageEl.find(".text");
+
export async function updateBar(
percentage: number,
duration: number
): Promise {
return new Promise((resolve) => {
- $(".pageLoading .fill")
+ barEl
+ .find(".fill")
.stop(true, false)
.animate(
{
@@ -21,22 +28,33 @@ export async function updateBar(
}
export function updateText(text: string): void {
- $(".pageLoading .text").text(text);
+ textEl.removeClass("hidden").html(text);
}
export function showSpinner(): void {
- $(".pageLoading .preloader .icon").removeClass("hidden");
- $(".pageLoading .preloader .barWrapper").addClass("hidden");
+ barEl.addClass("hidden");
+ errorEl.addClass("hidden");
+ spinnerEl.removeClass("hidden");
+ textEl.addClass("hidden");
+}
+
+export function showError(): void {
+ barEl.addClass("hidden");
+ spinnerEl.addClass("hidden");
+ errorEl.removeClass("hidden");
+ textEl.addClass("hidden");
}
export async function showBar(): Promise {
- $(".pageLoading .preloader .icon").addClass("hidden");
- $(".pageLoading .preloader .barWrapper").removeClass("hidden");
+ barEl.removeClass("hidden");
+ errorEl.addClass("hidden");
+ spinnerEl.addClass("hidden");
+ textEl.addClass("hidden");
}
export const page = new Page({
id: "loading",
- element: $(".page.pageLoading"),
+ element: pageEl,
path: "/",
afterHide: async (): Promise => {
Skeleton.remove("pageLoading");