Skip to content

Commit 36dcfe2

Browse files
authored
refactor: add url parameter handling to Page class (@fehmer) (monkeytypegame#6668)
1 parent 677c328 commit 36dcfe2

File tree

3 files changed

+70
-28
lines changed

3 files changed

+70
-28
lines changed

frontend/src/ts/controllers/page-controller.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import * as PageAccountSettings from "../pages/account-settings";
1515
import * as PageTransition from "../states/page-transition";
1616
import * as AdController from "../controllers/ad-controller";
1717
import * as Focus from "../test/focus";
18-
import { PageName } from "../pages/page";
18+
import { PageName, PageWithUrlParams } from "../pages/page";
1919

2020
type ChangeOptions = {
2121
force?: boolean;
@@ -110,6 +110,10 @@ export async function change(
110110
ActivePage.set(nextPage.id);
111111

112112
await previousPage?.afterHide();
113+
114+
if (nextPage instanceof PageWithUrlParams) {
115+
nextPage.readUrlParams();
116+
}
113117
await nextPage?.beforeShow({
114118
params: options.params,
115119
// @ts-expect-error for the future (i think)

frontend/src/ts/pages/leaderboards.ts

Lines changed: 9 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import Page from "./page";
1+
import { PageWithUrlParams } from "./page";
22
import * as Skeleton from "../utils/skeleton";
33
import Config from "../config";
44
import {
@@ -34,10 +34,6 @@ import { abbreviateNumber } from "../utils/numbers";
3434
import { formatDistanceToNow } from "date-fns/formatDistanceToNow";
3535
import { z } from "zod";
3636
import { LocalStorageWithSchema } from "../utils/local-storage-with-schema";
37-
import {
38-
safeParse as parseUrlSearchParams,
39-
serialize as serializeUrlSearchParams,
40-
} from "zod-urlsearchparams";
4137
import { UTCDateMini } from "@date-fns/utc";
4238
import * as ConfigEvent from "../observables/config-event";
4339
import * as ActivePage from "../states/active-page";
@@ -1135,34 +1131,17 @@ function updateGetParameters(): void {
11351131

11361132
params.page = state.page + 1;
11371133

1138-
const urlParams = serializeUrlSearchParams({
1139-
schema: UrlParameterSchema,
1140-
data: params,
1141-
});
1142-
1143-
const newUrl = `${window.location.pathname}?${urlParams.toString()}`;
1144-
window.history.replaceState({}, "", newUrl);
1134+
page.setUrlParams(params);
11451135

11461136
selectorLS.set(state);
11471137
}
11481138

1149-
function readGetParameters(): void {
1150-
const urlParams = new URLSearchParams(window.location.search);
1151-
1152-
if (urlParams.size === 0) {
1139+
function readGetParameters(params: UrlParameter | null): void {
1140+
if (params === null) {
11531141
Object.assign(state, selectorLS.get());
11541142
return;
11551143
}
11561144

1157-
const parsed = parseUrlSearchParams({
1158-
schema: UrlParameterSchema,
1159-
input: urlParams,
1160-
});
1161-
if (!parsed.success) {
1162-
return;
1163-
}
1164-
const params = parsed.data;
1165-
11661145
if (params.type !== undefined) {
11671146
state.type = params.type;
11681147
}
@@ -1288,18 +1267,21 @@ $(".page.pageLeaderboards .buttonGroup.secondary").on(
12881267
}
12891268
);
12901269

1291-
export const page = new Page({
1270+
export const page = new PageWithUrlParams({
12921271
id: "leaderboards",
12931272
element: $(".page.pageLeaderboards"),
12941273
path: "/leaderboards",
1274+
urlParams: {
1275+
schema: UrlParameterSchema,
1276+
onUrlParamUpdate: readGetParameters,
1277+
},
12951278
afterHide: async (): Promise<void> => {
12961279
Skeleton.remove("pageLeaderboards");
12971280
stopTimer();
12981281
},
12991282
beforeShow: async (): Promise<void> => {
13001283
Skeleton.append("pageLeaderboards", "main");
13011284
// await appendLanguageButtons(); //todo figure out this race condition
1302-
readGetParameters();
13031285
startTimer();
13041286
updateTypeButtons();
13051287
updateTitle();

frontend/src/ts/pages/page.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
import { z } from "zod";
2+
import {
3+
safeParse as parseUrlSearchParams,
4+
serialize as serializeUrlSearchParams,
5+
} from "zod-urlsearchparams";
6+
17
export type PageName =
28
| "loading"
39
| "test"
@@ -35,6 +41,7 @@ export default class Page<T> {
3541
public display: string | undefined;
3642
public element: JQuery;
3743
public pathname: string;
44+
3845
public beforeHide: () => Promise<void>;
3946
public afterHide: () => Promise<void>;
4047
public beforeShow: (options: Options<T>) => Promise<void>;
@@ -51,3 +58,52 @@ export default class Page<T> {
5158
this.afterShow = props.afterShow ?? empty;
5259
}
5360
}
61+
62+
type UrlParamsSchema = z.ZodObject<Record<string, z.ZodTypeAny>>;
63+
type PagePropertiesWithUrlParams<
64+
T,
65+
U extends UrlParamsSchema
66+
> = PageProperties<T> & {
67+
urlParams: {
68+
schema: U;
69+
onUrlParamUpdate?: (params: z.infer<U> | null) => void;
70+
};
71+
};
72+
73+
export class PageWithUrlParams<T, U extends UrlParamsSchema> extends Page<T> {
74+
private urlSchema: U;
75+
private onUrlParamUpdate?: (params: z.infer<U> | null) => void;
76+
77+
constructor(props: PagePropertiesWithUrlParams<T, U>) {
78+
super(props);
79+
this.urlSchema = props.urlParams.schema;
80+
this.onUrlParamUpdate = props.urlParams.onUrlParamUpdate;
81+
}
82+
83+
public readUrlParams(): void {
84+
if (this.onUrlParamUpdate === undefined) {
85+
return;
86+
}
87+
const urlParams = new URLSearchParams(window.location.search);
88+
89+
const parsed = parseUrlSearchParams({
90+
schema: this.urlSchema,
91+
input: urlParams,
92+
});
93+
94+
if (!parsed.success) {
95+
this.onUrlParamUpdate?.(null);
96+
return;
97+
}
98+
99+
this.onUrlParamUpdate?.(parsed.data);
100+
}
101+
public setUrlParams(params: z.infer<U>): void {
102+
const urlParams = serializeUrlSearchParams({
103+
schema: this.urlSchema,
104+
data: params,
105+
});
106+
const newUrl = `${window.location.pathname}?${urlParams.toString()}`;
107+
window.history.replaceState({}, "", newUrl);
108+
}
109+
}

0 commit comments

Comments
 (0)