refactor: add url parameter handling to Page class (@fehmer) (#6668)

This commit is contained in:
Christian Fehmer 2025-06-27 19:03:37 +02:00 committed by GitHub
parent 677c328bed
commit 36dcfe2f7f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 70 additions and 28 deletions

View file

@ -15,7 +15,7 @@ import * as PageAccountSettings from "../pages/account-settings";
import * as PageTransition from "../states/page-transition";
import * as AdController from "../controllers/ad-controller";
import * as Focus from "../test/focus";
import { PageName } from "../pages/page";
import { PageName, PageWithUrlParams } from "../pages/page";
type ChangeOptions = {
force?: boolean;
@ -110,6 +110,10 @@ export async function change(
ActivePage.set(nextPage.id);
await previousPage?.afterHide();
if (nextPage instanceof PageWithUrlParams) {
nextPage.readUrlParams();
}
await nextPage?.beforeShow({
params: options.params,
// @ts-expect-error for the future (i think)

View file

@ -1,4 +1,4 @@
import Page from "./page";
import { PageWithUrlParams } from "./page";
import * as Skeleton from "../utils/skeleton";
import Config from "../config";
import {
@ -34,10 +34,6 @@ import { abbreviateNumber } from "../utils/numbers";
import { formatDistanceToNow } from "date-fns/formatDistanceToNow";
import { z } from "zod";
import { LocalStorageWithSchema } from "../utils/local-storage-with-schema";
import {
safeParse as parseUrlSearchParams,
serialize as serializeUrlSearchParams,
} from "zod-urlsearchparams";
import { UTCDateMini } from "@date-fns/utc";
import * as ConfigEvent from "../observables/config-event";
import * as ActivePage from "../states/active-page";
@ -1135,34 +1131,17 @@ function updateGetParameters(): void {
params.page = state.page + 1;
const urlParams = serializeUrlSearchParams({
schema: UrlParameterSchema,
data: params,
});
const newUrl = `${window.location.pathname}?${urlParams.toString()}`;
window.history.replaceState({}, "", newUrl);
page.setUrlParams(params);
selectorLS.set(state);
}
function readGetParameters(): void {
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.size === 0) {
function readGetParameters(params: UrlParameter | null): void {
if (params === null) {
Object.assign(state, selectorLS.get());
return;
}
const parsed = parseUrlSearchParams({
schema: UrlParameterSchema,
input: urlParams,
});
if (!parsed.success) {
return;
}
const params = parsed.data;
if (params.type !== undefined) {
state.type = params.type;
}
@ -1288,10 +1267,14 @@ $(".page.pageLeaderboards .buttonGroup.secondary").on(
}
);
export const page = new Page({
export const page = new PageWithUrlParams({
id: "leaderboards",
element: $(".page.pageLeaderboards"),
path: "/leaderboards",
urlParams: {
schema: UrlParameterSchema,
onUrlParamUpdate: readGetParameters,
},
afterHide: async (): Promise<void> => {
Skeleton.remove("pageLeaderboards");
stopTimer();
@ -1299,7 +1282,6 @@ export const page = new Page({
beforeShow: async (): Promise<void> => {
Skeleton.append("pageLeaderboards", "main");
// await appendLanguageButtons(); //todo figure out this race condition
readGetParameters();
startTimer();
updateTypeButtons();
updateTitle();

View file

@ -1,3 +1,9 @@
import { z } from "zod";
import {
safeParse as parseUrlSearchParams,
serialize as serializeUrlSearchParams,
} from "zod-urlsearchparams";
export type PageName =
| "loading"
| "test"
@ -35,6 +41,7 @@ export default class Page<T> {
public display: string | undefined;
public element: JQuery;
public pathname: string;
public beforeHide: () => Promise<void>;
public afterHide: () => Promise<void>;
public beforeShow: (options: Options<T>) => Promise<void>;
@ -51,3 +58,52 @@ export default class Page<T> {
this.afterShow = props.afterShow ?? empty;
}
}
type UrlParamsSchema = z.ZodObject<Record<string, z.ZodTypeAny>>;
type PagePropertiesWithUrlParams<
T,
U extends UrlParamsSchema
> = PageProperties<T> & {
urlParams: {
schema: U;
onUrlParamUpdate?: (params: z.infer<U> | null) => void;
};
};
export class PageWithUrlParams<T, U extends UrlParamsSchema> extends Page<T> {
private urlSchema: U;
private onUrlParamUpdate?: (params: z.infer<U> | null) => void;
constructor(props: PagePropertiesWithUrlParams<T, U>) {
super(props);
this.urlSchema = props.urlParams.schema;
this.onUrlParamUpdate = props.urlParams.onUrlParamUpdate;
}
public readUrlParams(): void {
if (this.onUrlParamUpdate === undefined) {
return;
}
const urlParams = new URLSearchParams(window.location.search);
const parsed = parseUrlSearchParams({
schema: this.urlSchema,
input: urlParams,
});
if (!parsed.success) {
this.onUrlParamUpdate?.(null);
return;
}
this.onUrlParamUpdate?.(parsed.data);
}
public setUrlParams(params: z.infer<U>): void {
const urlParams = serializeUrlSearchParams({
schema: this.urlSchema,
data: params,
});
const newUrl = `${window.location.pathname}?${urlParams.toString()}`;
window.history.replaceState({}, "", newUrl);
}
}