Skip to content

Commit 8743251

Browse files
committed
refactor(demo): add isReady flag to OneSignal init
1 parent f0b7833 commit 8743251

2 files changed

Lines changed: 79 additions & 138 deletions

File tree

examples/demo/src/hooks/useOneSignal.ts

Lines changed: 65 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import {
55
useCallback,
66
useContext,
77
useEffect,
8-
useRef,
98
useState,
109
type ReactNode,
1110
} from 'react';
@@ -67,11 +66,12 @@ export type UseOneSignalReturn = {
6766
tagsList: [string, string][];
6867
triggersList: [string, string][];
6968
isLoading: boolean;
69+
isReady: boolean;
7070
loginUser: (externalUserId: string) => Promise<void>;
7171
logoutUser: () => Promise<void>;
7272
setConsentRequired: (required: boolean) => Promise<void>;
7373
setConsentGiven: (granted: boolean) => Promise<void>;
74-
promptPush: () => Promise<void>;
74+
promptPush: () => void;
7575
setPushEnabled: (enabled: boolean) => void;
7676
sendNotification: (type: NotificationType) => Promise<void>;
7777
sendCustomNotification: (title: string, body: string) => Promise<void>;
@@ -119,27 +119,16 @@ function useOneSignalState(): UseOneSignalReturn {
119119
const [tagsList, setTagsList] = useState<[string, string][]>([]);
120120
const [triggersList, setTriggersList] = useState<[string, string][]>([]);
121121
const [isLoading, setIsLoading] = useState(false);
122-
123-
const mountedRef = useRef(true);
124-
const requestSequenceRef = useRef(0);
122+
const [isReady, setIsReady] = useState(false);
125123

126124
const fetchUserDataFromApi = useCallback(async () => {
127-
const requestId = requestSequenceRef.current + 1;
128-
requestSequenceRef.current = requestId;
129-
130125
const onesignalId = await OneSignal.User.getOnesignalId();
131-
console.log('onesignalId', onesignalId);
132126
if (!onesignalId) return;
133127

134128
const userData = await apiService.fetchUser(onesignalId);
135-
console.log('userData1', userData);
136129
if (!userData) return;
137-
console.log('userDat2a', userData);
138130

139131
const externalId = await OneSignal.User.getExternalId();
140-
if (!mountedRef.current || requestSequenceRef.current !== requestId) {
141-
return;
142-
}
143132

144133
setAliasesList(Object.entries(userData.aliases));
145134
setTagsList(Object.entries(userData.tags));
@@ -149,15 +138,6 @@ function useOneSignalState(): UseOneSignalReturn {
149138
}, []);
150139

151140
useEffect(() => {
152-
mountedRef.current = true;
153-
return () => {
154-
mountedRef.current = false;
155-
};
156-
}, []);
157-
158-
useEffect(() => {
159-
let cancelled = false;
160-
161141
const handleIamWillDisplay = (e: InAppMessageWillDisplayEvent) => {
162142
console.log(`IAM willDisplay: ${e.message.messageId}`);
163143
};
@@ -188,24 +168,15 @@ function useOneSignalState(): UseOneSignalReturn {
188168
};
189169

190170
const pushSubHandler = async () => {
191-
if (!mountedRef.current) {
192-
return;
193-
}
194171
const [id, optedIn] = await Promise.all([
195172
OneSignal.User.pushSubscription.getIdAsync(),
196173
OneSignal.User.pushSubscription.getOptedInAsync(),
197174
]);
198-
if (!mountedRef.current) {
199-
return;
200-
}
201175
setPushSubscriptionId(id ?? undefined);
202176
setIsPushEnabled(optedIn);
203177
};
204178

205179
const permissionHandler = async () => {
206-
if (!mountedRef.current) {
207-
return;
208-
}
209180
setHasNotificationPermission(await OneSignal.Notifications.getPermissionAsync());
210181
};
211182

@@ -228,67 +199,53 @@ function useOneSignalState(): UseOneSignalReturn {
228199

229200
apiService.setAppId(nextAppId);
230201

231-
try {
232-
OneSignal.Debug.setLogLevel(LogLevel.Verbose);
233-
OneSignal.setConsentRequired(nextConsentRequired);
234-
OneSignal.setConsentGiven(nextPrivacyConsentGiven);
235-
OneSignal.initialize(nextAppId);
236-
237-
OneSignal.LiveActivities.setupDefault({
238-
enablePushToStart: true,
239-
enablePushToUpdate: true,
240-
});
241-
242-
OneSignal.InAppMessages.setPaused(nextIamPaused);
243-
OneSignal.Location.setShared(nextLocationShared);
244-
245-
if (storedExternalUserId) {
246-
OneSignal.login(storedExternalUserId);
247-
}
248-
249-
// Don't register listeners if the effect was cancelled while we were
250-
// awaiting AsyncStorage above — otherwise the cleanup function would
251-
// have already run and removeEventListener would no-op, leaking these
252-
// handlers (worst on React 18 StrictMode dev double-invoke).
253-
if (cancelled) {
254-
return;
255-
}
256-
257-
OneSignal.InAppMessages.addEventListener('willDisplay', handleIamWillDisplay);
258-
OneSignal.InAppMessages.addEventListener('didDisplay', handleIamDidDisplay);
259-
OneSignal.InAppMessages.addEventListener('willDismiss', handleIamWillDismiss);
260-
OneSignal.InAppMessages.addEventListener('didDismiss', handleIamDidDismiss);
261-
OneSignal.InAppMessages.addEventListener('click', handleIamClick);
262-
OneSignal.Notifications.addEventListener('click', handleNotificationClick);
263-
OneSignal.Notifications.addEventListener('permissionChange', permissionHandler);
264-
OneSignal.Notifications.addEventListener(
265-
'foregroundWillDisplay',
266-
handleForegroundWillDisplay,
267-
);
268-
269-
OneSignal.User.pushSubscription.addEventListener('change', pushSubHandler);
270-
OneSignal.User.addEventListener('change', userChangeHandler);
271-
272-
console.log(`OneSignal initialized with app ID: ${nextAppId}`);
273-
} catch (err) {
274-
console.error(`Init error: ${String(err)}`);
275-
}
202+
OneSignal.Debug.setLogLevel(LogLevel.Verbose);
203+
OneSignal.setConsentRequired(nextConsentRequired);
204+
OneSignal.setConsentGiven(nextPrivacyConsentGiven);
205+
OneSignal.initialize(nextAppId);
276206

277-
if (cancelled) {
207+
const onesignalId = await OneSignal.User.getOnesignalId();
208+
console.log('onesignalId', onesignalId);
209+
if (!onesignalId) {
278210
return;
279211
}
280212

213+
OneSignal.LiveActivities.setupDefault({
214+
enablePushToStart: true,
215+
enablePushToUpdate: true,
216+
});
217+
218+
OneSignal.InAppMessages.setPaused(nextIamPaused);
219+
OneSignal.Location.setShared(nextLocationShared);
220+
221+
if (storedExternalUserId) {
222+
OneSignal.login(storedExternalUserId);
223+
}
224+
225+
OneSignal.InAppMessages.addEventListener('willDisplay', handleIamWillDisplay);
226+
OneSignal.InAppMessages.addEventListener('didDisplay', handleIamDidDisplay);
227+
OneSignal.InAppMessages.addEventListener('willDismiss', handleIamWillDismiss);
228+
OneSignal.InAppMessages.addEventListener('didDismiss', handleIamDidDismiss);
229+
OneSignal.InAppMessages.addEventListener('click', handleIamClick);
230+
OneSignal.Notifications.addEventListener('click', handleNotificationClick);
231+
OneSignal.Notifications.addEventListener('permissionChange', permissionHandler);
232+
OneSignal.Notifications.addEventListener(
233+
'foregroundWillDisplay',
234+
handleForegroundWillDisplay,
235+
);
236+
237+
OneSignal.User.pushSubscription.addEventListener('change', pushSubHandler);
238+
OneSignal.User.addEventListener('change', userChangeHandler);
239+
240+
console.log(`OneSignal initialized with app ID: ${nextAppId}`);
241+
281242
const externalId = await OneSignal.User.getExternalId();
282243
const [pushId, pushOptedIn, hasPerm] = await Promise.all([
283244
OneSignal.User.pushSubscription.getIdAsync(),
284245
OneSignal.User.pushSubscription.getOptedInAsync(),
285246
OneSignal.Notifications.getPermissionAsync(),
286247
]);
287248

288-
if (cancelled || !mountedRef.current) {
289-
return;
290-
}
291-
292249
setAppId(nextAppId);
293250
setConsentRequiredState(nextConsentRequired);
294251
setPrivacyConsentGivenState(nextPrivacyConsentGiven);
@@ -298,33 +255,22 @@ function useOneSignalState(): UseOneSignalReturn {
298255
setPushSubscriptionId(pushId ?? undefined);
299256
setIsPushEnabled(pushOptedIn);
300257
setHasNotificationPermission(hasPerm);
258+
setIsReady(true);
301259

302-
const onesignalId = await OneSignal.User.getOnesignalId();
303-
if (cancelled || !mountedRef.current) {
304-
return;
305-
}
306-
307-
if (onesignalId) {
308-
setIsLoading(true);
309-
try {
310-
await fetchUserDataFromApi();
311-
} finally {
312-
if (mountedRef.current && !cancelled) {
313-
setIsLoading(false);
314-
}
315-
}
260+
setIsLoading(true);
261+
try {
262+
await fetchUserDataFromApi();
263+
} finally {
264+
setIsLoading(false);
316265
}
317266
};
318267

319268
void load().catch((err) => {
320269
console.error(`Initial load error: ${String(err)}`);
321-
if (mountedRef.current) {
322-
setIsLoading(false);
323-
}
270+
setIsLoading(false);
324271
});
325272

326273
return () => {
327-
cancelled = true;
328274
OneSignal.InAppMessages.removeEventListener('willDisplay', handleIamWillDisplay);
329275
OneSignal.InAppMessages.removeEventListener('didDisplay', handleIamDidDisplay);
330276
OneSignal.InAppMessages.removeEventListener('willDismiss', handleIamWillDismiss);
@@ -342,15 +288,13 @@ function useOneSignalState(): UseOneSignalReturn {
342288
}, [fetchUserDataFromApi]);
343289

344290
const loginUser = async (nextExternalUserId: string) => {
345-
if (mountedRef.current) {
346-
setAliasesList([]);
347-
setEmailsList([]);
348-
setSmsNumbersList([]);
349-
setTagsList([]);
350-
setTriggersList([]);
351-
setExternalUserId(nextExternalUserId);
352-
setIsLoading(true);
353-
}
291+
setAliasesList([]);
292+
setEmailsList([]);
293+
setSmsNumbersList([]);
294+
setTagsList([]);
295+
setTriggersList([]);
296+
setExternalUserId(nextExternalUserId);
297+
setIsLoading(true);
354298

355299
try {
356300
OneSignal.login(nextExternalUserId);
@@ -363,47 +307,36 @@ function useOneSignalState(): UseOneSignalReturn {
363307
} catch (err) {
364308
console.error(`Login error: ${String(err)}`);
365309
} finally {
366-
if (mountedRef.current) {
367-
setIsLoading(false);
368-
}
310+
setIsLoading(false);
369311
}
370312
};
371313

372314
const logoutUser = async () => {
373315
OneSignal.logout();
374316
await preferences.setExternalUserId(null);
375-
if (mountedRef.current) {
376-
setExternalUserId(undefined);
377-
setAliasesList([]);
378-
setEmailsList([]);
379-
setSmsNumbersList([]);
380-
setTagsList([]);
381-
setTriggersList([]);
382-
}
317+
setExternalUserId(undefined);
318+
setAliasesList([]);
319+
setEmailsList([]);
320+
setSmsNumbersList([]);
321+
setTagsList([]);
322+
setTriggersList([]);
383323
console.log('Logged out');
384324
};
385325

386326
const setConsentRequired = async (required: boolean) => {
387-
if (mountedRef.current) {
388-
setConsentRequiredState(required);
389-
}
327+
setConsentRequiredState(required);
390328
OneSignal.setConsentRequired(required);
391329
await preferences.setConsentRequired(required);
392330
};
393331

394332
const setConsentGiven = async (granted: boolean) => {
395-
if (mountedRef.current) {
396-
setPrivacyConsentGivenState(granted);
397-
}
333+
setPrivacyConsentGivenState(granted);
398334
OneSignal.setConsentGiven(granted);
399335
await preferences.setPrivacyConsent(granted);
400336
};
401337

402-
const promptPush = async () => {
403-
const granted = await OneSignal.Notifications.requestPermission(true);
404-
if (mountedRef.current) {
405-
setHasNotificationPermission(granted);
406-
}
338+
const promptPush = () => {
339+
OneSignal.Notifications.requestPermission(true);
407340
};
408341

409342
const setPushEnabled = (enabled: boolean) => {
@@ -614,6 +547,7 @@ function useOneSignalState(): UseOneSignalReturn {
614547
tagsList,
615548
triggersList,
616549
isLoading,
550+
isReady,
617551
loginUser,
618552
logoutUser,
619553
setConsentRequired,

examples/demo/src/screens/HomeScreen.tsx

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { useNavigation } from '@react-navigation/native';
2-
import React, { useEffect, useState } from 'react';
1+
import { useFocusEffect, useNavigation } from '@react-navigation/native';
2+
import React, { useCallback, useRef, useState } from 'react';
33
import { Platform, ScrollView, StyleSheet, View } from 'react-native';
44

55
import ActionButton from '../components/ActionButton';
@@ -32,11 +32,18 @@ export default function HomeScreen() {
3232
const [tooltipVisible, setTooltipVisible] = useState(false);
3333
const [activeTooltip, setActiveTooltip] = useState<TooltipData | null>(null);
3434

35-
// Auto-request push permission on load
36-
useEffect(() => {
37-
void os.promptPush();
38-
// eslint-disable-next-line react-hooks/exhaustive-deps
39-
}, []);
35+
// Prompt for push only after the screen is actually focused so the Android
36+
// Activity is resumed and can present the OS dialog. Otherwise the request
37+
// gets queued and the prompt only appears after the next foreground.
38+
const hasPromptedRef = useRef(false);
39+
useFocusEffect(
40+
useCallback(() => {
41+
if (os.isReady && !hasPromptedRef.current) {
42+
hasPromptedRef.current = true;
43+
os.promptPush();
44+
}
45+
}, [os.isReady, os.promptPush]),
46+
);
4047

4148
const showTooltipModal = (key: string) => {
4249
const tooltip = TooltipHelper.getInstance().getTooltip(key);

0 commit comments

Comments
 (0)