Skip to content

Commit 954f461

Browse files
authored
Merge pull request #1051 from OneSignal/fix/double_welcome_notification
[Fix] iOS double welcome notification
2 parents 34c0046 + 5c0edcd commit 954f461

File tree

2 files changed

+79
-88
lines changed

2 files changed

+79
-88
lines changed

src/helpers/EventHelper.ts

Lines changed: 79 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -11,52 +11,34 @@ import LocalStorage from '../utils/LocalStorage';
1111
import { CustomLinkManager } from '../managers/CustomLinkManager';
1212

1313
export default class EventHelper {
14-
static _mutexPromise: Promise<void> = Promise.resolve();
15-
static _mutexLocked = false;
16-
17-
static async onNotificationPermissionChange() {
18-
await EventHelper.checkAndTriggerSubscriptionChanged();
14+
static onNotificationPermissionChange() {
15+
EventHelper.checkAndTriggerSubscriptionChanged();
1916
}
2017

2118
static async onInternalSubscriptionSet(optedOut: boolean) {
2219
LimitStore.put('subscription.optedOut', optedOut);
2320
}
2421

2522
static async checkAndTriggerSubscriptionChanged() {
26-
if (EventHelper._mutexLocked) {
27-
await EventHelper._mutexPromise;
28-
}
29-
30-
EventHelper._mutexLocked = true;
31-
// eslint-disable-next-line no-async-promise-executor
32-
EventHelper._mutexPromise = new Promise(async (resolve, reject) => {
33-
try {
34-
OneSignalUtils.logMethodCall('checkAndTriggerSubscriptionChanged');
35-
const context: ContextSWInterface = OneSignal.context;
36-
const subscriptionState = await context.subscriptionManager.getSubscriptionState();
37-
const isPushEnabled = await OneSignal.privateIsPushNotificationsEnabled();
38-
const appState = await Database.getAppState();
39-
const { lastKnownPushEnabled } = appState;
40-
const didStateChange = (
41-
lastKnownPushEnabled === null ||
42-
isPushEnabled !== lastKnownPushEnabled
43-
);
44-
if (!didStateChange) return;
45-
Log.info(
46-
`The user's subscription state changed from ` +
47-
`${lastKnownPushEnabled === null ? '(not stored)' : lastKnownPushEnabled}${subscriptionState.subscribed}`
48-
);
49-
LocalStorage.setIsPushNotificationsEnabled(isPushEnabled);
50-
appState.lastKnownPushEnabled = isPushEnabled;
51-
await Database.setAppState(appState);
52-
EventHelper.triggerSubscriptionChanged(isPushEnabled);
53-
EventHelper._mutexLocked = false;
54-
resolve();
55-
} catch (e) {
56-
EventHelper._mutexLocked = false;
57-
reject(`checkAndTriggerSubscriptionChanged error: ${e}`);
58-
}
59-
});
23+
OneSignalUtils.logMethodCall('checkAndTriggerSubscriptionChanged');
24+
const context: ContextSWInterface = OneSignal.context;
25+
const subscriptionState = await context.subscriptionManager.getSubscriptionState();
26+
const isPushEnabled = await OneSignal.privateIsPushNotificationsEnabled();
27+
const appState = await Database.getAppState();
28+
const { lastKnownPushEnabled } = appState;
29+
const didStateChange = (
30+
lastKnownPushEnabled === null ||
31+
isPushEnabled !== lastKnownPushEnabled
32+
);
33+
if (!didStateChange) return;
34+
Log.info(
35+
`The user's subscription state changed from ` +
36+
`${lastKnownPushEnabled === null ? '(not stored)' : lastKnownPushEnabled}${subscriptionState.subscribed}`
37+
);
38+
LocalStorage.setIsPushNotificationsEnabled(isPushEnabled);
39+
appState.lastKnownPushEnabled = isPushEnabled;
40+
await Database.setAppState(appState);
41+
EventHelper.triggerSubscriptionChanged(isPushEnabled);
6042
}
6143

6244
static async _onSubscriptionChanged(newSubscriptionState: boolean | undefined) {
@@ -77,58 +59,70 @@ export default class EventHelper {
7759
}
7860
}
7961

62+
private static sendingOrSentWelcomeNotification = false;
8063
private static async onSubscriptionChanged_showWelcomeNotification(isSubscribed: boolean | undefined) {
8164
if (OneSignal.__doNotShowWelcomeNotification) {
8265
Log.debug('Not showing welcome notification because user has previously subscribed.');
8366
return;
8467
}
85-
if (isSubscribed === true) {
86-
const { deviceId } = await Database.getSubscription();
87-
const { appId } = await Database.getAppConfig();
88-
89-
const welcome_notification_opts = OneSignal.config.userConfig.welcomeNotification;
90-
const welcome_notification_disabled =
91-
welcome_notification_opts !== undefined && welcome_notification_opts['disable'] === true;
92-
let title =
93-
welcome_notification_opts !== undefined &&
94-
welcome_notification_opts['title'] !== undefined &&
95-
welcome_notification_opts['title'] !== null
96-
? welcome_notification_opts['title']
97-
: '';
98-
let message =
99-
welcome_notification_opts !== undefined &&
100-
welcome_notification_opts['message'] !== undefined &&
101-
welcome_notification_opts['message'] !== null &&
102-
welcome_notification_opts['message'].length > 0
103-
? welcome_notification_opts['message']
104-
: 'Thanks for subscribing!';
105-
const unopenableWelcomeNotificationUrl = new URL(location.href).origin + '?_osp=do_not_open';
106-
const url =
107-
welcome_notification_opts && welcome_notification_opts['url'] && welcome_notification_opts['url'].length > 0
108-
? welcome_notification_opts['url']
109-
: unopenableWelcomeNotificationUrl;
110-
title = BrowserUtils.decodeHtmlEntities(title);
111-
message = BrowserUtils.decodeHtmlEntities(message);
112-
113-
if (!welcome_notification_disabled) {
114-
Log.debug('Sending welcome notification.');
115-
OneSignalApiShared.sendNotification(
116-
appId,
117-
[deviceId],
118-
{ en: title },
119-
{ en: message },
120-
url,
121-
null,
122-
{ __isOneSignalWelcomeNotification: true },
123-
undefined
124-
);
125-
Event.trigger(OneSignal.EVENTS.WELCOME_NOTIFICATION_SENT, {
126-
title: title,
127-
message: message,
128-
url: url
129-
});
130-
}
68+
const welcome_notification_opts = OneSignal.config.userConfig.welcomeNotification;
69+
const welcome_notification_disabled =
70+
welcome_notification_opts !== undefined && welcome_notification_opts['disable'] === true;
71+
72+
if (welcome_notification_disabled) {
73+
return;
13174
}
75+
76+
if (isSubscribed !== true) {
77+
return;
78+
}
79+
80+
// Workaround only for this v15 branch; There are race conditions in the SDK
81+
// that result in the onSubscriptionChanged firing more than once sometimes.
82+
if (EventHelper.sendingOrSentWelcomeNotification) {
83+
return;
84+
}
85+
EventHelper.sendingOrSentWelcomeNotification = true;
86+
87+
const { deviceId } = await Database.getSubscription();
88+
const { appId } = await Database.getAppConfig();
89+
let title =
90+
welcome_notification_opts !== undefined &&
91+
welcome_notification_opts['title'] !== undefined &&
92+
welcome_notification_opts['title'] !== null
93+
? welcome_notification_opts['title']
94+
: '';
95+
let message =
96+
welcome_notification_opts !== undefined &&
97+
welcome_notification_opts['message'] !== undefined &&
98+
welcome_notification_opts['message'] !== null &&
99+
welcome_notification_opts['message'].length > 0
100+
? welcome_notification_opts['message']
101+
: 'Thanks for subscribing!';
102+
const unopenableWelcomeNotificationUrl = new URL(location.href).origin + '?_osp=do_not_open';
103+
const url =
104+
welcome_notification_opts && welcome_notification_opts['url'] && welcome_notification_opts['url'].length > 0
105+
? welcome_notification_opts['url']
106+
: unopenableWelcomeNotificationUrl;
107+
title = BrowserUtils.decodeHtmlEntities(title);
108+
message = BrowserUtils.decodeHtmlEntities(message);
109+
110+
Log.debug('Sending welcome notification.');
111+
OneSignalApiShared.sendNotification(
112+
appId,
113+
[deviceId],
114+
{ en: title },
115+
{ en: message },
116+
url,
117+
null,
118+
{ __isOneSignalWelcomeNotification: true },
119+
undefined
120+
);
121+
Event.trigger(OneSignal.EVENTS.WELCOME_NOTIFICATION_SENT, {
122+
title: title,
123+
message: message,
124+
url: url
125+
});
132126
}
133127

134128
private static async onSubscriptionChanged_evaluateNotifyButtonDisplayPredicate() {

test/unit/public-sdk-apis/onSession.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import {
1818
import { createSubscription } from "../../support/tester/utils";
1919
import EventsTestHelper from '../../support/tester/EventsTestHelper';
2020
import { DelayedPromptType } from '../../../src/models/Prompts';
21-
import EventHelper from "../../../src/helpers/EventHelper";
2221

2322

2423
const sinonSandbox: SinonSandbox = sinon.sandbox.create();
@@ -31,8 +30,6 @@ test.afterEach(function (_t: ExecutionContext) {
3130
OneSignal._initCalled = false;
3231
OneSignal.__initAlreadyCalled = false;
3332
OneSignal._sessionInitAlreadyRunning = false;
34-
EventHelper._mutexPromise = Promise.resolve();
35-
EventHelper._mutexLocked = false;
3633
});
3734

3835
/**

0 commit comments

Comments
 (0)