From 57eab6986d89ce62b0bcb29a61e372c4b75ca7b8 Mon Sep 17 00:00:00 2001 From: Timothy Armes Date: Mon, 25 Sep 2023 17:28:54 +0200 Subject: [PATCH] Sync URL parameters with elo form for sharing of url --- app/[locale]/elo/TournamentResults.tsx | 134 +++++++++--------- app/[locale]/elo/page.tsx | 98 +++++++++++-- .../procedures/fetchTournamentResults.ts | 1 - 3 files changed, 153 insertions(+), 80 deletions(-) diff --git a/app/[locale]/elo/TournamentResults.tsx b/app/[locale]/elo/TournamentResults.tsx index 511dba7..9e971b0 100644 --- a/app/[locale]/elo/TournamentResults.tsx +++ b/app/[locale]/elo/TournamentResults.tsx @@ -111,80 +111,84 @@ export const TournamentResults = ({ - {deltas.map((delta, i) => { - const { estimated, national, lostByForfeit, wonByForfeit } = delta; - const opponentElo = delta.opponentElo - ? ` (${delta.opponentElo} ${ - estimated ? "E" : national ? "N" : "F" - })` - : ""; + + {deltas.map((delta, i) => { + const { estimated, national, lostByForfeit, wonByForfeit } = delta; + const opponentElo = delta.opponentElo + ? ` (${delta.opponentElo} ${ + estimated ? "E" : national ? "N" : "F" + })` + : ""; - const opponentName = delta.opponentName ? ( - - - {delta.opponentName} + const opponentName = delta.opponentName ? ( + + + {delta.opponentName} + + {opponentElo} - {opponentElo} - - ) : ( - "-" - ); + ) : ( + "-" + ); - const playedWhite = delta.colour === "white"; + const playedWhite = delta.colour === "white"; - const whitePlayer = playedWhite ? playerResults.name : opponentName; - const blackPlayer = !playedWhite ? playerResults.name : opponentName; - const noChange = - wonByForfeit || lostByForfeit || estimated || national; + const whitePlayer = playedWhite ? playerResults.name : opponentName; + const blackPlayer = !playedWhite + ? playerResults.name + : opponentName; + const noChange = + wonByForfeit || lostByForfeit || estimated || national; - const forfeit = delta.opponentName === null; + const forfeit = delta.opponentName === null; - let result = ""; - if (wonByForfeit) { - result = t("wonByForfeit"); - } else if (lostByForfeit) { - result = t("lostByForfeit"); - } else if (delta.result === 0.5) { - result = t("draw"); - } else if ( - (playedWhite && delta.result === 1) || - (!playedWhite && delta.result === 0) - ) { - result = t("whiteWin"); - } else result = t("blackWin"); + let result = ""; + if (wonByForfeit) { + result = t("wonByForfeit"); + } else if (lostByForfeit) { + result = t("lostByForfeit"); + } else if (delta.result === 0.5) { + result = t("draw"); + } else if ( + (playedWhite && delta.result === 1) || + (!playedWhite && delta.result === 0) + ) { + result = t("whiteWin"); + } else result = t("blackWin"); - return ( - - {forfeit ? ( - - ) : ( - - )} - - + {forfeit ? ( + ) : ( - 0 && "text-success", - deltas[i].delta! < 0 && "text-error", - )} - > - {deltas[i]!.delta! >= 0 ? "+" : ""} - {deltas[i].delta!} - + )} - - - ); - })} + + + + ); + })} +
{t("forfeit")} - {whitePlayer} - {t("vs")} - {blackPlayer} - - {!forfeit && result} - - {noChange ? ( - "-" + return ( +
{t("forfeit")} + {whitePlayer} + {t("vs")} + {blackPlayer} +
+ {!forfeit && result} + + {noChange ? ( + "-" + ) : ( + 0 && "text-success", + deltas[i].delta! < 0 && "text-error", + )} + > + {deltas[i]!.delta! >= 0 ? "+" : ""} + {deltas[i].delta!} + + )} +
{noChangeCount > 0 && ( diff --git a/app/[locale]/elo/page.tsx b/app/[locale]/elo/page.tsx index 746b06e..5e342f7 100644 --- a/app/[locale]/elo/page.tsx +++ b/app/[locale]/elo/page.tsx @@ -1,11 +1,12 @@ "use client"; -import { useState } from "react"; +import { useEffect, useRef, useState } from "react"; import { zodResolver } from "@hookform/resolvers/zod"; import { isEmpty, sortBy } from "lodash"; import { useTranslations } from "next-intl"; import Link from "next/link"; +import { usePathname, useRouter, useSearchParams } from "next/navigation"; import { FormProvider, useForm } from "react-hook-form"; import { z } from "zod"; @@ -19,9 +20,11 @@ import { KFactor } from "./KFactor"; import { ManualEloForm } from "./ManualEloForm"; import { TournamentResults } from "./TournamentResults"; +const kFactors = ["40", "20", "10"]; + const formSchema = fetchTournamentResultsSchema.extend({ player: z.string().optional(), - kFactor: z.enum(["40", "30", "20", "15", "10"]), + kFactor: z.string(), }); type FetchResultsFormValues = z.infer; @@ -30,12 +33,19 @@ export default function Elo() { const t = useTranslations("Elo"); type TranslationKey = Parameters[0]; - const [formInput, setFormInput] = useState({ - url: "", - kFactor: "20", - }); + const router = useRouter(); + const pathname = usePathname(); + const searchParams = useSearchParams(); + const current = new URLSearchParams(Array.from(searchParams.entries())); - const hasUrl = !isEmpty(formInput?.url.trim()); + const url = searchParams.get("url") ?? ""; + const kFactorParam = searchParams.get("k"); + const player = searchParams.get("player") ?? ""; + + const kFactor = + kFactorParam && kFactors.includes(kFactorParam) ? kFactorParam : "20"; + + const hasUrl = !isEmpty(url.trim()); const { data: allResults, @@ -43,7 +53,7 @@ export default function Elo() { error, } = trpc.fetchTournamentResults.useQuery( { - url: formInput?.url.trim(), + url: url?.trim(), }, { refetchOnWindowFocus: false, @@ -65,26 +75,86 @@ export default function Elo() { const form = useForm({ resolver: zodResolver(fetchTournamentResultsSchema), defaultValues: { - kFactor: "20", + url: url ?? "", + kFactor: kFactor ?? "20", + player: player ?? "", }, }); - const [player, kFactor] = form.watch(["player", "kFactor"]); - const playerResults = allResults?.find((p) => p.id === player); - const onSubmit = async (input: FetchResultsFormValues) => { - setFormInput(input); + current.set("url", input.url); + if (input.kFactor) current.set("k", input.kFactor); + + const search = current.toString(); + const query = search ? `?${search}` : ""; + + // Push the new URL + router.push(`${pathname}${query}`); }; const clearForm = () => { + current.delete("url"); + current.delete("k"); + current.delete("player"); + form.setValue("url", ""); - setFormInput({ ...formInput, url: "" }); + form.setValue("kFactor", "20"); + form.setValue("player", ""); + + const search = current.toString(); + const query = search ? `?${search}` : ""; + + router.push(`${pathname}${query}`); }; + useEffect(() => { + // We subscribe to form changes and update the URL parameters + const subscription = form.watch((value, { name, type }) => { + let update = false; + if (type === "change") { + if (name === "player" && value.player !== player && value.player) { + current.set("player", value.player); + update = true; + } else if ( + name === "kFactor" && + value.kFactor !== kFactor && + value.kFactor + ) { + current.set("k", value.kFactor); + update = true; + } + + if (update) { + const search = current.toString(); + const query = search ? `?${search}` : ""; + router.replace(`${pathname}${query}`); + } + } + }); + return () => subscription.unsubscribe(); + }, [form.watch, kFactor, player]); + + useEffect(() => { + // When the URL changes, we update the form values + if (url !== form.getValues("url")) { + form.setValue("url", url); + } + + if (player !== form.getValues("player")) { + form.setValue("player", player); + } + + if (kFactor !== form.getValues("kFactor")) { + form.setValue("kFactor", form.getValues("kFactor")); + } + }, [searchParams, form]); + if (error) { console.error(error); } + const playerResults = allResults?.find((p) => p.id === player); + return (
diff --git a/app/server/procedures/fetchTournamentResults.ts b/app/server/procedures/fetchTournamentResults.ts index 9d42352..f2bb3aa 100644 --- a/app/server/procedures/fetchTournamentResults.ts +++ b/app/server/procedures/fetchTournamentResults.ts @@ -114,7 +114,6 @@ export const fetchTournamentResults = publicProcedure const tournamentId = input.url.match(/Ref=(\d+)/)?.[1] ?? input.url.match(/Id\/(\d+)/)?.[1]; - if (!tournamentId) { throw new Error("ERR_NO_TOURNAMENT_ID"); }