Popups, Search and Push
For developers
Operational deep-dive into three subsystems that emit events as a side effect of their lifecycle: search (src/hooks/useSearch.tsx), popups (src/hooks/usePopup.tsx), and push (example/src/services/PushService.ts). For the plain-language overview, see How Tracking Works.
Search (src/hooks/useSearch.tsx)
The useSearch hook wraps three responsibilities: running a search, sending search-result clicks, and sending widget interactions.
search(query) — the full SEARCH shape
Unlike the short events.Search(...) calls in the Event Playground, the hook sends a richer payload through sendEvent and returns the response so the UI can render results:
await events.sendEvent({ name: 'SEARCH', query, type: 'instant', async: true, noProcess: false, tryCount: 0, nextPage: false, dnt: false, dnp: false, params: { _search_no_cache_: 'false', ...searchParams }, recommendIds: [],});Products are read from response.search[0][0].products. The hook also fires onSearchStart, onSearchComplete, and onSearchError callbacks around the request.
sendClick(productId, query?) — result clicks
Sends a direct INTERACTION with type: 'search':
- with a query:
instanceId: 'product',params: { term: query },interactionId: productId - without a query (before-search):
instanceId: 'bs_product', emptyparams,interactionId: productId
sendInteraction(type, customInstanceId?) — widget interactions
Sends an INTERACTION with interactionId: 'static' and the instance id (defaulting to the hook’s instanceId, 'BEFORE_SEARCH' by default). Remember the interaction id override gotcha: because instanceId is present, interactionId is rewritten to match it on the wire.
Reference UI: example/src/screens/HomeScreen.tsx. See also Search, Interaction, Form and Custom.
Popups (src/hooks/usePopup.tsx)
PopupProvider receives campaigns from Segmentify (via Events.setPopupHandler) and manages a single visible popup plus a queue for any that arrive while one is shown.
Lifecycle and the interactions it emits
flowchart TD campaign["Campaign arrives (banner or html)"] --> visible{"Popup already visible?"} visible -->|Yes| queue["Queue it"] visible -->|No| show["Show popup"] show --> impression["Interaction: impression"] impression --> widgetview["Interaction: widget-view"] widgetview --> wait["Shopper acts"] wait -->|Taps| click["Interaction: click (returns href)"] wait -->|Dismisses| close["Interaction: close"] close --> drain["Drain queue -> next popup"] drain -->|Next item| draininteraction["Interaction: widget-view"]Notes:
showPopup(first display) emitsimpressionthenwidget-view.- Queued popups drained later emit only
widget-view(seetryDrain). clickemitstype: 'click'and returns the campaignhrefso the caller can navigate.closeemitstype: 'close'and hides the popup, which triggers draining the next queued item.- HTML-enabled campaigns are templated:
[[key]]is HTML-escaped,[[& key]]is raw insertion, using fields liketitle,description, button colors, and position.
All popup interactions go through events.Interaction(...), so the same instanceId/interactionId behavior from Known Gotchas applies.
Push (example/src/services/PushService.ts)
PushService wraps Firebase Cloud Messaging and emits Segmentify push events automatically.
Permission and token registration
requestPermission()handles iOS (AuthorizationStatus.AUTHORIZED/PROVISIONAL) and Android 13+ (POST_NOTIFICATIONS); pre-Android 13 is treated as granted.- When permission is granted and
autoRegisterTokenis on, it fetches the FCM token and callsregisterToken. registerTokencallsEvents.subscribePushNotification({ token })with retry (default 3 attempts, backoffretryDelay * attempt).
Receive / open listeners
setupNotificationListeners() wires Firebase callbacks (guarded by initialized so they attach once):
- Foreground
onMessage→ parses the message and sendspushNotificationInteraction({ type: 'VIEW', instanceId }). onNotificationOpenedApp(opened from background) → sendspushNotificationInteraction({ type: 'CLICK', instanceId }).getInitialNotification(opened from a cold start) → also sends aCLICK.
instanceId is parsed from the message data (instanceId / instanceID / instance_id).
Expo Go caveat
Firebase push is not initialized in Expo Go; the example only boots PushService when Constants.appOwnership !== 'expo'. Use a dev client or release build to exercise live push. See Push Notification Events for endpoint and payload details.