Skip to content

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', empty params, 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) emits impression then widget-view.
  • Queued popups drained later emit only widget-view (see tryDrain).
  • click emits type: 'click' and returns the campaign href so the caller can navigate.
  • close emits type: '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 like title, 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 autoRegisterToken is on, it fetches the FCM token and calls registerToken.
  • registerToken calls Events.subscribePushNotification({ token }) with retry (default 3 attempts, backoff retryDelay * attempt).

Receive / open listeners

setupNotificationListeners() wires Firebase callbacks (guarded by initialized so they attach once):

  • Foreground onMessage → parses the message and sends pushNotificationInteraction({ type: 'VIEW', instanceId }).
  • onNotificationOpenedApp (opened from background) → sends pushNotificationInteraction({ type: 'CLICK', instanceId }).
  • getInitialNotification (opened from a cold start) → also sends a CLICK.

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.