Systems

Chat overhaul: per-tab badges, item linking polish, and a code cleanup pass

The chat system got a thorough engineering pass this week. The most visible change: unread counts are now tracked per tab instead of globally. The chat button badge still shows a total, but opening chat and landing on Global no longer clears your Corp or Private unread indicators. Each tab tracks its own count and clears only when you actually look at it.

Under the hood, the realtime subscription architecture was tightened. Guild channel handlers now explicitly reject whispers — Supabase Realtime doesn't apply row-level security, so the client has always been responsible for filtering messages not meant for it. That guard is now explicit and documented rather than implicit. The subscribe-before-fetch ordering that prevents message loss at startup was already in place; the pattern is now codified so it doesn't get broken accidentally.

Cooldown state was simplified from two variables (a boolean flag and a countdown) to one number. Zero means ready, anything higher means cooling down. The send button renders the countdown directly. An interval cleanup on unmount prevents timer leaks.

FlatList render performance improved by memoizing the renderItem callback and the reversed message arrays used as list data. Previously, every state change that touched chat context would force a full list re-render. Now rerenders are scoped to actual message changes.

The item linking flow that shipped last week also got a fix: linked item previews now use snapshot data, so enhancing or discarding an item after you've shared it doesn't retroactively change what recipients see.