Files
ds-chat/frontend
ksmithandClaude Sonnet 5 2cd44dc3f7 Fix push notifications permanently suppressed by silent background reloads
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>
2026-08-16 19:14:05 -06:00
..

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