Lesson 6 of 7 · 4 min

Real-Time Delivery, Presence, and Notifications

How messages arrive instantly via WebSocket, how typing indicators and online presence work, and how push notifications route back to the right conversation.

Real-Time Delivery, Presence, and Notifications

Video coming soon

6/7

Lesson Notes

01

The webapp's messaging system uses a persistent WebSocket connection for real-time delivery. When you open a job's chat panel, the frontend joins the corresponding chat room for both the active channel and any additional channels you have access to. Incoming messages from other participants appear immediately without a page refresh — you see the new bubble slide in at the bottom of the thread. If the WebSocket is unavailable or disconnects (for example, on a flaky network), the system falls back to HTTP polling: it re-fetches the message list every 5 seconds for active rooms and every 15 seconds when the socket is connected as a safety net. This means messages are always delivered; they may just arrive up to 15 seconds late in the worst case.

02

Typing indicators appear at the bottom of the chat panel when another participant is composing a message in the same channel. The indicator reads '[Name] is typing...' with an animated three-dot ellipsis. If multiple people are typing simultaneously, the text reads '[Name] and [Name] are typing...' or '[Name] and N others are typing...' for larger groups. Your own typing activity is broadcast to others as soon as you begin entering text in the composer, and it stops automatically when you send or clear the field.

03

Online presence is shown as a small green circle overlaid on a sender's avatar in the chat panel. If a user has the job open and their WebSocket connection is active, they appear as online. This indicator updates live as users join or leave the room. This is a lightweight signal — it only tells you someone is looking at the job right now, not that they will respond immediately. The presence system is not surfaced in the iOS app, which uses polling rather than WebSocket subscriptions.

04

On the iOS app, new message delivery depends on push notifications when the app is backgrounded. The backend's notification service sends a push payload with the notification type `NEW_MESSAGE` and a `projectId` field whenever a new Customer-channel or Team-channel message arrives for a job you participate in. Tapping the notification banner opens the iOS app, which routes the payload to `OrderDetailView(orderId: id, openAction: .chat)`. This open action causes the chat sheet to appear automatically when the order detail mounts — you land directly in the conversation without any extra taps.

05

Read receipts work in both directions. On the webapp, when you scroll the chat panel and messages become visible, the frontend marks them as read via a WebSocket event. The sender's outgoing bubble then upgrades from two grey checks (delivered) to two blue checks (read). This is the same visual language as WhatsApp and iMessage. On the iOS app, messages are marked read via `POST /messages/:id/read` when the chat sheet is open. The web and iOS read states sync through the same backend, so if you read a message on iOS the blue checks appear on the web version as well.

Key Takeaways

  • The webapp uses WebSocket for instant delivery, with HTTP polling as an automatic fallback.
  • Typing indicators show other participants' names in real time at the bottom of the chat panel.
  • A green presence dot on an avatar means that person currently has the job open.
  • On iOS, tapping a NEW_MESSAGE push notification opens the order detail with the chat sheet pre-opened.
  • Blue double-check marks mean the other side has read your message; they sync between web and iOS.
Related documentation at docs.vremly.com