ComponentsCommunicationNotification Modal

Communication

Notification Modal

Live notification hub for bookings, conversations, and orders - polls the backend every 15 seconds, persists read and dismissed state per app, and adapts notification copy for customer or provider roles automatically.

Data sourcesBookings · Conversations · Orders

All three are fetched on mount and refreshed every 15 s. Role-based copy is applied per source automatically.

PersistenceAsyncStorage · per appId

Read and dismissed IDs are stored locally, capped at 500 per app. Users keep their read state between sessions.

TierFreemium

Available on free and paid plans.

What it does

Four things worth knowing

Three data sources, one merged feed

On mount the component fetches bookings, conversations, and orders in parallel via secureApiCall. Each source is mapped to a BuilderNotificationItem with role-specific copy - a provider sees the customer name and service status; a customer sees the provider name. All three are merged, sorted newest first, and refreshed every 15 seconds.

Read and dismissed state survives sessions

Read and dismissed notification IDs are written to AsyncStorage under a key scoped to appId. The list is capped at 500 IDs. On next mount the component loads these IDs before rendering, so users never see previously dismissed notifications come back. Do not change STORAGE_PREFIX without a migration plan - all persisted state will be lost.

The button is silent without appId and secureApiCall

If either appId or secureApiCall is missing, or if isPreview is false, the component renders a non-interactive button. Polling never starts. This is intentional - the component will not make unauthenticated API calls. Always verify both props are injected by the runtime before testing.

Trigger mode controls who opens the modal

With triggerMode=“internal” (default), the user taps the 46×46 circle button to open the modal. With triggerMode=“external”, the parent controls visibility via externalVisible and onRequestClose - the built-in button is bypassed. Use external mode when you want to open the notification panel from a custom nav bar element.

Builder setup

Find it, drop it, set the view mode

Where to find itAdd it to any screen

  1. Component picker → CommunicationNotification Modal.
  2. Drop it anywhere - it renders as a fixed-size button that opens an overlay modal.
  3. Set viewMode to customer or provider to get the correct notification copy.

Before you publishThree things to check

  1. Confirm isPreview is true - without it the button is non-interactive in the builder.
  2. Verify appId and secureApiCall are injected by the runtime; the component will not poll without them.
  3. After changing any registry defaults or component logic, run node collectSourceFiles.js in app/ to sync the deploy bundle.
Category: CommunicationComponent: BuilderNotificationModalRegistry key: notification-modalTier: Freemium
Props

Identity, text, and trigger mode

PropTypeDefaultDescription
viewModeselect’customer’customer or provider - determines notification copy for each source.
titlestring’Notifications’Modal header title and empty-state heading.
subtitlestring’New bookings, chats…’Modal header subtitle and empty-state subtext.
buttonIconNamestring’notifications’Ionicons icon rendered inside the trigger button.
triggerModeselect’internal’internal - button opens modal. external - parent controls via externalVisible.
borderRadiusnumber20Modal corner radius in px.
paddingnumber18Modal inner padding in px.
fontFamilystring-Font family applied to all text in the modal.
Toggles & colours

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.

PropDefaultWhat it controls
showSubtitletrueSubtitle visibility in both the modal header and empty state.
showBadgetrueUnread count badge on the trigger button.
showTimestamptrue”5 min ago” / “Yesterday” timestamp on each notification card.
showAudienceTagtrue”Provider” / “Consumer” audience tag on each card.
showTypeTagtrue”Booking” / “Chat” / “Order” type tag on each card.
showMarkAllReadtrue”Mark all read” action button in the modal header.
buttonBackgroundColor#F5F7FBTrigger button background.
buttonIconColor#1F2937Icon colour inside the trigger button.
badgeColor#EF4444Unread badge background.
badgeTextColor#FFFFFFUnread badge count text.
overlayColorrgba(15,23,42,0.45)Dark overlay behind the modal.
modalBackgroundColor#FFFFFFModal card background.
cardBackgroundColor#F8FAFCIndividual notification card background.
primaryColor#2563EBIcon tint and action link colour.
borderColor#E5E7EBCard borders.
unreadDotColor#2563EBUnread indicator dot on each card.
titleColor#111827Modal header title and notification card title text.
subtitleColor#6B7280Modal subtitle and timestamp text.
textColor#111827Notification body title text.
mutedTextColor#6B7280Notification body and timestamp muted text.
Tips

Common mistakes to avoid

Button is non-interactive in the builder

The modal will not open unless isPreview= is set. In the builder canvas this prop must be enabled explicitly. Without it the component renders a static button - no tap, no modal, no polling. This is by design to prevent unexpected API calls during layout editing.

Unread badge always shows 0

The badge count comes from unread logic applied per source: bookings unread if timestamp is within 36 hours and not cancelled; conversations unread if unreadCount > 0; orders unread if recent and not cancelled. If all sources return no unread items the badge will be empty - verify the backend data shape matches these rules.

Do not change STORAGE_PREFIX without migration

Read and dismissed IDs are stored in AsyncStorage under a key that includes STORAGE_PREFIX. If you rename it, all existing persisted state is orphaned - users will see every previously dismissed notification again. Always write a migration or clear AsyncStorage intentionally if you need to change the prefix.

Keep polling interval at 10 s or above

The default 15-second interval is a deliberate balance between freshness and backend load. Intervals below 10 s on apps with many concurrent users will cause disproportionate API pressure. Change the setInterval value in BuilderNotificationModal.tsx only if you have confirmed your backend can handle the increased rate.