The uChat Online Presence system provides real-time "active" status indicators across the Mallnline platform. It leverages a combination of WebSocket heartbeats and Redis TTL keys to track user connectivity globally, allowing users to see when their conversation partners are currently online.
Core Architecture
The presence system is built around a lightweight heartbeat protocol managed by the uChat service. It is designed to be highly performant and strictly privacy-aware.
Global WebSocket Connection
The presence heartbeat relies on the Global WebSocket. Unlike the per-conversation WebSocket which only connects when viewing a specific chat, the global WebSocket initializes as soon as an authenticated user accesses any page on the platform.
- Initialization: The
MessagesBadgecomponent in the global navigation triggersuchatStore.connectGlobalWebSocket(). - Heartbeat Loop: The frontend sends a
pingtext message every 20 seconds. - Redis Persistence: The uChat backend intercepts the
pingand sets a Redis keyuchat:presence:{userId}with a 30-second Time-to-Live (TTL). - Disconnection: If the user closes the tab or loses connection, the heartbeat stops, and the Redis key expires automatically 10 seconds later, marking them offline.
Presence Resolution
To avoid broadcasting presence state to every user (which is unscalable), presence is resolved via targeted polling:
- When viewing the conversation list, the frontend extracts the
userIdof all active participants. - The
uchatStoreexecutes theONLINE_USERSGraphQL query every 30 seconds. - The backend performs an
MGETagainst Redis for the requested IDs, returning the list of users who currently have an activeuchat:presence:{userId}key.
query OnlineUsers($userIds: [String!]!) {
onlineUsers(userIds: $userIds)
}
Privacy & Opt-Out
The system operates on a strict "opt-out" privacy model. If a user disables their online presence via their Privacy & Security settings, the system respects this invisibly.
When showOnlinePresence is false:
- The frontend appends
&invisible=trueto the WebSocket connection URL. - The
ws_handlerreads this flag and skips the Redis write upon connection and heartbeat. - The frontend completely stops polling the
ONLINE_USERSquery, saving network requests since the user has chosen not to participate in presence features.
Frontend State Management
The presence data is stored in a Svelte 5 $state Set within the uchatStore.
let onlineUsers = $state(new Set<string>());
export const uchatStore = {
isUserOnline: (userId: string) => onlineUsers.has(userId)
};
This ensures that the isOnline reactivity is granular and O(1). UI components like the conv-avatar and header-presence-dot simply call uchatStore.isUserOnline(userId), which Svelte tracks. Whenever the polling service replaces the onlineUsers set, Svelte automatically patches the DOM to show or hide the green pulsing indicators.
NOTE
For Users: See Understanding Online Presence for user-facing instructions on privacy toggles.
Related
- uChat โ E2EE Messenger โ Core backend architecture of the messaging service.
- uChat Client SDK & Interface โ Frontend integration details and state management.
- Privacy & Security APIs โ How user preferences like
showOnlinePresenceare managed and persisted.