Debuta’s real-time layer is built on two technologies: Socket.io for messaging and presence, and WebRTC (viaDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/desarrolladorandres2026-gif/Native-tailwind/llms.txt
Use this file to discover all available pages before exploring further.
react-native-webrtc) for peer-to-peer audio and video calls. Both are orchestrated by React context providers that live at the root of the app and are available to every screen.
SocketContext — App-Wide Socket Connection
SocketProvider wraps the entire navigator tree inside app/_layout.tsx. It creates and owns a single Socket.io connection for the lifetime of the app session. Any component can read the socket and its connection state with the useSocket() hook.
context/SocketContext.tsx
URL Resolution
The socket uses the same URL resolution strategy as the REST API client. IfEXPO_PUBLIC_API_URL is set it connects to that address; otherwise it auto-detects the Metro dev server host from Constants.expoConfig.hostUri:
context/SocketContext.tsx
Connection Lifecycle
Initial connection
On mount,
SocketProvider calls initSocket() which reads access_token, user_name, and user_photo from AsyncStorage and opens the socket with those values in the auth payload.context/SocketContext.tsx
Token polling
At startup the token may not yet be in AsyncStorage (e.g., the user is mid-registration). A
setInterval poller runs every 3 seconds and retries initSocket() until a token is found and a connection is established. The interval is cleared once the socket connects.App foreground reconnect
An
AppState listener calls initSocket() whenever the app returns to the foreground (active) and the socket is not already connected.Server-Side Authentication
The socketauth payload carries the JWT. The backend validates this token in a Socket.io middleware before accepting the connection. On successful validation the server joins the socket to a private room named user:<id>, which is used for targeted event delivery (incoming calls, notifications, etc.).
Messaging — useChat
The useChat(matchedUserId: string) hook manages the full lifecycle of a chat conversation: loading history, receiving real-time messages, sending messages, and handling date suggestions.
Loading Messages
On mount the hook fetches conversation history over HTTP:hooks/useChat.ts
Sending a Message
When the socket is connected, messages are sent over themensaje:enviar event. If the socket is offline, the hook falls back to an HTTP POST:
hooks/useChat.ts
Receiving Messages
Incoming messages arrive on themensaje:nuevo event. A deduplication guard (msgIds ref of type Set<string>) ensures that a message is never rendered twice, even if the HTTP load and socket event overlap.
hooks/useChat.ts
Fallback Polling
If the socket is not connected when the hook mounts, a 5-second interval polls the history endpoint. The interval is cleared as soon as the socket becomesconnected.
Presence
Debuta tracks online/offline status using socket events broadcast by the server. No client-side code needs to emit these — the server handles them automatically when users connect and disconnect.| Event | Direction | Description |
|---|---|---|
presencia:estado | client → server | Request the online status of a specific user |
presencia:respuesta | server → client | Server’s reply with the user’s current status |
usuario:online | server → client | Broadcast when a user’s socket connects |
usuario:offline | server → client | Broadcast when a user’s socket disconnects |
Video and Audio Calls
Calls are managed by two cooperating layers:CallContext handles the call state machine and socket signaling; useWebRTC manages the underlying RTCPeerConnection, media streams, and ICE negotiation.
CallContext
CallProvider wraps the app inside app/_layout.tsx. When an incoming call arrives it renders IncomingCallScreen as an overlay on top of whatever screen is currently active, ensuring the user is always notified regardless of where they are in the app.
context/CallContext.tsx
Call Flow
Caller: initiateCall()
- Requests microphone (and camera if
isVideo) permissions viarequestMediaPermissions. - Creates an SDP offer using
useWebRTC.createOffer(toId, isVideo). - Emits
call:requestwith the real SDP offer, caller name, and photo. - Navigates to
app/call.tsxwithtype: 'outgoing'. - A 30-second timeout automatically ends the call if no answer arrives.
Recipient: call:incoming event
The server forwards the offer to the recipient’s
user:<id> room. CallContext receives call:incoming, validates the SDP, stores the CallInfo, triggers haptic feedback, and plays a ringtone. IncomingCallScreen is rendered as an overlay.Recipient: acceptCall()
- Requests media permissions.
- Calls
useWebRTC.createAnswer(fromId, signalData, isVideo)to set the remote description and generate an SDP answer. - Emits
call:acceptwith the real SDP answer. - Navigates to
app/call.tsxwithtype: 'active'.
Caller: call:accepted event
On receiving
call:accepted, the caller calls useWebRTC.setRemoteAnswer(signalData) to complete the SDP handshake. Navigation updates to show the active call UI.ICE candidate exchange
Both sides emit local ICE candidates via
call:signal as they are generated. The receiving side adds them with useWebRTC.addIceCandidate. Candidates that arrive before the remote description is set are buffered and flushed automatically.context/CallContext.tsx
Pending Call Delivery
If the recipient’s socket is not currently connected, the server holds the call request for up to 60 seconds. During this window the server emitscall:waiting to the caller to indicate the call is pending. When the recipient reconnects within the window, the server delivers call:incoming normally.
If the recipient does not reconnect in time, the server emits call:unavailable to the caller with a reason field.
Call Socket Events Reference
| Event | Direction | Payload | Description |
|---|---|---|---|
call:request | client → server | { paraId, isVideo, signalData, callerName, callerPhoto } | Initiate a call with a real SDP offer |
call:incoming | server → client | { fromId, callerName, callerPhoto, isVideo, signalData } | Delivered to recipient with the SDP offer |
call:accept | client → server | { paraId, signalData } | Accept call with a real SDP answer |
call:accepted | server → client | { signalData, answererId } | Delivered to caller with the SDP answer |
call:reject | client → server | { paraId } | Reject an incoming call |
call:rejected | server → client | — | Delivered to caller when call is rejected |
call:end | client → server | { paraId } | End an active call |
call:ended | server → client | — | Delivered to the other party when call ends |
call:signal | client ↔ server ↔ client | { paraId, signalData } | Relay ICE candidates between peers |
call:unavailable | server → client | { paraId, reason } | Recipient could not be reached |
call:waiting | server → client | { paraId, reason } | Call is pending — recipient is offline |
useWebRTC
useWebRTC(socket) exports an identical API whether running in Expo Go or a native build. In Expo Go it returns a stub implementation that shows an alert and produces no real audio or video. In a native build it uses react-native-webrtc to create a real RTCPeerConnection.
hooks/useWebRTC.ts
hooks/useWebRTC.ts
The OpenRelay TURN servers are suitable for development and testing. For production, register your own TURN credentials at metered.ca and update
RTC_CONFIG.Date Suggestion Events
After 5 messages have been exchanged within a match, the backend automatically pushes acita:sugerencia event to both users recommending a venue for a date. The useChat hook handles this transparently:
| Event | Description |
|---|---|
cita:sugerencia | Initial suggestion pushed to both users after the 5-message threshold |
cita:nueva-sugerencia | Pushed when either user calls requestNewPlace() via POST /matches/:id/suggest-new-place |
cita:estado-actualizado | Pushed when one user accepts or rejects the current suggestion |
hooks/useChat.ts
DateSuggestion payload includes the full restaurante object (name, description, address, photos, menu, hours, price range), a suggested fecha, and a recomendacion status object tracking whether each user has accepted.