Communication
Chat List
Inbox and chat modal for 1:1 booking conversations - conversation list with search and unread badges, inline WhatsApp-style chat, optimistic local fallback from prefetched bookings, and role-aware identity so each party sees the correct counterpart.
Fetches from the conversations API. Builder preview injects _prefetchedConversations and sets disableBackendFetch to avoid auth noise.
Chat opens in a modal directly from the list row. chatScreenId is only needed for legacy external navigation.
Available on all plans.
Four things worth knowing
Conversations are booking-keyed
Each thread is identified by a conversationId or bookingId. Keeping these keys stable is essential - navigation, unread sync, and the optimistic local fallback all depend on them. Never reassign or mutate conversation IDs between renders.
Builder preview uses prefetched data, not live auth
The builder preview path injects _prefetchedConversations (mapped from preview bookings) and sets disableBackendFetch: true to prevent end-user API calls from firing with admin/builder credentials. If the inbox is empty in preview, check the mapping in builder/[appId].tsx.
Chat is role-aware
Identity is resolved from currentUserId, JWT decode helpers, and the optional _prefetchedMembership. A provider sees the customer as the other party; a customer sees the provider. viewMode must be set correctly - do not remove the role-aware identity logic or both parties will see themselves as the sender.
Optimistic fallback keeps messages visible
When a message is sent but the backend fetch fails, the local optimistic message still appears in the thread. This prevents a blank chat after a send error. Do not remove this behaviour unless you can guarantee the backend auth path is available in every runtime context.
Find it, drop it, leave inline chat on
Where to find itAdd it to any messaging screen
- Component picker → Communication → Chat List.
- Drop it on the screen users navigate to for their messages.
- Inline chat modal works out of the box - no screen navigation to wire unless you want a full chat screen.
Before you publishThree things to check
- Leave chatScreenId unset unless you specifically want chat to open on a separate screen instead of the inline modal.
- Confirm Header Title and Search Placeholder match the language used elsewhere in the app.
- Verify the standalone runtime in
AppPlayer.tsxinjectsappId,secureApiCall,currentUserEmail, and_prefetchedConversations.
Header, search, and navigation
| Prop | Type | Default | Description |
|---|---|---|---|
headerTitle | string | ’Messages’ | Heading displayed at the top of the inbox. |
searchPlaceholder | string | ’Search…’ | Placeholder text inside the search input. |
chatScreenId | string | - | Navigate to a full chat screen on row tap. Leave unset to use the inline modal. |
padding | number | 16 | Outer padding applied to the list container. |
borderRadius | number | 12 | Corner radius applied to conversation row cards. |
fontFamily | string | - | Font family applied to all text within the component. |
Make it yours
Every colour and visibility toggle below is yours to change. The defaults are a neutral starting point - adjust them to match the brand without touching anything else.
| Prop | Default | What it controls |
|---|---|---|
showSearch | true | Search input above the conversation list. |
showUnreadBadge | true | Unread count badge on each conversation row. |
showTimestamp | true | Last message timestamp on each conversation row. |
backgroundColor | #F9FAFB | Screen background colour. |
cardBackgroundColor | #FFFFFF | Individual conversation row background. |
titleColor | #111827 | Participant name text colour. |
subtitleColor | #6B7280 | Last message preview text colour. |
timestampColor | #9CA3AF | Timestamp text colour. |
unreadBadgeColor | #007AFF | Unread badge background colour. |
unreadBadgeTextColor | #FFFFFF | Unread badge count text colour. |
headerTextColor | #111827 | Header title text colour. |
borderColor | #E5E7EB | Row card border colour. |
Common mistakes to avoid
Empty inbox in builder preview
disableBackendFetch: true is set in preview to prevent auth errors. If the inbox is empty, _prefetchedConversations is not being mapped from preview bookings. Check the mapping block in builder/[appId].tsx - the bookings must be converted into the conversation shape the component expects.
401 errors in preview logs are expected
If end-user API endpoints fire with builder/admin credentials, you will see 401 Invalid token or missing tenantId. This is a signal that disableBackendFetch is not set, or the preview runtime is calling a live endpoint it should not. Fix by ensuring preview always uses prefetched data.
Do not mutate conversation IDs
Navigation, unread sync, and the optimistic fallback all key off conversationId / bookingId. If these change between renders - for example from a list re-sort that reassigns indexes - threads will duplicate or disappear. Always use stable, backend-assigned IDs.
After any code change, sync the deploy bundle
The standalone deploy bundle must stay in sync with component and runtime code. After editing BuilderChatList.tsx, the registry block, or either runtime file, run node collectSourceFiles.js from the app/ directory to update bundledSourceFiles.ts.