From 6499cedc28329daf873840b7aa7fab84db3a06d5 Mon Sep 17 00:00:00 2001 From: Jack Date: Fri, 24 Jun 2022 22:12:52 +0200 Subject: [PATCH] Reworked route controller (#3220) miodec * rewrote route controller * showing loading by default * links which are not external need data line attribute * need to rewrite this * page controller takes a page as parameter removed page type * default export * going through the route controller instead of changing page directly * resolving all code paths * using navigate * added 404, leaderboards route * changed leaderboards button to a link * removed click handler * added page about route * removed default export, added settings page route * removing pointer events from everything inside links * navigating to account when on login page * fixed console logs, using async * added login and account pages * moved code to their own functions * allowing async functions * defaulting content visible * async * fixed 404 not navigating correctly * setting public path to root * fixed paths * using uid passed in through url params * added 404 page, profile routes * removed comment * moved discord link flow to url handler * allowing html * not resetting state * removed function * handling logo click * removed comments * reomoved comments * removed comments * removed comments * using new router * basic 404 page * buttons whicha are links have no underline * correctly handling the take me back button * updated button * removed comments * fixed account page profile link button * updated 404 * removed comments * removed comments * removed comments --- frontend/src/styles/404.scss | 26 + frontend/src/styles/core.scss | 8 + frontend/src/styles/index.scss | 8 +- frontend/src/styles/z_media-queries.scss | 12 + .../src/ts/controllers/account-controller.ts | 27 +- .../src/ts/controllers/input-controller.ts | 6 +- .../src/ts/controllers/page-controller.ts | 99 +- .../src/ts/controllers/route-controller.ts | 178 ++- .../src/ts/controllers/theme-controller.ts | 4 +- .../ts/controllers/verification-controller.ts | 48 - frontend/src/ts/elements/commandline-lists.ts | 4 +- frontend/src/ts/elements/leaderboards.ts | 12 +- frontend/src/ts/elements/version-check.ts | 1 - frontend/src/ts/pages/404.ts | 19 + frontend/src/ts/pages/about.ts | 10 +- frontend/src/ts/pages/account.ts | 1295 ++++++++--------- frontend/src/ts/pages/loading.ts | 8 +- frontend/src/ts/pages/login.ts | 8 +- frontend/src/ts/pages/page.ts | 20 +- frontend/src/ts/pages/profile.ts | 15 +- frontend/src/ts/pages/settings.ts | 4 +- frontend/src/ts/pages/test.ts | 4 +- frontend/src/ts/popups/custom-theme-popup.ts | 4 - frontend/src/ts/popups/quote-submit-popup.ts | 2 - frontend/src/ts/ready.ts | 48 +- frontend/src/ts/states/active-page.ts | 6 +- frontend/src/ts/test/result.ts | 4 +- frontend/src/ts/types/types.d.ts | 11 - frontend/src/ts/utils/misc.ts | 42 +- frontend/src/ts/utils/url-handler.ts | 50 +- frontend/static/html/head.html | 10 +- frontend/static/html/pages/404.html | 10 + frontend/static/html/pages/account.html | 4 +- frontend/static/html/pages/loading.html | 2 +- frontend/static/html/pages/test.html | 2 +- frontend/static/html/top.html | 11 +- frontend/static/images/monkeymeme.png | Bin 0 -> 387174 bytes frontend/static/main.html | 13 +- frontend/webpack/config.dev.js | 3 + 39 files changed, 1029 insertions(+), 1009 deletions(-) create mode 100644 frontend/src/styles/404.scss delete mode 100644 frontend/src/ts/controllers/verification-controller.ts create mode 100644 frontend/src/ts/pages/404.ts create mode 100644 frontend/static/html/pages/404.html create mode 100644 frontend/static/images/monkeymeme.png diff --git a/frontend/src/styles/404.scss b/frontend/src/styles/404.scss new file mode 100644 index 000000000..3e32f659b --- /dev/null +++ b/frontend/src/styles/404.scss @@ -0,0 +1,26 @@ +.page404 { + display: grid; + justify-content: center; + .content { + justify-items: center; + display: grid; + gap: 2rem; + width: min-content; + text-align: center; + .image { + width: 300px; + background-image: url("./../images/monkeymeme.png"); + aspect-ratio: 1210/800; + background-size: contain; + } + .big { + font-size: 10rem; + line-height: 10rem; + color: var(--sub-color); + } + .button { + padding: 1rem 2rem; + width: max-content; + } + } +} diff --git a/frontend/src/styles/core.scss b/frontend/src/styles/core.scss index 76ebb2ebd..a50480c21 100644 --- a/frontend/src/styles/core.scss +++ b/frontend/src/styles/core.scss @@ -59,6 +59,14 @@ a { } } +a[data-link] * { + pointer-events: none; +} + +a.button { + text-decoration: none; +} + body { margin: 0; padding: 0; diff --git a/frontend/src/styles/index.scss b/frontend/src/styles/index.scss index 71b00dc48..9e10e95ad 100644 --- a/frontend/src/styles/index.scss +++ b/frontend/src/styles/index.scss @@ -1,4 +1,4 @@ -@import "about", "account", "animations", "banners", "caret", "commandline", - "core", "footer", "inputs", "keymap", "leaderboards", "login", "monkey", "nav", - "notifications", "popups", "profile", "scroll", "settings", "test", - "z_media-queries"; +@import "404", "about", "account", "animations", "banners", "caret", + "commandline", "core", "footer", "inputs", "keymap", "leaderboards", "login", + "monkey", "nav", "notifications", "popups", "profile", "scroll", "settings", + "test", "z_media-queries"; diff --git a/frontend/src/styles/z_media-queries.scss b/frontend/src/styles/z_media-queries.scss index 32e768b0c..350bda4ff 100644 --- a/frontend/src/styles/z_media-queries.scss +++ b/frontend/src/styles/z_media-queries.scss @@ -833,6 +833,18 @@ .pageLogin .side input { width: 90vw; } + .page404 { + .content { + width: 100%; + .image { + width: 100%; + } + .big { + font-size: 7rem; + line-height: 7rem; + } + } + } } @media (hover: none) and (pointer: coarse) { diff --git a/frontend/src/ts/controllers/account-controller.ts b/frontend/src/ts/controllers/account-controller.ts index 9f0821bec..fc9119904 100644 --- a/frontend/src/ts/controllers/account-controller.ts +++ b/frontend/src/ts/controllers/account-controller.ts @@ -2,13 +2,11 @@ import Ape from "../ape"; import * as Notifications from "../elements/notifications"; import Config, * as UpdateConfig from "../config"; import * as AccountButton from "../elements/account-button"; -import * as VerificationController from "./verification-controller"; import * as Misc from "../utils/misc"; import * as Settings from "../pages/settings"; import * as AllTimeStats from "../account/all-time-stats"; import * as DB from "../db"; import * as TestLogic from "../test/test-logic"; -import * as PageController from "./page-controller"; import * as PSA from "../elements/psa"; import * as Focus from "../test/focus"; import * as Loader from "../elements/loader"; @@ -49,6 +47,7 @@ import { hideFavoriteQuoteLength, showFavoriteQuoteLength, } from "../test/test-config"; +import { navigate } from "./route-controller"; export const gmailProvider = new GoogleAuthProvider(); let canCall = true; @@ -72,7 +71,7 @@ export function sendVerificationEmail(): void { export async function getDataAndInit(): Promise { try { console.log("getting account data"); - if (ActivePage.get() === "loading") { + if (window.location.pathname !== "/account") { LoadingPage.updateBar(90); } else { LoadingPage.updateBar(45); @@ -210,14 +209,16 @@ export async function getDataAndInit(): Promise { TagController.loadActiveFromLocalStorage(); ResultTagsPopup.updateButtons(); Settings.showAccountSection(); - if (ActivePage.get() === "account") { - Account.update(); + if (window.location.pathname === "/account") { + await Account.downloadResults(); } else { Focus.set(false); } - await PageController.change(undefined, true); - PageTransition.set(false); - console.log("account loading finished"); + if (window.location.pathname === "/login") { + navigate("/account"); + } else { + navigate(); + } return true; } @@ -247,10 +248,6 @@ export async function loadUser(user: UserType): Promise { // showFavouriteThemesAtTheTop(); - if (VerificationController.data !== null) { - VerificationController.verify(); - } - if (TestLogic.notSignedInLastResult !== null) { TestLogic.setNotSignedInUid(user.uid); @@ -271,6 +268,7 @@ export async function loadUser(user: UserType): Promise { const authListener = Auth.onAuthStateChanged(async function (user) { // await UpdateConfig.loadPromise; const search = window.location.search; + const hash = window.location.hash; console.log(`auth state changed, user ${user ? true : false}`); if (user) { await loadUser(user); @@ -281,7 +279,7 @@ const authListener = Auth.onAuthStateChanged(async function (user) { PageTransition.set(false); } if (!user) { - PageController.change(); + navigate(); setTimeout(() => { Focus.set(false); }, 125 / 2); @@ -289,6 +287,7 @@ const authListener = Auth.onAuthStateChanged(async function (user) { URLHandler.loadCustomThemeFromUrl(search); URLHandler.loadTestSettingsFromUrl(search); + URLHandler.linkDiscord(hash); if (/challenge_.+/g.test(window.location.pathname)) { Notifications.add( @@ -470,7 +469,7 @@ export function signOut(): void { AllTimeStats.clear(); Settings.hideAccountSection(); AccountButton.update(); - PageController.change("login"); + navigate("/login"); DB.setSnapshot(defaultSnap); $(".pageLogin .button").removeClass("disabled"); $(".pageLogin input").prop("disabled", false); diff --git a/frontend/src/ts/controllers/input-controller.ts b/frontend/src/ts/controllers/input-controller.ts index 8bd83dde7..adee497d0 100644 --- a/frontend/src/ts/controllers/input-controller.ts +++ b/frontend/src/ts/controllers/input-controller.ts @@ -13,7 +13,6 @@ import * as Caret from "../test/caret"; import * as ManualRestart from "../test/manual-restart-tracker"; import * as Notifications from "../elements/notifications"; import * as CustomText from "../test/custom-text"; -import * as PageController from "./page-controller"; import * as Settings from "../pages/settings"; import * as LayoutEmulator from "../test/layout-emulator"; import * as PaceCaret from "../test/pace-caret"; @@ -27,6 +26,7 @@ import * as ActivePage from "../states/active-page"; import * as TestActive from "../states/test-active"; import * as TestInput from "../test/test-input"; import * as TestWords from "../test/test-words"; +import { navigate } from "./route-controller"; let dontInsertSpace = false; let correctShiftUsed = true; @@ -620,7 +620,7 @@ function handleTab(event: JQuery.KeyDownEvent, popupVisible: boolean): void { // change page if not on test page if (ActivePage.get() !== "test") { - PageController.change("test"); + navigate("/"); return; } @@ -702,7 +702,7 @@ $(document).keydown(async (event) => { // change page if not on test page if (ActivePage.get() !== "test") { - PageController.change("test"); + navigate("/"); return; } diff --git a/frontend/src/ts/controllers/page-controller.ts b/frontend/src/ts/controllers/page-controller.ts index 36ddcb589..6ab49b3e8 100644 --- a/frontend/src/ts/controllers/page-controller.ts +++ b/frontend/src/ts/controllers/page-controller.ts @@ -2,72 +2,33 @@ import * as Misc from "../utils/misc"; import * as ActivePage from "../states/active-page"; import * as Settings from "../pages/settings"; import * as Account from "../pages/account"; -import * as ManualRestart from "../test/manual-restart-tracker"; import * as PageTest from "../pages/test"; import * as PageAbout from "../pages/about"; import * as PageLogin from "../pages/login"; import * as PageLoading from "../pages/loading"; import * as PageProfile from "../pages/profile"; +import * as Page404 from "../pages/404"; import * as PageTransition from "../states/page-transition"; -import { Auth } from "../firebase"; +import type Page from "../pages/page"; export async function change( - page?: MonkeyTypes.Page | "", - force = false -): Promise { + page: Page, + force = false, + params?: { [key: string]: string } +): Promise { return new Promise((resolve) => { if (PageTransition.get()) { - console.log(`change page ${page} stopped`); - return; + console.log(`change page ${page.name} stopped`); + return resolve(false); } - console.log(`change page ${page}`); + console.log(`change page ${page.name}`); - if (page === "") page = "test"; - if (page == undefined) { - //use window loacation - const pages: { - [key: string]: MonkeyTypes.Page; - } = { - "/": "test", - "/login": "login", - "/settings": "settings", - "/about": "about", - "/account": "account", - "/profile": "profile", - }; - let path = pages[window.location.pathname as keyof typeof pages]; - if (!path) { - path = "test"; - } - page = path; - - if (Auth.currentUser && page === "login") { - page = "account"; - } - - if ( - !Auth.currentUser && - window.location.search === "" && - page === "profile" - ) { - page = "login"; - } + if (!force && ActivePage.get() === page.name) { + console.log(`page ${page.name} already active`); + return resolve(false); } - if ( - Auth.currentUser && - window.location.pathname === "/profile" && - window.location.search === "" - ) { - page = "account"; - } - - if (!force && ActivePage.get() === page) { - console.log(`page ${page} already active`); - return; - } - - const pages = { + const pages: Record = { loading: PageLoading.page, test: PageTest.page, settings: Settings.page, @@ -75,52 +36,30 @@ export async function change( account: Account.page, login: PageLogin.page, profile: PageProfile.page, + 404: Page404.page, }; - const previousPage = pages[ActivePage.get() as MonkeyTypes.Page]; - const nextPage = pages[page]; - - const historyUrl = - nextPage.pathname + - (nextPage.pathname === "/profile" ? window.location.search : ""); + const previousPage = pages[ActivePage.get()]; + const nextPage = page; previousPage?.beforeHide(); PageTransition.set(true); - ActivePage.set(undefined); $(".page").removeClass("active"); Misc.swapElements( previousPage.element, nextPage.element, 250, - () => { + async () => { PageTransition.set(false); ActivePage.set(nextPage.name); previousPage?.afterHide(); nextPage.element.addClass("active"); - resolve(); - history.pushState(nextPage.pathname, "", historyUrl); + resolve(true); nextPage?.afterShow(); }, async () => { - await nextPage?.beforeShow(); + await nextPage?.beforeShow(params); } ); }); } - -$(document).on("click", "#top .logo", () => { - change("test"); -}); - -$(document).on("click", "#top #menu .text-button", (e) => { - if (!$(e.currentTarget).hasClass("leaderboards")) { - const href = $(e.currentTarget).attr("href") as string; - ManualRestart.set(); - change(href.replace("/", "") as MonkeyTypes.Page); - } - return false; -}); - -$(".pageTest .loginTip .link").on("click", async () => { - change("login"); -}); diff --git a/frontend/src/ts/controllers/route-controller.ts b/frontend/src/ts/controllers/route-controller.ts index 1f3e8b84a..217b5d64d 100644 --- a/frontend/src/ts/controllers/route-controller.ts +++ b/frontend/src/ts/controllers/route-controller.ts @@ -1,56 +1,142 @@ -// import * as Funbox from "../test/funbox"; import * as PageController from "./page-controller"; -// import Config from "../config"; +import * as PageTest from "../pages/test"; +import * as PageAbout from "../pages/about"; +import * as PageSettings from "../pages/settings"; +import * as PageAccount from "../pages/account"; +import * as PageLogin from "../pages/login"; +import * as Page404 from "../pages/404"; +import * as PageProfile from "../pages/profile"; +import * as Leaderboards from "../elements/leaderboards"; import * as ActivePage from "../states/active-page"; import { Auth } from "../firebase"; -const mappedRoutes = { - "/": "pageLoading", - "/login": "pageLoading", - "/settings": "pageLoading", - "/about": "pageLoading", - "/account": "pageAccount", - "/verify": "pageLoading", - "/profile": "pageLoading", -}; +//source: https://www.youtube.com/watch?v=OstALBk-jTc +// https://www.youtube.com/watch?v=OstALBk-jTc -export function handleInitialPageClasses(pathname: string): void { - if (!mappedRoutes[pathname as keyof typeof mappedRoutes]) { - pathname = "/"; - } - const el = $(".page." + mappedRoutes[pathname as keyof typeof mappedRoutes]); - $(el).removeClass("hidden"); - $(el).addClass("active"); - let pageName = "loading"; - if (pathname === "/account") pageName = "account"; - ActivePage.set(pageName as MonkeyTypes.Page); +function pathToRegex(path: string): RegExp { + return new RegExp( + "^" + path.replace(/\//g, "\\/").replace(/:\w+/g, "(.+)") + "$" + ); } -// honestly im not sure what this does -// (function (history): void { -// const pushState = history.pushState; -// history.pushState = function (state): void { -// if (Config.funbox === "memory" && state !== "/") { -// Funbox.resetMemoryTimer(); -// } -// // @ts-ignore -// return pushState.apply(history, arguments); -// }; -// })(window.history); +function getParams(match: { route: Route; result: RegExpMatchArray }): { + [key: string]: string; +} { + const values = match.result.slice(1); + const keys = Array.from(match.route.path.matchAll(/:(\w+)/g)).map( + (result) => result[1] + ); -$(window).on("popstate", (e) => { - const state = (e.originalEvent as unknown as PopStateEvent).state; - if (state == "" || state == "/") { - // show test - PageController.change("test"); - } else if (state == "about") { - // show about - PageController.change("about"); - } else if (state === "account" || state === "login") { - if (Auth.currentUser) { - PageController.change("account"); - } else { - PageController.change("login"); - } + return Object.fromEntries(keys.map((key, index) => [key, values[index]])); +} + +interface Route { + path: string; + load: (params: { [key: string]: string }) => void; +} + +const routes: Route[] = [ + { + path: "/", + load: (): void => { + PageController.change(PageTest.page); + }, + }, + { + path: "/verify", + load: (): void => { + PageController.change(PageTest.page); + }, + }, + { + path: "/leaderboards", + load: (): void => { + if (ActivePage.get() === "loading") { + PageController.change(PageTest.page); + } + Leaderboards.show(); + }, + }, + { + path: "/about", + load: (): void => { + PageController.change(PageAbout.page); + }, + }, + { + path: "/settings", + load: (): void => { + PageController.change(PageSettings.page); + }, + }, + { + path: "/login", + load: (): void => { + PageController.change(PageLogin.page); + }, + }, + { + path: "/account", + load: (): void => { + PageController.change(PageAccount.page); + }, + }, + { + path: "/profile", + load: (): void => { + if (Auth.currentUser) { + navigate("/account"); + } else { + navigate("/"); + } + }, + }, + { + path: "/profile/:uid", + load: (params): void => { + PageController.change(PageProfile.page, undefined, params); + }, + }, +]; + +export function navigate(url = window.location.pathname): void { + history.pushState(null, "", url); + router(); +} + +async function router(): Promise { + const matches = routes.map((r) => { + return { + route: r, + result: location.pathname.match(pathToRegex(r.path)), + }; + }); + + const match = matches.find((m) => m.result !== null) as { + route: Route; + result: RegExpMatchArray; + }; + + if (!match) { + PageController.change(Page404.page); + return; } + + match.route.load(getParams(match)); +} + +window.addEventListener("popstate", router); + +document.addEventListener("DOMContentLoaded", () => { + document.body.addEventListener("click", (e) => { + const target = e?.target as HTMLLinkElement; + if (target.matches("[data-link]") && target?.href) { + e.preventDefault(); + navigate(target.href); + } + }); +}); + +$("#top .logo").click(() => { + navigate("/"); }); diff --git a/frontend/src/ts/controllers/theme-controller.ts b/frontend/src/ts/controllers/theme-controller.ts index dfe07ad8a..24a0398c1 100644 --- a/frontend/src/ts/controllers/theme-controller.ts +++ b/frontend/src/ts/controllers/theme-controller.ts @@ -80,9 +80,9 @@ const loadStyle = async function (name: string): Promise { resolve(); }; if (name === "custom") { - link.href = `themes/serika_dark.css`; + link.href = `/./themes/serika_dark.css`; } else { - link.href = `themes/${name}.css`; + link.href = `/./themes/${name}.css`; } const headScript = document.querySelector("#currentTheme") as Element; diff --git a/frontend/src/ts/controllers/verification-controller.ts b/frontend/src/ts/controllers/verification-controller.ts deleted file mode 100644 index 178169ea7..000000000 --- a/frontend/src/ts/controllers/verification-controller.ts +++ /dev/null @@ -1,48 +0,0 @@ -import Ape from "../ape"; -import * as Notifications from "../elements/notifications"; -import * as Settings from "../pages/settings"; -import * as DB from "../db"; -import * as Loader from "../elements/loader"; -import * as AccountButton from "../elements/account-button"; - -interface Data { - accessToken: string; - tokenType: string; -} - -export let data: Data | null = null; - -export function set(val: Data): void { - data = val; -} - -export async function verify(): Promise { - if (data === null) return; - Loader.show(); - - const { accessToken, tokenType } = data; - - const response = await Ape.users.linkDiscord(tokenType, accessToken); - Loader.hide(); - - if (response.status !== 200) { - return Notifications.add("Failed to link Discord: " + response.message, -1); - } - - Notifications.add(response.message, 1); - - const snapshot = DB.getSnapshot(); - - const { discordId, discordAvatar } = response.data; - if (discordId) { - snapshot.discordId = discordId; - } else { - snapshot.discordAvatar = discordAvatar; - } - - DB.setSnapshot(snapshot); - - AccountButton.update(discordId, discordAvatar); - - Settings.updateDiscordSection(); -} diff --git a/frontend/src/ts/elements/commandline-lists.ts b/frontend/src/ts/elements/commandline-lists.ts index 169cde5c5..7d464c2bf 100644 --- a/frontend/src/ts/elements/commandline-lists.ts +++ b/frontend/src/ts/elements/commandline-lists.ts @@ -21,9 +21,9 @@ import * as ModesNotice from "../elements/modes-notice"; import * as ConfigEvent from "../observables/config-event"; import * as ShareTestSettingsPopup from "../popups/share-test-settings-popup"; import { Auth } from "../firebase"; -import * as PageController from "../controllers/page-controller"; import * as EditPresetPopup from "../popups/edit-preset-popup"; import * as EditTagPopup from "../popups/edit-tags-popup"; +import { navigate } from "../controllers/route-controller"; export let current: MonkeyTypes.CommandsGroup[] = []; @@ -2516,7 +2516,7 @@ Misc.getChallengeList().then((challenges) => { noIcon: true, display: challenge.display, exec: (): void => { - PageController.change("test"); + navigate("/"); ChallengeController.setup(challenge.name); TestLogic.restart(false, true); }, diff --git a/frontend/src/ts/elements/leaderboards.ts b/frontend/src/ts/elements/leaderboards.ts index b0087708f..3400fb224 100644 --- a/frontend/src/ts/elements/leaderboards.ts +++ b/frontend/src/ts/elements/leaderboards.ts @@ -6,8 +6,8 @@ import * as Notifications from "./notifications"; import format from "date-fns/format"; import { Auth } from "../firebase"; import differenceInSeconds from "date-fns/differenceInSeconds"; -import { change } from "../controllers/page-controller"; import { getHTMLById as getBadgeHTMLbyId } from "../controllers/badge-controller"; +import { navigate } from "../controllers/route-controller"; let currentTimeRange: "allTime" | "daily" = "allTime"; let currentLanguage = "english"; @@ -353,8 +353,7 @@ async function fillTable(lb: LbKey, prepend?: number): Promise { $(".entryName").on("click", (e) => { const uid = $(e.target).attr("uid"); if (uid) { - window.history.replaceState(null, "", "/profile?uid=" + uid); - change("profile", true); + navigate(`/profile/${uid}`); hide(); } }); @@ -793,13 +792,6 @@ $("#leaderboardsWrapper .showYesterdayButton").on("click", () => { update(); }); -$(document).on("click", "#top #menu .text-button", (e) => { - if ($(e.currentTarget).hasClass("leaderboards")) { - show(); - } - return false; -}); - $(document).on("keydown", (event) => { if (event.key === "Escape" && !$("#leaderboardsWrapper").hasClass("hidden")) { hide(); diff --git a/frontend/src/ts/elements/version-check.ts b/frontend/src/ts/elements/version-check.ts index c12f9e446..7923cdb9e 100644 --- a/frontend/src/ts/elements/version-check.ts +++ b/frontend/src/ts/elements/version-check.ts @@ -1,5 +1,4 @@ import * as Notifications from "./notifications"; -// import * as VersionPopup from "./version-popup"; function setMemory(v: string): void { window.localStorage.setItem("lastSeenVersion", v); diff --git a/frontend/src/ts/pages/404.ts b/frontend/src/ts/pages/404.ts new file mode 100644 index 000000000..6514bb931 --- /dev/null +++ b/frontend/src/ts/pages/404.ts @@ -0,0 +1,19 @@ +import Page from "./page"; + +export const page = new Page( + "404", + $(".page.page404"), + "/404", + async () => { + // + }, + async () => { + // + }, + async () => { + // + }, + async () => { + // + } +); diff --git a/frontend/src/ts/pages/about.ts b/frontend/src/ts/pages/about.ts index c13733bd4..dacf0d876 100644 --- a/frontend/src/ts/pages/about.ts +++ b/frontend/src/ts/pages/about.ts @@ -1,12 +1,12 @@ import * as Misc from "../utils/misc"; import Page from "./page"; -export function reset(): void { +function reset(): void { $(".pageAbout .contributors").empty(); $(".pageAbout .supporters").empty(); } -export async function fill(): Promise { +async function fill(): Promise { const supporters = await Misc.getSupportersList(); const contributors = await Misc.getContributorsList(); supporters.forEach((supporter) => { @@ -25,16 +25,16 @@ export const page = new Page( "about", $(".page.pageAbout"), "/about", - () => { + async () => { // }, async () => { reset(); }, - () => { + async () => { fill(); }, - () => { + async () => { // } ); diff --git a/frontend/src/ts/pages/account.ts b/frontend/src/ts/pages/account.ts index 69811b7a1..b6c9d3007 100644 --- a/frontend/src/ts/pages/account.ts +++ b/frontend/src/ts/pages/account.ts @@ -231,671 +231,654 @@ function applyHistorySmoothing(): void { smoothHistory(parseInt(smoothing)); } -export function update(): void { - function cont(): void { - LoadingPage.updateText("Displaying stats..."); - LoadingPage.updateBar(100); - console.log("updating account page"); - ThemeColors.update(); - ChartController.accountHistory.updateColors(); - ChartController.accountActivity.updateColors(); - AllTimeStats.update(); +function fillContent(): void { + LoadingPage.updateText("Displaying stats..."); + LoadingPage.updateBar(100); + console.log("updating account page"); + ThemeColors.update(); + ChartController.accountHistory.updateColors(); + ChartController.accountActivity.updateColors(); + AllTimeStats.update(); - const snapshot = DB.getSnapshot(); + const snapshot = DB.getSnapshot(); - PbTables.update(snapshot.personalBests); - Profile.update("account", snapshot); + PbTables.update(snapshot.personalBests); + Profile.update("account", snapshot); - chartData = []; - accChartData = []; - const wpmChartData: number[] = []; - visibleTableLines = 0; + chartData = []; + accChartData = []; + const wpmChartData: number[] = []; + visibleTableLines = 0; - let topWpm = 0; - let topMode = ""; - let testRestarts = 0; - let totalWpm = 0; - let testCount = 0; + let topWpm = 0; + let topMode = ""; + let testRestarts = 0; + let totalWpm = 0; + let testCount = 0; - let last10 = 0; - let wpmLast10total = 0; + let last10 = 0; + let wpmLast10total = 0; - let topAcc = 0; - let totalAcc = 0; - let totalAcc10 = 0; + let topAcc = 0; + let totalAcc = 0; + let totalAcc10 = 0; - const rawWpm = { - total: 0, - count: 0, - last10Total: 0, - last10Count: 0, - max: 0, + const rawWpm = { + total: 0, + count: 0, + last10Total: 0, + last10Count: 0, + max: 0, + }; + + // let totalSeconds = 0; + totalSecondsFiltered = 0; + + let topCons = 0; + let totalCons = 0; + let totalCons10 = 0; + let consCount = 0; + + interface ActivityChartData { + [key: number]: { + amount: number; + time: number; + totalWpm: number; }; - - // let totalSeconds = 0; - totalSecondsFiltered = 0; - - let topCons = 0; - let totalCons = 0; - let totalCons10 = 0; - let consCount = 0; - - interface ActivityChartData { - [key: number]: { - amount: number; - time: number; - totalWpm: number; - }; - } - - const activityChartData: ActivityChartData = {}; - - filteredResults = []; - $(".pageAccount .history table tbody").empty(); - DB.getSnapshot().results?.forEach( - (result: MonkeyTypes.Result) => { - // totalSeconds += tt; - - //apply filters - try { - let resdiff = result.difficulty; - if (resdiff == undefined) { - resdiff = "normal"; - } - if (!ResultFilters.getFilter("difficulty", resdiff)) { - if (filterDebug) { - console.log(`skipping result due to difficulty filter`, result); - } - return; - } - if (!ResultFilters.getFilter("mode", result.mode)) { - if (filterDebug) { - console.log(`skipping result due to mode filter`, result); - } - return; - } - - if (result.mode == "time") { - let timefilter: MonkeyTypes.Mode2Custom<"time"> = "custom"; - if ([15, 30, 60, 120].includes(parseInt(result.mode2 as string))) { - timefilter = result.mode2; - } - if (!ResultFilters.getFilter("time", timefilter)) { - if (filterDebug) { - console.log(`skipping result due to time filter`, result); - } - return; - } - } else if (result.mode == "words") { - let wordfilter: MonkeyTypes.Mode2Custom<"words"> = "custom"; - if ( - [10, 25, 50, 100, 200].includes(parseInt(result.mode2 as string)) - ) { - wordfilter = result.mode2; - } - if (!ResultFilters.getFilter("words", wordfilter)) { - if (filterDebug) { - console.log(`skipping result due to word filter`, result); - } - return; - } - } - - if (result.quoteLength != null) { - let filter: MonkeyTypes.QuoteModes | undefined = undefined; - if (result.quoteLength === 0) { - filter = "short"; - } else if (result.quoteLength === 1) { - filter = "medium"; - } else if (result.quoteLength === 2) { - filter = "long"; - } else if (result.quoteLength === 3) { - filter = "thicc"; - } - if ( - filter !== undefined && - !ResultFilters.getFilter("quoteLength", filter) - ) { - if (filterDebug) { - console.log( - `skipping result due to quoteLength filter`, - result - ); - } - return; - } - } - - let langFilter = ResultFilters.getFilter( - "language", - result.language ?? "english" - ); - - if ( - result.language === "english_expanded" && - ResultFilters.getFilter("language", "english_1k") - ) { - langFilter = true; - } - if (!langFilter) { - if (filterDebug) { - console.log(`skipping result due to language filter`, result); - } - return; - } - - let puncfilter: MonkeyTypes.Filter<"punctuation"> = "off"; - if (result.punctuation) { - puncfilter = "on"; - } - if (!ResultFilters.getFilter("punctuation", puncfilter)) { - if (filterDebug) { - console.log(`skipping result due to punctuation filter`, result); - } - return; - } - - let numfilter: MonkeyTypes.Filter<"numbers"> = "off"; - if (result.numbers) { - numfilter = "on"; - } - if (!ResultFilters.getFilter("numbers", numfilter)) { - if (filterDebug) { - console.log(`skipping result due to numbers filter`, result); - } - return; - } - - if (result.funbox === "none" || result.funbox === undefined) { - if (!ResultFilters.getFilter("funbox", "none")) { - if (filterDebug) { - console.log(`skipping result due to funbox filter`, result); - } - return; - } - } else { - if (!ResultFilters.getFilter("funbox", result.funbox)) { - if (filterDebug) { - console.log(`skipping result due to funbox filter`, result); - } - return; - } - } - - let tagHide = true; - if (result.tags === undefined || result.tags.length === 0) { - //no tags, show when no tag is enabled - if (DB.getSnapshot().tags?.length || 0 > 0) { - if (ResultFilters.getFilter("tags", "none")) tagHide = false; - } else { - tagHide = false; - } - } else { - //tags exist - const validTags = DB.getSnapshot().tags?.map((t) => t._id); - - if (validTags === undefined) return; - - result.tags.forEach((tag) => { - //check if i even need to check tags anymore - if (!tagHide) return; - //check if tag is valid - if (validTags?.includes(tag)) { - //tag valid, check if filter is on - if (ResultFilters.getFilter("tags", tag)) tagHide = false; - } else { - //tag not found in valid tags, meaning probably deleted - if (ResultFilters.getFilter("tags", "none")) tagHide = false; - } - }); - } - - if (tagHide) { - if (filterDebug) { - console.log(`skipping result due to tag filter`, result); - } - return; - } - - const timeSinceTest = Math.abs(result.timestamp - Date.now()) / 1000; - - let datehide = true; - - if ( - ResultFilters.getFilter("date", "all") || - (ResultFilters.getFilter("date", "last_day") && - timeSinceTest <= 86400) || - (ResultFilters.getFilter("date", "last_week") && - timeSinceTest <= 604800) || - (ResultFilters.getFilter("date", "last_month") && - timeSinceTest <= 2592000) || - (ResultFilters.getFilter("date", "last_3months") && - timeSinceTest <= 7776000) - ) { - datehide = false; - } - - if (datehide) { - if (filterDebug) { - console.log(`skipping result due to date filter`, result); - } - return; - } - - filteredResults.push(result); - } catch (e) { - Notifications.add( - "Something went wrong when filtering. Resetting filters.", - 0 - ); - console.log(result); - console.error(e); - ResultFilters.reset(); - ResultFilters.updateActive(); - update(); - } - //filters done - //======================================= - - const resultDate = new Date(result.timestamp); - resultDate.setSeconds(0); - resultDate.setMinutes(0); - resultDate.setHours(0); - resultDate.setMilliseconds(0); - const resultTimestamp = resultDate.getTime(); - - if (Object.keys(activityChartData).includes(String(resultTimestamp))) { - activityChartData[resultTimestamp].amount++; - activityChartData[resultTimestamp].time += - result.testDuration + - result.incompleteTestSeconds - - (result.afkDuration ?? 0); - activityChartData[resultTimestamp].totalWpm += result.wpm; - } else { - activityChartData[resultTimestamp] = { - amount: 1, - time: - result.testDuration + - result.incompleteTestSeconds - - (result.afkDuration ?? 0), - totalWpm: result.wpm, - }; - } - - let tt = 0; - if ( - result.testDuration == undefined && - result.mode2 !== "custom" && - result.mode2 !== "zen" - ) { - //test finished before testDuration field was introduced - estimate - if (result.mode == "time") { - tt = result.mode2; - } else if (result.mode == "words") { - tt = (result.mode2 / result.wpm) * 60; - } - } else { - tt = parseFloat(result.testDuration as unknown as string); //legacy results could have a string here - } - if (result.incompleteTestSeconds != undefined) { - tt += result.incompleteTestSeconds; - } else if ( - result.restartCount != undefined && - result.restartCount > 0 - ) { - tt += (tt / 4) * result.restartCount; - } - - // if (result.incompleteTestSeconds != undefined) { - // tt += result.incompleteTestSeconds; - // } else if (result.restartCount != undefined && result.restartCount > 0) { - // tt += (tt / 4) * result.restartCount; - // } - totalSecondsFiltered += tt; - - if (last10 < 10) { - last10++; - wpmLast10total += result.wpm; - totalAcc10 += result.acc; - result.consistency !== undefined - ? (totalCons10 += result.consistency) - : 0; - } - testCount++; - - if (result.consistency !== undefined) { - consCount++; - totalCons += result.consistency; - if (result.consistency > topCons) { - topCons = result.consistency; - } - } - - if (result.rawWpm != null) { - if (rawWpm.last10Count < 10) { - rawWpm.last10Count++; - rawWpm.last10Total += result.rawWpm; - } - rawWpm.total += result.rawWpm; - rawWpm.count++; - if (result.rawWpm > rawWpm.max) { - rawWpm.max = result.rawWpm; - } - } - - if (result.acc > topAcc) { - topAcc = result.acc; - } - - totalAcc += result.acc; - - if (result.restartCount != undefined) { - testRestarts += result.restartCount; - } - - chartData.push({ - x: result.timestamp, - y: Config.alwaysShowCPM ? Misc.roundTo2(result.wpm * 5) : result.wpm, - wpm: Config.alwaysShowCPM - ? Misc.roundTo2(result.wpm * 5) - : result.wpm, - acc: result.acc, - mode: result.mode, - mode2: result.mode2, - punctuation: result.punctuation as boolean, - language: result.language, - timestamp: result.timestamp, - difficulty: result.difficulty, - raw: Config.alwaysShowCPM - ? Misc.roundTo2(result.rawWpm * 5) - : result.rawWpm, - }); - - wpmChartData.push(result.wpm); - - accChartData.push({ - x: result.timestamp, - y: 100 - result.acc, - errorRate: 100 - result.acc, - }); - - if (result.wpm > topWpm) { - const puncsctring = result.punctuation ? ",
with punctuation" : ""; - const numbsctring = result.numbers - ? ",
" + (result.punctuation ? "&" : "") + "with numbers" - : ""; - topWpm = result.wpm; - if (result.mode == "custom") topMode = result.mode; - else { - topMode = - result.mode + " " + result.mode2 + puncsctring + numbsctring; - } - } - - totalWpm += result.wpm; - } - ); - - if (Config.alwaysShowCPM) { - $(".pageAccount .group.history table thead tr td:nth-child(2)").text( - "cpm" - ); - } else { - $(".pageAccount .group.history table thead tr td:nth-child(2)").text( - "wpm" - ); - } - - loadMoreLines(); - //////// - - const activityChartData_amount: MonkeyTypes.ActivityChartDataPoint[] = []; - const activityChartData_time: MonkeyTypes.ActivityChartDataPoint[] = []; - const activityChartData_avgWpm: MonkeyTypes.ActivityChartDataPoint[] = []; - // let lastTimestamp = 0; - Object.keys(activityChartData).forEach((date) => { - const dateInt = parseInt(date); - activityChartData_amount.push({ - x: dateInt, - y: activityChartData[dateInt].amount, - }); - activityChartData_time.push({ - x: dateInt, - y: Misc.roundTo2(activityChartData[dateInt].time), - amount: activityChartData[dateInt].amount, - }); - activityChartData_avgWpm.push({ - x: dateInt, - y: Misc.roundTo2( - (Config.alwaysShowCPM - ? activityChartData[dateInt].totalWpm * 5 - : activityChartData[dateInt].totalWpm) / - activityChartData[dateInt].amount - ), - }); - // lastTimestamp = date; - }); - - const accountActivityScaleOptions = ( - ChartController.accountActivity.options as ScaleChartOptions< - "bar" | "line" - > - ).scales; - - if (Config.alwaysShowCPM) { - accountActivityScaleOptions["avgWpm"].title.text = "Average Cpm"; - } else { - accountActivityScaleOptions["avgWpm"].title.text = "Average Wpm"; - } - - ChartController.accountActivity.data.datasets[0].data = - activityChartData_time; - ChartController.accountActivity.data.datasets[1].data = - activityChartData_avgWpm; - - const accountHistoryScaleOptions = ( - ChartController.accountHistory.options as ScaleChartOptions<"line"> - ).scales; - - if (Config.alwaysShowCPM) { - accountHistoryScaleOptions["wpm"].title.text = "Characters per Minute"; - } else { - accountHistoryScaleOptions["wpm"].title.text = "Words per Minute"; - } - - ChartController.accountHistory.data.datasets[0].data = chartData; - ChartController.accountHistory.data.datasets[1].data = accChartData; - - const wpms = chartData.map((r) => r.y); - const minWpmChartVal = Math.min(...wpms); - const maxWpmChartVal = Math.max(...wpms); - - // let accuracies = accChartData.map((r) => r.y); - accountHistoryScaleOptions["wpm"].max = - Math.floor(maxWpmChartVal) + (10 - (Math.floor(maxWpmChartVal) % 10)); - - if (!Config.startGraphsAtZero) { - accountHistoryScaleOptions["wpm"].min = Math.floor(minWpmChartVal); - } else { - accountHistoryScaleOptions["wpm"].min = 0; - } - - if (chartData == [] || chartData.length == 0) { - $(".pageAccount .group.noDataError").removeClass("hidden"); - $(".pageAccount .group.chart").addClass("hidden"); - $(".pageAccount .group.dailyActivityChart").addClass("hidden"); - $(".pageAccount .group.history").addClass("hidden"); - $(".pageAccount .triplegroup.stats").addClass("hidden"); - } else { - $(".pageAccount .group.noDataError").addClass("hidden"); - $(".pageAccount .group.chart").removeClass("hidden"); - $(".pageAccount .group.dailyActivityChart").removeClass("hidden"); - $(".pageAccount .group.history").removeClass("hidden"); - $(".pageAccount .triplegroup.stats").removeClass("hidden"); - } - - $(".pageAccount .timeTotalFiltered .val").text( - Misc.secondsToString(Math.round(totalSecondsFiltered), true, true) - ); - - if (Config.alwaysShowCPM) { - $(".pageAccount .highestWpm .title").text("highest cpm"); - $(".pageAccount .highestWpm .val").text(Misc.roundTo2(topWpm * 5)); - } else { - $(".pageAccount .highestWpm .title").text("highest wpm"); - $(".pageAccount .highestWpm .val").text(Misc.roundTo2(topWpm)); - } - - if (Config.alwaysShowCPM) { - $(".pageAccount .averageWpm .title").text("average cpm"); - $(".pageAccount .averageWpm .val").text( - Math.round((totalWpm * 5) / testCount) - ); - } else { - $(".pageAccount .averageWpm .title").text("average wpm"); - $(".pageAccount .averageWpm .val").text(Math.round(totalWpm / testCount)); - } - - if (Config.alwaysShowCPM) { - $(".pageAccount .averageWpm10 .title").text( - "average cpm (last 10 tests)" - ); - $(".pageAccount .averageWpm10 .val").text( - Math.round((wpmLast10total * 5) / last10) - ); - } else { - $(".pageAccount .averageWpm10 .title").text( - "average wpm (last 10 tests)" - ); - $(".pageAccount .averageWpm10 .val").text( - Math.round(wpmLast10total / last10) - ); - } - - if (Config.alwaysShowCPM) { - $(".pageAccount .highestRaw .title").text("highest raw cpm"); - $(".pageAccount .highestRaw .val").text(Misc.roundTo2(rawWpm.max * 5)); - } else { - $(".pageAccount .highestRaw .title").text("highest raw wpm"); - $(".pageAccount .highestRaw .val").text(Misc.roundTo2(rawWpm.max)); - } - - if (Config.alwaysShowCPM) { - $(".pageAccount .averageRaw .title").text("average raw cpm"); - $(".pageAccount .averageRaw .val").text( - Math.round((rawWpm.total * 5) / rawWpm.count) - ); - } else { - $(".pageAccount .averageRaw .title").text("average raw wpm"); - $(".pageAccount .averageRaw .val").text( - Math.round(rawWpm.total / rawWpm.count) - ); - } - - if (Config.alwaysShowCPM) { - $(".pageAccount .averageRaw10 .title").text( - "average raw cpm (last 10 tests)" - ); - $(".pageAccount .averageRaw10 .val").text( - Math.round((rawWpm.last10Total * 5) / rawWpm.last10Count) - ); - } else { - $(".pageAccount .averageRaw10 .title").text( - "average raw wpm (last 10 tests)" - ); - $(".pageAccount .averageRaw10 .val").text( - Math.round(rawWpm.last10Total / rawWpm.last10Count) - ); - } - - $(".pageAccount .highestWpm .mode").html(topMode); - $(".pageAccount .testsTaken .val").text(testCount); - - $(".pageAccount .highestAcc .val").text(topAcc + "%"); - $(".pageAccount .avgAcc .val").text(Math.round(totalAcc / testCount) + "%"); - $(".pageAccount .avgAcc10 .val").text( - Math.round(totalAcc10 / last10) + "%" - ); - - if (totalCons == 0 || totalCons == undefined) { - $(".pageAccount .avgCons .val").text("-"); - $(".pageAccount .avgCons10 .val").text("-"); - } else { - $(".pageAccount .highestCons .val").text(topCons + "%"); - $(".pageAccount .avgCons .val").text( - Math.round(totalCons / consCount) + "%" - ); - $(".pageAccount .avgCons10 .val").text( - Math.round(totalCons10 / Math.min(last10, consCount)) + "%" - ); - } - $(".pageAccount .testsStarted .val").text(`${testCount + testRestarts}`); - $(".pageAccount .testsCompleted .val").text( - `${testCount}(${Math.floor( - (testCount / (testCount + testRestarts)) * 100 - )}%)` - ); - - $(".pageAccount .testsCompleted .avgres").text(` - ${(testRestarts / testCount).toFixed(1)} restarts per completed test - `); - - const wpmPoints = filteredResults.map((r) => r.wpm).reverse(); - - const trend = Misc.findLineByLeastSquares(wpmPoints); - - const wpmChange = trend[1][1] - trend[0][1]; - - const wpmChangePerHour = wpmChange * (3600 / totalSecondsFiltered); - - const plus = wpmChangePerHour > 0 ? "+" : ""; - - $(".pageAccount .group.chart .below .text").text( - `Speed change per hour spent typing: ${ - plus + - Misc.roundTo2( - Config.alwaysShowCPM ? wpmChangePerHour * 5 : wpmChangePerHour - ) - } ${Config.alwaysShowCPM ? "cpm" : "wpm"}.` - ); - - applyHistorySmoothing(); - ChartController.accountActivity.updateColors(); - LoadingPage.updateBar(100, true); - setTimeout(() => { - if (ActivePage.get() == "account") SignOutButton.show(); - }, 125); - Focus.set(false); - Misc.swapElements( - $(".pageAccount .preloader"), - $(".pageAccount .content"), - 250, - () => { - // Profile.updateNameFontSize("account"); - }, - () => { - setTimeout(() => { - Profile.updateNameFontSize("account"); - }, 1); - } - ); } - if (DB.getSnapshot() === null) { - Notifications.add(`Missing account data. Please refresh.`, -1); - $(".pageAccount .preloader").html("Missing account data. Please refresh."); - } else if (DB.getSnapshot().results === undefined) { - LoadingPage.updateBar(45, true); - DB.getUserResults().then((d: boolean) => { - TodayTracker.addAllFromToday(); - if (d) { + + const activityChartData: ActivityChartData = {}; + + filteredResults = []; + $(".pageAccount .history table tbody").empty(); + DB.getSnapshot().results?.forEach( + (result: MonkeyTypes.Result) => { + // totalSeconds += tt; + + //apply filters + try { + let resdiff = result.difficulty; + if (resdiff == undefined) { + resdiff = "normal"; + } + if (!ResultFilters.getFilter("difficulty", resdiff)) { + if (filterDebug) { + console.log(`skipping result due to difficulty filter`, result); + } + return; + } + if (!ResultFilters.getFilter("mode", result.mode)) { + if (filterDebug) { + console.log(`skipping result due to mode filter`, result); + } + return; + } + + if (result.mode == "time") { + let timefilter: MonkeyTypes.Mode2Custom<"time"> = "custom"; + if ([15, 30, 60, 120].includes(parseInt(result.mode2 as string))) { + timefilter = result.mode2; + } + if (!ResultFilters.getFilter("time", timefilter)) { + if (filterDebug) { + console.log(`skipping result due to time filter`, result); + } + return; + } + } else if (result.mode == "words") { + let wordfilter: MonkeyTypes.Mode2Custom<"words"> = "custom"; + if ( + [10, 25, 50, 100, 200].includes(parseInt(result.mode2 as string)) + ) { + wordfilter = result.mode2; + } + if (!ResultFilters.getFilter("words", wordfilter)) { + if (filterDebug) { + console.log(`skipping result due to word filter`, result); + } + return; + } + } + + if (result.quoteLength != null) { + let filter: MonkeyTypes.QuoteModes | undefined = undefined; + if (result.quoteLength === 0) { + filter = "short"; + } else if (result.quoteLength === 1) { + filter = "medium"; + } else if (result.quoteLength === 2) { + filter = "long"; + } else if (result.quoteLength === 3) { + filter = "thicc"; + } + if ( + filter !== undefined && + !ResultFilters.getFilter("quoteLength", filter) + ) { + if (filterDebug) { + console.log(`skipping result due to quoteLength filter`, result); + } + return; + } + } + + let langFilter = ResultFilters.getFilter( + "language", + result.language ?? "english" + ); + + if ( + result.language === "english_expanded" && + ResultFilters.getFilter("language", "english_1k") + ) { + langFilter = true; + } + if (!langFilter) { + if (filterDebug) { + console.log(`skipping result due to language filter`, result); + } + return; + } + + let puncfilter: MonkeyTypes.Filter<"punctuation"> = "off"; + if (result.punctuation) { + puncfilter = "on"; + } + if (!ResultFilters.getFilter("punctuation", puncfilter)) { + if (filterDebug) { + console.log(`skipping result due to punctuation filter`, result); + } + return; + } + + let numfilter: MonkeyTypes.Filter<"numbers"> = "off"; + if (result.numbers) { + numfilter = "on"; + } + if (!ResultFilters.getFilter("numbers", numfilter)) { + if (filterDebug) { + console.log(`skipping result due to numbers filter`, result); + } + return; + } + + if (result.funbox === "none" || result.funbox === undefined) { + if (!ResultFilters.getFilter("funbox", "none")) { + if (filterDebug) { + console.log(`skipping result due to funbox filter`, result); + } + return; + } + } else { + if (!ResultFilters.getFilter("funbox", result.funbox)) { + if (filterDebug) { + console.log(`skipping result due to funbox filter`, result); + } + return; + } + } + + let tagHide = true; + if (result.tags === undefined || result.tags.length === 0) { + //no tags, show when no tag is enabled + if (DB.getSnapshot().tags?.length || 0 > 0) { + if (ResultFilters.getFilter("tags", "none")) tagHide = false; + } else { + tagHide = false; + } + } else { + //tags exist + const validTags = DB.getSnapshot().tags?.map((t) => t._id); + + if (validTags === undefined) return; + + result.tags.forEach((tag) => { + //check if i even need to check tags anymore + if (!tagHide) return; + //check if tag is valid + if (validTags?.includes(tag)) { + //tag valid, check if filter is on + if (ResultFilters.getFilter("tags", tag)) tagHide = false; + } else { + //tag not found in valid tags, meaning probably deleted + if (ResultFilters.getFilter("tags", "none")) tagHide = false; + } + }); + } + + if (tagHide) { + if (filterDebug) { + console.log(`skipping result due to tag filter`, result); + } + return; + } + + const timeSinceTest = Math.abs(result.timestamp - Date.now()) / 1000; + + let datehide = true; + + if ( + ResultFilters.getFilter("date", "all") || + (ResultFilters.getFilter("date", "last_day") && + timeSinceTest <= 86400) || + (ResultFilters.getFilter("date", "last_week") && + timeSinceTest <= 604800) || + (ResultFilters.getFilter("date", "last_month") && + timeSinceTest <= 2592000) || + (ResultFilters.getFilter("date", "last_3months") && + timeSinceTest <= 7776000) + ) { + datehide = false; + } + + if (datehide) { + if (filterDebug) { + console.log(`skipping result due to date filter`, result); + } + return; + } + + filteredResults.push(result); + } catch (e) { + Notifications.add( + "Something went wrong when filtering. Resetting filters.", + 0 + ); + console.log(result); + console.error(e); + ResultFilters.reset(); ResultFilters.updateActive(); update(); } - }); + //filters done + //======================================= + + const resultDate = new Date(result.timestamp); + resultDate.setSeconds(0); + resultDate.setMinutes(0); + resultDate.setHours(0); + resultDate.setMilliseconds(0); + const resultTimestamp = resultDate.getTime(); + + if (Object.keys(activityChartData).includes(String(resultTimestamp))) { + activityChartData[resultTimestamp].amount++; + activityChartData[resultTimestamp].time += + result.testDuration + + result.incompleteTestSeconds - + (result.afkDuration ?? 0); + activityChartData[resultTimestamp].totalWpm += result.wpm; + } else { + activityChartData[resultTimestamp] = { + amount: 1, + time: + result.testDuration + + result.incompleteTestSeconds - + (result.afkDuration ?? 0), + totalWpm: result.wpm, + }; + } + + let tt = 0; + if ( + result.testDuration == undefined && + result.mode2 !== "custom" && + result.mode2 !== "zen" + ) { + //test finished before testDuration field was introduced - estimate + if (result.mode == "time") { + tt = result.mode2; + } else if (result.mode == "words") { + tt = (result.mode2 / result.wpm) * 60; + } + } else { + tt = parseFloat(result.testDuration as unknown as string); //legacy results could have a string here + } + if (result.incompleteTestSeconds != undefined) { + tt += result.incompleteTestSeconds; + } else if (result.restartCount != undefined && result.restartCount > 0) { + tt += (tt / 4) * result.restartCount; + } + + // if (result.incompleteTestSeconds != undefined) { + // tt += result.incompleteTestSeconds; + // } else if (result.restartCount != undefined && result.restartCount > 0) { + // tt += (tt / 4) * result.restartCount; + // } + totalSecondsFiltered += tt; + + if (last10 < 10) { + last10++; + wpmLast10total += result.wpm; + totalAcc10 += result.acc; + result.consistency !== undefined + ? (totalCons10 += result.consistency) + : 0; + } + testCount++; + + if (result.consistency !== undefined) { + consCount++; + totalCons += result.consistency; + if (result.consistency > topCons) { + topCons = result.consistency; + } + } + + if (result.rawWpm != null) { + if (rawWpm.last10Count < 10) { + rawWpm.last10Count++; + rawWpm.last10Total += result.rawWpm; + } + rawWpm.total += result.rawWpm; + rawWpm.count++; + if (result.rawWpm > rawWpm.max) { + rawWpm.max = result.rawWpm; + } + } + + if (result.acc > topAcc) { + topAcc = result.acc; + } + + totalAcc += result.acc; + + if (result.restartCount != undefined) { + testRestarts += result.restartCount; + } + + chartData.push({ + x: result.timestamp, + y: Config.alwaysShowCPM ? Misc.roundTo2(result.wpm * 5) : result.wpm, + wpm: Config.alwaysShowCPM ? Misc.roundTo2(result.wpm * 5) : result.wpm, + acc: result.acc, + mode: result.mode, + mode2: result.mode2, + punctuation: result.punctuation as boolean, + language: result.language, + timestamp: result.timestamp, + difficulty: result.difficulty, + raw: Config.alwaysShowCPM + ? Misc.roundTo2(result.rawWpm * 5) + : result.rawWpm, + }); + + wpmChartData.push(result.wpm); + + accChartData.push({ + x: result.timestamp, + y: 100 - result.acc, + errorRate: 100 - result.acc, + }); + + if (result.wpm > topWpm) { + const puncsctring = result.punctuation ? ",
with punctuation" : ""; + const numbsctring = result.numbers + ? ",
" + (result.punctuation ? "&" : "") + "with numbers" + : ""; + topWpm = result.wpm; + if (result.mode == "custom") topMode = result.mode; + else { + topMode = + result.mode + " " + result.mode2 + puncsctring + numbsctring; + } + } + + totalWpm += result.wpm; + } + ); + + if (Config.alwaysShowCPM) { + $(".pageAccount .group.history table thead tr td:nth-child(2)").text("cpm"); } else { - console.log("using db snap"); + $(".pageAccount .group.history table thead tr td:nth-child(2)").text("wpm"); + } + + loadMoreLines(); + //////// + + const activityChartData_amount: MonkeyTypes.ActivityChartDataPoint[] = []; + const activityChartData_time: MonkeyTypes.ActivityChartDataPoint[] = []; + const activityChartData_avgWpm: MonkeyTypes.ActivityChartDataPoint[] = []; + // let lastTimestamp = 0; + Object.keys(activityChartData).forEach((date) => { + const dateInt = parseInt(date); + activityChartData_amount.push({ + x: dateInt, + y: activityChartData[dateInt].amount, + }); + activityChartData_time.push({ + x: dateInt, + y: Misc.roundTo2(activityChartData[dateInt].time), + amount: activityChartData[dateInt].amount, + }); + activityChartData_avgWpm.push({ + x: dateInt, + y: Misc.roundTo2( + (Config.alwaysShowCPM + ? activityChartData[dateInt].totalWpm * 5 + : activityChartData[dateInt].totalWpm) / + activityChartData[dateInt].amount + ), + }); + // lastTimestamp = date; + }); + + const accountActivityScaleOptions = ( + ChartController.accountActivity.options as ScaleChartOptions<"bar" | "line"> + ).scales; + + if (Config.alwaysShowCPM) { + accountActivityScaleOptions["avgWpm"].title.text = "Average Cpm"; + } else { + accountActivityScaleOptions["avgWpm"].title.text = "Average Wpm"; + } + + ChartController.accountActivity.data.datasets[0].data = + activityChartData_time; + ChartController.accountActivity.data.datasets[1].data = + activityChartData_avgWpm; + + const accountHistoryScaleOptions = ( + ChartController.accountHistory.options as ScaleChartOptions<"line"> + ).scales; + + if (Config.alwaysShowCPM) { + accountHistoryScaleOptions["wpm"].title.text = "Characters per Minute"; + } else { + accountHistoryScaleOptions["wpm"].title.text = "Words per Minute"; + } + + ChartController.accountHistory.data.datasets[0].data = chartData; + ChartController.accountHistory.data.datasets[1].data = accChartData; + + const wpms = chartData.map((r) => r.y); + const minWpmChartVal = Math.min(...wpms); + const maxWpmChartVal = Math.max(...wpms); + + // let accuracies = accChartData.map((r) => r.y); + accountHistoryScaleOptions["wpm"].max = + Math.floor(maxWpmChartVal) + (10 - (Math.floor(maxWpmChartVal) % 10)); + + if (!Config.startGraphsAtZero) { + accountHistoryScaleOptions["wpm"].min = Math.floor(minWpmChartVal); + } else { + accountHistoryScaleOptions["wpm"].min = 0; + } + + if (chartData == [] || chartData.length == 0) { + $(".pageAccount .group.noDataError").removeClass("hidden"); + $(".pageAccount .group.chart").addClass("hidden"); + $(".pageAccount .group.dailyActivityChart").addClass("hidden"); + $(".pageAccount .group.history").addClass("hidden"); + $(".pageAccount .triplegroup.stats").addClass("hidden"); + } else { + $(".pageAccount .group.noDataError").addClass("hidden"); + $(".pageAccount .group.chart").removeClass("hidden"); + $(".pageAccount .group.dailyActivityChart").removeClass("hidden"); + $(".pageAccount .group.history").removeClass("hidden"); + $(".pageAccount .triplegroup.stats").removeClass("hidden"); + } + + $(".pageAccount .timeTotalFiltered .val").text( + Misc.secondsToString(Math.round(totalSecondsFiltered), true, true) + ); + + if (Config.alwaysShowCPM) { + $(".pageAccount .highestWpm .title").text("highest cpm"); + $(".pageAccount .highestWpm .val").text(Misc.roundTo2(topWpm * 5)); + } else { + $(".pageAccount .highestWpm .title").text("highest wpm"); + $(".pageAccount .highestWpm .val").text(Misc.roundTo2(topWpm)); + } + + if (Config.alwaysShowCPM) { + $(".pageAccount .averageWpm .title").text("average cpm"); + $(".pageAccount .averageWpm .val").text( + Math.round((totalWpm * 5) / testCount) + ); + } else { + $(".pageAccount .averageWpm .title").text("average wpm"); + $(".pageAccount .averageWpm .val").text(Math.round(totalWpm / testCount)); + } + + if (Config.alwaysShowCPM) { + $(".pageAccount .averageWpm10 .title").text("average cpm (last 10 tests)"); + $(".pageAccount .averageWpm10 .val").text( + Math.round((wpmLast10total * 5) / last10) + ); + } else { + $(".pageAccount .averageWpm10 .title").text("average wpm (last 10 tests)"); + $(".pageAccount .averageWpm10 .val").text( + Math.round(wpmLast10total / last10) + ); + } + + if (Config.alwaysShowCPM) { + $(".pageAccount .highestRaw .title").text("highest raw cpm"); + $(".pageAccount .highestRaw .val").text(Misc.roundTo2(rawWpm.max * 5)); + } else { + $(".pageAccount .highestRaw .title").text("highest raw wpm"); + $(".pageAccount .highestRaw .val").text(Misc.roundTo2(rawWpm.max)); + } + + if (Config.alwaysShowCPM) { + $(".pageAccount .averageRaw .title").text("average raw cpm"); + $(".pageAccount .averageRaw .val").text( + Math.round((rawWpm.total * 5) / rawWpm.count) + ); + } else { + $(".pageAccount .averageRaw .title").text("average raw wpm"); + $(".pageAccount .averageRaw .val").text( + Math.round(rawWpm.total / rawWpm.count) + ); + } + + if (Config.alwaysShowCPM) { + $(".pageAccount .averageRaw10 .title").text( + "average raw cpm (last 10 tests)" + ); + $(".pageAccount .averageRaw10 .val").text( + Math.round((rawWpm.last10Total * 5) / rawWpm.last10Count) + ); + } else { + $(".pageAccount .averageRaw10 .title").text( + "average raw wpm (last 10 tests)" + ); + $(".pageAccount .averageRaw10 .val").text( + Math.round(rawWpm.last10Total / rawWpm.last10Count) + ); + } + + $(".pageAccount .highestWpm .mode").html(topMode); + $(".pageAccount .testsTaken .val").text(testCount); + + $(".pageAccount .highestAcc .val").text(topAcc + "%"); + $(".pageAccount .avgAcc .val").text(Math.round(totalAcc / testCount) + "%"); + $(".pageAccount .avgAcc10 .val").text(Math.round(totalAcc10 / last10) + "%"); + + if (totalCons == 0 || totalCons == undefined) { + $(".pageAccount .avgCons .val").text("-"); + $(".pageAccount .avgCons10 .val").text("-"); + } else { + $(".pageAccount .highestCons .val").text(topCons + "%"); + $(".pageAccount .avgCons .val").text( + Math.round(totalCons / consCount) + "%" + ); + $(".pageAccount .avgCons10 .val").text( + Math.round(totalCons10 / Math.min(last10, consCount)) + "%" + ); + } + $(".pageAccount .testsStarted .val").text(`${testCount + testRestarts}`); + $(".pageAccount .testsCompleted .val").text( + `${testCount}(${Math.floor( + (testCount / (testCount + testRestarts)) * 100 + )}%)` + ); + + $(".pageAccount .testsCompleted .avgres").text(` + ${(testRestarts / testCount).toFixed(1)} restarts per completed test + `); + + const wpmPoints = filteredResults.map((r) => r.wpm).reverse(); + + const trend = Misc.findLineByLeastSquares(wpmPoints); + + const wpmChange = trend[1][1] - trend[0][1]; + + const wpmChangePerHour = wpmChange * (3600 / totalSecondsFiltered); + + const plus = wpmChangePerHour > 0 ? "+" : ""; + + $(".pageAccount .group.chart .below .text").text( + `Speed change per hour spent typing: ${ + plus + + Misc.roundTo2( + Config.alwaysShowCPM ? wpmChangePerHour * 5 : wpmChangePerHour + ) + } ${Config.alwaysShowCPM ? "cpm" : "wpm"}.` + ); + + applyHistorySmoothing(); + ChartController.accountActivity.updateColors(); + LoadingPage.updateBar(100, true); + setTimeout(() => { + if (ActivePage.get() == "account") SignOutButton.show(); + }, 125); + Focus.set(false); + Misc.swapElements( + $(".pageAccount .preloader"), + $(".pageAccount .content"), + 250, + async () => { + // Profile.updateNameFontSize("account"); + }, + async () => { + setTimeout(() => { + Profile.updateNameFontSize("account"); + }, 1); + } + ); +} + +export async function downloadResults(): Promise { + if (DB.getSnapshot().results !== undefined) return; + LoadingPage.updateBar(45, true); + const results = await DB.getUserResults(); + TodayTracker.addAllFromToday(); + if (results) { + ResultFilters.updateActive(); + } +} + +export async function update(): Promise { + LoadingPage.updateBar(0, true); + if (DB.getSnapshot() === null) { + Notifications.add(`Missing account data. Please refresh.`, -1); + $(".pageAccount .preloader").html("Missing account data. Please refresh."); + } else { + await downloadResults(); try { - cont(); + fillContent(); } catch (e) { console.error(e); Notifications.add(`Something went wrong: ${e}`, -1); @@ -1091,7 +1074,7 @@ $(".pageAccount .content .group.aboveHistory .exportCSV").on("click", () => { }); $(document).on("click", ".pageAccount .profile .details .copyLink", () => { - const url = "https://monkeytype.com/profile?uid=" + Auth.currentUser?.uid; + const url = `${location.origin}/profile/${Auth.currentUser?.uid}`; navigator.clipboard.writeText(url).then( function () { @@ -1107,17 +1090,17 @@ export const page = new Page( "account", $(".page.pageAccount"), "/account", - () => { + async () => { SignOutButton.hide(); }, async () => { reset(); }, - () => { - update(); + async () => { + await update(); // SignOutButton.show(); }, - () => { + async () => { // } ); diff --git a/frontend/src/ts/pages/loading.ts b/frontend/src/ts/pages/loading.ts index 14e8d6034..e48aa4621 100644 --- a/frontend/src/ts/pages/loading.ts +++ b/frontend/src/ts/pages/loading.ts @@ -23,7 +23,7 @@ export function showBar(): Promise { $(".pageLoading .icon"), $(".pageLoading .barWrapper"), 125, - () => { + async () => { resolve(); } ); @@ -31,7 +31,7 @@ export function showBar(): Promise { $(".pageAccount .icon"), $(".pageAccount .barWrapper"), 125, - () => { + async () => { resolve(); } ); @@ -48,10 +48,10 @@ export const page = new Page( async () => { // }, - () => { + async () => { // }, - () => { + async () => { // } ); diff --git a/frontend/src/ts/pages/login.ts b/frontend/src/ts/pages/login.ts index 8609b47a7..b16b88848 100644 --- a/frontend/src/ts/pages/login.ts +++ b/frontend/src/ts/pages/login.ts @@ -297,16 +297,16 @@ export const page = new Page( "login", $(".page.pageLogin"), "/login", - () => { + async () => { // }, - () => { + async () => { // }, - () => { + async () => { // }, - () => { + async () => { // } ); diff --git a/frontend/src/ts/pages/page.ts b/frontend/src/ts/pages/page.ts index 536da67ed..87b5f2e23 100644 --- a/frontend/src/ts/pages/page.ts +++ b/frontend/src/ts/pages/page.ts @@ -1,19 +1,19 @@ export default class Page { - public name: MonkeyTypes.Page; + public name: string; public element: JQuery; public pathname: string; - public beforeHide: () => void; - public afterHide: () => void; - public beforeShow: () => void; - public afterShow: () => void; + public beforeHide: () => Promise; + public afterHide: () => Promise; + public beforeShow: (params?: { [key: string]: string }) => Promise; + public afterShow: () => Promise; constructor( - name: MonkeyTypes.Page, + name: string, element: JQuery, pathname: string, - beforeHide: () => void, - afterHide: () => void, - beforeShow: () => void, - afterShow: () => void + beforeHide: () => Promise, + afterHide: () => Promise, + beforeShow: (params?: { [key: string]: string }) => Promise, + afterShow: () => Promise ) { this.name = name; this.element = element; diff --git a/frontend/src/ts/pages/profile.ts b/frontend/src/ts/pages/profile.ts index 3d633206d..696bc1e7d 100644 --- a/frontend/src/ts/pages/profile.ts +++ b/frontend/src/ts/pages/profile.ts @@ -1,6 +1,5 @@ import Ape from "../ape"; import Page from "./page"; -import * as Misc from "../utils/misc"; import * as Profile from "../elements/profile"; import * as PbTables from "../account/pb-tables"; import * as Notifications from "../elements/notifications"; @@ -119,9 +118,7 @@ function reset(): void { `); } -async function update(): Promise { - const userId = Misc.findGetParameter("uid"); - +async function update(userId: string): Promise { const response = await Ape.users.getProfile(userId ?? ""); $(".page.pageProfile .preloader").addClass("hidden"); @@ -138,17 +135,17 @@ export const page = new Page( "profile", $(".page.pageProfile"), "/profile", - () => { + async () => { // }, - () => { + async () => { reset(); }, - () => { + async (params) => { reset(); - update(); + update(params?.["uid"] ?? ""); }, - () => { + async () => { // } ); diff --git a/frontend/src/ts/pages/settings.ts b/frontend/src/ts/pages/settings.ts index ad0da6875..f5e318df7 100644 --- a/frontend/src/ts/pages/settings.ts +++ b/frontend/src/ts/pages/settings.ts @@ -1052,7 +1052,7 @@ export const page = new Page( "settings", $(".page.pageSettings"), "/settings", - () => { + async () => { // }, async () => { @@ -1062,7 +1062,7 @@ export const page = new Page( await fillSettingsPage(); update(); }, - () => { + async () => { // } ); diff --git a/frontend/src/ts/pages/test.ts b/frontend/src/ts/pages/test.ts index 56e244d73..48a770e3d 100644 --- a/frontend/src/ts/pages/test.ts +++ b/frontend/src/ts/pages/test.ts @@ -20,14 +20,14 @@ export const page = new Page( async () => { // }, - () => { + async () => { TestConfig.show(); TestStats.resetIncomplete(); ManualRestart.set(); TestLogic.restart(undefined, undefined, undefined, undefined, true); Funbox.activate(Config.funbox); }, - () => { + async () => { TestUI.focusWords(); } ); diff --git a/frontend/src/ts/popups/custom-theme-popup.ts b/frontend/src/ts/popups/custom-theme-popup.ts index f262d8769..fdd849664 100644 --- a/frontend/src/ts/popups/custom-theme-popup.ts +++ b/frontend/src/ts/popups/custom-theme-popup.ts @@ -1,7 +1,3 @@ -// import Config, * as UpdateConfig from "./config"; -// import * as Notifications from "./notifications"; -// import * as ThemePicker from "./theme-picker"; - export function show(value: string): void { if ($("#customThemeShareWrapper").hasClass("hidden")) { // let save = []; diff --git a/frontend/src/ts/popups/quote-submit-popup.ts b/frontend/src/ts/popups/quote-submit-popup.ts index c1b8eec6f..117109c57 100644 --- a/frontend/src/ts/popups/quote-submit-popup.ts +++ b/frontend/src/ts/popups/quote-submit-popup.ts @@ -1,8 +1,6 @@ import Ape from "../ape"; import * as Loader from "../elements/loader"; import * as Notifications from "../elements/notifications"; -// import Config from "../config"; -// import * as Misc from "../misc"; // let dropdownReady = false; // async function initDropdown(): Promise { diff --git a/frontend/src/ts/ready.ts b/frontend/src/ts/ready.ts index b081f69d5..9e5a27e55 100644 --- a/frontend/src/ts/ready.ts +++ b/frontend/src/ts/ready.ts @@ -1,9 +1,6 @@ import * as ManualRestart from "./test/manual-restart-tracker"; import Config, * as UpdateConfig from "./config"; import * as Misc from "./utils/misc"; -import * as VerificationController from "./controllers/verification-controller"; -import * as RouteController from "./controllers/route-controller"; -import * as PageController from "./controllers/page-controller"; import * as MonkeyPower from "./elements/monkey-power"; import * as NewVersionNotification from "./elements/version-check"; import * as Notifications from "./elements/notifications"; @@ -41,11 +38,7 @@ $("#nocss .requestedStylesheets").html( ); Focus.set(true, true); -RouteController.handleInitialPageClasses(window.location.pathname); $(document).ready(() => { - if (window.location.pathname === "/") { - // $("#top .config").removeClass("hidden"); - } CookiePopup.check(); $("body").css("transition", "all .25s, transform .05s"); if (Config.quickRestart === "tab" || Config.quickRestart === "esc") { @@ -63,48 +56,11 @@ $(document).ready(() => { true ); } - // if (!window.localStorage.getItem("dasbannerclosed")) { - // Notifications.addBanner( - // `Looking to buy a new keyboard? Check out DasKeyboard. `, - // 1, - // "images/dasbanner.png", - // false, - // () => { - // window.localStorage.setItem("dasbannerclosed", "true"); - // } - // ); - // } $("#centerContent") .css("opacity", "0") .removeClass("hidden") .stop(true, true) - .animate({ opacity: 1 }, 250, () => { - if (window.location.pathname === "/verify") { - const fragment = new URLSearchParams(window.location.hash.slice(1)); - if (fragment.has("access_token")) { - const accessToken = fragment.get("access_token") as string; - const tokenType = fragment.get("token_type") as string; - VerificationController.set({ - accessToken: accessToken, - tokenType: tokenType, - }); - history.replaceState("/", "", "/"); - } - const page = window.location.pathname.replace( - "/", - "" - ) as MonkeyTypes.Page; - PageController.change(page); - } else if (window.location.pathname === "/account") { - // history.replaceState("/", null, "/"); - } else if (/challenge_.+/g.test(window.location.pathname)) { - //do nothing - // } - } else if (window.location.pathname !== "/") { - // let page = window.location.pathname.replace("/", ""); - // PageController.change(page); - } - }); - // Settings.settingsFillPromise.then(Settings.update); + .animate({ opacity: 1 }, 250); + MonkeyPower.init(); }); diff --git a/frontend/src/ts/states/active-page.ts b/frontend/src/ts/states/active-page.ts index f469f8cfc..5bc975c65 100644 --- a/frontend/src/ts/states/active-page.ts +++ b/frontend/src/ts/states/active-page.ts @@ -1,9 +1,9 @@ -let activePage: MonkeyTypes.Page | undefined = "loading"; +let activePage = "loading"; -export function get(): MonkeyTypes.Page | undefined { +export function get(): string { return activePage; } -export function set(active: MonkeyTypes.Page | undefined): void { +export function set(active: string): void { activePage = active; } diff --git a/frontend/src/ts/test/result.ts b/frontend/src/ts/test/result.ts index 2011648d4..6594f6822 100644 --- a/frontend/src/ts/test/result.ts +++ b/frontend/src/ts/test/result.ts @@ -709,7 +709,7 @@ export async function update( $("#typingTest"), $("#result"), 250, - () => { + async () => { TestUI.setResultCalculating(false); $("#words").empty(); ChartController.result.resize(); @@ -721,7 +721,7 @@ export async function update( window.scrollTo({ top: 0 }); $("#testModesNotice").addClass("hidden"); }, - () => { + async () => { $("#resultExtraButtons").removeClass("hidden").css("opacity", 0).animate( { opacity: 1, diff --git a/frontend/src/ts/types/types.d.ts b/frontend/src/ts/types/types.d.ts index 1ea7a0ff9..8844b9235 100644 --- a/frontend/src/ts/types/types.d.ts +++ b/frontend/src/ts/types/types.d.ts @@ -687,17 +687,6 @@ declare namespace MonkeyTypes { colorfulErrorExtra: string; } - type Page = - | "loading" - | "test" - | "about" - | "settings" - | "account" - | "login" - | "profile"; - - // type ActivePage = `page${Page}` | undefined; - interface Layout { keymapShowTopRow: boolean; type: "iso" | "ansi" | "ortho" | "matrix"; diff --git a/frontend/src/ts/utils/misc.ts b/frontend/src/ts/utils/misc.ts index 606fa0e92..fdf142b80 100644 --- a/frontend/src/ts/utils/misc.ts +++ b/frontend/src/ts/utils/misc.ts @@ -83,7 +83,7 @@ interface Theme { let themesList: Theme[] = []; export async function getThemesList(): Promise { if (themesList.length == 0) { - return $.getJSON("themes/_list.json", function (data) { + return $.getJSON("/./themes/_list.json", function (data) { const list = data.sort(function (a: Theme, b: Theme) { const nameA = a.name.toLowerCase(); const nameB = b.name.toLowerCase(); @@ -121,7 +121,7 @@ export async function getSortedThemesList(): Promise { let funboxList: MonkeyTypes.FunboxObject[] = []; export async function getFunboxList(): Promise { if (funboxList.length === 0) { - return $.getJSON("funbox/_list.json", function (data) { + return $.getJSON("/./funbox/_list.json", function (data) { funboxList = data.sort(function ( a: MonkeyTypes.FunboxObject, b: MonkeyTypes.FunboxObject @@ -151,7 +151,7 @@ export async function getFunbox( let layoutsList: MonkeyTypes.Layouts = {}; export async function getLayoutsList(): Promise { if (Object.keys(layoutsList).length === 0) { - return $.getJSON("layouts/_list.json", function (data) { + return $.getJSON("/./layouts/_list.json", function (data) { layoutsList = data; return layoutsList; }); @@ -177,7 +177,7 @@ interface Font { let fontsList: Font[] = []; export async function getFontsList(): Promise { if (fontsList.length === 0) { - return $.getJSON("fonts/_list.json", function (data) { + return $.getJSON("/./fonts/_list.json", function (data) { fontsList = data.sort(function (a: Font, b: Font) { const nameA = a.name.toLowerCase(); const nameB = b.name.toLowerCase(); @@ -219,7 +219,7 @@ export async function getContributorsList(): Promise { let languageList: string[] = []; export async function getLanguageList(): Promise { if (languageList.length === 0) { - return $.getJSON("languages/_list.json", function (data) { + return $.getJSON("/./languages/_list.json", function (data) { languageList = data; return languageList; }); @@ -288,7 +288,7 @@ export async function findCurrentGroup( let challengeList: MonkeyTypes.Challenge[] = []; export async function getChallengeList(): Promise { if (challengeList.length === 0) { - return $.getJSON("challenges/_list.json", function (data) { + return $.getJSON("/./challenges/_list.json", function (data) { challengeList = data; return challengeList; }); @@ -927,25 +927,25 @@ export function convertRemToPixels(rem: number): number { return rem * parseFloat(getComputedStyle(document.documentElement).fontSize); } -export function swapElements( +export async function swapElements( el1: JQuery, el2: JQuery, totalDuration: number, - callback = function (): void { - return; + callback = function (): Promise { + return Promise.resolve(); }, - middleCallback = function (): void { - return; + middleCallback = function (): Promise { + return Promise.resolve(); } -): boolean | undefined { +): Promise { if ( (el1.hasClass("hidden") && !el2.hasClass("hidden")) || (!el1.hasClass("hidden") && el2.hasClass("hidden")) ) { //one of them is hidden and the other is visible if (el1.hasClass("hidden")) { - middleCallback(); - callback(); + await middleCallback(); + await callback(); return false; } $(el1) @@ -956,8 +956,8 @@ export function swapElements( opacity: 0, }, totalDuration / 2, - () => { - middleCallback(); + async () => { + await middleCallback(); $(el1).addClass("hidden"); $(el2) .removeClass("hidden") @@ -975,7 +975,7 @@ export function swapElements( ); } else if (el1.hasClass("hidden") && el2.hasClass("hidden")) { //both are hidden, only fade in the second - middleCallback(); + await middleCallback(); $(el2) .removeClass("hidden") .css("opacity", 0) @@ -984,13 +984,13 @@ export function swapElements( opacity: 1, }, totalDuration, - () => { - callback(); + async () => { + await callback(); } ); } else { - middleCallback(); - callback(); + await middleCallback(); + await callback(); } return; diff --git a/frontend/src/ts/utils/url-handler.ts b/frontend/src/ts/utils/url-handler.ts index c10eee050..94aed0154 100644 --- a/frontend/src/ts/utils/url-handler.ts +++ b/frontend/src/ts/utils/url-handler.ts @@ -5,8 +5,52 @@ import { decompressFromURI } from "lz-ts"; import * as QuoteSearchPopup from "../popups/quote-search-popup"; import * as ManualRestart from "../test/manual-restart-tracker"; import * as CustomText from "../test/custom-text"; +import Ape from "../ape"; +import * as Settings from "../pages/settings"; +import * as DB from "../db"; +import * as Loader from "../elements/loader"; +import * as AccountButton from "../elements/account-button"; import { restart as restartTest } from "../test/test-logic"; +export async function linkDiscord(hashOverride: string): Promise { + if (!hashOverride) return; + const fragment = new URLSearchParams(hashOverride.slice(1)); + if (fragment.has("access_token")) { + history.replaceState(null, "", "/"); + const accessToken = fragment.get("access_token") as string; + const tokenType = fragment.get("token_type") as string; + + Loader.show(); + + const response = await Ape.users.linkDiscord(tokenType, accessToken); + Loader.hide(); + + if (response.status !== 200) { + return Notifications.add( + "Failed to link Discord: " + response.message, + -1 + ); + } + + Notifications.add(response.message, 1); + + const snapshot = DB.getSnapshot(); + + const { discordId, discordAvatar } = response.data; + if (discordId) { + snapshot.discordId = discordId; + } else { + snapshot.discordAvatar = discordAvatar; + } + + DB.setSnapshot(snapshot); + + AccountButton.update(discordId, discordAvatar); + + Settings.updateDiscordSection(); + } +} + export function loadCustomThemeFromUrl(getOverride?: string): void { const getValue = Misc.findGetParameter("customTheme", getOverride); if (getValue === null) return; @@ -130,7 +174,11 @@ export function loadTestSettingsFromUrl(getOverride?: string): void { Notifications.add( "Settings applied from URL:

" + appliedString, 1, - 10 + 10, + undefined, + undefined, + undefined, + true ); } } diff --git a/frontend/static/html/head.html b/frontend/static/html/head.html index 2a74e65ff..6f49c6f31 100644 --- a/frontend/static/html/head.html +++ b/frontend/static/html/head.html @@ -7,11 +7,11 @@ Monkeytype - - - + + + - + - + +
+
+
Ooops! Looks like you found a page that doesn't exist.
+ + + Go Home + +
+ diff --git a/frontend/static/html/pages/account.html b/frontend/static/html/pages/account.html index 3e922329f..aaa069f34 100644 --- a/frontend/static/html/pages/account.html +++ b/frontend/static/html/pages/account.html @@ -2,7 +2,7 @@
-
+
-