Private
Public Access
Root cause of "still not getting push notifications while backgrounded" despite #31: joinRoom() was made to send unconditionally on the theory that opening a room always implies genuine visibility (a real click can't happen on a truly hidden tab). That's wrong for one real case -- mobile Chrome can silently discard and later reload a long-backgrounded tab from memory, which re-mounts the room and calls joinRoom() again with nobody actually looking at the screen. Each such reload re-joined the room's presence with no matching "leave" (a discard skips normal unmount cleanup), so a room could accumulate a stuck presence entry that permanently suppressed push notifications for it -- confirmed live via a user's server logs (repeated silent WS reconnects, and their account still showing present in the room's Redis presence hash while genuinely backgrounded). joinRoom() and the reconnect replay now check document.visibilityState live instead of trusting a cached ref or sending unconditionally: a still-hidden reload correctly stays "left" (the room stays in desiredRoomsRef, so the next genuine foreground transition still joins it, just deferred instead of wrongly immediate), while a real user-driven open still joins immediately as before. Verified both directions in the browser: mounting a room while genuinely hidden leaves the room's presence hash empty; a subsequent real visibility transition to visible correctly triggers the deferred join. Note: this prevents new stuck entries but doesn't retroactively clear any that already exist -- an affected user needs one real close (not just backgrounding) to send a clean disconnect and reset the stuck refcount. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
DS Chat frontend (Phase 1)
React + Vite PWA. Login, room list, and chat views wired to the backend's
REST API and /ws/chat WebSocket endpoint. See ../README.md
and ../backend/README.md for full local setup.
Dev
npm install
npm run dev
The dev server proxies /api and /ws to http://localhost:8000 (see
vite.config.ts), so the backend must be running for anything beyond the
login page to work.
Build
npm run build
Generates the PWA manifest and service worker via vite-plugin-pwa into dist/.
Layout
src/
main.tsx, App.tsx routes: /login, /rooms, /rooms/:roomId
api/ fetch wrappers (client, auth, rooms)
ws/useChatSocket.ts WebSocket hook (join/send/receive)
context/AuthContext.tsx current-user state, hydrated via GET /api/auth/me
components/ ProtectedRoute, RoomListItem, MessageList, MessageInput
pages/ LoginPage, RoomListPage, ChatRoomPage