@clix-so/react-native-sdk 1.0.0 → 1.1.0
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 +252 -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 +101 -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 +318 -533
- package/src/services/TokenService.ts +4 -71
- package/src/utils/http/HTTPClient.ts +141 -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,209 +61,154 @@ 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> {
|
|
@@ -278,16 +228,7 @@ export class NotificationService {
|
|
|
278
228
|
return;
|
|
279
229
|
}
|
|
280
230
|
|
|
281
|
-
this.
|
|
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);
|
|
231
|
+
await this.trackPushReceivedEvent(clixPayload);
|
|
291
232
|
|
|
292
233
|
if (!remoteMessage.notification) {
|
|
293
234
|
await this.displayNotification(remoteMessage, clixPayload);
|
|
@@ -297,161 +238,202 @@ export class NotificationService {
|
|
|
297
238
|
}
|
|
298
239
|
}
|
|
299
240
|
|
|
241
|
+
/**
|
|
242
|
+
* iOS & Android: foreground message handler
|
|
243
|
+
*/
|
|
300
244
|
private async handleForegroundMessage(
|
|
301
245
|
remoteMessage: FirebaseMessagingTypes.RemoteMessage
|
|
302
246
|
): Promise<void> {
|
|
303
247
|
ClixLogger.debug('Handling foreground message:', remoteMessage.messageId);
|
|
304
248
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
249
|
+
const messageId = remoteMessage.messageId;
|
|
250
|
+
if (!messageId) {
|
|
251
|
+
ClixLogger.warn('No messageId found in foreground message');
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (this.processedMessageIds.has(messageId)) {
|
|
256
|
+
ClixLogger.debug(
|
|
257
|
+
'Message already processed, skipping duplicate:',
|
|
258
|
+
messageId
|
|
259
|
+
);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
311
262
|
|
|
312
|
-
|
|
263
|
+
const data = remoteMessage.data ?? {};
|
|
264
|
+
try {
|
|
265
|
+
const result = await this.messageHandler?.(data);
|
|
266
|
+
if (result === false) {
|
|
313
267
|
ClixLogger.debug(
|
|
314
|
-
'
|
|
315
|
-
messageId
|
|
268
|
+
'Foreground message suppressed by user handler:',
|
|
269
|
+
remoteMessage.messageId
|
|
316
270
|
);
|
|
317
271
|
return;
|
|
318
272
|
}
|
|
273
|
+
} catch (error) {
|
|
274
|
+
ClixLogger.error('Foreground message handler failed', error);
|
|
275
|
+
}
|
|
319
276
|
|
|
320
|
-
|
|
277
|
+
try {
|
|
321
278
|
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
|
-
}
|
|
279
|
+
if (!clixPayload) {
|
|
280
|
+
ClixLogger.warn('No Clix payload found in background message');
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
329
283
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
messageId
|
|
336
|
-
);
|
|
337
|
-
}
|
|
338
|
-
} else {
|
|
339
|
-
ClixLogger.warn('No Clix payload found in foreground message');
|
|
284
|
+
this.processedMessageIds.add(messageId);
|
|
285
|
+
|
|
286
|
+
if (Platform.OS === 'android') {
|
|
287
|
+
// NOTE(nyanxyz): on iOS, Received event is tracked in Notification Service Extension
|
|
288
|
+
await this.trackPushReceivedEvent(clixPayload);
|
|
340
289
|
}
|
|
290
|
+
|
|
291
|
+
await this.displayNotification(remoteMessage, clixPayload);
|
|
341
292
|
} catch (error) {
|
|
342
293
|
ClixLogger.error('Failed to handle foreground message', error);
|
|
343
294
|
}
|
|
344
295
|
}
|
|
345
296
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
297
|
+
/**
|
|
298
|
+
* iOS: background notification tap handler
|
|
299
|
+
*/
|
|
300
|
+
private async handleNotificationOpenedApp(
|
|
301
|
+
remoteMessage: FirebaseMessagingTypes.RemoteMessage
|
|
302
|
+
): Promise<void> {
|
|
303
|
+
ClixLogger.debug('Handling notification opened from background:', {
|
|
304
|
+
messageId: remoteMessage.messageId,
|
|
305
|
+
data: remoteMessage.data,
|
|
355
306
|
});
|
|
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
307
|
|
|
308
|
+
const data = remoteMessage.data ?? {};
|
|
374
309
|
try {
|
|
375
|
-
await this.
|
|
376
|
-
ClixLogger.debug(
|
|
377
|
-
`Push permission status reported to server: ${
|
|
378
|
-
isGranted ? 'granted' : 'denied'
|
|
379
|
-
}`
|
|
380
|
-
);
|
|
310
|
+
await this.notificationOpenedAppHandler?.(data);
|
|
381
311
|
} catch (error) {
|
|
382
|
-
ClixLogger.
|
|
312
|
+
ClixLogger.error('Notification opened app handler failed', error);
|
|
383
313
|
}
|
|
384
|
-
}
|
|
385
314
|
|
|
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
|
-
}
|
|
315
|
+
try {
|
|
316
|
+
const clixPayload = this.parseClixPayload(data);
|
|
317
|
+
if (clixPayload) {
|
|
318
|
+
await this.trackPushTappedEvent(clixPayload);
|
|
397
319
|
}
|
|
398
|
-
|
|
320
|
+
if (this.autoHandleLandingUrl) {
|
|
321
|
+
await this.handleUrlNavigation(data);
|
|
322
|
+
}
|
|
323
|
+
} catch (error) {
|
|
324
|
+
ClixLogger.error(
|
|
325
|
+
'Failed to handle notification opened from background',
|
|
326
|
+
error
|
|
327
|
+
);
|
|
328
|
+
}
|
|
399
329
|
}
|
|
400
330
|
|
|
401
|
-
|
|
331
|
+
/**
|
|
332
|
+
* iOS: app launched from a quit state via a notification
|
|
333
|
+
*/
|
|
334
|
+
private async handleInitialNotification(): Promise<void> {
|
|
402
335
|
try {
|
|
403
|
-
const initialNotification =
|
|
404
|
-
await this.messagingService.getInitialNotification();
|
|
336
|
+
const initialNotification = await messaging().getInitialNotification();
|
|
405
337
|
if (initialNotification) {
|
|
406
338
|
ClixLogger.debug(
|
|
407
339
|
'App launched from notification:',
|
|
408
340
|
initialNotification.messageId
|
|
409
341
|
);
|
|
410
|
-
|
|
342
|
+
|
|
343
|
+
const data = initialNotification.data ?? {};
|
|
344
|
+
const clixPayload = this.parseClixPayload(data);
|
|
345
|
+
if (clixPayload) {
|
|
346
|
+
await this.trackPushTappedEvent(clixPayload);
|
|
347
|
+
}
|
|
348
|
+
if (this.autoHandleLandingUrl) {
|
|
349
|
+
await this.handleUrlNavigation(data);
|
|
350
|
+
}
|
|
411
351
|
}
|
|
412
352
|
} catch (error) {
|
|
413
353
|
ClixLogger.error('Failed to handle initial notification', error);
|
|
414
354
|
}
|
|
415
355
|
}
|
|
416
356
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
357
|
+
/**
|
|
358
|
+
* Android: background notification tap handler
|
|
359
|
+
*/
|
|
360
|
+
private async handleNotificationEvent(event: Event): Promise<void> {
|
|
361
|
+
const { type, detail } = event;
|
|
362
|
+
|
|
363
|
+
switch (type) {
|
|
364
|
+
case EventType.PRESS:
|
|
365
|
+
case EventType.ACTION_PRESS: {
|
|
366
|
+
const data = detail.notification?.data || {};
|
|
367
|
+
const clixPayload = this.parseClixPayload(data);
|
|
368
|
+
if (clixPayload) {
|
|
369
|
+
await this.trackPushTappedEvent(clixPayload);
|
|
370
|
+
}
|
|
371
|
+
if (this.autoHandleLandingUrl) {
|
|
372
|
+
await this.handleUrlNavigation(data);
|
|
373
|
+
}
|
|
374
|
+
break;
|
|
422
375
|
}
|
|
423
|
-
|
|
424
|
-
|
|
376
|
+
case EventType.DISMISSED:
|
|
377
|
+
ClixLogger.debug('Notification dismissed');
|
|
378
|
+
break;
|
|
379
|
+
default:
|
|
380
|
+
ClixLogger.debug('Unhandled notification event type:', type);
|
|
425
381
|
}
|
|
426
382
|
}
|
|
427
383
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
384
|
+
/**
|
|
385
|
+
* iOS & Android: foreground notification tap handler
|
|
386
|
+
*/
|
|
387
|
+
private async handleForegroundNotificationEvent(event: Event): Promise<void> {
|
|
388
|
+
try {
|
|
389
|
+
await this.foregroundEventHandler?.(event);
|
|
390
|
+
} catch (error) {
|
|
391
|
+
ClixLogger.error('Foreground notification event handler failed', error);
|
|
432
392
|
}
|
|
433
393
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
if (token) {
|
|
437
|
-
ClixLogger.debug('Got push token:', token.substring(0, 20) + '...');
|
|
438
|
-
this.tokenService?.saveToken(token);
|
|
439
|
-
}
|
|
440
|
-
return token;
|
|
394
|
+
await this.handleNotificationEvent(event);
|
|
441
395
|
}
|
|
442
396
|
|
|
443
|
-
private async
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
397
|
+
private async trackPushReceivedEvent(
|
|
398
|
+
payload: ClixPushNotificationPayload
|
|
399
|
+
): Promise<void> {
|
|
400
|
+
try {
|
|
401
|
+
await this.eventService.trackEvent(
|
|
402
|
+
'PUSH_NOTIFICATION_RECEIVED',
|
|
403
|
+
{},
|
|
404
|
+
payload.messageId,
|
|
405
|
+
payload.userJourneyId,
|
|
406
|
+
payload.userJourneyNodeId
|
|
407
|
+
);
|
|
408
|
+
ClixLogger.debug(
|
|
409
|
+
'PUSH_NOTIFICATION_RECEIVED event tracked:',
|
|
410
|
+
payload.messageId
|
|
411
|
+
);
|
|
412
|
+
} catch (error) {
|
|
413
|
+
ClixLogger.error(
|
|
414
|
+
'Failed to track PUSH_NOTIFICATION_RECEIVED event',
|
|
415
|
+
error
|
|
416
|
+
);
|
|
447
417
|
}
|
|
448
|
-
await this.registerTokenWithServer(token);
|
|
449
418
|
}
|
|
450
419
|
|
|
451
|
-
private async
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
420
|
+
private async trackPushTappedEvent(
|
|
421
|
+
payload: ClixPushNotificationPayload
|
|
422
|
+
): Promise<void> {
|
|
423
|
+
try {
|
|
424
|
+
await this.eventService.trackEvent(
|
|
425
|
+
'PUSH_NOTIFICATION_TAPPED',
|
|
426
|
+
{},
|
|
427
|
+
payload.messageId,
|
|
428
|
+
payload.userJourneyId,
|
|
429
|
+
payload.userJourneyNodeId
|
|
430
|
+
);
|
|
431
|
+
ClixLogger.debug(
|
|
432
|
+
'PUSH_NOTIFICATION_TAPPED event tracked:',
|
|
433
|
+
payload.messageId
|
|
434
|
+
);
|
|
435
|
+
} catch (error) {
|
|
436
|
+
ClixLogger.error('Failed to track PUSH_NOTIFICATION_TAPPED event', error);
|
|
455
437
|
}
|
|
456
438
|
}
|
|
457
439
|
|
|
@@ -460,29 +442,23 @@ export class NotificationService {
|
|
|
460
442
|
clixPayload: ClixPushNotificationPayload
|
|
461
443
|
): Promise<void> {
|
|
462
444
|
try {
|
|
463
|
-
const notificationContent = this.extractNotificationContent(
|
|
464
|
-
remoteMessage.notification,
|
|
465
|
-
clixPayload
|
|
466
|
-
);
|
|
467
|
-
|
|
468
445
|
ClixLogger.debug('Creating notification config with content:', {
|
|
469
|
-
title:
|
|
470
|
-
body:
|
|
471
|
-
hasImage: !!
|
|
472
|
-
imageUrl:
|
|
446
|
+
title: clixPayload.title,
|
|
447
|
+
body: clixPayload.body,
|
|
448
|
+
hasImage: !!clixPayload.imageUrl,
|
|
449
|
+
imageUrl: clixPayload.imageUrl,
|
|
473
450
|
});
|
|
474
451
|
|
|
475
452
|
const notificationConfig = await this.createNotificationConfig(
|
|
476
453
|
remoteMessage,
|
|
477
454
|
clixPayload,
|
|
478
|
-
notificationContent,
|
|
479
455
|
NotificationService.DEFAULT_CHANNEL.id
|
|
480
456
|
);
|
|
481
457
|
|
|
482
458
|
await notifee.displayNotification(notificationConfig);
|
|
483
459
|
ClixLogger.debug(
|
|
484
460
|
'Notification displayed successfully:',
|
|
485
|
-
|
|
461
|
+
clixPayload.title
|
|
486
462
|
);
|
|
487
463
|
} catch (error) {
|
|
488
464
|
ClixLogger.error('Failed to display notification', error);
|
|
@@ -501,16 +477,15 @@ export class NotificationService {
|
|
|
501
477
|
private async createNotificationConfig(
|
|
502
478
|
remoteMessage: FirebaseMessagingTypes.RemoteMessage,
|
|
503
479
|
clixPayload: ClixPushNotificationPayload,
|
|
504
|
-
notificationContent: NotificationContent,
|
|
505
480
|
channelId: string
|
|
506
481
|
) {
|
|
507
|
-
const imageUrl =
|
|
482
|
+
const imageUrl = clixPayload.imageUrl;
|
|
508
483
|
|
|
509
484
|
const config: Notification &
|
|
510
485
|
Required<Pick<Notification, 'android' | 'ios'>> = {
|
|
511
486
|
id: remoteMessage.messageId || Date.now().toString(),
|
|
512
|
-
title:
|
|
513
|
-
body:
|
|
487
|
+
title: clixPayload.title,
|
|
488
|
+
body: clixPayload.body,
|
|
514
489
|
data: remoteMessage.data ?? {},
|
|
515
490
|
android: {
|
|
516
491
|
channelId,
|
|
@@ -521,11 +496,10 @@ export class NotificationService {
|
|
|
521
496
|
groupSummary: false,
|
|
522
497
|
groupAlertBehavior: AndroidGroupAlertBehavior.CHILDREN,
|
|
523
498
|
sound: 'default',
|
|
524
|
-
ticker:
|
|
525
|
-
actions: this.createNotificationActions(clixPayload),
|
|
499
|
+
ticker: clixPayload.body,
|
|
526
500
|
style: {
|
|
527
501
|
type: AndroidStyle.BIGTEXT,
|
|
528
|
-
text:
|
|
502
|
+
text: clixPayload.body,
|
|
529
503
|
},
|
|
530
504
|
pressAction: {
|
|
531
505
|
id: 'default',
|
|
@@ -549,122 +523,6 @@ export class NotificationService {
|
|
|
549
523
|
return config;
|
|
550
524
|
}
|
|
551
525
|
|
|
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
526
|
private async handleUrlNavigation(data: Record<string, any>): Promise<void> {
|
|
669
527
|
try {
|
|
670
528
|
let url: string | undefined;
|
|
@@ -699,128 +557,55 @@ export class NotificationService {
|
|
|
699
557
|
userInfo: Record<string, any>
|
|
700
558
|
): ClixPushNotificationPayload | null {
|
|
701
559
|
try {
|
|
702
|
-
let
|
|
703
|
-
if (
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
} else if (typeof userInfo.clix === 'string') {
|
|
707
|
-
payload = JSON.parse(userInfo.clix);
|
|
708
|
-
}
|
|
560
|
+
let data = userInfo?.clix;
|
|
561
|
+
if (data == null) {
|
|
562
|
+
ClixLogger.debug("No 'clix' entry found in notification data");
|
|
563
|
+
return null;
|
|
709
564
|
}
|
|
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];
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
ClixLogger.debug('Parsed Clix payload result:', result);
|
|
719
565
|
|
|
720
|
-
if (
|
|
566
|
+
if (typeof data === 'string') {
|
|
567
|
+
try {
|
|
568
|
+
data = JSON.parse(data);
|
|
569
|
+
} catch (parseError) {
|
|
570
|
+
ClixLogger.error(
|
|
571
|
+
'Failed to parse Clix payload JSON string',
|
|
572
|
+
parseError
|
|
573
|
+
);
|
|
574
|
+
return null;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
721
577
|
|
|
722
|
-
|
|
723
|
-
const customProperties: Record<string, any> = {};
|
|
724
|
-
if (result.title) customProperties.title = result.title;
|
|
725
|
-
if (result.body) customProperties.body = result.body;
|
|
578
|
+
ClixLogger.debug('Parsing Clix payload from notification data:', data);
|
|
726
579
|
|
|
727
|
-
const
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
580
|
+
const payload: ClixPushNotificationPayload = {
|
|
581
|
+
messageId: data.message_id,
|
|
582
|
+
title: data.title,
|
|
583
|
+
body: data.body,
|
|
584
|
+
imageUrl: data.image_url || undefined,
|
|
585
|
+
landingUrl: data.landing_url || undefined,
|
|
586
|
+
userJourneyId: data.user_journey_id || undefined,
|
|
587
|
+
userJourneyNodeId: data.user_journey_node_id || undefined,
|
|
733
588
|
};
|
|
734
589
|
|
|
735
|
-
ClixLogger.debug('
|
|
590
|
+
ClixLogger.debug('Constructed Clix payload:', payload);
|
|
736
591
|
|
|
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;
|
|
592
|
+
if (!payload.messageId) {
|
|
593
|
+
ClixLogger.error('No messageId found in Clix payload');
|
|
594
|
+
return null;
|
|
778
595
|
}
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
596
|
+
if (!payload.title) {
|
|
597
|
+
ClixLogger.error('No title found in Clix payload');
|
|
598
|
+
return null;
|
|
599
|
+
}
|
|
600
|
+
if (!payload.body) {
|
|
601
|
+
ClixLogger.error('No body found in Clix payload');
|
|
602
|
+
return null;
|
|
786
603
|
}
|
|
787
604
|
|
|
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
|
-
);
|
|
797
|
-
} catch (error) {
|
|
798
|
-
ClixLogger.error('Error tracking event in background', 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;
|
|
605
|
+
return payload;
|
|
808
606
|
} catch (error) {
|
|
809
|
-
ClixLogger.
|
|
810
|
-
|
|
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);
|
|
607
|
+
ClixLogger.error('Failed to parse Clix payload', error);
|
|
608
|
+
return null;
|
|
824
609
|
}
|
|
825
610
|
}
|
|
826
611
|
}
|