aboutsummaryrefslogtreecommitdiff
path: root/web/pw-server/src/routes/matches
diff options
context:
space:
mode:
Diffstat (limited to 'web/pw-server/src/routes/matches')
-rw-r--r--web/pw-server/src/routes/matches/[match_id].svelte40
-rw-r--r--web/pw-server/src/routes/matches/index.svelte129
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>