From 043ac80784bd6e71c0afc92a311b08fb30e5481d Mon Sep 17 00:00:00 2001 From: Antonio De Lucreziis Date: Wed, 20 Nov 2024 16:56:26 +0100 Subject: [PATCH] even more changes --- bun.lockb | Bin 220583 -> 220615 bytes package.json | 1 + src/components/Leaderboard.tsx | 16 +++++++--- src/components/LiveLeaderboard.tsx | 47 ++++++++++++++++++++++++++--- src/db/index.ts | 3 ++ src/ggwp/index.ts | 22 ++++++++------ src/pages/api/room/[id]/index.ts | 33 +++++++++++++++++--- src/styles.css | 7 +++++ 8 files changed, 106 insertions(+), 23 deletions(-) diff --git a/bun.lockb b/bun.lockb index 902a4bee3d417db8aa59ef8c628165a3a70bd872..e199d0ba6b652c2c8184dffb6cf7ecc167947dd4 100755 GIT binary patch delta 29580 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 delta 29666 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 diff --git a/package.json b/package.json index 83a3c82..4479caf 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "@paralleldrive/cuid2": "^2.2.2", "astro": "^4.16.13", "better-sqlite3": "^11.5.0", + "clsx": "^2.1.1", "nanoid": "^5.0.8", "preact": "^10.24.3", "typescript": "^5.6.3" diff --git a/src/components/Leaderboard.tsx b/src/components/Leaderboard.tsx index 9d19d83..4355f4e 100644 --- a/src/components/Leaderboard.tsx +++ b/src/components/Leaderboard.tsx @@ -1,4 +1,5 @@ import type { Question, Scoreboard } from '@/ggwp' +import clsx from 'clsx' type Props = { questions: Question[] @@ -12,7 +13,7 @@ export const Leaderboard = ({ questions, scoreboard, selectTeamQuestion }: Props
@@ -24,7 +25,7 @@ export const Leaderboard = ({ questions, scoreboard, selectTeamQuestion }: Props ))}
- {scoreboard.map(({ team, totalScore, questionScores }, i) => ( + {scoreboard.board.map(({ team, totalScore, questionScores }, i) => (
{team}
@@ -33,11 +34,16 @@ export const Leaderboard = ({ questions, scoreboard, selectTeamQuestion }: Props
{questions.map((question, j) => (
{ - console.log('emitting', { team, question: question.id }) - return selectTeamQuestion?.({ team, question: question.id }) + if (selectTeamQuestion) { + console.log('emitting', { team, question: question.id }) + selectTeamQuestion({ team, question: question.id }) + } }} > {questionScores[question.id]} diff --git a/src/components/LiveLeaderboard.tsx b/src/components/LiveLeaderboard.tsx index f652c82..349fb68 100644 --- a/src/components/LiveLeaderboard.tsx +++ b/src/components/LiveLeaderboard.tsx @@ -1,17 +1,54 @@ +import { requestJSON } from '@/client/utils' import type { Room } from '@/db/model' +import { useEffect, useState } from 'preact/hooks' import { Leaderboard } from './Leaderboard' -import { type Dispatch, type StateUpdater } from 'preact/hooks' import { computeScoreboardState } from '@/ggwp' type Props = { - room: Room - setRoom: Dispatch> + roomId: string +} + +const useEventSource = (roomId: string, onMessage: (data: any) => void) => { + useEffect(() => { + const es = new EventSource(`/api/room/${roomId}?sse`) + es.onmessage = e => { + onMessage(JSON.parse(e.data)) + } - selectTeamQuestion?: ({ team, question }: { team: string; question: string }) => void + return () => { + es.close() + } + }, [roomId]) } -export const LiveLeaderboard = ({ room, selectTeamQuestion }: Props) => { +export const LiveLeaderboard = ({ roomId }: Props) => { + const [room, setRoom] = useState(null) + const fetchRoom = async () => { + await requestJSON(`/api/room/${roomId}`).then(room => { + setRoom({ id: roomId, ...room }) + }) + } + + useEffect(() => { + fetchRoom() + }, []) + + useEventSource(roomId, data => { + console.log('event', data) + fetchRoom() + }) + if (!room) { return
Loading...
} + + const scoreboard = computeScoreboardState( + { + teamIds: room.teams, + questions: room.questions, + }, + room.actions + ) + + return } diff --git a/src/db/index.ts b/src/db/index.ts index b73ebe5..a7f2ae8 100644 --- a/src/db/index.ts +++ b/src/db/index.ts @@ -2,6 +2,7 @@ import cuid2 from '@paralleldrive/cuid2' import Database from 'better-sqlite3' import type { Room, RoomData } from './model' import type { Question } from '@/ggwp' +import { emitRoomUpdate } from './events' const db = new Database('ggwp.db') db.pragma('journal_mode = WAL') @@ -54,6 +55,8 @@ export function createRoom(id: string, teams: string[], questions: Question[]): } export function updateRoom(id: string, data: RoomData): void { + emitRoomUpdate(id, data) + db.prepare<[string, string]>( ` UPDATE rooms diff --git a/src/ggwp/index.ts b/src/ggwp/index.ts index 04dc026..3ec3962 100644 --- a/src/ggwp/index.ts +++ b/src/ggwp/index.ts @@ -12,10 +12,13 @@ export type Game = { } export type Scoreboard = { - team: string - totalScore: number - questionScores: { [id: string]: number } -}[] + teamJollyGroup: { [team: string]: string } + board: { + team: string + totalScore: number + questionScores: { [id: string]: number } + }[] +} function getCorrectnessMultiplier(outcome: 'correct' | 'partial' | 'wrong'): number { switch (outcome) { @@ -101,8 +104,6 @@ export function computeScoreboardState(game: Game, rawActions: Action[]): Scoreb }) } - console.log(actionsByQuestion) - const scoreboardByTeam: { [team: string]: { totalScore: number @@ -163,17 +164,20 @@ export function computeScoreboardState(game: Game, rawActions: Action[]): Scoreb scoreboardByTeam[team].bonusJolly = teamBonus } - const scoreboard: Scoreboard = [] + const scoreboard: Scoreboard = { + teamJollyGroup: teamsJollyGroup, + board: [], + } for (const team of game.teamIds) { - scoreboard.push({ + scoreboard.board.push({ team, totalScore: scoreboardByTeam[team].totalScore, questionScores: scoreboardByTeam[team].questionScores, }) } - scoreboard.sort((a, b) => b.totalScore - a.totalScore) + scoreboard.board.sort((a, b) => b.totalScore - a.totalScore) return scoreboard } diff --git a/src/pages/api/room/[id]/index.ts b/src/pages/api/room/[id]/index.ts index 302b516..ce56951 100644 --- a/src/pages/api/room/[id]/index.ts +++ b/src/pages/api/room/[id]/index.ts @@ -1,10 +1,35 @@ import { getRoom, updateRoom } from '@/db' +import { addRoomUpdateListener, removeRoomUpdateListener } from '@/db/events' import type { RoomData } from '@/db/model' import type { APIRoute } from 'astro' -async function sseHandler() { - // https://github.com/MicroWebStacks/astro-examples/blob/main/03_sse-counter/src/pages/api/stream.js - return new Response('SSE not implemented', { status: 501 }) +function sseHandler(roomId: string) { + let events_listener: () => void + + const stream = new ReadableStream({ + start(controller) { + events_listener = () => { + const data = `data: ${JSON.stringify('room')}\r\n\r\n` + controller.enqueue(data) + } + + removeRoomUpdateListener(roomId, events_listener) + addRoomUpdateListener(roomId, events_listener) + }, + cancel() { + console.log('stream.js> cancel()') + removeRoomUpdateListener(roomId, events_listener) + }, + }) + + return new Response(stream, { + status: 200, + headers: { + 'Content-Type': 'text/event-stream', + 'Connection': 'keep-alive', + 'Cache-Control': 'no-cache', + }, + }) } export const GET: APIRoute = async ({ params, url }) => { @@ -14,7 +39,7 @@ export const GET: APIRoute = async ({ params, url }) => { } if (url.searchParams.has('sse')) { - return await sseHandler() + return sseHandler(roomId) } const room = getRoom(roomId) diff --git a/src/styles.css b/src/styles.css index ed2d90f..97ec932 100644 --- a/src/styles.css +++ b/src/styles.css @@ -452,6 +452,13 @@ button { border-right: 2px solid #333; border-bottom: 2px solid #333; + &.jolly { + /* background: oklch(from gold 1 0.15 h); */ + + color: oklch(from gold 0.2 0.2 h); + background: linear-gradient(-45deg, oklch(from gold 0.9 0.2 h), oklch(from gold 1 0.05 h)); + } + &:first-child { border-left: 2px solid #333; }