From 2d5f913a431c4223a16c88551ffff4100ac483c4 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sun, 28 Jul 2024 16:01:41 +0900 Subject: feat: implement game entry --- frontend/app/.server/api/schema.d.ts | 78 ++++++++++++++++++++++++++++++++++++ frontend/app/.server/auth.ts | 12 ++++-- frontend/app/routes/dashboard.tsx | 44 +++++++++++++++++--- 3 files changed, 125 insertions(+), 9 deletions(-) (limited to 'frontend/app') diff --git a/frontend/app/.server/api/schema.d.ts b/frontend/app/.server/api/schema.d.ts index d9ce187..cd87705 100644 --- a/frontend/app/.server/api/schema.d.ts +++ b/frontend/app/.server/api/schema.d.ts @@ -64,6 +64,60 @@ export interface paths { patch?: never; trace?: never; }; + "/games": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List games */ + get: { + parameters: { + query?: { + player_id?: number; + }; + header: { + Authorization: string; + }; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description List of games */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + games: components["schemas"]["Game"][]; + }; + }; + }; + /** @description Forbidden */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example Forbidden operation */ + message: string; + }; + }; + }; + }; + }; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; } export type webhooks = Record; export interface components { @@ -80,6 +134,30 @@ export interface components { /** @example false */ is_admin: boolean; }; + Game: { + /** @example 1 */ + game_id: number; + /** + * @example active + * @enum {string} + */ + state: "closed" | "waiting_entries" | "waiting_start" | "prepare" | "starting" | "gaming" | "finished"; + /** @example Game 1 */ + display_name: string; + /** @example 360 */ + duration_seconds: number; + /** @example 946684800 */ + started_at?: number; + problem?: components["schemas"]["Problem"]; + }; + Problem: { + /** @example 1 */ + problem_id: number; + /** @example Problem 1 */ + title: string; + /** @example This is a problem */ + description: string; + }; }; responses: never; parameters: never; diff --git a/frontend/app/.server/auth.ts b/frontend/app/.server/auth.ts index 822d4b9..988b30c 100644 --- a/frontend/app/.server/auth.ts +++ b/frontend/app/.server/auth.ts @@ -39,7 +39,7 @@ export async function isAuthenticated( failureRedirect?: never; headers?: never; }, -): Promise; +): Promise<{ user: User; token: string } | null>; export async function isAuthenticated( request: Request | Session, options: { @@ -55,7 +55,7 @@ export async function isAuthenticated( failureRedirect: string; headers?: HeadersInit; }, -): Promise; +): Promise<{ user: User; token: string }>; export async function isAuthenticated( request: Request | Session, options: { @@ -87,7 +87,7 @@ export async function isAuthenticated( failureRedirect: string; headers?: HeadersInit; } = {}, -): Promise { +): Promise<{ user: User; token: string } | null> { // This function's signature should be compatible with `authenticator.isAuthenticated` but TypeScript does not infer it correctly. let jwt; const { successRedirect, failureRedirect, headers } = options; @@ -114,5 +114,9 @@ export async function isAuthenticated( if (!jwt) { return null; } - return jwtDecode(jwt); + const user = jwtDecode(jwt); + return { + user, + token: jwt, + }; } diff --git a/frontend/app/routes/dashboard.tsx b/frontend/app/routes/dashboard.tsx index 3ad465f..9836d1b 100644 --- a/frontend/app/routes/dashboard.tsx +++ b/frontend/app/routes/dashboard.tsx @@ -1,15 +1,33 @@ import type { LoaderFunctionArgs } from "@remix-run/node"; +import { Link, useLoaderData } from "@remix-run/react"; import { isAuthenticated } from "../.server/auth"; -import { useLoaderData } from "@remix-run/react"; +import { apiClient } from "../.server/api/client"; export async function loader({ request }: LoaderFunctionArgs) { - return await isAuthenticated(request, { + const { user, token } = await isAuthenticated(request, { failureRedirect: "/login", }); + const { data, error } = await apiClient.GET("/games", { + params: { + query: { + player_id: user.user_id, + }, + header: { + Authorization: `Bearer ${token}`, + }, + }, + }); + if (error) { + throw new Error(error.message); + } + return { + user, + games: data.games, + }; } export default function Dashboard() { - const user = useLoaderData()!; + const { user, games } = useLoaderData()!; return (
@@ -24,10 +42,26 @@ export default function Dashboard() {
  • Name: {user.display_name}
  • -

    Game

    +

    Games

      -
    • TODO
    • + {games.map((game) => ( +
    • + {game.display_name}{" "} + {game.state === "closed" || game.state === "finished" ? ( + + Entry + + ) : ( + + Entry + + )} +
    • + ))}
    -- cgit v1.2.3-70-g09d2