@clix-so/react-native-sdk 1.0.0 → 1.1.1-beta.1
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/lib/module/core/Clix.js +40 -95
- package/lib/module/core/Clix.js.map +1 -1
- package/lib/module/core/ClixInitCoordinator.js +3 -14
- package/lib/module/core/ClixInitCoordinator.js.map +1 -1
- package/lib/module/core/ClixNotification.js +25 -28
- package/lib/module/core/ClixNotification.js.map +1 -1
- package/lib/module/models/ClixDevice.js +0 -6
- package/lib/module/models/ClixDevice.js.map +1 -1
- package/lib/module/models/ClixPushNotificationPayload.js +0 -19
- package/lib/module/models/ClixPushNotificationPayload.js.map +1 -1
- package/lib/module/services/ClixAPIClient.js +50 -99
- package/lib/module/services/ClixAPIClient.js.map +1 -1
- package/lib/module/services/DeviceAPIService.js +37 -45
- package/lib/module/services/DeviceAPIService.js.map +1 -1
- package/lib/module/services/DeviceService.js +97 -116
- package/lib/module/services/DeviceService.js.map +1 -1
- package/lib/module/services/EventAPIService.js +3 -5
- package/lib/module/services/EventAPIService.js.map +1 -1
- package/lib/module/services/EventService.js +13 -20
- package/lib/module/services/EventService.js.map +1 -1
- package/lib/module/services/NotificationService.js +253 -402
- package/lib/module/services/NotificationService.js.map +1 -1
- package/lib/module/services/TokenService.js +3 -59
- package/lib/module/services/TokenService.js.map +1 -1
- package/lib/module/utils/http/HTTPClient.js +109 -0
- package/lib/module/utils/http/HTTPClient.js.map +1 -0
- package/lib/module/utils/http/HTTPMethod.js +10 -0
- package/lib/module/utils/http/HTTPMethod.js.map +1 -0
- package/lib/module/utils/http/HTTPRequest.js +4 -0
- package/lib/module/utils/http/HTTPRequest.js.map +1 -0
- package/lib/module/utils/http/HTTPResponse.js +2 -0
- package/lib/module/utils/http/HTTPResponse.js.map +1 -0
- package/lib/module/utils/types.js +2 -0
- package/lib/module/utils/types.js.map +1 -0
- package/lib/typescript/src/core/Clix.d.ts +13 -15
- package/lib/typescript/src/core/Clix.d.ts.map +1 -1
- package/lib/typescript/src/core/ClixConfig.d.ts +3 -3
- package/lib/typescript/src/core/ClixConfig.d.ts.map +1 -1
- package/lib/typescript/src/core/ClixInitCoordinator.d.ts +0 -3
- package/lib/typescript/src/core/ClixInitCoordinator.d.ts.map +1 -1
- package/lib/typescript/src/core/ClixNotification.d.ts +6 -5
- package/lib/typescript/src/core/ClixNotification.d.ts.map +1 -1
- package/lib/typescript/src/models/ClixDevice.d.ts +0 -2
- package/lib/typescript/src/models/ClixDevice.d.ts.map +1 -1
- package/lib/typescript/src/models/ClixPushNotificationPayload.d.ts +8 -21
- package/lib/typescript/src/models/ClixPushNotificationPayload.d.ts.map +1 -1
- package/lib/typescript/src/services/ClixAPIClient.d.ts +6 -22
- package/lib/typescript/src/services/ClixAPIClient.d.ts.map +1 -1
- package/lib/typescript/src/services/DeviceAPIService.d.ts +1 -1
- package/lib/typescript/src/services/DeviceAPIService.d.ts.map +1 -1
- package/lib/typescript/src/services/DeviceService.d.ts +10 -5
- package/lib/typescript/src/services/DeviceService.d.ts.map +1 -1
- package/lib/typescript/src/services/EventAPIService.d.ts.map +1 -1
- package/lib/typescript/src/services/EventService.d.ts +1 -0
- package/lib/typescript/src/services/EventService.d.ts.map +1 -1
- package/lib/typescript/src/services/NotificationService.d.ts +50 -57
- package/lib/typescript/src/services/NotificationService.d.ts.map +1 -1
- package/lib/typescript/src/services/TokenService.d.ts +1 -7
- package/lib/typescript/src/services/TokenService.d.ts.map +1 -1
- package/lib/typescript/src/utils/http/HTTPClient.d.ts +15 -0
- package/lib/typescript/src/utils/http/HTTPClient.d.ts.map +1 -0
- package/lib/typescript/src/utils/http/HTTPMethod.d.ts +7 -0
- package/lib/typescript/src/utils/http/HTTPMethod.d.ts.map +1 -0
- package/lib/typescript/src/utils/http/HTTPRequest.d.ts +9 -0
- package/lib/typescript/src/utils/http/HTTPRequest.d.ts.map +1 -0
- package/lib/typescript/src/utils/http/HTTPResponse.d.ts +6 -0
- package/lib/typescript/src/utils/http/HTTPResponse.d.ts.map +1 -0
- package/lib/typescript/src/utils/types.d.ts +5 -0
- package/lib/typescript/src/utils/types.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/core/Clix.ts +62 -115
- package/src/core/ClixConfig.ts +3 -3
- package/src/core/ClixInitCoordinator.ts +5 -17
- package/src/core/ClixNotification.ts +36 -37
- package/src/models/ClixDevice.ts +17 -25
- package/src/models/ClixPushNotificationPayload.ts +8 -37
- package/src/services/ClixAPIClient.ts +84 -144
- package/src/services/DeviceAPIService.ts +39 -47
- package/src/services/DeviceService.ts +122 -156
- package/src/services/EventAPIService.ts +3 -5
- package/src/services/EventService.ts +26 -33
- package/src/services/NotificationService.ts +321 -534
- package/src/services/TokenService.ts +4 -71
- package/src/utils/http/HTTPClient.ts +154 -0
- package/src/utils/http/HTTPMethod.ts +6 -0
- package/src/utils/http/HTTPRequest.ts +9 -0
- package/src/utils/http/HTTPResponse.ts +5 -0
- package/src/utils/types.ts +7 -0
|
@@ -15,35 +15,40 @@ import messaging, {
|
|
|
15
15
|
FirebaseMessagingTypes,
|
|
16
16
|
} from '@react-native-firebase/messaging';
|
|
17
17
|
import { Linking, Platform } from 'react-native';
|
|
18
|
-
import { ClixPushNotificationPayload } from '../models/ClixPushNotificationPayload';
|
|
18
|
+
import type { ClixPushNotificationPayload } from '../models/ClixPushNotificationPayload';
|
|
19
19
|
import { ClixLogger } from '../utils/logging/ClixLogger';
|
|
20
|
-
import { UUID } from '../utils/UUID';
|
|
21
20
|
import { DeviceService } from './DeviceService';
|
|
22
21
|
import { EventService } from './EventService';
|
|
23
|
-
import { StorageService } from './StorageService';
|
|
24
22
|
import { TokenService } from './TokenService';
|
|
25
23
|
|
|
26
|
-
interface NotificationContent {
|
|
27
|
-
title: string;
|
|
28
|
-
body: string;
|
|
29
|
-
imageUrl?: string;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
24
|
type NotificationData = Record<string, any>;
|
|
33
25
|
|
|
34
|
-
export type
|
|
26
|
+
export type MessageHandler = (
|
|
35
27
|
data: NotificationData
|
|
36
28
|
) => Promise<boolean> | boolean;
|
|
37
29
|
export type BackgroundMessageHandler = (
|
|
38
30
|
data: NotificationData
|
|
39
31
|
) => Promise<void> | void;
|
|
40
|
-
export type
|
|
32
|
+
export type NotificationOpenedAppHandler = (
|
|
41
33
|
data: NotificationData
|
|
42
34
|
) => Promise<void> | void;
|
|
43
|
-
export type
|
|
35
|
+
export type TokenRefreshHandler = (token: string) => Promise<void> | void;
|
|
36
|
+
export type ForegroundEventHandler = (event: Event) => Promise<void> | void;
|
|
44
37
|
|
|
45
38
|
export class NotificationService {
|
|
46
|
-
|
|
39
|
+
autoHandleLandingUrl = true;
|
|
40
|
+
messageHandler?: MessageHandler;
|
|
41
|
+
backgroundMessageHandler?: BackgroundMessageHandler;
|
|
42
|
+
notificationOpenedAppHandler?: NotificationOpenedAppHandler;
|
|
43
|
+
tokenRefreshHandler?: TokenRefreshHandler;
|
|
44
|
+
foregroundEventHandler?: ForegroundEventHandler;
|
|
45
|
+
|
|
46
|
+
private isInitialized = false;
|
|
47
|
+
private processedMessageIds = new Set<string>();
|
|
48
|
+
private unsubscribeMessage?: () => void;
|
|
49
|
+
private unsubscribeNotificationOpenedApp?: () => void;
|
|
50
|
+
private unsubscribeTokenRefresh?: () => void;
|
|
51
|
+
private unsubscribeForegroundEvent?: () => void;
|
|
47
52
|
|
|
48
53
|
private static readonly DEFAULT_CHANNEL: AndroidChannel = {
|
|
49
54
|
id: 'clix_channel',
|
|
@@ -56,214 +61,161 @@ export class NotificationService {
|
|
|
56
61
|
};
|
|
57
62
|
private static readonly ANDROID_GROUP_ID = 'clix_notification_group';
|
|
58
63
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
private eventService!: EventService;
|
|
65
|
-
private storageService!: StorageService;
|
|
66
|
-
private deviceService?: DeviceService;
|
|
67
|
-
private tokenService?: TokenService;
|
|
64
|
+
constructor(
|
|
65
|
+
private readonly deviceService: DeviceService,
|
|
66
|
+
private readonly tokenService: TokenService,
|
|
67
|
+
private readonly eventService: EventService
|
|
68
|
+
) {}
|
|
68
69
|
|
|
69
|
-
|
|
70
|
-
private messageHandler?: ForegroundMessageHandler;
|
|
71
|
-
private backgroundMessageHandler?: BackgroundMessageHandler;
|
|
72
|
-
private openedHandler?: NotificationOpenedHandler;
|
|
73
|
-
private fcmTokenErrorHandler?: FcmTokenErrorHandler;
|
|
74
|
-
|
|
75
|
-
private unsubscribeForegroundMessage?: () => void;
|
|
76
|
-
private unsubscribeNotificationOpened?: () => void;
|
|
77
|
-
private unsubscribeTokenRefresh?: () => void;
|
|
78
|
-
private unsubscribeNotificationEvents?: () => void;
|
|
79
|
-
|
|
80
|
-
private constructor() {}
|
|
81
|
-
|
|
82
|
-
public static getInstance(): NotificationService {
|
|
83
|
-
if (!NotificationService.instance) {
|
|
84
|
-
NotificationService.instance = new NotificationService();
|
|
85
|
-
}
|
|
86
|
-
return NotificationService.instance;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
public static resetInstance(): void {
|
|
90
|
-
if (NotificationService.instance) {
|
|
91
|
-
NotificationService.instance.cleanup();
|
|
92
|
-
NotificationService.instance = null;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
async initialize(
|
|
97
|
-
eventService: EventService,
|
|
98
|
-
storageService: StorageService,
|
|
99
|
-
deviceService?: DeviceService,
|
|
100
|
-
tokenService?: TokenService
|
|
101
|
-
): Promise<NotificationService> {
|
|
70
|
+
async initialize(): Promise<void> {
|
|
102
71
|
if (this.isInitialized) {
|
|
103
|
-
ClixLogger.debug(
|
|
104
|
-
|
|
105
|
-
);
|
|
106
|
-
return this;
|
|
72
|
+
ClixLogger.debug('Notification service already initialized');
|
|
73
|
+
return;
|
|
107
74
|
}
|
|
108
75
|
|
|
109
76
|
try {
|
|
110
|
-
ClixLogger.debug('Initializing notification service');
|
|
77
|
+
ClixLogger.debug('Initializing notification service...');
|
|
111
78
|
|
|
112
|
-
this.
|
|
113
|
-
this.
|
|
114
|
-
this.
|
|
115
|
-
this.tokenService = tokenService;
|
|
79
|
+
this.setupTokenRefreshListener();
|
|
80
|
+
this.setupPushReceivedHandler(); // NOTE(nyanxyz): must be set up before any await calls
|
|
81
|
+
await this.setupPushTappedHandler();
|
|
116
82
|
|
|
117
|
-
|
|
118
|
-
|
|
83
|
+
if (Platform.OS === 'android') {
|
|
84
|
+
await this.createNotificationChannels();
|
|
85
|
+
}
|
|
119
86
|
|
|
120
87
|
this.isInitialized = true;
|
|
121
88
|
ClixLogger.debug('Notification service initialized successfully');
|
|
122
|
-
return this;
|
|
123
89
|
} catch (error) {
|
|
124
90
|
ClixLogger.error('Failed to initialize notification service', error);
|
|
125
91
|
throw error;
|
|
126
92
|
}
|
|
127
93
|
}
|
|
128
94
|
|
|
129
|
-
async getCurrentToken(): Promise<string | null> {
|
|
130
|
-
try {
|
|
131
|
-
this.currentPushToken = await this.getOrFetchToken();
|
|
132
|
-
return this.currentPushToken;
|
|
133
|
-
} catch (error) {
|
|
134
|
-
ClixLogger.error('Failed to get push token', error);
|
|
135
|
-
await this.handleFcmTokenError(error);
|
|
136
|
-
return null;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
95
|
cleanup(): void {
|
|
141
|
-
this.
|
|
142
|
-
this.
|
|
96
|
+
this.unsubscribeMessage?.();
|
|
97
|
+
this.unsubscribeNotificationOpenedApp?.();
|
|
143
98
|
this.unsubscribeTokenRefresh?.();
|
|
144
|
-
this.
|
|
99
|
+
this.unsubscribeForegroundEvent?.();
|
|
145
100
|
this.isInitialized = false;
|
|
146
101
|
this.processedMessageIds.clear();
|
|
147
102
|
ClixLogger.debug('Notification service cleaned up');
|
|
148
103
|
}
|
|
149
104
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
setFcmTokenErrorHandler(handler?: FcmTokenErrorHandler): void {
|
|
163
|
-
this.fcmTokenErrorHandler = handler;
|
|
164
|
-
}
|
|
105
|
+
async requestPermission(): Promise<NotificationSettings> {
|
|
106
|
+
const settings = await notifee.requestPermission({
|
|
107
|
+
alert: true,
|
|
108
|
+
badge: true,
|
|
109
|
+
sound: true,
|
|
110
|
+
provisional: false,
|
|
111
|
+
announcement: false,
|
|
112
|
+
carPlay: false,
|
|
113
|
+
criticalAlert: false,
|
|
114
|
+
});
|
|
115
|
+
ClixLogger.debug('Push notification permission status:', settings);
|
|
165
116
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
117
|
+
const isGranted =
|
|
118
|
+
settings.authorizationStatus === AuthorizationStatus.AUTHORIZED ||
|
|
119
|
+
settings.authorizationStatus === AuthorizationStatus.PROVISIONAL;
|
|
169
120
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
await this.createNotificationChannels();
|
|
173
|
-
}
|
|
174
|
-
this.setupNotificationEventListeners();
|
|
121
|
+
await this.setPermissionGranted(isGranted);
|
|
122
|
+
return settings;
|
|
175
123
|
}
|
|
176
124
|
|
|
177
|
-
|
|
125
|
+
async setPermissionGranted(isGranted: boolean): Promise<void> {
|
|
178
126
|
try {
|
|
179
|
-
await
|
|
180
|
-
ClixLogger.debug(
|
|
127
|
+
await this.deviceService.updatePushPermission(isGranted);
|
|
128
|
+
ClixLogger.debug(
|
|
129
|
+
`Push permission status reported to server: ${
|
|
130
|
+
isGranted ? 'granted' : 'denied'
|
|
131
|
+
}`
|
|
132
|
+
);
|
|
181
133
|
} catch (error) {
|
|
182
|
-
ClixLogger.
|
|
134
|
+
ClixLogger.warn('Failed to upsert push permission status', error);
|
|
183
135
|
}
|
|
184
136
|
}
|
|
185
137
|
|
|
186
|
-
private
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
138
|
+
private setupPushReceivedHandler(): void {
|
|
139
|
+
/**
|
|
140
|
+
* Android: background message handler
|
|
141
|
+
*/
|
|
142
|
+
messaging().setBackgroundMessageHandler(
|
|
143
|
+
this.handleBackgroundMessage.bind(this)
|
|
191
144
|
);
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
145
|
+
/**
|
|
146
|
+
* iOS & Android: foreground message handler
|
|
147
|
+
*/
|
|
148
|
+
this.unsubscribeMessage = messaging().onMessage(
|
|
149
|
+
this.handleForegroundMessage.bind(this)
|
|
150
|
+
);
|
|
151
|
+
/**
|
|
152
|
+
* iOS: background messages are handled in the Notification Service Extension
|
|
153
|
+
*/
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
private async setupPushTappedHandler(): Promise<void> {
|
|
157
|
+
/**
|
|
158
|
+
* Android: background notification tap handler
|
|
159
|
+
* & app launched from quit state via a notification
|
|
160
|
+
*/
|
|
161
|
+
notifee.onBackgroundEvent(this.handleNotificationEvent.bind(this));
|
|
162
|
+
/**
|
|
163
|
+
* iOS & Android: foreground notification tap handler
|
|
164
|
+
*/
|
|
165
|
+
this.unsubscribeForegroundEvent = notifee.onForegroundEvent(
|
|
166
|
+
this.handleForegroundNotificationEvent.bind(this)
|
|
167
|
+
);
|
|
168
|
+
/**
|
|
169
|
+
* iOS: background notification tap handler
|
|
170
|
+
*/
|
|
171
|
+
this.unsubscribeNotificationOpenedApp = messaging().onNotificationOpenedApp(
|
|
172
|
+
this.handleNotificationOpenedApp.bind(this)
|
|
173
|
+
);
|
|
174
|
+
/**
|
|
175
|
+
* iOS: app launched from a quit state via a notification
|
|
176
|
+
*/
|
|
177
|
+
await this.handleInitialNotification();
|
|
195
178
|
}
|
|
196
179
|
|
|
197
|
-
private
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
180
|
+
private setupTokenRefreshListener(): void {
|
|
181
|
+
this.unsubscribeTokenRefresh = messaging().onTokenRefresh(
|
|
182
|
+
async (token: string) => {
|
|
183
|
+
try {
|
|
184
|
+
await this.tokenRefreshHandler?.(token);
|
|
185
|
+
} catch (error) {
|
|
186
|
+
ClixLogger.error('Token refresh handler failed', error);
|
|
204
187
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
);
|
|
188
|
+
|
|
189
|
+
try {
|
|
190
|
+
ClixLogger.debug(`Push token refreshed: ${token}`);
|
|
191
|
+
this.tokenService.saveToken(token);
|
|
192
|
+
await this.deviceService.updatePushToken(token, 'FCM');
|
|
193
|
+
} catch (error) {
|
|
194
|
+
ClixLogger.error('Failed to handle token refresh', error);
|
|
212
195
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
ClixLogger.debug('Notification dismissed');
|
|
216
|
-
break;
|
|
217
|
-
default:
|
|
218
|
-
ClixLogger.debug('Unhandled notification event type:', type);
|
|
219
|
-
}
|
|
196
|
+
}
|
|
197
|
+
);
|
|
220
198
|
}
|
|
221
199
|
|
|
222
|
-
private async
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
await this.handleNotificationTap(data);
|
|
200
|
+
private async createNotificationChannels(): Promise<void> {
|
|
201
|
+
try {
|
|
202
|
+
await notifee.createChannel(NotificationService.DEFAULT_CHANNEL);
|
|
203
|
+
ClixLogger.debug('Notification channels created successfully');
|
|
204
|
+
} catch (error) {
|
|
205
|
+
ClixLogger.error('Failed to create notification channels', error);
|
|
229
206
|
}
|
|
230
207
|
}
|
|
231
208
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
this.setupTokenRefreshListener();
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
private setupMessageHandlers(): void {
|
|
239
|
-
this.messagingService.setBackgroundMessageHandler(
|
|
240
|
-
async (remoteMessage: FirebaseMessagingTypes.RemoteMessage) => {
|
|
241
|
-
await this.handleBackgroundMessage(remoteMessage);
|
|
242
|
-
}
|
|
243
|
-
);
|
|
244
|
-
this.unsubscribeForegroundMessage = this.messagingService.onMessage(
|
|
245
|
-
async (remoteMessage: FirebaseMessagingTypes.RemoteMessage) => {
|
|
246
|
-
await this.handleForegroundMessage(remoteMessage);
|
|
247
|
-
}
|
|
248
|
-
);
|
|
249
|
-
this.unsubscribeNotificationOpened =
|
|
250
|
-
this.messagingService.onNotificationOpenedApp(
|
|
251
|
-
async (remoteMessage: FirebaseMessagingTypes.RemoteMessage) => {
|
|
252
|
-
ClixLogger.debug('Notification opened from background state:', {
|
|
253
|
-
messageId: remoteMessage.messageId,
|
|
254
|
-
data: remoteMessage.data,
|
|
255
|
-
});
|
|
256
|
-
await this.handleNotificationTap(remoteMessage.data ?? {});
|
|
257
|
-
}
|
|
258
|
-
);
|
|
259
|
-
this.checkInitialNotification();
|
|
260
|
-
}
|
|
261
|
-
|
|
209
|
+
/**
|
|
210
|
+
* Android: background message handler
|
|
211
|
+
*/
|
|
262
212
|
private async handleBackgroundMessage(
|
|
263
213
|
remoteMessage: FirebaseMessagingTypes.RemoteMessage
|
|
264
214
|
): Promise<void> {
|
|
265
215
|
ClixLogger.debug('Handling background message:', remoteMessage.messageId);
|
|
266
216
|
|
|
217
|
+
setTimeout(() => ClixLogger.debug('still alive after 3s'), 3000);
|
|
218
|
+
|
|
267
219
|
const data = remoteMessage.data ?? {};
|
|
268
220
|
try {
|
|
269
221
|
await this.backgroundMessageHandler?.(data);
|
|
@@ -278,180 +230,212 @@ export class NotificationService {
|
|
|
278
230
|
return;
|
|
279
231
|
}
|
|
280
232
|
|
|
281
|
-
this.storageService.set('last_background_notification', {
|
|
282
|
-
messageId: remoteMessage.messageId,
|
|
283
|
-
data: data,
|
|
284
|
-
timestamp: Date.now(),
|
|
285
|
-
clixMessageId: clixPayload.messageId,
|
|
286
|
-
campaignId: clixPayload.campaignId,
|
|
287
|
-
trackingId: clixPayload.trackingId,
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
await this.trackEventInBackground(clixPayload);
|
|
291
|
-
|
|
292
233
|
if (!remoteMessage.notification) {
|
|
293
234
|
await this.displayNotification(remoteMessage, clixPayload);
|
|
294
235
|
}
|
|
236
|
+
|
|
237
|
+
await this.trackPushReceivedEvent(clixPayload);
|
|
295
238
|
} catch (error) {
|
|
296
239
|
ClixLogger.error('Background message handler error:', error);
|
|
297
240
|
}
|
|
298
241
|
}
|
|
299
242
|
|
|
243
|
+
/**
|
|
244
|
+
* iOS & Android: foreground message handler
|
|
245
|
+
*/
|
|
300
246
|
private async handleForegroundMessage(
|
|
301
247
|
remoteMessage: FirebaseMessagingTypes.RemoteMessage
|
|
302
248
|
): Promise<void> {
|
|
303
249
|
ClixLogger.debug('Handling foreground message:', remoteMessage.messageId);
|
|
304
250
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
251
|
+
const messageId = remoteMessage.messageId;
|
|
252
|
+
if (!messageId) {
|
|
253
|
+
ClixLogger.warn('No messageId found in foreground message');
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (this.processedMessageIds.has(messageId)) {
|
|
258
|
+
ClixLogger.debug(
|
|
259
|
+
'Message already processed, skipping duplicate:',
|
|
260
|
+
messageId
|
|
261
|
+
);
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
311
264
|
|
|
312
|
-
|
|
265
|
+
const data = remoteMessage.data ?? {};
|
|
266
|
+
try {
|
|
267
|
+
const result = await this.messageHandler?.(data);
|
|
268
|
+
if (result === false) {
|
|
313
269
|
ClixLogger.debug(
|
|
314
|
-
'
|
|
315
|
-
messageId
|
|
270
|
+
'Foreground message suppressed by user handler:',
|
|
271
|
+
remoteMessage.messageId
|
|
316
272
|
);
|
|
317
273
|
return;
|
|
318
274
|
}
|
|
275
|
+
} catch (error) {
|
|
276
|
+
ClixLogger.error('Foreground message handler failed', error);
|
|
277
|
+
}
|
|
319
278
|
|
|
320
|
-
|
|
279
|
+
try {
|
|
321
280
|
const clixPayload = this.parseClixPayload(data);
|
|
322
|
-
if (clixPayload) {
|
|
323
|
-
ClixLogger.
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
// NOTE(nyanxyz): on iOS, Received event is tracked in NSE
|
|
327
|
-
await this.handlePushReceived(data);
|
|
328
|
-
}
|
|
281
|
+
if (!clixPayload) {
|
|
282
|
+
ClixLogger.warn('No Clix payload found in background message');
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
329
285
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
}
|
|
338
|
-
} else {
|
|
339
|
-
ClixLogger.warn('No Clix payload found in foreground message');
|
|
286
|
+
this.processedMessageIds.add(messageId);
|
|
287
|
+
|
|
288
|
+
await this.displayNotification(remoteMessage, clixPayload);
|
|
289
|
+
|
|
290
|
+
if (Platform.OS === 'android') {
|
|
291
|
+
// NOTE(nyanxyz): on iOS, Received event is tracked in Notification Service Extension
|
|
292
|
+
await this.trackPushReceivedEvent(clixPayload);
|
|
340
293
|
}
|
|
341
294
|
} catch (error) {
|
|
342
295
|
ClixLogger.error('Failed to handle foreground message', error);
|
|
343
296
|
}
|
|
344
297
|
}
|
|
345
298
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
299
|
+
/**
|
|
300
|
+
* iOS: background notification tap handler
|
|
301
|
+
*/
|
|
302
|
+
private async handleNotificationOpenedApp(
|
|
303
|
+
remoteMessage: FirebaseMessagingTypes.RemoteMessage
|
|
304
|
+
): Promise<void> {
|
|
305
|
+
ClixLogger.debug('Handling notification opened from background:', {
|
|
306
|
+
messageId: remoteMessage.messageId,
|
|
307
|
+
data: remoteMessage.data,
|
|
355
308
|
});
|
|
356
|
-
ClixLogger.debug('Push notification permission status:', settings);
|
|
357
|
-
|
|
358
|
-
const isGranted =
|
|
359
|
-
settings.authorizationStatus === AuthorizationStatus.AUTHORIZED ||
|
|
360
|
-
settings.authorizationStatus === AuthorizationStatus.PROVISIONAL;
|
|
361
|
-
await this.setPermissionGranted(isGranted);
|
|
362
|
-
|
|
363
|
-
return settings;
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
async setPermissionGranted(isGranted: boolean): Promise<void> {
|
|
367
|
-
if (!this.deviceService) {
|
|
368
|
-
ClixLogger.debug(
|
|
369
|
-
'Device service is not initialized, skipping push permission upsert'
|
|
370
|
-
);
|
|
371
|
-
return;
|
|
372
|
-
}
|
|
373
309
|
|
|
310
|
+
const data = remoteMessage.data ?? {};
|
|
374
311
|
try {
|
|
375
|
-
await this.
|
|
376
|
-
ClixLogger.debug(
|
|
377
|
-
`Push permission status reported to server: ${
|
|
378
|
-
isGranted ? 'granted' : 'denied'
|
|
379
|
-
}`
|
|
380
|
-
);
|
|
312
|
+
await this.notificationOpenedAppHandler?.(data);
|
|
381
313
|
} catch (error) {
|
|
382
|
-
ClixLogger.
|
|
314
|
+
ClixLogger.error('Notification opened app handler failed', error);
|
|
383
315
|
}
|
|
384
|
-
}
|
|
385
316
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
ClixLogger.debug('Push token refreshed');
|
|
391
|
-
this.currentPushToken = token;
|
|
392
|
-
await this.saveAndRegisterToken(token);
|
|
393
|
-
} catch (error) {
|
|
394
|
-
ClixLogger.error('Failed to handle token refresh', error);
|
|
395
|
-
await this.handleFcmTokenError(error);
|
|
396
|
-
}
|
|
317
|
+
try {
|
|
318
|
+
const clixPayload = this.parseClixPayload(data);
|
|
319
|
+
if (clixPayload) {
|
|
320
|
+
await this.trackPushTappedEvent(clixPayload);
|
|
397
321
|
}
|
|
398
|
-
|
|
322
|
+
if (this.autoHandleLandingUrl) {
|
|
323
|
+
await this.handleUrlNavigation(data);
|
|
324
|
+
}
|
|
325
|
+
} catch (error) {
|
|
326
|
+
ClixLogger.error(
|
|
327
|
+
'Failed to handle notification opened from background',
|
|
328
|
+
error
|
|
329
|
+
);
|
|
330
|
+
}
|
|
399
331
|
}
|
|
400
332
|
|
|
401
|
-
|
|
333
|
+
/**
|
|
334
|
+
* iOS: app launched from a quit state via a notification
|
|
335
|
+
*/
|
|
336
|
+
private async handleInitialNotification(): Promise<void> {
|
|
402
337
|
try {
|
|
403
|
-
const initialNotification =
|
|
404
|
-
await this.messagingService.getInitialNotification();
|
|
338
|
+
const initialNotification = await messaging().getInitialNotification();
|
|
405
339
|
if (initialNotification) {
|
|
406
340
|
ClixLogger.debug(
|
|
407
341
|
'App launched from notification:',
|
|
408
342
|
initialNotification.messageId
|
|
409
343
|
);
|
|
410
|
-
|
|
344
|
+
|
|
345
|
+
const data = initialNotification.data ?? {};
|
|
346
|
+
const clixPayload = this.parseClixPayload(data);
|
|
347
|
+
if (clixPayload) {
|
|
348
|
+
await this.trackPushTappedEvent(clixPayload);
|
|
349
|
+
}
|
|
350
|
+
if (this.autoHandleLandingUrl) {
|
|
351
|
+
await this.handleUrlNavigation(data);
|
|
352
|
+
}
|
|
411
353
|
}
|
|
412
354
|
} catch (error) {
|
|
413
355
|
ClixLogger.error('Failed to handle initial notification', error);
|
|
414
356
|
}
|
|
415
357
|
}
|
|
416
358
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
359
|
+
/**
|
|
360
|
+
* Android: background notification tap handler
|
|
361
|
+
*/
|
|
362
|
+
private async handleNotificationEvent(event: Event): Promise<void> {
|
|
363
|
+
const { type, detail } = event;
|
|
364
|
+
|
|
365
|
+
switch (type) {
|
|
366
|
+
case EventType.PRESS:
|
|
367
|
+
case EventType.ACTION_PRESS: {
|
|
368
|
+
const data = detail.notification?.data || {};
|
|
369
|
+
const clixPayload = this.parseClixPayload(data);
|
|
370
|
+
if (clixPayload) {
|
|
371
|
+
await this.trackPushTappedEvent(clixPayload);
|
|
372
|
+
}
|
|
373
|
+
if (this.autoHandleLandingUrl) {
|
|
374
|
+
await this.handleUrlNavigation(data);
|
|
375
|
+
}
|
|
376
|
+
break;
|
|
422
377
|
}
|
|
423
|
-
|
|
424
|
-
|
|
378
|
+
case EventType.DISMISSED:
|
|
379
|
+
ClixLogger.debug('Notification dismissed');
|
|
380
|
+
break;
|
|
381
|
+
default:
|
|
382
|
+
ClixLogger.debug('Unhandled notification event type:', type);
|
|
425
383
|
}
|
|
426
384
|
}
|
|
427
385
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
386
|
+
/**
|
|
387
|
+
* iOS & Android: foreground notification tap handler
|
|
388
|
+
*/
|
|
389
|
+
private async handleForegroundNotificationEvent(event: Event): Promise<void> {
|
|
390
|
+
try {
|
|
391
|
+
await this.foregroundEventHandler?.(event);
|
|
392
|
+
} catch (error) {
|
|
393
|
+
ClixLogger.error('Foreground notification event handler failed', error);
|
|
432
394
|
}
|
|
433
395
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
if (token) {
|
|
437
|
-
ClixLogger.debug('Got push token:', token.substring(0, 20) + '...');
|
|
438
|
-
this.tokenService?.saveToken(token);
|
|
439
|
-
}
|
|
440
|
-
return token;
|
|
396
|
+
await this.handleNotificationEvent(event);
|
|
441
397
|
}
|
|
442
398
|
|
|
443
|
-
private async
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
399
|
+
private async trackPushReceivedEvent(
|
|
400
|
+
payload: ClixPushNotificationPayload
|
|
401
|
+
): Promise<void> {
|
|
402
|
+
try {
|
|
403
|
+
await this.eventService.trackEvent(
|
|
404
|
+
'PUSH_NOTIFICATION_RECEIVED',
|
|
405
|
+
{},
|
|
406
|
+
payload.messageId,
|
|
407
|
+
payload.userJourneyId,
|
|
408
|
+
payload.userJourneyNodeId
|
|
409
|
+
);
|
|
410
|
+
ClixLogger.debug(
|
|
411
|
+
'PUSH_NOTIFICATION_RECEIVED event tracked:',
|
|
412
|
+
payload.messageId
|
|
413
|
+
);
|
|
414
|
+
} catch (error) {
|
|
415
|
+
ClixLogger.error(
|
|
416
|
+
'Failed to track PUSH_NOTIFICATION_RECEIVED event',
|
|
417
|
+
error
|
|
418
|
+
);
|
|
447
419
|
}
|
|
448
|
-
await this.registerTokenWithServer(token);
|
|
449
420
|
}
|
|
450
421
|
|
|
451
|
-
private async
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
422
|
+
private async trackPushTappedEvent(
|
|
423
|
+
payload: ClixPushNotificationPayload
|
|
424
|
+
): Promise<void> {
|
|
425
|
+
try {
|
|
426
|
+
await this.eventService.trackEvent(
|
|
427
|
+
'PUSH_NOTIFICATION_TAPPED',
|
|
428
|
+
{},
|
|
429
|
+
payload.messageId,
|
|
430
|
+
payload.userJourneyId,
|
|
431
|
+
payload.userJourneyNodeId
|
|
432
|
+
);
|
|
433
|
+
ClixLogger.debug(
|
|
434
|
+
'PUSH_NOTIFICATION_TAPPED event tracked:',
|
|
435
|
+
payload.messageId
|
|
436
|
+
);
|
|
437
|
+
} catch (error) {
|
|
438
|
+
ClixLogger.error('Failed to track PUSH_NOTIFICATION_TAPPED event', error);
|
|
455
439
|
}
|
|
456
440
|
}
|
|
457
441
|
|
|
@@ -460,29 +444,23 @@ export class NotificationService {
|
|
|
460
444
|
clixPayload: ClixPushNotificationPayload
|
|
461
445
|
): Promise<void> {
|
|
462
446
|
try {
|
|
463
|
-
const notificationContent = this.extractNotificationContent(
|
|
464
|
-
remoteMessage.notification,
|
|
465
|
-
clixPayload
|
|
466
|
-
);
|
|
467
|
-
|
|
468
447
|
ClixLogger.debug('Creating notification config with content:', {
|
|
469
|
-
title:
|
|
470
|
-
body:
|
|
471
|
-
hasImage: !!
|
|
472
|
-
imageUrl:
|
|
448
|
+
title: clixPayload.title,
|
|
449
|
+
body: clixPayload.body,
|
|
450
|
+
hasImage: !!clixPayload.imageUrl,
|
|
451
|
+
imageUrl: clixPayload.imageUrl,
|
|
473
452
|
});
|
|
474
453
|
|
|
475
454
|
const notificationConfig = await this.createNotificationConfig(
|
|
476
455
|
remoteMessage,
|
|
477
456
|
clixPayload,
|
|
478
|
-
notificationContent,
|
|
479
457
|
NotificationService.DEFAULT_CHANNEL.id
|
|
480
458
|
);
|
|
481
459
|
|
|
482
460
|
await notifee.displayNotification(notificationConfig);
|
|
483
461
|
ClixLogger.debug(
|
|
484
462
|
'Notification displayed successfully:',
|
|
485
|
-
|
|
463
|
+
clixPayload.title
|
|
486
464
|
);
|
|
487
465
|
} catch (error) {
|
|
488
466
|
ClixLogger.error('Failed to display notification', error);
|
|
@@ -501,16 +479,15 @@ export class NotificationService {
|
|
|
501
479
|
private async createNotificationConfig(
|
|
502
480
|
remoteMessage: FirebaseMessagingTypes.RemoteMessage,
|
|
503
481
|
clixPayload: ClixPushNotificationPayload,
|
|
504
|
-
notificationContent: NotificationContent,
|
|
505
482
|
channelId: string
|
|
506
483
|
) {
|
|
507
|
-
const imageUrl =
|
|
484
|
+
const imageUrl = clixPayload.imageUrl;
|
|
508
485
|
|
|
509
486
|
const config: Notification &
|
|
510
487
|
Required<Pick<Notification, 'android' | 'ios'>> = {
|
|
511
488
|
id: remoteMessage.messageId || Date.now().toString(),
|
|
512
|
-
title:
|
|
513
|
-
body:
|
|
489
|
+
title: clixPayload.title,
|
|
490
|
+
body: clixPayload.body,
|
|
514
491
|
data: remoteMessage.data ?? {},
|
|
515
492
|
android: {
|
|
516
493
|
channelId,
|
|
@@ -521,11 +498,10 @@ export class NotificationService {
|
|
|
521
498
|
groupSummary: false,
|
|
522
499
|
groupAlertBehavior: AndroidGroupAlertBehavior.CHILDREN,
|
|
523
500
|
sound: 'default',
|
|
524
|
-
ticker:
|
|
525
|
-
actions: this.createNotificationActions(clixPayload),
|
|
501
|
+
ticker: clixPayload.body,
|
|
526
502
|
style: {
|
|
527
503
|
type: AndroidStyle.BIGTEXT,
|
|
528
|
-
text:
|
|
504
|
+
text: clixPayload.body,
|
|
529
505
|
},
|
|
530
506
|
pressAction: {
|
|
531
507
|
id: 'default',
|
|
@@ -549,122 +525,6 @@ export class NotificationService {
|
|
|
549
525
|
return config;
|
|
550
526
|
}
|
|
551
527
|
|
|
552
|
-
private createNotificationActions(clixPayload: ClixPushNotificationPayload) {
|
|
553
|
-
const actions = [];
|
|
554
|
-
|
|
555
|
-
actions.push({
|
|
556
|
-
title: 'Open',
|
|
557
|
-
pressAction: {
|
|
558
|
-
id: 'default',
|
|
559
|
-
},
|
|
560
|
-
});
|
|
561
|
-
|
|
562
|
-
if (clixPayload.customProperties?.actions) {
|
|
563
|
-
const customActions = clixPayload.customProperties.actions;
|
|
564
|
-
if (Array.isArray(customActions)) {
|
|
565
|
-
customActions.forEach((action) => {
|
|
566
|
-
if (action.title && action.actionId) {
|
|
567
|
-
actions.push({
|
|
568
|
-
title: action.title,
|
|
569
|
-
pressAction: {
|
|
570
|
-
id: action.actionId,
|
|
571
|
-
},
|
|
572
|
-
});
|
|
573
|
-
}
|
|
574
|
-
});
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
return actions;
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
private isValidImageUrl(url: string): boolean {
|
|
582
|
-
return url.trim() !== '' && url.startsWith('http');
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
private extractNotificationContent(
|
|
586
|
-
fcmNotification: FirebaseMessagingTypes.Notification | null | undefined,
|
|
587
|
-
clixPayload: ClixPushNotificationPayload
|
|
588
|
-
): NotificationContent {
|
|
589
|
-
ClixLogger.debug('Extracting notification content from payload:', {
|
|
590
|
-
fcmTitle: fcmNotification?.title,
|
|
591
|
-
fcmBody: fcmNotification?.body,
|
|
592
|
-
customProperties: clixPayload.customProperties,
|
|
593
|
-
messageId: clixPayload.messageId,
|
|
594
|
-
imageUrl: clixPayload.imageUrl,
|
|
595
|
-
});
|
|
596
|
-
|
|
597
|
-
const title =
|
|
598
|
-
fcmNotification?.title ||
|
|
599
|
-
clixPayload.customProperties?.title ||
|
|
600
|
-
'New Message';
|
|
601
|
-
const body =
|
|
602
|
-
fcmNotification?.body || clixPayload.customProperties?.body || '';
|
|
603
|
-
|
|
604
|
-
ClixLogger.debug('Extracted notification content:', { title, body });
|
|
605
|
-
|
|
606
|
-
// Validate imageUrl before using it
|
|
607
|
-
let imageUrl: string | undefined;
|
|
608
|
-
if (clixPayload.imageUrl) {
|
|
609
|
-
ClixLogger.debug('Processing image URL:', clixPayload.imageUrl);
|
|
610
|
-
if (this.isValidImageUrl(clixPayload.imageUrl)) {
|
|
611
|
-
imageUrl = clixPayload.imageUrl;
|
|
612
|
-
ClixLogger.debug('Image URL validated successfully');
|
|
613
|
-
} else {
|
|
614
|
-
ClixLogger.warn('Invalid image URL, skipping:', clixPayload.imageUrl);
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
return {
|
|
619
|
-
title,
|
|
620
|
-
body,
|
|
621
|
-
imageUrl,
|
|
622
|
-
};
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
private async handlePushReceived(data: Record<string, any>): Promise<void> {
|
|
626
|
-
try {
|
|
627
|
-
const clixPayload = this.parseClixPayload(data);
|
|
628
|
-
if (clixPayload) {
|
|
629
|
-
await this.trackPushEvent('PUSH_NOTIFICATION_RECEIVED', clixPayload);
|
|
630
|
-
}
|
|
631
|
-
ClixLogger.debug('Push notification received and processed');
|
|
632
|
-
} catch (error) {
|
|
633
|
-
ClixLogger.error('Failed to handle push received', error);
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
private async handlePushTapped(data: Record<string, any>): Promise<void> {
|
|
638
|
-
try {
|
|
639
|
-
const clixPayload = this.parseClixPayload(data);
|
|
640
|
-
if (clixPayload) {
|
|
641
|
-
await this.trackPushEvent('PUSH_NOTIFICATION_TAPPED', clixPayload);
|
|
642
|
-
}
|
|
643
|
-
if (this.autoHandleLandingUrl) {
|
|
644
|
-
await this.handleUrlNavigation(data);
|
|
645
|
-
}
|
|
646
|
-
ClixLogger.debug('Push notification tapped and processed');
|
|
647
|
-
} catch (error) {
|
|
648
|
-
ClixLogger.error('Failed to handle push tapped', error);
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
private async handleNotificationTap(
|
|
653
|
-
data: Record<string, any>
|
|
654
|
-
): Promise<void> {
|
|
655
|
-
try {
|
|
656
|
-
await this.openedHandler?.(data);
|
|
657
|
-
} catch (error) {
|
|
658
|
-
ClixLogger.error('Failed to handle notification tap', error);
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
try {
|
|
662
|
-
await this.handlePushTapped(data);
|
|
663
|
-
} catch (error) {
|
|
664
|
-
ClixLogger.error('Failed to handle notification tap', error);
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
|
|
668
528
|
private async handleUrlNavigation(data: Record<string, any>): Promise<void> {
|
|
669
529
|
try {
|
|
670
530
|
let url: string | undefined;
|
|
@@ -699,128 +559,55 @@ export class NotificationService {
|
|
|
699
559
|
userInfo: Record<string, any>
|
|
700
560
|
): ClixPushNotificationPayload | null {
|
|
701
561
|
try {
|
|
702
|
-
let
|
|
703
|
-
if (
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
} else if (typeof userInfo.clix === 'string') {
|
|
707
|
-
payload = JSON.parse(userInfo.clix);
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
const toCamel = (s: string) =>
|
|
711
|
-
s.replace(/_([a-z])/g, (g) => (g[1] ?? '').toUpperCase());
|
|
712
|
-
const result: any = {};
|
|
713
|
-
if (!payload) return null;
|
|
714
|
-
for (const key in payload) {
|
|
715
|
-
result[toCamel(key)] = payload[key];
|
|
562
|
+
let data = userInfo?.clix;
|
|
563
|
+
if (data == null) {
|
|
564
|
+
ClixLogger.debug("No 'clix' entry found in notification data");
|
|
565
|
+
return null;
|
|
716
566
|
}
|
|
717
567
|
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
568
|
+
if (typeof data === 'string') {
|
|
569
|
+
try {
|
|
570
|
+
data = JSON.parse(data);
|
|
571
|
+
} catch (parseError) {
|
|
572
|
+
ClixLogger.error(
|
|
573
|
+
'Failed to parse Clix payload JSON string',
|
|
574
|
+
parseError
|
|
575
|
+
);
|
|
576
|
+
return null;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
721
579
|
|
|
722
|
-
|
|
723
|
-
const customProperties: Record<string, any> = {};
|
|
724
|
-
if (result.title) customProperties.title = result.title;
|
|
725
|
-
if (result.body) customProperties.body = result.body;
|
|
580
|
+
ClixLogger.debug('Parsing Clix payload from notification data:', data);
|
|
726
581
|
|
|
727
|
-
const
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
582
|
+
const payload: ClixPushNotificationPayload = {
|
|
583
|
+
messageId: data.message_id,
|
|
584
|
+
title: data.title,
|
|
585
|
+
body: data.body,
|
|
586
|
+
imageUrl: data.image_url || undefined,
|
|
587
|
+
landingUrl: data.landing_url || undefined,
|
|
588
|
+
userJourneyId: data.user_journey_id || undefined,
|
|
589
|
+
userJourneyNodeId: data.user_journey_node_id || undefined,
|
|
733
590
|
};
|
|
734
591
|
|
|
735
|
-
ClixLogger.debug('
|
|
592
|
+
ClixLogger.debug('Constructed Clix payload:', payload);
|
|
736
593
|
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
return null;
|
|
741
|
-
}
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
private async trackPushEvent(
|
|
745
|
-
eventType: string,
|
|
746
|
-
clixPayload: ClixPushNotificationPayload
|
|
747
|
-
): Promise<void> {
|
|
748
|
-
const properties = this.extractTrackingProperties(clixPayload);
|
|
749
|
-
const messageId = clixPayload.messageId;
|
|
750
|
-
await this.eventService.trackEvent(eventType, properties, messageId);
|
|
751
|
-
ClixLogger.debug(`${eventType} tracked:`, messageId);
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
private extractTrackingProperties(
|
|
755
|
-
clixPayload: ClixPushNotificationPayload
|
|
756
|
-
): Record<string, any> {
|
|
757
|
-
const properties: Record<string, any> = {};
|
|
758
|
-
if (clixPayload.messageId) properties.messageId = clixPayload.messageId;
|
|
759
|
-
if (clixPayload.campaignId) properties.campaignId = clixPayload.campaignId;
|
|
760
|
-
if (clixPayload.trackingId) properties.trackingId = clixPayload.trackingId;
|
|
761
|
-
return properties;
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
private async trackEventInBackground(
|
|
765
|
-
clixPayload: ClixPushNotificationPayload
|
|
766
|
-
): Promise<void> {
|
|
767
|
-
const messageId = clixPayload.messageId;
|
|
768
|
-
if (!messageId) {
|
|
769
|
-
ClixLogger.warn('No messageId found in payload, skipping event tracking');
|
|
770
|
-
return;
|
|
771
|
-
}
|
|
772
|
-
try {
|
|
773
|
-
const configData =
|
|
774
|
-
this.storageService.get<Record<string, any>>('clix_config');
|
|
775
|
-
if (!configData) {
|
|
776
|
-
ClixLogger.error('No Clix config found in storage');
|
|
777
|
-
return;
|
|
594
|
+
if (!payload.messageId) {
|
|
595
|
+
ClixLogger.error('No messageId found in Clix payload');
|
|
596
|
+
return null;
|
|
778
597
|
}
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
598
|
+
if (!payload.title) {
|
|
599
|
+
ClixLogger.error('No title found in Clix payload');
|
|
600
|
+
return null;
|
|
601
|
+
}
|
|
602
|
+
if (!payload.body) {
|
|
603
|
+
ClixLogger.error('No body found in Clix payload');
|
|
604
|
+
return null;
|
|
786
605
|
}
|
|
787
606
|
|
|
788
|
-
|
|
789
|
-
await this.eventService.trackEvent(
|
|
790
|
-
'PUSH_NOTIFICATION_RECEIVED',
|
|
791
|
-
properties,
|
|
792
|
-
messageId
|
|
793
|
-
);
|
|
794
|
-
ClixLogger.debug(
|
|
795
|
-
'PUSH_NOTIFICATION_RECEIVED event tracked in background'
|
|
796
|
-
);
|
|
607
|
+
return payload;
|
|
797
608
|
} catch (error) {
|
|
798
|
-
ClixLogger.error('
|
|
799
|
-
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
private async shouldDisplayForegroundNotification(
|
|
803
|
-
data: Record<string, any>
|
|
804
|
-
): Promise<boolean> {
|
|
805
|
-
try {
|
|
806
|
-
const result = await this.messageHandler?.(data);
|
|
807
|
-
return result !== false;
|
|
808
|
-
} catch (error) {
|
|
809
|
-
ClixLogger.warn(
|
|
810
|
-
'Foreground message handler failed, displaying notification by default',
|
|
811
|
-
error
|
|
812
|
-
);
|
|
813
|
-
return true;
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
|
|
817
|
-
private async handleFcmTokenError(error: any): Promise<void> {
|
|
818
|
-
try {
|
|
819
|
-
const errorInstance =
|
|
820
|
-
error instanceof Error ? error : new Error(String(error));
|
|
821
|
-
await this.fcmTokenErrorHandler?.(errorInstance);
|
|
822
|
-
} catch (handlerError) {
|
|
823
|
-
ClixLogger.warn('FCM token error handler failed', handlerError);
|
|
609
|
+
ClixLogger.error('Failed to parse Clix payload', error);
|
|
610
|
+
return null;
|
|
824
611
|
}
|
|
825
612
|
}
|
|
826
613
|
}
|