ULagos 360° keeps every tutor’s browser in sync through a persistent WebSocket connection managed byDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/Pewiz/ulagos360/llms.txt
Use this file to discover all available pages before exploring further.
useSocketConnection.js. Rather than polling the server for changes, the app maintains a long-lived Socket.IO connection to the Railway backend at https://ulagos360-backend-production.up.railway.app. When any tutor updates a space, the change propagates to all connected clients in real time, ensuring the shared view of the campus stays accurate throughout the open-day event.
Optimistic Updates
To keep the interface feeling instant even on slow connections, ULagos 360° uses an optimistic update strategy: the local Zustand store is updated immediately when a tutor makes a change, before the network round-trip completes. The full flow for a single status change is:Tutor triggers an action
A tutor taps a button on a
SpaceCard — for example, marking a room as ocupado.Local state updates first
updateSpaceStatus in the Zustand store applies the change immediately. The UI reflects the new status with no perceptible delay.Persisted to localStorage
The updated spaces map is written to
localStorage under both the spaces-storage Zustand key and the ulagos360_spaces_backup emergency key.update_space emitted to server
sendSpaceUpdate fires the update_space event over the Socket.IO connection, carrying the spaceId, the full spaceData updates, the userId, userName, sessionId, and a monotonic timestamp.Server broadcasts space_updated
The backend retransmits a
space_updated event to every other connected client.Client-Emitted Events
These are the Socket.IO events that the browser sends to the backend.| Event | When it is emitted |
|---|---|
register_user | On tutor identification or on reconnect when a session already exists in localStorage. Carries id, name, sessionId, and persistentSession: true. |
get_all_spaces | Immediately after connecting, to hydrate the local store with the server’s authoritative state. Also called by forceServerSync. |
update_space | Every time a tutor changes the status of a space from the UI. |
sync_spaces | Backup upload triggered ~5 s after connect if the server has not yet responded with a full state. Sends the entire local spaces map. |
user_logout | When a tutor explicitly logs out. Signals the server to clean up the session before the socket disconnects. |
pong | Heartbeat reply sent automatically whenever the server emits ping. Carries userId, sessionId, and the current timestamp. |
Server-to-Client Events
These are the events the browser listens for.| Event | Effect on the client |
|---|---|
spaces_state | Full state hydration — iterates all entries and calls updateSpace for each. Also handled by all_spaces_state, full_state_response, spaces_sync_response, spaces_data, state_update, sync_response, and user_state_response. |
space_updated | Incremental update for a single space. Applies data.updates to the matching data.spaceId in the Zustand store. |
user_registered / login_success | Confirms tutor registration. The client persists the returned user object to localStorage and updates the store’s currentUser. Also handled by register_response and user_confirmed. |
ping | Server keepalive probe. The client replies immediately with pong. |
The client registers listeners for multiple event name aliases for the same logical action (e.g.
spaces_state, all_spaces_state, full_state_response) to stay compatible with different versions of the Railway backend without requiring a coordinated deploy.Connection Configuration
The Socket.IO client is initialised inuseSocketConnection.js with the following options:
- Transport order — the connection begins as HTTP long-polling (
polling) and automatically upgrades to a full WebSocket (websocket) once the handshake succeeds. This maximises compatibility with Railway’s proxy layer. - Reconnection — up to 5 attempts, starting 2 s after the first disconnect, backing off to a maximum of 10 s with a ±50 % jitter factor.
- Server-initiated disconnect — if the server sends a
disconnectreason of"io server disconnect", the client immediately callssocket.connect()to re-establish the link rather than waiting for the reconnection timer.
Conflict Resolution
Because multiple tutors can update the same space nearly simultaneously, the Zustand store uses ashouldAcceptServerUpdate guard before applying inbound space_updated payloads:
The conflict resolution rule is: the newer
lastUpdate timestamp wins. If the difference between the server update’s timestamp and the local record’s timestamp is less than 1 second, the server update is accepted unconditionally (to avoid rejecting near-simultaneous changes). If the difference is 1 s or more, the update with the later timestamp is kept.