Skip to content

Commit a6f5146

Browse files
committed
refactor(demo): simplify SendIamSection and UserSection
1 parent 180b5cd commit a6f5146

17 files changed

Lines changed: 259 additions & 235 deletions

examples/build.md

Lines changed: 117 additions & 65 deletions
Large diffs are not rendered by default.

examples/demo/App.tsx

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ import { createNativeStackNavigator } from '@react-navigation/native-stack';
33
import React, { useEffect } from 'react';
44
import { StatusBar, StyleSheet, Text, View } from 'react-native';
55
import { SafeAreaProvider } from 'react-native-safe-area-context';
6-
import Toast from 'react-native-toast-message';
76

87
import OneSignalLogo from './assets/onesignal_logo.svg';
98
import AppHeader from './src/components/AppHeader';
9+
import { ToastProvider } from './src/components/ToastProvider';
1010
import { OneSignalProvider } from './src/hooks/useOneSignal';
1111
import HomeScreen from './src/screens/HomeScreen';
1212
import SecondaryScreen from './src/screens/SecondaryScreen';
@@ -23,34 +23,35 @@ function App() {
2323
return (
2424
<SafeAreaProvider>
2525
<StatusBar backgroundColor={AppColors.osPrimary} barStyle="light-content" />
26-
<OneSignalProvider>
27-
<NavigationContainer>
28-
<Stack.Navigator
29-
screenOptions={{
30-
header: (props) => <AppHeader {...props} />,
31-
}}
32-
>
33-
<Stack.Screen
34-
name="Home"
35-
component={HomeScreen}
36-
options={{
37-
headerTitle: () => (
38-
<View style={headerStyles.container}>
39-
<OneSignalLogo height={22} width={99} style={headerStyles.logo} />
40-
<Text style={headerStyles.subtitle}>React Native</Text>
41-
</View>
42-
),
26+
<ToastProvider>
27+
<OneSignalProvider>
28+
<NavigationContainer>
29+
<Stack.Navigator
30+
screenOptions={{
31+
header: (props) => <AppHeader {...props} />,
4332
}}
44-
/>
45-
<Stack.Screen
46-
name="Secondary"
47-
component={SecondaryScreen}
48-
options={{ title: 'Secondary Activity' }}
49-
/>
50-
</Stack.Navigator>
51-
</NavigationContainer>
52-
</OneSignalProvider>
53-
<Toast position="bottom" bottomOffset={20} />
33+
>
34+
<Stack.Screen
35+
name="Home"
36+
component={HomeScreen}
37+
options={{
38+
headerTitle: () => (
39+
<View style={headerStyles.container}>
40+
<OneSignalLogo height={22} width={99} style={headerStyles.logo} />
41+
<Text style={headerStyles.subtitle}>React Native</Text>
42+
</View>
43+
),
44+
}}
45+
/>
46+
<Stack.Screen
47+
name="Secondary"
48+
component={SecondaryScreen}
49+
options={{ title: 'Secondary Activity' }}
50+
/>
51+
</Stack.Navigator>
52+
</NavigationContainer>
53+
</OneSignalProvider>
54+
</ToastProvider>
5455
</SafeAreaProvider>
5556
);
5657
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import type { FC, ReactNode } from 'react';
2+
import { createContext, useCallback, useContext } from 'react';
3+
import Toast from 'react-native-toast-message';
4+
5+
const TOAST_DURATION_MS = 3000;
6+
7+
type ShowSnackbar = (message: string) => void;
8+
9+
const ToastContext = createContext<ShowSnackbar | null>(null);
10+
11+
interface ToastProviderProps {
12+
children: ReactNode;
13+
}
14+
15+
export const ToastProvider: FC<ToastProviderProps> = ({ children }) => {
16+
const showSnackbar = useCallback<ShowSnackbar>((message) => {
17+
// Hide first so a new message replaces any in-flight toast and restarts
18+
// the visibility timer instead of queuing behind it.
19+
Toast.hide();
20+
Toast.show({ type: 'info', text1: message, visibilityTime: TOAST_DURATION_MS });
21+
}, []);
22+
23+
return (
24+
<ToastContext.Provider value={showSnackbar}>
25+
{children}
26+
<Toast position="bottom" bottomOffset={20} />
27+
</ToastContext.Provider>
28+
);
29+
};
30+
31+
export function useSnackbar(): ShowSnackbar {
32+
const ctx = useContext(ToastContext);
33+
if (!ctx) {
34+
throw new Error('useSnackbar must be used within ToastProvider');
35+
}
36+
return ctx;
37+
}

examples/demo/src/components/sections/AliasesSection.tsx

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ export default function AliasesSection({
2525
onAddMultiple,
2626
onInfoTap,
2727
}: Props) {
28-
const [addVisible, setAddVisible] = useState(false);
29-
const [addMultipleVisible, setAddMultipleVisible] = useState(false);
28+
const [addOpen, setAddOpen] = useState(false);
29+
const [addMultipleOpen, setAddMultipleOpen] = useState(false);
3030

3131
const filtered = aliases.filter(([k]) => !FILTERED_KEYS.includes(k));
3232

@@ -45,33 +45,29 @@ export default function AliasesSection({
4545
<PairList items={filtered} layout="stacked" sectionKey="aliases" />
4646
</View>
4747
)}
48-
<ActionButton
49-
label="ADD ALIAS"
50-
onPress={() => setAddVisible(true)}
51-
testID="add_alias_button"
52-
/>
48+
<ActionButton label="ADD ALIAS" onPress={() => setAddOpen(true)} testID="add_alias_button" />
5349
<ActionButton
5450
label="ADD MULTIPLE ALIASES"
55-
onPress={() => setAddMultipleVisible(true)}
51+
onPress={() => setAddMultipleOpen(true)}
5652
testID="add_multiple_aliases_button"
5753
/>
5854
<PairInputModal
59-
visible={addVisible}
55+
visible={addOpen}
6056
title="Add Alias"
6157
keyPlaceholder="Label"
6258
valuePlaceholder="ID"
6359
onConfirm={onAdd}
64-
onClose={() => setAddVisible(false)}
60+
onClose={() => setAddOpen(false)}
6561
keyTestID="alias_label_input"
6662
valueTestID="alias_id_input"
6763
/>
6864
<MultiPairInputModal
69-
visible={addMultipleVisible}
65+
visible={addMultipleOpen}
7066
title="Add Multiple Aliases"
7167
keyPlaceholder="Label"
7268
valuePlaceholder="ID"
7369
onConfirm={onAddMultiple}
74-
onClose={() => setAddMultipleVisible(false)}
70+
onClose={() => setAddMultipleOpen(false)}
7571
/>
7672
</SectionCard>
7773
);

examples/demo/src/components/sections/CustomEventsSection.tsx

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,30 @@
11
import React, { useState } from 'react';
22

3-
import { showSnackbar } from '../../utils/showSnackbar';
43
import ActionButton from '../ActionButton';
54
import TrackEventModal from '../modals/TrackEventModal';
65
import SectionCard from '../SectionCard';
6+
import { useSnackbar } from '../ToastProvider';
77

88
interface Props {
99
onTrackEvent: (name: string, properties?: Record<string, unknown>) => void;
1010
onInfoTap?: () => void;
1111
}
1212

1313
export default function CustomEventsSection({ onTrackEvent, onInfoTap }: Props) {
14-
const [modalVisible, setModalVisible] = useState(false);
14+
const [open, setOpen] = useState(false);
15+
const showSnackbar = useSnackbar();
1516

1617
return (
1718
<SectionCard title="Custom Events" onInfoTap={onInfoTap} sectionKey="custom_events">
18-
<ActionButton
19-
label="TRACK EVENT"
20-
onPress={() => setModalVisible(true)}
21-
testID="track_event_button"
22-
/>
19+
<ActionButton label="TRACK EVENT" onPress={() => setOpen(true)} testID="track_event_button" />
2320
<TrackEventModal
24-
visible={modalVisible}
21+
visible={open}
2522
onConfirm={(name, properties) => {
2623
onTrackEvent(name, properties);
2724
showSnackbar(`Event tracked: ${name}`);
28-
setModalVisible(false);
25+
setOpen(false);
2926
}}
30-
onClose={() => setModalVisible(false)}
27+
onClose={() => setOpen(false)}
3128
/>
3229
</SectionCard>
3330
);

examples/demo/src/components/sections/EmailsSection.tsx

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export default function EmailsSection({
2222
onRemove,
2323
onInfoTap,
2424
}: Props) {
25-
const [addVisible, setAddVisible] = useState(false);
25+
const [addOpen, setAddOpen] = useState(false);
2626

2727
return (
2828
<SectionCard title="Emails" onInfoTap={onInfoTap} sectionKey="emails">
@@ -35,17 +35,13 @@ export default function EmailsSection({
3535
sectionKey="emails"
3636
/>
3737
</View>
38-
<ActionButton
39-
label="ADD EMAIL"
40-
onPress={() => setAddVisible(true)}
41-
testID="add_email_button"
42-
/>
38+
<ActionButton label="ADD EMAIL" onPress={() => setAddOpen(true)} testID="add_email_button" />
4339
<SingleInputModal
44-
visible={addVisible}
40+
visible={addOpen}
4541
title="Add Email"
4642
placeholder="Email address"
4743
onConfirm={onAdd}
48-
onClose={() => setAddVisible(false)}
44+
onClose={() => setAddOpen(false)}
4945
keyboardType="email-address"
5046
testID="email_input"
5147
/>

examples/demo/src/components/sections/LocationSection.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import React from 'react';
22
import { View, StyleSheet } from 'react-native';
33

44
import { AppTheme, AppSpacing } from '../../theme';
5-
import { showSnackbar } from '../../utils/showSnackbar';
65
import ActionButton from '../ActionButton';
76
import SectionCard from '../SectionCard';
7+
import { useSnackbar } from '../ToastProvider';
88
import ToggleRow from '../ToggleRow';
99

1010
interface Props {
@@ -22,6 +22,8 @@ export default function LocationSection({
2222
onRequestLocationPermission,
2323
onInfoTap,
2424
}: Props) {
25+
const showSnackbar = useSnackbar();
26+
2527
const handleCheckLocation = async () => {
2628
const shared = await onCheckLocationShared();
2729
showSnackbar(`Location shared: ${shared}`);

examples/demo/src/components/sections/OutcomesSection.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import React, { useState } from 'react';
22

3-
import { showSnackbar } from '../../utils/showSnackbar';
43
import ActionButton from '../ActionButton';
54
import OutcomeModal from '../modals/OutcomeModal';
65
import SectionCard from '../SectionCard';
6+
import { useSnackbar } from '../ToastProvider';
77

88
interface Props {
99
onSendNormal: (name: string) => void;
@@ -18,7 +18,8 @@ export default function OutcomesSection({
1818
onSendWithValue,
1919
onInfoTap,
2020
}: Props) {
21-
const [modalVisible, setModalVisible] = useState(false);
21+
const [open, setOpen] = useState(false);
22+
const showSnackbar = useSnackbar();
2223

2324
const handleSendNormal = (name: string) => {
2425
onSendNormal(name);
@@ -39,15 +40,15 @@ export default function OutcomesSection({
3940
<SectionCard title="Outcome Events" onInfoTap={onInfoTap} sectionKey="outcomes">
4041
<ActionButton
4142
label="SEND OUTCOME"
42-
onPress={() => setModalVisible(true)}
43+
onPress={() => setOpen(true)}
4344
testID="send_outcome_button"
4445
/>
4546
<OutcomeModal
46-
visible={modalVisible}
47+
visible={open}
4748
onSendNormal={handleSendNormal}
4849
onSendUnique={handleSendUnique}
4950
onSendWithValue={handleSendWithValue}
50-
onClose={() => setModalVisible(false)}
51+
onClose={() => setOpen(false)}
5152
/>
5253
</SectionCard>
5354
);
Lines changed: 5 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import React from 'react';
2-
import { TouchableOpacity, Text, View, StyleSheet } from 'react-native';
3-
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
42

5-
import { InAppMessageType, iamTypeLabel, iamTypeIcon } from '../../models/InAppMessageType';
6-
import { AppColors, AppSpacing } from '../../theme';
3+
import { InAppMessageType, iamTypeLabel } from '../../models/InAppMessageType';
4+
import ActionButton from '../ActionButton';
75
import SectionCard from '../SectionCard';
86

97
interface Props {
@@ -22,45 +20,13 @@ export default function SendIamSection({ onSendIam, onInfoTap }: Props) {
2220
return (
2321
<SectionCard title="Send In-App Message" onInfoTap={onInfoTap} sectionKey="send_iam">
2422
{IAM_TYPES.map((type) => (
25-
<TouchableOpacity
23+
<ActionButton
2624
key={type}
27-
style={styles.button}
25+
label={iamTypeLabel[type]}
2826
onPress={() => onSendIam(type)}
29-
activeOpacity={0.8}
3027
testID={`send_iam_${type}_button`}
31-
accessibilityLabel={iamTypeLabel[type]}
32-
>
33-
<View style={styles.inner}>
34-
<Icon name={iamTypeIcon[type]} size={20} color={AppColors.white} style={styles.icon} />
35-
<Text style={styles.label}>{iamTypeLabel[type]}</Text>
36-
</View>
37-
</TouchableOpacity>
28+
/>
3829
))}
3930
</SectionCard>
4031
);
4132
}
42-
43-
const styles = StyleSheet.create({
44-
button: {
45-
backgroundColor: AppColors.osPrimary,
46-
borderRadius: 8,
47-
paddingVertical: 14,
48-
paddingHorizontal: 16,
49-
marginBottom: AppSpacing.gap,
50-
},
51-
inner: {
52-
flexDirection: 'row',
53-
alignItems: 'center',
54-
justifyContent: 'flex-start',
55-
},
56-
icon: {
57-
marginRight: 8,
58-
},
59-
label: {
60-
fontSize: 14,
61-
fontWeight: '600',
62-
color: AppColors.white,
63-
letterSpacing: 0.5,
64-
textTransform: 'uppercase',
65-
},
66-
});

examples/demo/src/components/sections/SendPushSection.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export default function SendPushSection({
1818
onClearAll,
1919
onInfoTap,
2020
}: Props) {
21-
const [customVisible, setCustomVisible] = useState(false);
21+
const [customOpen, setCustomOpen] = useState(false);
2222

2323
return (
2424
<SectionCard title="Send Push Notification" onInfoTap={onInfoTap} sectionKey="send_push">
@@ -39,7 +39,7 @@ export default function SendPushSection({
3939
/>
4040
<ActionButton
4141
label="CUSTOM"
42-
onPress={() => setCustomVisible(true)}
42+
onPress={() => setCustomOpen(true)}
4343
testID="send_custom_button"
4444
/>
4545
<ActionButton
@@ -49,12 +49,12 @@ export default function SendPushSection({
4949
testID="clear_all_button"
5050
/>
5151
<CustomNotificationModal
52-
visible={customVisible}
52+
visible={customOpen}
5353
onConfirm={(title, body) => {
5454
onSendCustomNotification(title, body);
55-
setCustomVisible(false);
55+
setCustomOpen(false);
5656
}}
57-
onClose={() => setCustomVisible(false)}
57+
onClose={() => setCustomOpen(false)}
5858
/>
5959
</SectionCard>
6060
);

0 commit comments

Comments
 (0)