web-push-notifications 3.40.3 → 3.44.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.editorconfig +11 -0
- package/.gitlab-ci.yml +190 -0
- package/babel.config.js +7 -0
- package/ci/cdn/Dockerfile +12 -0
- package/ci/dev/Dockerfile +30 -0
- package/ci/dev/rootfs/entrypoint.sh +18 -0
- package/ci/dev/rootfs/entrypoint.sh.d/nginx.sh +6 -0
- package/ci/dev/rootfs/entrypoint.sh.d/supervisor.sh +5 -0
- package/ci/dev/rootfs/etc/nginx/_real_ip.conf +2 -0
- package/ci/dev/rootfs/etc/nginx/conf.d/default.conf +20 -0
- package/ci/dev/rootfs/etc/supervisor.d/nginx.ini +11 -0
- package/ci/github/Dockerfile +59 -0
- package/ci/github/release-zip.js +61 -0
- package/ci/npm/Dockerfile +19 -0
- package/config/config.js +24 -0
- package/config/configBuilder.js +126 -0
- package/config/helpers.js +9 -0
- package/config/index.js +1 -0
- package/develop/README.md +42 -0
- package/develop/favicon.png +0 -0
- package/develop/index.html +511 -0
- package/eslint.config.mjs +114 -0
- package/package.json +4 -34
- package/{lib → public}/index.d.ts +10 -10
- package/scripts/zip.js +26 -0
- package/src/core/Pushwoosh.ts +768 -0
- package/src/core/Pushwoosh.types.ts +254 -0
- package/src/core/Safari.types.ts +26 -0
- package/src/core/constants.ts +58 -0
- package/src/core/events.types.ts +46 -0
- package/src/core/functions.ts +33 -0
- package/src/core/legacyEventsMap.ts +64 -0
- package/src/core/logger.ts +64 -0
- package/src/core/modules/EventBus/EventBus.ts +66 -0
- package/src/core/modules/EventBus/index.ts +1 -0
- package/src/core/storage.ts +254 -0
- package/src/helpers/logger.ts +81 -0
- package/src/helpers/pwlogger/Logger.constants.ts +31 -0
- package/src/helpers/pwlogger/Logger.ts +218 -0
- package/src/helpers/pwlogger/Logger.types.ts +66 -0
- package/src/helpers/pwlogger/handlers/handler-console/handler-console.ts +40 -0
- package/src/helpers/pwlogger/index.ts +2 -0
- package/src/helpers/unescape.ts +36 -0
- package/src/models/InboxMessages.ts +202 -0
- package/src/models/InboxMessages.types.ts +111 -0
- package/src/models/NotificationPayload.ts +216 -0
- package/src/models/NotificationPayload.types.ts +65 -0
- package/src/modules/Api/Api.ts +386 -0
- package/src/modules/Api/Api.types.ts +7 -0
- package/src/modules/ApiClient/ApiClient.ts +153 -0
- package/src/modules/ApiClient/ApiClient.types.ts +222 -0
- package/src/modules/Data/Data.ts +345 -0
- package/src/modules/DateModule.ts +53 -0
- package/src/modules/InboxMessagesPublic.ts +222 -0
- package/src/modules/PlatformChecker/PlatformChecker.ts +170 -0
- package/src/modules/PlatformChecker/PlatformChecker.types.ts +5 -0
- package/src/modules/PlatformChecker/index.ts +1 -0
- package/src/modules/storage/Storage.ts +164 -0
- package/src/modules/storage/Storage.types.ts +54 -0
- package/src/modules/storage/Store.ts +104 -0
- package/src/modules/storage/migrations/26-11-2018.ts +25 -0
- package/src/modules/storage/migrations/MigrationExecutor.ts +31 -0
- package/src/modules/storage/migrations/Migrations.ts +41 -0
- package/src/modules/storage/migrations/constants.ts +8 -0
- package/src/modules/storage/migrations/helpers.ts +16 -0
- package/src/modules/storage/migrations/initial.ts +47 -0
- package/src/modules/storage/version.ts +2 -0
- package/src/npm.ts +1 -0
- package/src/pushwoosh-web-notifications.ts +47 -0
- package/src/pushwoosh-widget-inbox.ts +8 -0
- package/src/pushwoosh-widget-subscribe-popup.ts +9 -0
- package/src/pushwoosh-widget-subscription-button.ts +8 -0
- package/src/pushwoosh-widget-subscription-prompt.ts +6 -0
- package/src/service-worker.ts +455 -0
- package/src/services/PushService/PushService.ts +2 -0
- package/src/services/PushService/PushService.types.ts +74 -0
- package/src/services/PushService/drivers/PushServiceDefault/PushServiceDefault.ts +235 -0
- package/src/services/PushService/drivers/PushServiceDefault/PushServiceDefault.types.ts +3 -0
- package/src/services/PushService/drivers/PushServiceSafari/PushServiceSafari.ts +125 -0
- package/src/services/PushService/drivers/PushServiceSafari/PushServiceSafari.types.ts +4 -0
- package/src/widget-inbox.ts +1 -0
- package/src/widget-subscribe-popup.ts +1 -0
- package/src/widget-subscription-button.ts +1 -0
- package/src/widget-subscription-prompt.ts +33 -0
- package/src/widgets/Inbox/InboxWidget.ts +564 -0
- package/src/widgets/Inbox/constants.ts +49 -0
- package/src/widgets/Inbox/css/inboxWidgetStyle.css +274 -0
- package/src/widgets/Inbox/helpers.ts +63 -0
- package/src/widgets/Inbox/inbox.d.ts +9 -0
- package/src/widgets/Inbox/inbox_widget.types.ts +41 -0
- package/src/widgets/Inbox/index.ts +1 -0
- package/src/widgets/Inbox/widgetTemplates.ts +55 -0
- package/src/widgets/SubscribePopup/SubscribePopup.ts +241 -0
- package/src/widgets/SubscribePopup/constants.ts +66 -0
- package/src/widgets/SubscribePopup/helpers.ts +11 -0
- package/src/widgets/SubscribePopup/index.ts +1 -0
- package/src/widgets/SubscribePopup/popupTemplates.ts +24 -0
- package/src/widgets/SubscribePopup/styles/popup.css +226 -0
- package/src/widgets/SubscribePopup/types/subscribe-popup.ts +68 -0
- package/src/widgets/SubscriptionButton/assets/css/main.css +205 -0
- package/src/widgets/SubscriptionButton/bell.ts +67 -0
- package/src/widgets/SubscriptionButton/constants.ts +28 -0
- package/src/widgets/SubscriptionButton/index.ts +377 -0
- package/src/widgets/SubscriptionButton/positioning.ts +165 -0
- package/src/widgets/SubscriptionButton/subscribe_widget.types.ts +53 -0
- package/src/widgets/SubscriptionPrompt/SubscriptionPromptWidget.constants.ts +1 -0
- package/src/widgets/SubscriptionPrompt/SubscriptionPromptWidget.helpers.ts +110 -0
- package/src/widgets/SubscriptionPrompt/SubscriptionPromptWidget.ts +102 -0
- package/src/widgets/SubscriptionPrompt/SubscriptionPromptWidget.types.ts +23 -0
- package/src/widgets/SubscriptionPrompt/constants.ts +22 -0
- package/src/widgets/SubscriptionPrompt/helpers.ts +42 -0
- package/src/widgets/widgets.d.ts +4 -0
- package/src/worker/global.ts +36 -0
- package/src/worker/notification.ts +34 -0
- package/src/worker/worker.types.ts +4 -0
- package/test/__helpers__/apiHelpers.ts +22 -0
- package/test/__helpers__/keyValueHelpers.ts +15 -0
- package/test/__helpers__/platformHelpers.ts +54 -0
- package/test/__helpers__/sinonHelpers.ts +7 -0
- package/test/__helpers__/storageHelpers.ts +56 -0
- package/test/__mocks__/apiRequests.ts +26 -0
- package/test/__mocks__/idbMock.ts +12 -0
- package/test/__mocks__/idbObjectStoreMock.ts +38 -0
- package/test/__mocks__/inboxMessages.ts +292 -0
- package/test/__mocks__/models/inboxModel.ts +71 -0
- package/test/__mocks__/modules/apiClientModule.ts +18 -0
- package/test/__mocks__/modules/dateModule.ts +34 -0
- package/test/__mocks__/modules/inboxParamsModule.ts +21 -0
- package/test/__mocks__/modules/paramsBuilder.ts +12 -0
- package/test/__mocks__/modules/paramsModule.ts +35 -0
- package/test/__mocks__/modules/payloadBuilderModule.ts +15 -0
- package/test/__mocks__/modules/storageModule.ts +58 -0
- package/test/__mocks__/navigator.ts +38 -0
- package/test/__mocks__/notification.ts +84 -0
- package/test/__mocks__/pushwoosh.ts +12 -0
- package/test/__mocks__/userAgents +8 -0
- package/test/functions.test.ts +22 -0
- package/test/ignore-html.js +6 -0
- package/test/mocha.opts +6 -0
- package/test/modules/DateModule/unit.test.ts +80 -0
- package/test/modules/storage/Storage/unit.test.ts +180 -0
- package/test/modules/storage/Store/unit.test.ts +192 -0
- package/testRegister.js +24 -0
- package/tsconfig.json +31 -0
- package/webpack.config.js +163 -0
- package/lib/index.js +0 -2
- package/lib/index.js.map +0 -1
- package/lib/service-worker.js +0 -2
- package/lib/service-worker.js.map +0 -1
|
@@ -0,0 +1,768 @@
|
|
|
1
|
+
import * as CONSTANTS from './constants';
|
|
2
|
+
import { type EventHandlerMap, type EventName } from './events.types';
|
|
3
|
+
import { getGlobal, getVersion, isFunction, v4 } from './functions';
|
|
4
|
+
import { legacyEventsMap } from './legacyEventsMap';
|
|
5
|
+
import { Logger } from './logger';
|
|
6
|
+
import { EventBus } from './modules/EventBus';
|
|
7
|
+
import { type IInitParams, type PWInput } from './Pushwoosh.types';
|
|
8
|
+
import { keyValue, log as logStorage, message as messageStorage } from './storage';
|
|
9
|
+
import InboxMessagesModel from '../models/InboxMessages';
|
|
10
|
+
import { Api } from '../modules/Api/Api';
|
|
11
|
+
import { ApiClient } from '../modules/ApiClient/ApiClient';
|
|
12
|
+
import { type IMapResponse } from '../modules/ApiClient/ApiClient.types';
|
|
13
|
+
import { Data } from '../modules/Data/Data';
|
|
14
|
+
import InboxMessagesPublic from '../modules/InboxMessagesPublic';
|
|
15
|
+
import { PlatformChecker } from '../modules/PlatformChecker';
|
|
16
|
+
import { PushServiceDefault, PushServiceSafari } from '../services/PushService/PushService';
|
|
17
|
+
import { type IPushService } from '../services/PushService/PushService.types';
|
|
18
|
+
|
|
19
|
+
export class Pushwoosh {
|
|
20
|
+
public ready: boolean = false;
|
|
21
|
+
|
|
22
|
+
public initParams: IInitParams;
|
|
23
|
+
|
|
24
|
+
private readonly eventBus: EventBus;
|
|
25
|
+
|
|
26
|
+
public readonly data: Data;
|
|
27
|
+
private readonly apiClient: ApiClient;
|
|
28
|
+
private isCommunicationDisabled?: boolean;
|
|
29
|
+
public readonly api: Api;
|
|
30
|
+
public driver: IPushService;
|
|
31
|
+
public platformChecker: PlatformChecker;
|
|
32
|
+
|
|
33
|
+
// Inbox messages public interface
|
|
34
|
+
public pwinbox: InboxMessagesPublic;
|
|
35
|
+
private inboxModel: InboxMessagesModel;
|
|
36
|
+
|
|
37
|
+
public moduleRegistry: Record<string, any> = {};
|
|
38
|
+
|
|
39
|
+
constructor() {
|
|
40
|
+
this.eventBus = new EventBus();
|
|
41
|
+
|
|
42
|
+
this.data = new Data();
|
|
43
|
+
this.apiClient = new ApiClient(this.data);
|
|
44
|
+
|
|
45
|
+
this.api = new Api(this.eventBus, this.data, this.apiClient);
|
|
46
|
+
|
|
47
|
+
this.platformChecker = new PlatformChecker(getGlobal());
|
|
48
|
+
this.inboxModel = new InboxMessagesModel(this.eventBus, this.data, this.api);
|
|
49
|
+
this.pwinbox = new InboxMessagesPublic(this.data, this.api, this.inboxModel);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Add Web SDK Event Handler.
|
|
54
|
+
* Alias to addEventHandler method of EventBus module.
|
|
55
|
+
*
|
|
56
|
+
* @public
|
|
57
|
+
* @readonly
|
|
58
|
+
*
|
|
59
|
+
* @param {string} name - name of Web SDK event.
|
|
60
|
+
* @param {function} handler - handler of Web SDK event.
|
|
61
|
+
*
|
|
62
|
+
* @returns {void}
|
|
63
|
+
*/
|
|
64
|
+
public addEventHandler = <Name extends EventName>(
|
|
65
|
+
name: Name,
|
|
66
|
+
handler: EventHandlerMap[Name],
|
|
67
|
+
): void => this.eventBus.addEventHandler(name, handler);
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Remove Web SDK Event Handler.
|
|
71
|
+
* Alias to removeEventHandler method of EventBus module.
|
|
72
|
+
*
|
|
73
|
+
* @public
|
|
74
|
+
* @readonly
|
|
75
|
+
*
|
|
76
|
+
* @param {string} name - name of Web SDK event.
|
|
77
|
+
* @param {function} handler - handler of Web SDK event.
|
|
78
|
+
*
|
|
79
|
+
* @returns {void}
|
|
80
|
+
*/
|
|
81
|
+
public removeEventHandler = <Name extends EventName>(
|
|
82
|
+
name: Name,
|
|
83
|
+
handler: EventHandlerMap[Name],
|
|
84
|
+
): void => this.eventBus.removeEventHandler(name, handler);
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Dispatch Web SDK Event.
|
|
88
|
+
* Alias to dispatchEvent method of EventBus module.
|
|
89
|
+
*
|
|
90
|
+
* @public
|
|
91
|
+
* @readonly
|
|
92
|
+
*
|
|
93
|
+
* @param {string} name - name of Web SDK event.
|
|
94
|
+
* @param {object} payload - event payload.
|
|
95
|
+
*
|
|
96
|
+
* @returns {string} - event id.
|
|
97
|
+
*/
|
|
98
|
+
public dispatchEvent = <Name extends EventName>(
|
|
99
|
+
name: Name,
|
|
100
|
+
payload: Omit<Parameters<EventHandlerMap[Name]>[0], 'eventId'> & { eventId?: string },
|
|
101
|
+
): string => this.eventBus.dispatchEvent(name, payload);
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Method that puts the stored error/info messages to browser console.
|
|
105
|
+
* @type {{showLog: (() => Promise<any>); showKeyValues: (() => Promise<any>); showMessages: (() => Promise<any>)}}
|
|
106
|
+
*/
|
|
107
|
+
public debug = {
|
|
108
|
+
async showLog() {
|
|
109
|
+
const items = await logStorage.getAll();
|
|
110
|
+
console.log(items);
|
|
111
|
+
},
|
|
112
|
+
async showKeyValues() {
|
|
113
|
+
const items = await keyValue.getAll();
|
|
114
|
+
console.log(items);
|
|
115
|
+
},
|
|
116
|
+
async showMessages() {
|
|
117
|
+
const items = await messageStorage.getAll();
|
|
118
|
+
items.forEach((i: any) => console.log(i));
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Polymorph PW method.
|
|
124
|
+
* Can get array in format [string, params | callback] or function.
|
|
125
|
+
*
|
|
126
|
+
* // with callback:
|
|
127
|
+
* Pushwoosh.push(['onNotificationClick', function(api, payload) {
|
|
128
|
+
* // click on the notificationn
|
|
129
|
+
* }]);
|
|
130
|
+
*
|
|
131
|
+
* // with function:
|
|
132
|
+
* Pushwoosh.push(function(api) {
|
|
133
|
+
* // this is a bit easier way to subscribe to onReady
|
|
134
|
+
* });
|
|
135
|
+
*
|
|
136
|
+
* // with params:
|
|
137
|
+
* // initiates Pushwoosh service and notification subscription
|
|
138
|
+
* Pushwoosh.push(['init', {
|
|
139
|
+
* applicationCode: 'XXXXX-XXXXX',
|
|
140
|
+
* // see more about params in documentation
|
|
141
|
+
* // https://docs.pushwoosh.com/docs/web-push-sdk-30#section-integration
|
|
142
|
+
* }]);
|
|
143
|
+
*
|
|
144
|
+
* @param command
|
|
145
|
+
*/
|
|
146
|
+
public push(command: PWInput) {
|
|
147
|
+
if (isFunction(command)) {
|
|
148
|
+
this.subscribeToLegacyEvents(CONSTANTS.LEGACY_EVENT_ON_READY, command);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (!Array.isArray(command)) {
|
|
153
|
+
throw new Error('Invalid command!');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (command[0] === 'init') {
|
|
157
|
+
this.initialize(command[1]).catch((err) => {
|
|
158
|
+
console.error('Pushwoosh: Error during initialization', err);
|
|
159
|
+
});
|
|
160
|
+
} else if (!this.subscribeToLegacyEvents(command[0], command[1])) {
|
|
161
|
+
console.log('Pushwoosh: Unknown command', command);
|
|
162
|
+
throw new Error('Unknown command!');
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Method initializes the permission dialog on the device
|
|
168
|
+
* and registers through the API in case the device hasn't been registered before.
|
|
169
|
+
* @returns {Promise<void>}
|
|
170
|
+
*/
|
|
171
|
+
public async subscribe(isForceSubscribe = true): Promise<void> {
|
|
172
|
+
if (this.isCommunicationDisabled) {
|
|
173
|
+
Logger.error('Communication is disabled!');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const isPermissionDefault = this.driver.checkIsPermissionDefault();
|
|
177
|
+
|
|
178
|
+
// if permission granted need ask permission for send notifications
|
|
179
|
+
if (isPermissionDefault) {
|
|
180
|
+
// emit event when permission dialog show
|
|
181
|
+
this.eventBus.dispatchEvent('show-notification-permission-dialog', {});
|
|
182
|
+
// all action before this MUST be a synchrony because
|
|
183
|
+
// in new release in ff 72 we must call this event by user
|
|
184
|
+
// ask permission
|
|
185
|
+
// ask permissions only show prompt window, not register device for send push notifications
|
|
186
|
+
await this.driver.askPermission();
|
|
187
|
+
|
|
188
|
+
const permission = this.driver.getPermission();
|
|
189
|
+
|
|
190
|
+
// emit event when permission dialog hide with permission state
|
|
191
|
+
this.eventBus.dispatchEvent('hide-notification-permission-dialog', { permission });
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const permission = this.driver.getPermission();
|
|
195
|
+
const isManualUnsubscribed = await this.data.getStatusManualUnsubscribed();
|
|
196
|
+
const isDeviceRegister = await this.api.checkDeviceSubscribeForPushNotifications(false);
|
|
197
|
+
|
|
198
|
+
// if permission granted emit event and register device into pushwoosh
|
|
199
|
+
if (permission === CONSTANTS.PERMISSION_GRANTED) {
|
|
200
|
+
this.eventBus.dispatchEvent('permission-granted', {});
|
|
201
|
+
const needSubscribe = !isDeviceRegister && !isManualUnsubscribed;
|
|
202
|
+
|
|
203
|
+
if (needSubscribe || isForceSubscribe) {
|
|
204
|
+
await this.driver.subscribe();
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
this.eventBus.dispatchEvent('subscribe', {});
|
|
208
|
+
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// if permission denied emit event
|
|
213
|
+
if (permission === CONSTANTS.PERMISSION_DENIED) {
|
|
214
|
+
this.eventBus.dispatchEvent('permission-denied', {});
|
|
215
|
+
|
|
216
|
+
if (isDeviceRegister) {
|
|
217
|
+
await this.driver.unsubscribe();
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Unsubscribe device.
|
|
226
|
+
* @returns {Promise<void>}
|
|
227
|
+
*/
|
|
228
|
+
public async unsubscribe(): Promise<void> {
|
|
229
|
+
try {
|
|
230
|
+
await this.driver.unsubscribe();
|
|
231
|
+
} catch (error) {
|
|
232
|
+
Logger.error(error, 'Error occurred during the unsubscribe');
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* force subscribe if there was a manual unsubscribe
|
|
238
|
+
* @returns {Promise<void>}
|
|
239
|
+
*/
|
|
240
|
+
public async forceSubscribe() {
|
|
241
|
+
await this.subscribe(true);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Check device's registration status
|
|
246
|
+
* @returns {boolean}
|
|
247
|
+
*/
|
|
248
|
+
public isDeviceRegistered(): boolean {
|
|
249
|
+
return localStorage.getItem(CONSTANTS.KEY_DEVICE_REGISTRATION_STATUS) === CONSTANTS.DEVICE_REGISTRATION_STATUS_REGISTERED;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
public isDeviceUnregistered(): boolean {
|
|
253
|
+
return localStorage.getItem(CONSTANTS.KEY_DEVICE_REGISTRATION_STATUS) === CONSTANTS.DEVICE_REGISTRATION_STATUS_UNREGISTERED;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Check device's subscription status
|
|
258
|
+
* @returns {Promise<boolean>}
|
|
259
|
+
*/
|
|
260
|
+
public async isSubscribed(): Promise<boolean> {
|
|
261
|
+
return this.api.checkDeviceSubscribeForPushNotifications();
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Check current communication state
|
|
266
|
+
* @returns {Promise<boolean>}
|
|
267
|
+
*/
|
|
268
|
+
public async isCommunicationEnabled() {
|
|
269
|
+
const isCommunicationDisabled = await this.data.getStatusCommunicationDisabled();
|
|
270
|
+
|
|
271
|
+
return !isCommunicationDisabled;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Send "GDPRConsent" postEvent and depends on param "isEnabled"
|
|
276
|
+
* device will be registered/unregistered from all communication channels.
|
|
277
|
+
* @param {boolean} isEnabled
|
|
278
|
+
* @returns {Promise<void>}
|
|
279
|
+
*/
|
|
280
|
+
public async setCommunicationEnabled(isEnabled: boolean = true) {
|
|
281
|
+
const deviceType = await this.data.getDeviceType();
|
|
282
|
+
const isPermissionGranted = this.driver.checkIsPermissionGranted();
|
|
283
|
+
|
|
284
|
+
await this.data.setStatusCommunicationDisabled(!isEnabled);
|
|
285
|
+
|
|
286
|
+
if (isEnabled) {
|
|
287
|
+
await this.data.setStatusDropAllData(false);
|
|
288
|
+
if (isPermissionGranted) {
|
|
289
|
+
await this.api.registerDevice();
|
|
290
|
+
}
|
|
291
|
+
} else {
|
|
292
|
+
await this.api.unregisterDevice();
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
this.eventBus.dispatchEvent('change-enabled-communication', { isEnabled });
|
|
296
|
+
|
|
297
|
+
await this.api.postEvent(CONSTANTS.EVENT_GDPR_CONSENT, {
|
|
298
|
+
channel: isEnabled,
|
|
299
|
+
device_type: deviceType,
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Send "GDPRDelete" postEvent and remove all device device data from Pushwoosh.
|
|
305
|
+
* @returns {Promise<void>}
|
|
306
|
+
*/
|
|
307
|
+
public async removeAllDeviceData() {
|
|
308
|
+
const deviceType = await this.data.getDeviceType();
|
|
309
|
+
|
|
310
|
+
await this.api.postEvent(CONSTANTS.EVENT_GDPR_DELETE, {
|
|
311
|
+
status: true,
|
|
312
|
+
device_type: deviceType,
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
await this.api.deleteDevice();
|
|
316
|
+
await this.data.clearAll();
|
|
317
|
+
|
|
318
|
+
await this.data.setStatusDropAllData(true);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Method returns hardware id.
|
|
323
|
+
* @returns {Promise<string>}
|
|
324
|
+
*/
|
|
325
|
+
public async getHWID(): Promise<string> {
|
|
326
|
+
return await this.data.getHwid();
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Method returns push token.
|
|
331
|
+
* @returns {Promise<string>}
|
|
332
|
+
*/
|
|
333
|
+
public async getPushToken(): Promise<string | undefined> {
|
|
334
|
+
const { pushToken } = await this.data.getTokens();
|
|
335
|
+
|
|
336
|
+
return pushToken;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Method returns userId
|
|
341
|
+
* @returns {Promise<string | null>}
|
|
342
|
+
*/
|
|
343
|
+
public async getUserId(): Promise<string | undefined> {
|
|
344
|
+
return await this.data.getUserId();
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Method returns an object with all params.
|
|
349
|
+
* @returns {Promise<IPWParams>}
|
|
350
|
+
*/
|
|
351
|
+
public async getParams() {
|
|
352
|
+
return await this.api.getParams();
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Method returns true if notifications available.
|
|
357
|
+
* @returns {boolean}
|
|
358
|
+
*/
|
|
359
|
+
public isAvailableNotifications() {
|
|
360
|
+
return this.platformChecker.isAvailableNotifications;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
public async sendStatisticsVisitedPage() {
|
|
364
|
+
const {
|
|
365
|
+
document: { title },
|
|
366
|
+
location: { origin, pathname, href },
|
|
367
|
+
} = window;
|
|
368
|
+
|
|
369
|
+
await this.api.pageVisit({
|
|
370
|
+
title,
|
|
371
|
+
url_path: `${origin}${pathname}`,
|
|
372
|
+
url: href,
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
private async initialize(params: IInitParams) {
|
|
377
|
+
this.initParams = params;
|
|
378
|
+
// base logger configuration
|
|
379
|
+
const manualDebug = localStorage.getItem(CONSTANTS.MANUAL_SET_LOGGER_LEVEL);
|
|
380
|
+
Logger.setLevel(manualDebug || params.logLevel || 'error');
|
|
381
|
+
|
|
382
|
+
// if not available push notifications -> exit
|
|
383
|
+
if (!this.platformChecker.isAvailableNotifications) {
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
if (!params.applicationCode) {
|
|
388
|
+
throw new Error('Can\'t find application code!');
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// get current application code
|
|
392
|
+
const currentApplicationCode = await this.data.getApplicationCode();
|
|
393
|
+
|
|
394
|
+
// if have not old application code or application code was change => remove all info about init params and subscription
|
|
395
|
+
if (!currentApplicationCode || currentApplicationCode !== params.applicationCode) {
|
|
396
|
+
await this.data.clearAll();
|
|
397
|
+
await this.data.setApplicationCode(params.applicationCode);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// check hwid
|
|
401
|
+
const hwid = await this.data.getHwid();
|
|
402
|
+
|
|
403
|
+
if (!hwid) {
|
|
404
|
+
const id = params.applicationCode + '_' + v4();
|
|
405
|
+
|
|
406
|
+
await this.data.setHwid(id);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// add info about platform
|
|
410
|
+
await this.data.setDeviceType(this.platformChecker.getPlatformType());
|
|
411
|
+
await this.data.setDeviceModel(this.platformChecker.getBrowserVersion());
|
|
412
|
+
await this.data.setLanguage(params.tags && params.tags.Language || navigator.language);
|
|
413
|
+
|
|
414
|
+
// set configuration info
|
|
415
|
+
await this.data.setApiEntrypoint(params.pushwooshUrl || '');
|
|
416
|
+
await this.data.setApiToken(params.apiToken || '');
|
|
417
|
+
await this.data.setSdkVersion(getVersion());
|
|
418
|
+
|
|
419
|
+
// get remote config
|
|
420
|
+
const config = await this.api.getConfig([
|
|
421
|
+
'page_visit',
|
|
422
|
+
'vapid_key',
|
|
423
|
+
'web_in_apps',
|
|
424
|
+
'events',
|
|
425
|
+
'subscription_prompt',
|
|
426
|
+
]);
|
|
427
|
+
|
|
428
|
+
this.onGetConfig(config && config.features);
|
|
429
|
+
|
|
430
|
+
// check communication disabled
|
|
431
|
+
this.isCommunicationDisabled = await this.data.getStatusCommunicationDisabled();
|
|
432
|
+
|
|
433
|
+
await this.open();
|
|
434
|
+
|
|
435
|
+
// check user id
|
|
436
|
+
const userIdWasChange = await this.data.getStatusUserIdWasChanged();
|
|
437
|
+
|
|
438
|
+
if (params.userId && params.userId !== 'user_id' && !userIdWasChange) {
|
|
439
|
+
await this.api.registerUser(params.userId);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// check email
|
|
443
|
+
const emailWasChange = await this.data.getStatusEmailWasChanged();
|
|
444
|
+
|
|
445
|
+
if (params.email && params.email !== '' && !emailWasChange) {
|
|
446
|
+
// validate email
|
|
447
|
+
if (/^\S+@\S+\.\S+$/.test(params.email)) {
|
|
448
|
+
await this.api.registerEmail(params.email);
|
|
449
|
+
} else {
|
|
450
|
+
Logger.write('error', `can't register invalid email: ${params.email}`);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// set tags
|
|
455
|
+
if (params.tags) {
|
|
456
|
+
this.api.setTags(params.tags);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// init push notification
|
|
460
|
+
const applicationServerKey = await this.data.getApplicationServerKey();
|
|
461
|
+
if (this.platformChecker.isAvailableNotifications && applicationServerKey) {
|
|
462
|
+
await this.initPushNotifications(params);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// init submodules (inbox)
|
|
466
|
+
try {
|
|
467
|
+
await this.inboxModel.updateMessages();
|
|
468
|
+
} catch (error) {
|
|
469
|
+
Logger.write('error', error);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// ready
|
|
473
|
+
this.ready = true;
|
|
474
|
+
this.eventBus.dispatchEvent('ready', {});
|
|
475
|
+
|
|
476
|
+
const delayedEvent = await this.data.getDelayedEvent();
|
|
477
|
+
|
|
478
|
+
if (delayedEvent) {
|
|
479
|
+
const { type, payload } = delayedEvent;
|
|
480
|
+
this.emitLegacyEventsFromServiceWorker(type, payload);
|
|
481
|
+
await this.data.setDelayedEvent(null);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
if ('serviceWorker' in navigator) {
|
|
485
|
+
navigator.serviceWorker.onmessage = (event: MessageEvent) => this.onServiceWorkerMessage(event);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
localStorage.setItem('pushwoosh-websdk-status', 'init');
|
|
489
|
+
|
|
490
|
+
// Dispatch 'pushwoosh.initialized' event
|
|
491
|
+
document.dispatchEvent(new CustomEvent('pushwoosh.initialized', {
|
|
492
|
+
detail: {
|
|
493
|
+
pw: this,
|
|
494
|
+
},
|
|
495
|
+
}));
|
|
496
|
+
|
|
497
|
+
// send push stat only in safari, because safari haven't service worker
|
|
498
|
+
// in other browsers stat will be send in service worker
|
|
499
|
+
if (this.platformChecker.isSafari) {
|
|
500
|
+
const hashReg = /#P(.*)/;
|
|
501
|
+
const hash = decodeURIComponent(document.location.hash);
|
|
502
|
+
|
|
503
|
+
if (hashReg.test(hash)) {
|
|
504
|
+
this.api
|
|
505
|
+
.pushStat(hashReg.exec(hash)![1])
|
|
506
|
+
.then(() => history.pushState(null, '', '#'));
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* Default process during PW initialization.
|
|
513
|
+
* Init API. Subscription to notifications.
|
|
514
|
+
* Emit delayed events.
|
|
515
|
+
*/
|
|
516
|
+
private async defaultProcess(): Promise<void> {
|
|
517
|
+
const permission = this.driver.getPermission();
|
|
518
|
+
|
|
519
|
+
if (permission === 'granted') {
|
|
520
|
+
await this.data.setLastPermissionStatus(permission);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
const isCommunicationDisabled = await this.data.getStatusCommunicationDisabled();
|
|
524
|
+
const isDropAllData = await this.data.getStatusDropAllData();
|
|
525
|
+
const isNeedResubscribe = await this.driver.checkIsNeedResubscribe();
|
|
526
|
+
|
|
527
|
+
if (isCommunicationDisabled || isDropAllData) {
|
|
528
|
+
await this.unsubscribe();
|
|
529
|
+
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
if (isNeedResubscribe) {
|
|
534
|
+
await this.unsubscribe();
|
|
535
|
+
await this.data.setStatusManualUnsubscribed(false);
|
|
536
|
+
await this.data.setIsVapidChanged(false);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
const isManualUnsubscribed = await this.data.getStatusManualUnsubscribed();
|
|
540
|
+
|
|
541
|
+
// update status is register
|
|
542
|
+
const isRegister = await this.api.checkDeviceSubscribeForPushNotifications(false);
|
|
543
|
+
|
|
544
|
+
// Actions depending of the permissions
|
|
545
|
+
switch (permission) {
|
|
546
|
+
case CONSTANTS.PERMISSION_PROMPT:
|
|
547
|
+
{
|
|
548
|
+
// emit event permission default
|
|
549
|
+
this.eventBus.dispatchEvent('permission-default', {});
|
|
550
|
+
|
|
551
|
+
// device can't be register if permission default
|
|
552
|
+
if (isRegister) {
|
|
553
|
+
await this.unsubscribe();
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
break;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
case CONSTANTS.PERMISSION_DENIED:
|
|
560
|
+
// emit event permission denied
|
|
561
|
+
this.eventBus.dispatchEvent('permission-denied', {});
|
|
562
|
+
|
|
563
|
+
// device can't be register if permission default
|
|
564
|
+
if (isRegister) {
|
|
565
|
+
await this.unsubscribe();
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
break;
|
|
569
|
+
case CONSTANTS.PERMISSION_GRANTED:
|
|
570
|
+
// emit event permission granted
|
|
571
|
+
this.eventBus.dispatchEvent('permission-granted', {});
|
|
572
|
+
|
|
573
|
+
// device can't be register if manual unsubscribed
|
|
574
|
+
if (isManualUnsubscribed && isRegister) {
|
|
575
|
+
await this.unsubscribe();
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// device must be register if not manual unsubscribed
|
|
579
|
+
// or if change configuration -> resubscribe device for get new push token
|
|
580
|
+
if (!isRegister && !isManualUnsubscribed || isNeedResubscribe) {
|
|
581
|
+
await this.subscribe(true);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
break;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
*
|
|
590
|
+
* @param {MessageEvent} event
|
|
591
|
+
*/
|
|
592
|
+
private onServiceWorkerMessage(event: MessageEvent) {
|
|
593
|
+
const { data = {} } = event || {};
|
|
594
|
+
const { type = '', payload = {} } = data || {};
|
|
595
|
+
this.emitLegacyEventsFromServiceWorker(type, payload);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* Check device's session and call the appOpen method,
|
|
600
|
+
* no more than once an hour.
|
|
601
|
+
* Force need to Safari await subscribe status
|
|
602
|
+
* @param {boolean} isForce
|
|
603
|
+
* @returns {Promise<void>}
|
|
604
|
+
*/
|
|
605
|
+
private async open(isForce?: boolean) {
|
|
606
|
+
let lastApplicationOpenTime = await this.data.getLastOpenApplicationTime();
|
|
607
|
+
const currentTime = Date.now();
|
|
608
|
+
|
|
609
|
+
if (!lastApplicationOpenTime) {
|
|
610
|
+
lastApplicationOpenTime = 0;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
const isSendingPeriodExceeded = currentTime - lastApplicationOpenTime < CONSTANTS.PERIOD_SEND_APP_OPEN;
|
|
614
|
+
const needSendOpenStatistics = isForce || !isSendingPeriodExceeded;
|
|
615
|
+
|
|
616
|
+
if (!needSendOpenStatistics) {
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
await this.data.setLastOpenApplicationTime(currentTime);
|
|
621
|
+
await this.api.applicationOpen();
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
private async onGetConfig(features: IMapResponse['getConfig']['features']) {
|
|
625
|
+
await this.data.setFeatures(features);
|
|
626
|
+
|
|
627
|
+
if (features) {
|
|
628
|
+
// page visited feature
|
|
629
|
+
if (features.page_visit && features.page_visit.enabled) {
|
|
630
|
+
await keyValue.set(CONSTANTS.PAGE_VISITED_URL, features.page_visit.entrypoint);
|
|
631
|
+
this.sendStatisticsVisitedPage();
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// send default event, page visited
|
|
635
|
+
if (features.events && features.events.length) {
|
|
636
|
+
const isPageVisitedEvent = features.events.some(
|
|
637
|
+
(event: string): boolean => event === CONSTANTS.EVENT_PW_SITE_OPENED,
|
|
638
|
+
);
|
|
639
|
+
if (isPageVisitedEvent) {
|
|
640
|
+
this.sendPostEventVisitedPage();
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// vapid key
|
|
645
|
+
if (features.vapid_key) {
|
|
646
|
+
const previousServerKey = await this.data.getApplicationServerKey();
|
|
647
|
+
|
|
648
|
+
await this.data.setApplicationServerKey(features.vapid_key);
|
|
649
|
+
if (previousServerKey !== features.vapid_key) {
|
|
650
|
+
await this.data.setIsVapidChanged(true);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
private async initPushNotifications(params: IInitParams): Promise<void> {
|
|
657
|
+
await this.data.setDefaultNotificationImage(params.defaultNotificationImage);
|
|
658
|
+
await this.data.setDefaultNotificationTitle(params.defaultNotificationTitle);
|
|
659
|
+
await this.data.setServiceWorkerUrl(params.serviceWorkerUrl);
|
|
660
|
+
await this.data.setServiceWorkerScope(params.scope);
|
|
661
|
+
|
|
662
|
+
await this.data.setInitParams(params);
|
|
663
|
+
|
|
664
|
+
await this.initDriver();
|
|
665
|
+
|
|
666
|
+
// Default actions on init
|
|
667
|
+
try {
|
|
668
|
+
await this.defaultProcess();
|
|
669
|
+
} catch (error) {
|
|
670
|
+
Logger.error(error, 'Internal error: defaultProcess fail');
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
private async initDriver(): Promise<void> {
|
|
675
|
+
if (this.platformChecker.isSafari) {
|
|
676
|
+
const { safariWebsitePushID: webSitePushId } = await this.data.getInitParams();
|
|
677
|
+
|
|
678
|
+
if (!webSitePushId) {
|
|
679
|
+
Logger.info('For work with Safari Push Notification add safariWebsitePushID to initParams!');
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
this.driver = new PushServiceSafari(this.api, this.data, { webSitePushId });
|
|
684
|
+
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
if (this.platformChecker.isAvailableServiceWorker) {
|
|
689
|
+
this.driver = new PushServiceDefault(this.api, this.data, {});
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
public sendPostEventVisitedPage() {
|
|
694
|
+
const {
|
|
695
|
+
document: { title },
|
|
696
|
+
location: { href },
|
|
697
|
+
} = window;
|
|
698
|
+
|
|
699
|
+
this.api.postEvent(CONSTANTS.EVENT_PW_SITE_OPENED, {
|
|
700
|
+
url: href,
|
|
701
|
+
title: title,
|
|
702
|
+
device_type: this.platformChecker.platform,
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
/**
|
|
707
|
+
* @private
|
|
708
|
+
*
|
|
709
|
+
* @param {string} type - legacy event type
|
|
710
|
+
* @param {function} handler - legacy handler
|
|
711
|
+
*/
|
|
712
|
+
private subscribeToLegacyEvents(type: string, handler: (api?: Api, payload?: any) => void): boolean {
|
|
713
|
+
let isHandled = true;
|
|
714
|
+
|
|
715
|
+
switch (true) {
|
|
716
|
+
case type === CONSTANTS.LEGACY_EVENT_ON_LOAD:
|
|
717
|
+
handler();
|
|
718
|
+
break;
|
|
719
|
+
|
|
720
|
+
case type === CONSTANTS.LEGACY_EVENT_ON_READY:
|
|
721
|
+
if (this.ready) {
|
|
722
|
+
handler(this.api);
|
|
723
|
+
break;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
this.eventBus.addEventHandler(
|
|
727
|
+
'ready',
|
|
728
|
+
() => handler(this.api),
|
|
729
|
+
);
|
|
730
|
+
break;
|
|
731
|
+
|
|
732
|
+
case type in legacyEventsMap:
|
|
733
|
+
this.eventBus.addEventHandler(
|
|
734
|
+
legacyEventsMap[type].name as keyof EventHandlerMap,
|
|
735
|
+
(payload: any) => {
|
|
736
|
+
const { prop } = legacyEventsMap[type];
|
|
737
|
+
handler(this.api, prop ? payload[prop] : undefined);
|
|
738
|
+
},
|
|
739
|
+
);
|
|
740
|
+
break;
|
|
741
|
+
|
|
742
|
+
default:
|
|
743
|
+
isHandled = false;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
return isHandled;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
private emitLegacyEventsFromServiceWorker(type: string, payload?: any): void {
|
|
750
|
+
switch (type) {
|
|
751
|
+
case CONSTANTS.LEGACY_EVENT_ON_PUSH_DELIVERY:
|
|
752
|
+
this.eventBus.dispatchEvent('receive-push', { notification: payload });
|
|
753
|
+
break;
|
|
754
|
+
|
|
755
|
+
case CONSTANTS.LEGACY_EVENT_ON_NOTIFICATION_CLICK:
|
|
756
|
+
this.eventBus.dispatchEvent('open-notification', { notification: payload });
|
|
757
|
+
break;
|
|
758
|
+
|
|
759
|
+
case CONSTANTS.LEGACY_EVENT_ON_NOTIFICATION_CLOSE:
|
|
760
|
+
this.eventBus.dispatchEvent('hide-notification', { notification: payload });
|
|
761
|
+
break;
|
|
762
|
+
|
|
763
|
+
case CONSTANTS.LEGACY_EVENT_ON_PUT_NEW_MESSAGE_TO_INBOX_STORE:
|
|
764
|
+
this.eventBus.dispatchEvent('receive-inbox-message', { message: payload });
|
|
765
|
+
break;
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
}
|