Skip to content

Chat

Chat covers all messaging surfaces: 1:1 direct messages, freeform group chats, and the embedded chat rooms that every event spawns. The UX is unified — the user moves between flavours with the same gestures.

Entry points

From Action
Nav dock The chat bubble icon opens the inbox at /connect.
Profile → "Message" Opens or creates a 1:1 room with that user.
Event detail → "Chat" Opens the event's embedded group room. Available to approved participants only.
Friend marker on the map Profile-mini → "Message" → 1:1 room.
New chat (+) The compose action in the inbox opens a contact picker.
Notification deep link (Future: push notification opens the room.)

Primary flow — open a room and send

  1. Inbox. The inbox lists the user's rooms sorted by last activity. Each row shows: avatar (or group mosaic), name, last-message preview, time, unread count.
  2. Tap a room. Mobile pushes the room as a fullscreen view; desktop / wide tablet shows the room in a right-pane split.
  3. Connect. The app opens a WebSocket to the room. Existing messages render bottom-up. A "Joined room" indicator briefly appears for the user, visible only to others.
  4. Compose. The composer at the bottom accepts text. Tapping the attachment icon offers Image (camera or library). Tapping the smile icon offers an emoji picker.
  5. Send. The message appears immediately in the user's view with a "sending" tick. On server ack it flips to "sent". On read by the other party, it flips to "read" (when receipts are enabled by both sides).
  6. Receive. Incoming messages slide in from the bottom. If the user is scrolled away from the bottom, a "↓ N new" pill appears.
  7. Leave the room. Back-navigates to the inbox; the WebSocket disconnects with a clean close. Other users see the user's presence drop.

Sub-flows

Sub-flow Behavior
1:1 DM Two-person room. No "members" management. Profile avatar of the other user as the header.
Group chat (freeform) Created via "+ → New group". User picks members from friends. After creation, the group has a name, an avatar, and a settings page.
Event chat Auto-created when the event is created. The host is admin; approved participants are members. The room name follows the event title.
Reply to a message Long-press → "Reply" pins the target message above the composer as a preview chip. Sending bundles the reply reference; recipients see the preview inline above the new message.
Edit a message Long-press a sent message → "Edit". The composer prefills. Saved messages show an "edited" hint below the bubble. Edits push to other clients in real time.
Delete a message Long-press → "Delete". Soft-deletes the message — bubble collapses to "Message deleted" for everyone.
React to a message Long-press → emoji bar appears. Tap one to add a reaction. A user can have at most one reaction per message; tapping the same emoji removes it.
Read receipts When both parties have read receipts on, sent messages show a "read" tick when the recipient opens the room. Either user can disable them in preferences; the indicator goes blank for both sides.
Typing indicator While the other party is composing, an animated "..." appears under the room header. Stops when they pause or send.
Disappearing messages The group / DM settings has a "Disappearing messages" picker: off / seen / 24h / 7d. Toggling applies to future messages only — historic messages don't suddenly start expiring.
Image attachment Selecting an image uploads it; an inline progress overlay shows on the placeholder bubble. On completion, the bubble becomes the image with rounded corners.
Forward Long-press → "Forward" opens a picker of recent rooms; selecting one inserts the message there as a forwarded card.
Mute Group settings → "Mute". Mute hides notifications for the room. The icon next to the room name shows a muted indicator.
Nickname Each user can set a nickname for themselves in a group room. Other members see them by nickname.
Add / remove member Group admin: settings → "Members" → "+" for picker, swipe-left on a row for remove. A system message announces the change.
Search Tap the search icon in the room header for in-room search, or use the inbox search for cross-room search. Results highlight the matched term.

States

State What the user sees
Inbox empty A muted illustration + copy: "No conversations yet — find a friend or join an event."
Inbox loading Skeleton rows.
Room loading A spinner in the center; composer is disabled.
WebSocket disconnected A thin banner under the header: "Reconnecting..." Messages typed during disconnect queue locally; on reconnect they send in order.
Send failed The message shows a red ⚠ icon. Tapping retries the send.
No messages yet The room shows the room avatar + name in the center with copy: "Say hi."
Anonymized sender When the original sender deleted their account, their bubbles render as "Deleted user" with a neutral avatar. History is preserved.
Disappearing-msg ticker Messages with an expiry show a small timer pill in the corner indicating remaining time.
Expired in-session When a disappearing message's TTL elapses while the user is reading, the bubble fades out with a brief animation and disappears.

Edge cases & gotchas

  • Heartbeat keeps the room open. The client pings every 30 seconds. Backgrounding the app pauses the ping; the OS may close the socket. On foreground, the client reconnects automatically and re-fetches anything missed.
  • Reconnect is exponential with a cap. Five attempts at 1s, 2s, 4s, 8s, 10s. After that, the user sees a persistent "Couldn't reconnect — pull to refresh" banner.
  • Edits and deletes apply locally first. They appear immediately and confirm asynchronously. If the server rejects (e.g., editing someone else's message), the change reverts and a toast appears.
  • Read receipts are mutual. If the other side has disabled them, the user can't see their own "read" ticks either — there's no one-way leak.
  • Disappearing messages are forward-only. Toggling 7d on does not retroactively age old messages; only messages sent after the toggle have an expiry. This is by design and is mentioned in the settings sheet.
  • Event room retention. Once the parent event passes end_time + retention_days, the event chat's messages are wiped by a background job. The user sees the room go empty on next open. The room itself stays (in case anyone wants to revisit the participant list).
  • DM retention. Direct and freeform group rooms are not purged by the retention worker — only event rooms are.
  • Friends-only check on DMs. Sending a DM does not require friendship — anyone can initiate. (Product rule may tighten this; see product/domains/.)
  • Forwarding a friends-only moment. Forwarded preview cards inherit the original visibility; a non-friend recipient sees "Not available" in place of the preview.
  • Image upload during background. The OS may suspend the upload. On resume, the bubble shows a retry icon — the user can re-send.
  • Multi-device. A user with the app on two devices opens both — both connect to the same room. Messages render on both; the read receipt is set when either device opens the room.

See also