Revert the automated member-ordering pass that broke Angular field init
(TS2729) and disable that rule until a safe reorder strategy exists.
Fix modal/confirm dialog i18n defaults via template fallbacks, search all
active endpoints (including offline), register foreign rooms with actor
owner IDs, sync profile display names from avatar summaries, and guard
dm-chat when a private call converts to a group conversation.
Co-authored-by: Cursor <cursoragent@cursor.com>
Remove member-ordering and complexity eslint-disable comments by reordering
class members and applying targeted fixes. Add metoyou/no-maybe-in-naming,
type-safe WebRTC e2e harness helpers, and resolve remaining lint errors so
npm run lint exits cleanly.
Co-authored-by: Cursor <cursoragent@cursor.com>
Bind custom emoji library membership to the signed-in user instead of the
client. CustomEmojiService now tracks saved emoji ids per user id in
localStorage (metoyou_custom_emoji_saved:<userId>) and the picker only shows
the active user's set, seeded on first load from legacy savedByUser rows the
user created. This stops a second account on the same client (or Electron's
shared SQLite database) from inheriting another user's emoji picker, while
keeping synced assets available for message rendering.
Adds unit coverage for per-user scoping and a single-page-load Playwright e2e
that switches users client-side (second user joins the first user's server)
and asserts no library leak.
Co-authored-by: Cursor <cursoragent@cursor.com>
Gate the "Shared from your device" label and the hidden download
affordance on whether this device actually holds the file bytes, not on
whether the current user uploaded it. uploaderPeerId is the user id, so
the old check claimed ownership on every device of the uploader,
blocking view/download on second devices that only synced metadata.
Also include attachment metadata in the account_sync chat-sync-batch so
sibling devices learn about synced attachments at all.
Co-authored-by: Cursor <cursoragent@cursor.com>
stale server sockets, passive non-initiators, and race conditions
during peer connection setup.
Fix users unable to see or hear each other in voice channels due to
stale server sockets, passive non-initiators, and race conditions
during peer connection setup.
Server:
- Close stale WebSocket connections sharing the same oderId in
handleIdentify instead of letting them linger up to 45s
- Make user_joined/user_left broadcasts identity-aware so duplicate
sockets don't produce phantom join/leave events
- Include serverIds in user_left payload for multi-room presence
- Simplify findUserByOderId now that stale sockets are cleaned up
Client - signaling:
- Add fallback offer system with 1s timer for missed user_joined races
- Add non-initiator takeover after 5s when the initiator fails to send
an offer (NON_INITIATOR_GIVE_UP_MS)
- Scope peerServerMap per signaling URL to prevent cross-server
collisions
- Add socket identity guards on all signaling event handlers
- Replace canReusePeerConnection with hasActivePeerConnection and
isPeerConnectionNegotiating with extended grace periods
Client - peer connections:
- Extract replaceUnusablePeer helper to deduplicate stale peer
replacement in offer and ICE handlers
- Add stale connectionstatechange guard to ignore events from replaced
RTCPeerConnection instances
- Use deterministic initiator election in peer recovery reconnects
- Track createdAt on PeerData for staleness detection
Client - presence:
- Add multi-room presence tracking via presenceServerIds on User
- Replace clearUsers + individual userJoined with syncServerPresence
for atomic server roster updates
- Make userLeft handle partial server removal instead of full eviction
Documentation:
- Add server-side connection hygiene, non-initiator takeover, and stale
peer replacement sections to the realtime README