diff options
Diffstat (limited to 'web/pw-server/src/routes/matches')
-rw-r--r-- | web/pw-server/src/routes/matches/[match_id].svelte | 40 | ||||
-rw-r--r-- | web/pw-server/src/routes/matches/index.svelte | 129 |
2 files changed, 123 insertions, 46 deletions
diff --git a/web/pw-server/src/routes/matches/[match_id].svelte b/web/pw-server/src/routes/matches/[match_id].svelte index 2c0a3fa..7c1507c 100644 --- a/web/pw-server/src/routes/matches/[match_id].svelte +++ b/web/pw-server/src/routes/matches/[match_id].svelte @@ -1,33 +1,25 @@ <script lang="ts" context="module"> - function fetchJson(url: string): Promise<Response> { - return fetch(url, { - headers: { - "Content-Type": "application/json", - }, - }); - } - - export async function load({ params }) { - // TODO: handle failure cases better - const matchId = params["match_id"]; - const matchDataResponse = await fetchJson(`/api/matches/${matchId}`); - if (!matchDataResponse.ok) { - } - const matchLogResponse = await fetchJson(`/api/matches/${matchId}/log`); - - if (matchDataResponse.ok && matchLogResponse.ok) { + import { ApiClient } from "$lib/api_client"; + export async function load({ params, fetch }) { + try { + const matchId = params["match_id"]; + const apiClient = new ApiClient(fetch); + const [matchData, matchLog] = await Promise.all([ + apiClient.get(`/api/matches/${matchId}`), + apiClient.getText(`/api/matches/${matchId}/log`), + ]); return { props: { - matchData: await matchDataResponse.json(), - matchLog: await matchLogResponse.text(), + matchData: matchData, + matchLog: matchLog, }, }; + } catch (error) { + return { + status: error.status, + error: error, + }; } - - return { - status: matchDataResponse.status, - error: new Error("failed to load match"), - }; } </script> diff --git a/web/pw-server/src/routes/matches/index.svelte b/web/pw-server/src/routes/matches/index.svelte index 448048b..8c106fa 100644 --- a/web/pw-server/src/routes/matches/index.svelte +++ b/web/pw-server/src/routes/matches/index.svelte @@ -1,36 +1,121 @@ <script lang="ts" context="module"> - export async function load() { - const res = await fetch("/api/matches", { - headers: { - "Content-Type": "application/json", - }, - }); + import { ApiClient } from "$lib/api_client"; + + const PAGE_SIZE = "50"; + + export async function load({ url, fetch }) { + try { + const apiClient = new ApiClient(fetch); + const botName = url.searchParams.get("bot"); + + let query = { + count: PAGE_SIZE, + before: url.searchParams.get("before"), + after: url.searchParams.get("after"), + bot: botName, + }; + + let { matches, has_next } = await apiClient.get("/api/matches", removeUndefined(query)); + + // TODO: should this be done client-side? + if (query["after"]) { + matches = matches.reverse(); + } - if (res.ok) { return { props: { - matches: await res.json(), + matches, + botName, + hasNext: has_next, + query, }, }; + } catch (error) { + return { + status: error.status, + error: new Error("failed to load matches"), + }; } + } - return { - status: res.status, - error: new Error("failed to load matches"), - }; + function removeUndefined(obj: Record<string, string>): Record<string, string> { + Object.keys(obj).forEach((key) => { + if (obj[key] === undefined || obj[key] === null) { + delete obj[key]; + } + }); + return obj; } </script> <script lang="ts"> - import dayjs from "dayjs"; - export let matches; + import LinkButton from "$lib/components/LinkButton.svelte"; + import MatchList from "$lib/components/matches/MatchList.svelte"; + + export let matches: object[]; + export let botName: string | null; + // whether a next page exists in the current iteration direction (before/after) + export let hasNext: boolean; + export let query: object; + + type Cursor = { + before?: string; + after?: string; + }; + + function pageLink(cursor: Cursor) { + let paramsObj = { + ...cursor, + }; + if (botName) { + paramsObj["bot"] = botName; + } + const params = new URLSearchParams(paramsObj); + return `?${params}`; + } + + function olderMatchesLink(matches: object[]): string { + if (matches.length == 0 || (query["before"] && !hasNext)) { + return null; + } + const lastTimestamp = matches[matches.length - 1]["timestamp"]; + return pageLink({ before: lastTimestamp }); + } + + function newerMatchesLink(matches: object[]): string { + if ( + matches.length == 0 || + (query["after"] && !hasNext) || + // we are viewing the first page, so there should be no newer matches. + // alternatively, we could show a "refresh" here. + (!query["before"] && !query["after"]) + ) { + return null; + } + const firstTimestamp = matches[0]["timestamp"]; + return pageLink({ after: firstTimestamp }); + } </script> -<a href="/matches/new">new match</a> -<ul> - {#each matches as match} - <li> - <a href="/matches/{match['id']}">{dayjs(match["created_at"]).format("YYYY-MM-DD HH:mm")}</a> - </li> - {/each} -</ul> +<div class="container"> + <MatchList {matches} /> + <div class="page-controls"> + <div class="btn-group"> + <LinkButton href={newerMatchesLink(matches)}>Newer</LinkButton> + <LinkButton href={olderMatchesLink(matches)}>Older</LinkButton> + </div> + </div> +</div> + +<style lang="scss"> + .container { + width: 800px; + margin: 0 auto; + } + + .page-controls { + display: flex; + justify-content: center; + margin: 24px 0; + } +</style> |