Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions example/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ export default function App({ appName }: { appName: string }) {
? {
Home: 'home',
Settings: 'settings',
// Inbox screens are signed-in only: the inbox is per-profile, so an anonymous
// deep link should land on Login rather than an empty inbox.
'Inbox Messages': 'inbox-messages',
'Visual Inbox': 'visual-inbox',
}
: {
Login: 'login',
Expand Down
2 changes: 2 additions & 0 deletions example/src/navigation/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export const CustomDeviceAttrScreenName = 'Device Attributes' as const;
export const InternalSettingsScreenName = 'Internal Settings' as const;
export const InlineExamplesScreenName = 'Inline Examples' as const;
export const InboxMessagesScreenName = 'Inbox Messages' as const;
export const VisualInboxScreenName = 'Visual Inbox' as const;
export const LocationScreenName = 'Location' as const;

export type NavigationStackParamList = {
Expand All @@ -25,6 +26,7 @@ export type NavigationStackParamList = {
[InternalSettingsScreenName]: undefined;
[InlineExamplesScreenName]: undefined;
[InboxMessagesScreenName]: undefined;
[VisualInboxScreenName]: undefined;
[LocationScreenName]: undefined;
};

Expand Down
10 changes: 10 additions & 0 deletions example/src/screens/content-navigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
NavigationStackParamList,
SettingsScreenName,
TrackScreenName,
VisualInboxScreenName,
} from '@navigation';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { screenStylingOptions } from '@utils';
Expand All @@ -27,6 +28,7 @@ import {
LogingScreen,
SettingsScreen,
TrackScreen,
VisualInboxScreen,
} from '@screens';

import { Storage } from '@services';
Expand Down Expand Up @@ -130,6 +132,14 @@ export const ContentNavigator = ({ appName }: { appName: string }) => {
headerBackVisible: true,
}}
/>
<Stack.Screen
name={VisualInboxScreenName}
component={VisualInboxScreen}
options={{
headerBackButtonDisplayMode: 'minimal',
headerBackVisible: true,
}}
/>
<Stack.Screen
name={LocationScreenName}
component={LocationScreen}
Expand Down
7 changes: 7 additions & 0 deletions example/src/screens/home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
LocationScreenName,
NavigationCallbackContext,
NavigationScreenProps,
VisualInboxScreenName,
} from '@navigation';
import { Storage } from '@services';
import { CioPushPermissionStatus } from 'customerio-reactnative';
Expand Down Expand Up @@ -77,6 +78,12 @@ export const HomeScreen = ({
navigation.navigate(InboxMessagesScreenName);
}}
/>
<Button
title="Visual Inbox (UI)"
onPress={() => {
navigation.navigate(VisualInboxScreenName);
}}
/>
<Button
title="Location (test)"
onPress={() => navigation.navigate(LocationScreenName)}
Expand Down
1 change: 1 addition & 0 deletions example/src/screens/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ export * from './location';
export * from './login';
export * from './settings';
export * from './track';
export * from './visual-inbox';
136 changes: 136 additions & 0 deletions example/src/screens/visual-inbox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import { NavigationScreenProps } from '@navigation';
import {
CustomerIO,
InboxEventType,
NotificationInboxBellView,
NotificationInboxView,
} from 'customerio-reactnative';
import React, { useEffect } from 'react';
import { Linking, StyleSheet, Text, View } from 'react-native';
import { showMessage } from 'react-native-flash-message';

/**
* Demonstrates the two native Visual Notification Inbox UI components exposed by the SDK:
*
* 1. NotificationInboxBellView — the branded bell; tapping it opens the SDK's own inbox panel.
* 2. NotificationInboxView — the Jist-rendered message list, placed by this screen.
*
* The host places both wherever it likes; the SDK owns the panel and all rendering.
*
* Message actions are handled by the global InboxEventListener (configured elsewhere), so these
* components take no per-message action callbacks.
*/
export const VisualInboxScreen = ({}: NavigationScreenProps<'Visual Inbox'>) => {

// Register a global inbox event listener while this screen is mounted.
// While registered, the host (this app) owns action navigation: the native
// SDK forwards taps here and suppresses its default handling.
useEffect(() => {
const subscription = CustomerIO.inAppMessaging.registerInboxEventListener(
(event) => {
switch (event.eventType) {
case InboxEventType.messageActionTaken:
showMessage({
message: `Inbox action taken: ${event.actionName ?? ''} -> ${
event.actionValue ?? ''
}`,
type: 'success',
});
// While a listener is registered the SDK suppresses its own deep-link handling and
// treats the action as host-handled, so nothing navigates unless the host does it.
// Hand the value to the OS exactly as the SDK would: our own scheme round-trips back
// through the NavigationContainer `linking` config, anything else opens externally.
if (event.actionValue) {
Linking.openURL(event.actionValue).catch((error) => {
showMessage({
message: `Could not open ${event.actionValue}`,
type: 'danger',
});
// eslint-disable-next-line no-console
console.warn('Failed to open inbox action deep link', error);
});
}
break;
case InboxEventType.messageShown:
showMessage({ message: 'Inbox message shown', type: 'info' });
break;
case InboxEventType.messageOpened:
showMessage({ message: 'Inbox message opened', type: 'info' });
break;
case InboxEventType.messageDismissed:
showMessage({ message: 'Inbox message dismissed', type: 'info' });
break;
}
// eslint-disable-next-line no-console
console.log('Inbox event received', event.eventType, event.message);
}
);

return () => {
subscription.remove();
};
}, []);

return (
<View style={styles.root}>
{/*
Deliberately NOT a ScrollView. NotificationInboxView scrolls its own message list, and a parent
ScrollView intercepts the vertical drag before the native list sees it. These components are
meant to own their screen (or a fixed region of one), not to sit in a scrolling page.
*/}
<View style={styles.content}>
{/* 1. Branded bell — tapping it opens the SDK's inbox panel. Host owns placement. */}
<View style={styles.bellRow}>
<Text style={styles.sectionTitle}>NotificationInboxBellView</Text>
<NotificationInboxBellView
style={styles.bell}
onTap={() => {
// Observational only: the SDK is already opening its panel.
showMessage({ message: 'Bell tapped', type: 'info' });
}}
/>
</View>
<Text style={styles.sectionBody}>
Tapping the bell opens the SDK's inbox panel — this screen presents nothing.
</Text>

{/* 2. The message list, placed inline by this screen. */}
<Text style={styles.sectionTitle}>NotificationInboxView</Text>
<Text style={styles.sectionBody}>
The Jist-rendered message list, filling the rest of the screen.
</Text>
<View style={styles.embeddedList}>
<NotificationInboxView style={styles.fill} />
</View>
</View>
</View>
);
};

const styles = StyleSheet.create({
root: { flex: 1, backgroundColor: '#f5f5f5' },
content: { flex: 1, padding: 16, gap: 8 },
fill: { flex: 1 },
sectionTitle: {
fontSize: 16,
fontWeight: 'bold',
color: '#333',
marginTop: 12,
},
sectionBody: { fontSize: 13, color: '#666', marginBottom: 4 },
bellRow: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingVertical: 8,
},
// 88 not 56: the native composition insets the 56dp bell by 16 on each side, so a
// 56-square box squeezes the circle down onto the glyph.
bell: { width: 88, height: 88 },
embeddedList: {
flex: 1,
backgroundColor: '#fff',
borderRadius: 8,
overflow: 'hidden',
},
});
Loading