Commit graph

17 commits

Author SHA1 Message Date
lila
540155788a fix(api): use server.listen instead of app.listen for WebSocket support
- server.ts: switch from app.listen() to server.listen() so WebSocket
  upgrade handler is on the same server as HTTP requests
- lobbyService: add host as first player on lobby creation
- ws-client: guard against reconnect when already connecting
- ws-provider: skip connect if already connected
2026-04-18 21:57:58 +02:00
lila
974646ebfb feat(web): update navigation with Play and Multiplayer links
- Add Play link to /play
- Add Multiplayer link to /multiplayer
- Remove About link (route kept, just not linked)
- Simplify signOut onClick to .then() chain
2026-04-18 10:59:50 +02:00
lila
f2eb6ce17f feat(web): add multiplayer lobby, game, and score screen routes
- lobby.$code.tsx: waiting room with live player list via lobby:state,
  copyable lobby code, host Start Game button (disabled until 2+ players),
  sends lobby:join on connect, lobby:leave on unmount
- game.$code.tsx: in-game view, sends game:ready on mount to get current
  question, handles game:question/answer_result/finished messages,
  reuses QuestionCard component, shows round results after each answer
- MultiplayerScoreScreen: final score screen sorted by score, highlights
  winner(s) with crown, handles ties via winnerIds array, Play Again
  navigates back to lobby, Leave goes to multiplayer landing
- GameRouteSearchSchema added to shared for typed lobbyId search param
  without requiring Zod in apps/web
2026-04-18 10:33:48 +02:00
lila
d064338145 feat(web): add multiplayer lobby waiting room
- connects WebSocket on mount, sends lobby:join after connection open
- registers handlers for lobby:state, game:question, error messages
- lobby:state updates player list in real time
- game:question navigates to game route (server re-sends via game:ready)
- displays lobby code as copyable button
- host sees Start Game button, disabled until 2+ players connected
- non-host sees waiting message
- cleanup sends lobby:leave and disconnects on unmount
- lobbyIdRef tracks lobby id for reliable cleanup before lobby state arrives
2026-04-18 10:10:25 +02:00
lila
4d4715b4ee feat(web): add multiplayer layout route and landing page
- multiplayer.tsx: layout route wrapping all multiplayer children
  with WsProvider, auth guard via beforeLoad
- multiplayer/index.tsx: create/join landing page
  - POST /api/v1/lobbies to create, navigates to lobby waiting room
  - POST /api/v1/lobbies/:code/join to join, normalizes code to
    uppercase before sending
  - loading states per action, error display, Enter key on join input
  - imports Lobby type from @lila/shared (single source of truth)
2026-04-17 21:33:40 +02:00
lila
9affe339c6 feat(web): add WebSocket client and context infrastructure
- WsClient class: connect/disconnect/send/on/off/isConnected/clearCallbacks
- connect() derives wss:// from https:// automatically, returns Promise<void>
- on/off typed with Extract<WsServerMessage, { type: T }> for precise
  callback narrowing, callbacks stored as Map<string, Set<fn>>
- ws-context.ts: WsContextValue type + WsContext definition
- ws-provider.tsx: WsProvider with module-level wsClient singleton,
  owns connection lifecycle (connect/disconnect/isConnected state)
- ws-hooks.ts: useWsClient, useWsConnected, useWsConnect, useWsDisconnect
2026-04-17 21:12:15 +02:00
lila
d60b0da9df feat(web): add WsClient class for multiplayer WebSocket communication
- connect(apiUrl) derives wss:// from https:// automatically, returns
  Promise<void> resolving on open, rejecting on error
- disconnect() closes connection, no-op if already closed
- isConnected() checks readyState === OPEN
- send(message) typed to WsClientMessage discriminated union
- on/off typed with Extract<WsServerMessage, { type: T }> for
  precise callback narrowing per message type
- callbacks stored as Map<string, Set<fn>> supporting multiple
  listeners per message type
- clearCallbacks() for explicit cleanup on provider unmount
- onError/onClose as separate lifecycle properties distinct
  from message handlers
2026-04-17 20:44:33 +02:00
lila
ce19740cc8 fix(lint): resolve all eslint errors across monorepo
- Type response bodies in gameController.test.ts to fix no-unsafe-member-access
- Replace async methods with Promise.resolve() in InMemoryGameSessionStore
  and InMemoryLobbyGameStore to satisfy require-await rule
- Add argsIgnorePattern and varsIgnorePattern to eslint config so
  underscore-prefixed params are globally ignored
- Fix no-misused-promises in ws/index.ts, lobbyHandlers, gameHandlers,
  __root.tsx, login.tsx and play.tsx by using void + .catch()
- Fix no-floating-promises on navigate calls in login.tsx
- Move API_URL outside Play component to fix useCallback dependency warning
- Type fetch response bodies in play.tsx to fix no-unsafe-assignment
- Add only-throw-error: off for route files (TanStack Router throw redirect)
- Remove unused WebSocket import from express.d.ts
- Fix unsafe return in connections.ts by typing empty Map constructor
- Exclude scripts/ folder from eslint
- Add targeted override for better-auth auth-client.ts (upstream typing issue)
2026-04-17 16:46:33 +02:00
lila
bc38137a12 feat: add production deployment config
- Add docker-compose.prod.yml and Caddyfile for Caddy reverse proxy
- Add production stages to frontend Dockerfile (nginx for static files)
- Fix monorepo package exports for production builds (dist/src paths)
- Add CORS_ORIGIN env var for cross-origin config
- Add Better Auth baseURL, cookie domain, and trusted origins from env
- Use VITE_API_URL for API calls in auth-client and play route
- Add credentials: include for cross-origin fetch requests
- Remove unused users table from schema
2026-04-14 11:38:40 +02:00
lila
3f7bc4111e chore: rename project from glossa to lila
- Update all package names from @glossa/* to @lila/*
- Update all imports, container names, volume names
- Update documentation references
- Recreate database with new credentials
2026-04-13 10:00:52 +02:00
lila
a3685a9e68 feat(api): add auth middleware to protect game endpoints
- Add requireAuth middleware using Better Auth session validation
- Apply to all game routes (start, answer)
- Unauthenticated requests return 401
2026-04-12 13:38:32 +02:00
lila
bc7977463e feat(web): add game settings screen and submit confirmation
- Add GameSetup component with Duolingo-style button selectors for
  language pair, POS, difficulty, and rounds
- Language swap: selecting the same language for source and target
  automatically swaps them instead of allowing duplicates
- Add selection-before-submission flow: user clicks an option to
  highlight it, then confirms with a Submit button to prevent misclicks
- Add selected state to OptionButton (purple ring highlight)
- Play Again on score screen returns to settings instead of
  auto-restarting with the same configuration
- Remove hardcoded GAME_SETTINGS, game configuration is now user-driven
2026-04-11 21:18:35 +02:00
lila
b7b1cd383f refactoring ui into separate components, updating ui, adding color scheme 2026-04-11 20:53:10 +02:00
lila
ea33b7fcc8 feat(web): add minimal playable quiz at /play
- Add Vite proxy for /api → localhost:3000 (no CORS needed in dev)
- Create /play route with hardcoded game settings (en→it, nouns, easy)
- Three-phase state machine: loading → playing → finished
- Show prompt, optional gloss, and 4 answer buttons per question
- Submit answers to /api/v1/game/answer, show correct/wrong feedback
- Manual Next button to advance after answering
- Score screen on completion
- Add selectedOptionId to AnswerResult schema (discovered during
  frontend work that the result needs to be self-contained for
  rendering feedback without separate client state)

Intentionally unstyled — component extraction and polish come next.
2026-04-11 12:56:03 +02:00
lila
681c6d2b4f installing and configuring tailwind 2026-03-21 20:59:26 +01:00
lila
2025cc7298 chore: configure root eslint with react and tanstack router rules 2026-03-21 19:32:38 +01:00
lila
1765923cb6 feat: scaffold vite react app and configure web package 2026-03-21 11:59:52 +01:00