@@ -6,11 +6,7 @@ import {
66} from "@monkeytype/contracts/schemas/leaderboards" ;
77import { capitalizeFirstLetter } from "../utils/strings" ;
88import Ape from "../ape" ;
9- import {
10- Mode ,
11- Mode2Schema ,
12- ModeSchema ,
13- } from "@monkeytype/contracts/schemas/shared" ;
9+ import { Mode } from "@monkeytype/contracts/schemas/shared" ;
1410import * as Notifications from "../elements/notifications" ;
1511import Format from "../utils/format" ;
1612import { Auth , isAuthenticated } from "../firebase" ;
@@ -34,7 +30,10 @@ import {
3430import { formatDistanceToNow } from "date-fns/formatDistanceToNow" ;
3531import { z } from "zod" ;
3632import { LocalStorageWithSchema } from "../utils/local-storage-with-schema" ;
37- import { LanguageSchema } from "@monkeytype/contracts/schemas/util" ;
33+ import {
34+ safeParse as parseUrlSearchParams ,
35+ serialize as serializeUrlSearchParams ,
36+ } from "zod-urlsearchparams" ;
3837// import * as ServerConfiguration from "../ape/server-configuration";
3938
4039const LeaderboardTypeSchema = z . enum ( [ "allTime" , "weekly" , "daily" ] ) ;
@@ -97,17 +96,20 @@ const state = {
9796
9897const SelectorSchema = z . object ( {
9998 type : LeaderboardTypeSchema ,
100- mode : ModeSchema . optional ( ) ,
101- mode2 : Mode2Schema . optional ( ) ,
102- language : LanguageSchema . optional ( ) ,
99+ mode2 : z . enum ( [ "15" , "60" ] ) . optional ( ) ,
100+ language : z . string ( ) . optional ( ) ,
103101 yesterday : z . boolean ( ) . optional ( ) ,
104102 lastWeek : z . boolean ( ) . optional ( ) ,
105103} ) ;
104+ const UrlParameterSchema = SelectorSchema . extend ( {
105+ page : z . number ( ) ,
106+ } ) . partial ( ) ;
107+ type UrlParameter = z . infer < typeof UrlParameterSchema > ;
106108
107109const selectorLS = new LocalStorageWithSchema ( {
108110 key : "leaderboardSelector" ,
109111 schema : SelectorSchema ,
110- fallback : { type : "allTime" , mode : "time" , mode2 : "15" } ,
112+ fallback : { type : "allTime" , mode2 : "15" } ,
111113} ) ;
112114
113115function updateTitle ( ) : void {
@@ -1138,76 +1140,82 @@ function handleYesterdayLastWeekButton(action: string): void {
11381140}
11391141
11401142function updateGetParameters ( ) : void {
1141- const params = new URLSearchParams ( ) ;
1143+ const params : UrlParameter = { } ;
11421144
1143- params . set ( " type" , state . type ) ;
1145+ params . type = state . type ;
11441146 if ( state . type === "allTime" ) {
1145- params . set ( " mode2" , state . mode2 ) ;
1147+ params . mode2 = state . mode2 ;
11461148 } else if ( state . type === "daily" ) {
1147- params . set ( " language" , state . language ) ;
1148- params . set ( " mode2" , state . mode2 ) ;
1149+ params . language = state . language ;
1150+ params . mode2 = state . mode2 ;
11491151 if ( state . yesterday ) {
1150- params . set ( "yesterday" , "true" ) ;
1151- } else {
1152- params . delete ( "yesterday" ) ;
1152+ params . yesterday = true ;
11531153 }
11541154 } else if ( state . type === "weekly" ) {
11551155 if ( state . lastWeek ) {
1156- params . set ( "lastWeek" , "true" ) ;
1157- } else {
1158- params . delete ( "lastWeek" ) ;
1156+ params . lastWeek = true ;
11591157 }
11601158 }
11611159
1162- params . set ( " page" , ( state . page + 1 ) . toString ( ) ) ;
1160+ params . page = state . page + 1 ;
11631161
1164- const newUrl = `${ window . location . pathname } ?${ params . toString ( ) } ` ;
1162+ const urlParams = serializeUrlSearchParams ( {
1163+ schema : UrlParameterSchema ,
1164+ data : params ,
1165+ } ) ;
1166+
1167+ const newUrl = `${ window . location . pathname } ?${ urlParams . toString ( ) } ` ;
11651168 window . history . replaceState ( { } , "" , newUrl ) ;
11661169
11671170 selectorLS . set ( state ) ;
11681171}
11691172
11701173function readGetParameters ( ) : void {
1171- const params = new URLSearchParams ( window . location . search ) ;
1174+ const urlParams = new URLSearchParams ( window . location . search ) ;
11721175
1173- if ( params . size == 0 ) {
1176+ if ( urlParams . size == 0 ) {
11741177 Object . assign ( state , selectorLS . get ( ) ) ;
11751178 return ;
11761179 }
11771180
1178- const type = params . get ( "type" ) as "allTime" | "weekly" | "daily" ;
1179- if ( type ) {
1180- state . type = type ;
1181+ const parsed = parseUrlSearchParams ( {
1182+ schema : UrlParameterSchema ,
1183+ input : urlParams ,
1184+ } ) ;
1185+ if ( ! parsed . success ) {
1186+ return ;
1187+ }
1188+ const params = parsed . data ;
1189+
1190+ if ( params . type !== undefined ) {
1191+ state . type = params . type ;
11811192 }
11821193
11831194 if ( state . type === "allTime" ) {
1184- const mode = params . get ( "mode2" ) as "15" | "60" ;
1185- if ( mode ) {
1186- state . mode2 = mode ;
1195+ if ( params . mode2 ) {
1196+ state . mode2 = params . mode2 ;
11871197 }
11881198 } else if ( state . type === "daily" ) {
1189- const language = params . get ( "language" ) ;
1190- const dailyMode = params . get ( "mode2" ) as "15" | "60" ;
1191- const yesterday = params . get ( "yesterday" ) as string ;
1192- if ( language !== null ) {
1193- state . language = language ;
1199+ if ( params . language !== undefined ) {
1200+ state . language = params . language ;
1201+ }
1202+ if ( state . language === undefined ) {
1203+ state . language = "english" ;
11941204 }
1195- if ( dailyMode ) {
1196- state . mode2 = dailyMode ;
1205+ if ( params . mode2 !== undefined ) {
1206+ state . mode2 = params . mode2 ;
11971207 }
1198- if ( yesterday !== null && yesterday === "true" ) {
1199- state . yesterday = true ;
1208+ if ( params . yesterday !== undefined ) {
1209+ state . yesterday = params . yesterday ;
12001210 }
12011211 } else if ( state . type === "weekly" ) {
1202- const lastWeek = params . get ( "lastWeek" ) as string ;
1203- if ( lastWeek !== null && lastWeek === "true" ) {
1204- state . lastWeek = true ;
1212+ if ( params . lastWeek !== undefined ) {
1213+ state . lastWeek = params . lastWeek ;
12051214 }
12061215 }
12071216
1208- const page = params . get ( "page" ) ;
1209- if ( page !== null ) {
1210- state . page = parseInt ( page , 10 ) - 1 ;
1217+ if ( params . page !== undefined ) {
1218+ state . page = params . page - 1 ;
12111219
12121220 if ( state . page < 0 ) {
12131221 state . page = 0 ;
@@ -1264,6 +1272,7 @@ $(".page.pageLeaderboards .buttonGroup.secondary").on(
12641272 ) {
12651273 if ( state . mode2 === mode ) return ;
12661274 state . mode2 = mode ;
1275+ state . page = 0 ;
12671276 } else if ( language !== undefined && state . type === "daily" ) {
12681277 if ( state . language === language ) return ;
12691278 state . language = language ;
0 commit comments