@usergist/feedback-react-native 0.2.0-beta.0 → 0.2.0-beta.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/CHANGELOG.md +18 -0
- package/README.md +65 -3
- package/dist/Provider.d.ts.map +1 -1
- package/dist/Provider.js +51 -23
- package/dist/Provider.js.map +1 -1
- package/dist/Push.d.ts +2 -0
- package/dist/Push.d.ts.map +1 -1
- package/dist/Push.js +41 -2
- package/dist/Push.js.map +1 -1
- package/dist/UserGist.d.ts +17 -2
- package/dist/UserGist.d.ts.map +1 -1
- package/dist/UserGist.js +431 -139
- package/dist/UserGist.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/internal/context.d.ts +1 -1
- package/dist/internal/context.js +1 -1
- package/dist/internal/engine.d.ts +16 -2
- package/dist/internal/engine.d.ts.map +1 -1
- package/dist/internal/engine.js +278 -76
- package/dist/internal/engine.js.map +1 -1
- package/dist/internal/events.d.ts +6 -1
- package/dist/internal/events.d.ts.map +1 -1
- package/dist/internal/events.js +39 -18
- package/dist/internal/events.js.map +1 -1
- package/dist/internal/identity-lifecycle.d.ts +16 -0
- package/dist/internal/identity-lifecycle.d.ts.map +1 -0
- package/dist/internal/identity-lifecycle.js +96 -0
- package/dist/internal/identity-lifecycle.js.map +1 -0
- package/dist/internal/inapp-rules-cache.d.ts +1 -0
- package/dist/internal/inapp-rules-cache.d.ts.map +1 -1
- package/dist/internal/inapp-rules-cache.js +7 -0
- package/dist/internal/inapp-rules-cache.js.map +1 -1
- package/dist/internal/mutation-queue.d.ts +2 -2
- package/dist/internal/mutation-queue.d.ts.map +1 -1
- package/dist/internal/mutation-queue.js +43 -5
- package/dist/internal/mutation-queue.js.map +1 -1
- package/dist/internal/push-subscription.d.ts +11 -0
- package/dist/internal/push-subscription.d.ts.map +1 -0
- package/dist/internal/push-subscription.js +20 -0
- package/dist/internal/push-subscription.js.map +1 -0
- package/dist/internal/rules-cache.d.ts +1 -0
- package/dist/internal/rules-cache.d.ts.map +1 -1
- package/dist/internal/rules-cache.js +7 -0
- package/dist/internal/rules-cache.js.map +1 -1
- package/dist/internal/session-revocations.d.ts +5 -0
- package/dist/internal/session-revocations.d.ts.map +1 -0
- package/dist/internal/session-revocations.js +58 -0
- package/dist/internal/session-revocations.js.map +1 -0
- package/dist/internal/storage.d.ts +2 -0
- package/dist/internal/storage.d.ts.map +1 -1
- package/dist/internal/storage.js +75 -60
- package/dist/internal/storage.js.map +1 -1
- package/dist/internal/survey-invitations.d.ts +25 -0
- package/dist/internal/survey-invitations.d.ts.map +1 -0
- package/dist/internal/survey-invitations.js +54 -0
- package/dist/internal/survey-invitations.js.map +1 -0
- package/dist/internal/survey-matcher.d.ts +1 -0
- package/dist/internal/survey-matcher.d.ts.map +1 -1
- package/dist/internal/survey-matcher.js +3 -0
- package/dist/internal/survey-matcher.js.map +1 -1
- package/dist/internal/survey-rules-cache.d.ts +1 -0
- package/dist/internal/survey-rules-cache.d.ts.map +1 -1
- package/dist/internal/survey-rules-cache.js +7 -0
- package/dist/internal/survey-rules-cache.js.map +1 -1
- package/dist/internal/survey-store.d.ts +9 -2
- package/dist/internal/survey-store.d.ts.map +1 -1
- package/dist/internal/survey-store.js +37 -16
- package/dist/internal/survey-store.js.map +1 -1
- package/dist/internal/transport.d.ts +17 -4
- package/dist/internal/transport.d.ts.map +1 -1
- package/dist/internal/transport.js +40 -19
- package/dist/internal/transport.js.map +1 -1
- package/dist/internal/types.d.ts +2 -0
- package/dist/internal/types.d.ts.map +1 -1
- package/dist/internal/user-state.d.ts +2 -1
- package/dist/internal/user-state.d.ts.map +1 -1
- package/dist/internal/user-state.js +3 -2
- package/dist/internal/user-state.js.map +1 -1
- package/dist/ui/SurveyView.d.ts.map +1 -1
- package/dist/ui/SurveyView.js +5 -5
- package/dist/ui/SurveyView.js.map +1 -1
- package/package.json +2 -2
package/dist/UserGist.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { drainRevocations, hasPendingRevocations } from './internal/session-revocations.js';
|
|
2
|
+
import { setPushSubscriptionStateHandler, pushSubscriptionChanged } from './internal/push-subscription.js';
|
|
3
|
+
import { cancelIdentityRecovery, identityChanged, setIdentityStateHandler, setSubjectTokenProvider, getIdentityState } from './internal/identity-lifecycle.js';
|
|
4
|
+
import { userPropertiesUpdateSchema } from '@usergist/sdk-core/mobile';
|
|
1
5
|
import { hydratePushDedupe } from './internal/push-dedupe.js';
|
|
2
6
|
import { REQUEST_COMMENTED_EVENT_NAME, REQUEST_COMMENT_EDITED_EVENT_NAME, REQUEST_COMMENT_DELETED_EVENT_NAME, } from '@usergist/sdk-core/mobile';
|
|
3
7
|
import { asEventProps, clearAllState, createEngine, emitAppOpenWhenConsentReady, emitAppVersionChanged, ensureHydrated, enqueueAndEvaluate, flushNow, flushMutations, pollInstructions, refreshTargetingRules, recordServerBackedEventLocally, submitResponse, } from './internal/engine.js';
|
|
@@ -7,7 +11,7 @@ import { createSurveyStore } from './internal/survey-store.js';
|
|
|
7
11
|
import { UserGistPushNative, onTokenReceived, onNotificationReceived, onNotificationOpened, onNotificationDisplayed, onNotificationDismissed, onTokenError, } from './native/push-bridge.js';
|
|
8
12
|
import { Push } from './Push.js';
|
|
9
13
|
import { AppState, Platform } from 'react-native';
|
|
10
|
-
import { generateEventId, validateIdentifyTransition } from './internal/identity.js';
|
|
14
|
+
import { generateEventId, validateIdentifyTransition, } from './internal/identity.js';
|
|
11
15
|
import { configureStorageAdapter, STORAGE_KEYS, } from './internal/storage.js';
|
|
12
16
|
import { surveyCompletionOutcome } from './internal/survey-completion.js';
|
|
13
17
|
import { USERGIST_SDK_VERSION } from './internal/context.js';
|
|
@@ -21,6 +25,7 @@ function serializePushRegistration(operation) {
|
|
|
21
25
|
let runtimeStarted = false;
|
|
22
26
|
let runtimeStartPromise = null;
|
|
23
27
|
let resetPromise = null;
|
|
28
|
+
const surveyStarts = new Map();
|
|
24
29
|
let surveyStore = null;
|
|
25
30
|
let surveyHandlers = {};
|
|
26
31
|
let inAppHandlers = {};
|
|
@@ -39,8 +44,10 @@ function requestSessionIsCurrent(e) {
|
|
|
39
44
|
const generation = e.resetGeneration;
|
|
40
45
|
const consentVersion = e.consent.get().version;
|
|
41
46
|
const identity = e.identity.get();
|
|
42
|
-
return () => !e.resetting &&
|
|
43
|
-
|
|
47
|
+
return () => !e.resetting &&
|
|
48
|
+
generation === e.resetGeneration &&
|
|
49
|
+
consentVersion === e.consent.get().version &&
|
|
50
|
+
e.consent.allowsFeedback() &&
|
|
44
51
|
identity.anonymousId === e.identity.get().anonymousId &&
|
|
45
52
|
identity.externalId === e.identity.get().externalId;
|
|
46
53
|
}
|
|
@@ -49,9 +56,16 @@ async function syncNativePushState(e, push = e.consent.allowsPush()) {
|
|
|
49
56
|
const generation = e.resetGeneration;
|
|
50
57
|
const version = e.consent.get().version;
|
|
51
58
|
const operation = nativeStateQueue.then(async () => {
|
|
52
|
-
if (generation !== e.resetGeneration ||
|
|
59
|
+
if (generation !== e.resetGeneration ||
|
|
60
|
+
anonymousId !== e.identity.get().anonymousId ||
|
|
61
|
+
version !== e.consent.get().version)
|
|
53
62
|
return;
|
|
54
|
-
await UserGistPushNative.syncState({
|
|
63
|
+
await UserGistPushNative.syncState({
|
|
64
|
+
writeKey: e.config.writeKey,
|
|
65
|
+
apiUrl: e.config.apiUrl,
|
|
66
|
+
anonymousId,
|
|
67
|
+
push: push && !e.resetting,
|
|
68
|
+
});
|
|
55
69
|
});
|
|
56
70
|
nativeStateQueue = operation.catch(() => undefined);
|
|
57
71
|
await operation;
|
|
@@ -75,8 +89,24 @@ function requireEngine() {
|
|
|
75
89
|
function ensureEngine(config) {
|
|
76
90
|
if (engine)
|
|
77
91
|
return engine;
|
|
78
|
-
|
|
79
|
-
|
|
92
|
+
// A host-owned invitation is not a survey start. Only automatic rendering
|
|
93
|
+
// requests a session bundled with the delivery authorization.
|
|
94
|
+
engine = createEngine(config, () => !surveyHandlers.onInvite);
|
|
95
|
+
const pushEngine = engine;
|
|
96
|
+
pushEngine.retryPushRegistration = async () => {
|
|
97
|
+
if (pushEngine.resetting)
|
|
98
|
+
return;
|
|
99
|
+
const saved = await pushEngine.storage.getJson(STORAGE_KEYS.pushDevice);
|
|
100
|
+
if (!saved)
|
|
101
|
+
return;
|
|
102
|
+
pushEngine.pushTokenAvailable = true;
|
|
103
|
+
if (pushEngine !== engine || pushEngine.resetting)
|
|
104
|
+
return;
|
|
105
|
+
pushSubscriptionChanged(pushEngine);
|
|
106
|
+
if (pushEngine.consent.allowsPush())
|
|
107
|
+
await registerPushDevice(saved.token, saved.platform, { environment: saved.environment }, true);
|
|
108
|
+
};
|
|
109
|
+
surveyStore = createSurveyStore(config.writeKey, () => engine.identity.get());
|
|
80
110
|
return engine;
|
|
81
111
|
}
|
|
82
112
|
async function startRuntime(e) {
|
|
@@ -145,7 +175,9 @@ function attachEnginePushListeners() {
|
|
|
145
175
|
try {
|
|
146
176
|
const platform = (p.platform === 'ios' ? 'ios' : 'android');
|
|
147
177
|
await UserGist.registerPushToken(p.token, platform, {
|
|
148
|
-
environment: lastEnablePushOptions?.environment ??
|
|
178
|
+
environment: lastEnablePushOptions?.environment ??
|
|
179
|
+
(await UserGistPushNative.defaultEnvironment()) ??
|
|
180
|
+
defaultEnvironment(),
|
|
149
181
|
});
|
|
150
182
|
}
|
|
151
183
|
catch (err) {
|
|
@@ -159,8 +191,8 @@ function attachEnginePushListeners() {
|
|
|
159
191
|
onNotificationReceived((p) => {
|
|
160
192
|
forwardToPushHandler(p, 'received');
|
|
161
193
|
});
|
|
162
|
-
onNotificationDisplayed(p => forwardToPushHandler(p, 'displayed'));
|
|
163
|
-
onNotificationDismissed(p => forwardToPushHandler(p, 'dismissed'));
|
|
194
|
+
onNotificationDisplayed((p) => forwardToPushHandler(p, 'displayed'));
|
|
195
|
+
onNotificationDismissed((p) => forwardToPushHandler(p, 'dismissed'));
|
|
164
196
|
onNotificationOpened((p) => {
|
|
165
197
|
forwardToPushHandler(p, 'opened');
|
|
166
198
|
});
|
|
@@ -169,7 +201,8 @@ function forwardToPushHandler(p, kind) {
|
|
|
169
201
|
if (!engine?.consent.allowsPush() || engine.resetting)
|
|
170
202
|
return;
|
|
171
203
|
const data = p.data ?? {};
|
|
172
|
-
if (data.usergist_anonymous_id &&
|
|
204
|
+
if (data.usergist_anonymous_id &&
|
|
205
|
+
data.usergist_anonymous_id !== engine.identity.get().anonymousId)
|
|
173
206
|
return;
|
|
174
207
|
if (p.deliveryId) {
|
|
175
208
|
;
|
|
@@ -186,7 +219,10 @@ function forwardToPushHandler(p, kind) {
|
|
|
186
219
|
};
|
|
187
220
|
Object.assign(userInfo, data);
|
|
188
221
|
if (kind === 'received') {
|
|
189
|
-
Push.handleReceived({
|
|
222
|
+
Push.handleReceived({
|
|
223
|
+
userInfo,
|
|
224
|
+
notification: { title: p.title, body: p.body },
|
|
225
|
+
});
|
|
190
226
|
}
|
|
191
227
|
else if (kind === 'displayed') {
|
|
192
228
|
Push.handleDisplayed({ userInfo });
|
|
@@ -204,7 +240,10 @@ function forwardToPushHandler(p, kind) {
|
|
|
204
240
|
dataStr[k] = String(v);
|
|
205
241
|
}
|
|
206
242
|
if (kind === 'received') {
|
|
207
|
-
Push.handleReceived({
|
|
243
|
+
Push.handleReceived({
|
|
244
|
+
data: dataStr,
|
|
245
|
+
notification: { title: p.title, body: p.body },
|
|
246
|
+
});
|
|
208
247
|
}
|
|
209
248
|
else if (kind === 'displayed') {
|
|
210
249
|
Push.handleDisplayed({ data: dataStr });
|
|
@@ -240,6 +279,11 @@ export const UserGist = {
|
|
|
240
279
|
init(config) {
|
|
241
280
|
void UserGist.initAsync(config);
|
|
242
281
|
},
|
|
282
|
+
/** Defer campaign UI without interrupting analytics or an active modal. */
|
|
283
|
+
pausePresentation() { engine?.presentation.setPaused(true); },
|
|
284
|
+
/** Call after the host's loaded screen and navigation are ready. */
|
|
285
|
+
resumePresentation() { engine?.presentation.setPaused(false); },
|
|
286
|
+
__internal_presentationGate() { return requireEngine().presentation; },
|
|
243
287
|
async initAsync(config, initialIdentity) {
|
|
244
288
|
try {
|
|
245
289
|
const e = ensureEngine(config);
|
|
@@ -259,6 +303,10 @@ export const UserGist = {
|
|
|
259
303
|
return 'rejected';
|
|
260
304
|
}
|
|
261
305
|
},
|
|
306
|
+
setPushSubscriptionStateHandler,
|
|
307
|
+
setIdentityStateHandler,
|
|
308
|
+
setSubjectTokenProvider,
|
|
309
|
+
getIdentityState,
|
|
262
310
|
identify(userId, properties, subjectToken) {
|
|
263
311
|
void UserGist.identifyAsync(userId, properties, subjectToken);
|
|
264
312
|
},
|
|
@@ -269,18 +317,26 @@ export const UserGist = {
|
|
|
269
317
|
return 'rejected';
|
|
270
318
|
}
|
|
271
319
|
const e = requireEngine();
|
|
320
|
+
const generation = e.resetGeneration;
|
|
272
321
|
await ensureHydrated(e);
|
|
273
|
-
if (e.resetting)
|
|
322
|
+
if (e.resetting || e.resetGeneration !== generation)
|
|
274
323
|
return 'rejected';
|
|
275
324
|
const identity = e.identity.get();
|
|
276
325
|
const pendingIdentity = e.mutations.peek();
|
|
277
|
-
const transition = validateIdentifyTransition(identity.externalId, pendingIdentity?.kind === 'identify'
|
|
326
|
+
const transition = validateIdentifyTransition(identity.externalId, pendingIdentity?.kind === 'identify'
|
|
327
|
+
? pendingIdentity.payload.externalId
|
|
328
|
+
: null, userId);
|
|
278
329
|
if (transition === 'reset_required') {
|
|
279
330
|
reportError('identify rejected: call reset() before switching users');
|
|
280
331
|
return 'rejected';
|
|
281
332
|
}
|
|
282
|
-
const cleanProps = e.consent.allowsAnalytics()
|
|
333
|
+
const cleanProps = e.consent.allowsAnalytics()
|
|
334
|
+
? (properties && Object.keys(properties).length
|
|
335
|
+
? userPropertiesUpdateSchema.parse({ mutationId: generateEventId(), set: properties }).set
|
|
336
|
+
: undefined)
|
|
337
|
+
: undefined;
|
|
283
338
|
if (transition === 'already_identified' &&
|
|
339
|
+
(!subjectToken || subjectToken === e.subjectToken) &&
|
|
284
340
|
(!cleanProps || Object.keys(cleanProps).length === 0)) {
|
|
285
341
|
return 'synced';
|
|
286
342
|
}
|
|
@@ -288,6 +344,7 @@ export const UserGist = {
|
|
|
288
344
|
reportError('identify requires a server-minted subject token');
|
|
289
345
|
return 'rejected';
|
|
290
346
|
}
|
|
347
|
+
identityChanged(e, 'identifying');
|
|
291
348
|
const mutationId = await e.mutations.enqueue('identify', 'essential', {
|
|
292
349
|
subjectToken,
|
|
293
350
|
anonymousId: identity.anonymousId,
|
|
@@ -295,7 +352,10 @@ export const UserGist = {
|
|
|
295
352
|
...(cleanProps ? { properties: cleanProps } : {}),
|
|
296
353
|
}, `identify:${userId}`);
|
|
297
354
|
const result = await flushMutations(e);
|
|
355
|
+
if (e.resetting || e.resetGeneration !== generation)
|
|
356
|
+
return 'rejected';
|
|
298
357
|
if (result.permanentlyRejectedIds.has(mutationId)) {
|
|
358
|
+
identityChanged(e, 'rejected');
|
|
299
359
|
reportError('identify was rejected by the server');
|
|
300
360
|
return 'rejected';
|
|
301
361
|
}
|
|
@@ -312,6 +372,33 @@ export const UserGist = {
|
|
|
312
372
|
return 'rejected';
|
|
313
373
|
}
|
|
314
374
|
},
|
|
375
|
+
/** Update the current user, including anonymous users. Durable and retried on reconnect. */
|
|
376
|
+
async setUserProperties(properties, unset = []) {
|
|
377
|
+
try {
|
|
378
|
+
const e = requireEngine();
|
|
379
|
+
const generation = e.resetGeneration;
|
|
380
|
+
await ensureHydrated(e);
|
|
381
|
+
if (e !== engine ||
|
|
382
|
+
e.resetting ||
|
|
383
|
+
e.resetGeneration !== generation ||
|
|
384
|
+
!e.consent.allowsAnalytics())
|
|
385
|
+
return 'rejected';
|
|
386
|
+
const update = userPropertiesUpdateSchema.parse({
|
|
387
|
+
mutationId: generateEventId(),
|
|
388
|
+
set: properties,
|
|
389
|
+
unset: [...unset],
|
|
390
|
+
});
|
|
391
|
+
const mutationId = await e.mutations.enqueue('user-properties', 'analytics', { ...update, anonymousId: e.identity.get().anonymousId });
|
|
392
|
+
const result = await flushMutations(e);
|
|
393
|
+
if (result.permanentlyRejectedIds.has(mutationId))
|
|
394
|
+
return 'rejected';
|
|
395
|
+
return e.mutations.has(mutationId) ? 'queued' : 'synced';
|
|
396
|
+
}
|
|
397
|
+
catch (error) {
|
|
398
|
+
reportError('setUserProperties failed', error);
|
|
399
|
+
return 'rejected';
|
|
400
|
+
}
|
|
401
|
+
},
|
|
315
402
|
track(eventName, properties) {
|
|
316
403
|
try {
|
|
317
404
|
if (typeof eventName !== 'string' || eventName.length === 0) {
|
|
@@ -329,6 +416,10 @@ export const UserGist = {
|
|
|
329
416
|
async setConsent(purposes) {
|
|
330
417
|
try {
|
|
331
418
|
const e = requireEngine();
|
|
419
|
+
if (purposes.feedback === false)
|
|
420
|
+
e.presentation.invalidate('feedback');
|
|
421
|
+
if (purposes.survey === false)
|
|
422
|
+
e.presentation.invalidate('survey');
|
|
332
423
|
await ensureHydrated(e);
|
|
333
424
|
if (e.resetting)
|
|
334
425
|
return false;
|
|
@@ -348,7 +439,7 @@ export const UserGist = {
|
|
|
348
439
|
if (e.resetting || next.version !== e.consent.get().version)
|
|
349
440
|
return false;
|
|
350
441
|
if (e.lastPushToken)
|
|
351
|
-
await
|
|
442
|
+
await invalidatePushDevice(e.lastPushToken, true);
|
|
352
443
|
await disableNativePushForConsent(e, next.version);
|
|
353
444
|
}
|
|
354
445
|
if (e.resetting || next.version !== e.consent.get().version)
|
|
@@ -372,6 +463,9 @@ export const UserGist = {
|
|
|
372
463
|
reportError('transport.consent failed', err);
|
|
373
464
|
return false;
|
|
374
465
|
}
|
|
466
|
+
pushSubscriptionChanged(e);
|
|
467
|
+
if (next.push)
|
|
468
|
+
void e.retryPushRegistration?.().catch(error => reportError('push registration deferred', error));
|
|
375
469
|
if (next.feedback || next.survey) {
|
|
376
470
|
await refreshTargetingRules(e, true);
|
|
377
471
|
}
|
|
@@ -388,27 +482,30 @@ export const UserGist = {
|
|
|
388
482
|
reset() {
|
|
389
483
|
try {
|
|
390
484
|
const e = requireEngine();
|
|
391
|
-
if (
|
|
392
|
-
return resetPromise
|
|
485
|
+
if (resetPromise)
|
|
486
|
+
return resetPromise;
|
|
393
487
|
e.resetting = true;
|
|
488
|
+
identityChanged(e, 'resetting');
|
|
489
|
+
pushSubscriptionChanged(e);
|
|
394
490
|
e.resetGeneration += 1;
|
|
491
|
+
e.transport.cancelAll();
|
|
492
|
+
cancelIdentityRecovery(e);
|
|
493
|
+
e.presentation.invalidate();
|
|
395
494
|
requestsCache = null;
|
|
396
495
|
lastEnablePushOptions = null;
|
|
397
496
|
e.events.emit('resetSurfaces', undefined);
|
|
398
497
|
const operation = (async () => {
|
|
399
498
|
await ensureHydrated(e);
|
|
400
499
|
await syncNativePushState(e, false);
|
|
401
|
-
|
|
402
|
-
if (e.lastPushToken)
|
|
403
|
-
await UserGist.invalidatePushToken(e.lastPushToken);
|
|
404
|
-
await UserGistPushNative.disablePush().catch(() => undefined);
|
|
500
|
+
void UserGistPushNative.disablePush().catch(() => undefined);
|
|
405
501
|
await clearAllState(e);
|
|
502
|
+
await surveyStore?.clear();
|
|
406
503
|
await hydratePushDedupe(e.config.writeKey, e.identity.get().anonymousId);
|
|
407
504
|
})();
|
|
408
505
|
const finalized = operation
|
|
409
|
-
.
|
|
506
|
+
.then(() => { e.resetting = false; pushSubscriptionChanged(e); })
|
|
507
|
+
.catch((err) => { identityChanged(e, 'reset-failed'); reportError('reset failed', err); throw err; })
|
|
410
508
|
.finally(() => {
|
|
411
|
-
e.resetting = false;
|
|
412
509
|
if (resetPromise === finalized)
|
|
413
510
|
resetPromise = null;
|
|
414
511
|
});
|
|
@@ -517,75 +614,10 @@ export const UserGist = {
|
|
|
517
614
|
}
|
|
518
615
|
},
|
|
519
616
|
async registerPushToken(token, platform, opts) {
|
|
520
|
-
|
|
521
|
-
if (!token)
|
|
522
|
-
return;
|
|
523
|
-
const e = requireEngine();
|
|
524
|
-
await ensureHydrated(e);
|
|
525
|
-
if (!e.consent.allowsPush() || e.resetting)
|
|
526
|
-
return;
|
|
527
|
-
const generation = e.resetGeneration;
|
|
528
|
-
await serializePushRegistration(async () => {
|
|
529
|
-
if (e.resetting || generation !== e.resetGeneration || !e.consent.allowsPush())
|
|
530
|
-
return;
|
|
531
|
-
const id = e.identity.get();
|
|
532
|
-
const environment = opts?.environment ?? await UserGistPushNative.defaultEnvironment() ??
|
|
533
|
-
(e.config.environment === 'production' ? 'production' : 'sandbox');
|
|
534
|
-
const registrationKey = [
|
|
535
|
-
id.anonymousId,
|
|
536
|
-
id.externalId ?? '',
|
|
537
|
-
platform,
|
|
538
|
-
environment,
|
|
539
|
-
token,
|
|
540
|
-
].join(':');
|
|
541
|
-
if (e.lastPushRegistrationKey === registrationKey &&
|
|
542
|
-
Date.now() - e.lastPushRegistrationAt < 24 * 60 * 60_000)
|
|
543
|
-
return;
|
|
544
|
-
if (e.resetting || generation !== e.resetGeneration || !e.consent.allowsPush())
|
|
545
|
-
return;
|
|
546
|
-
// Persist before the request so logout can invalidate a token even when
|
|
547
|
-
// the response is lost or the process exits after the server accepts it.
|
|
548
|
-
e.lastPushToken = token;
|
|
549
|
-
await e.storage.setJson(STORAGE_KEYS.pushToken, token);
|
|
550
|
-
await e.transport.pushRegisterToken({
|
|
551
|
-
anonymousId: id.anonymousId,
|
|
552
|
-
externalId: id.externalId ?? null,
|
|
553
|
-
token,
|
|
554
|
-
platform,
|
|
555
|
-
environment,
|
|
556
|
-
language: undefined,
|
|
557
|
-
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
558
|
-
appVersion: undefined,
|
|
559
|
-
sdkVersion: `rn-${USERGIST_SDK_VERSION}`,
|
|
560
|
-
optIn: true,
|
|
561
|
-
});
|
|
562
|
-
e.lastPushToken = token;
|
|
563
|
-
e.lastPushRegistrationKey = registrationKey;
|
|
564
|
-
e.lastPushRegistrationAt = Date.now();
|
|
565
|
-
debugLog('push token registered with server');
|
|
566
|
-
});
|
|
567
|
-
}
|
|
568
|
-
catch (err) {
|
|
569
|
-
reportError('registerPushToken failed', err);
|
|
570
|
-
}
|
|
617
|
+
await registerPushDevice(token, platform, opts);
|
|
571
618
|
},
|
|
572
619
|
async invalidatePushToken(token) {
|
|
573
|
-
|
|
574
|
-
if (!token)
|
|
575
|
-
return;
|
|
576
|
-
const e = requireEngine();
|
|
577
|
-
await ensureHydrated(e);
|
|
578
|
-
const id = e.identity.get();
|
|
579
|
-
e.lastPushRegistrationKey = null;
|
|
580
|
-
e.lastPushRegistrationAt = 0;
|
|
581
|
-
await e.transport.pushInvalidateToken({
|
|
582
|
-
anonymousId: id.anonymousId,
|
|
583
|
-
token,
|
|
584
|
-
});
|
|
585
|
-
}
|
|
586
|
-
catch (err) {
|
|
587
|
-
reportError('invalidatePushToken failed', err);
|
|
588
|
-
}
|
|
620
|
+
await invalidatePushDevice(token);
|
|
589
621
|
},
|
|
590
622
|
/**
|
|
591
623
|
* Re-bind the latest registered device token to a newly-identified user.
|
|
@@ -603,7 +635,10 @@ export const UserGist = {
|
|
|
603
635
|
return;
|
|
604
636
|
const id = e.identity.get();
|
|
605
637
|
const token = e.lastPushToken;
|
|
606
|
-
if (!token ||
|
|
638
|
+
if (!token ||
|
|
639
|
+
e.resetting ||
|
|
640
|
+
!e.consent.allowsPush() ||
|
|
641
|
+
id.externalId !== externalId)
|
|
607
642
|
return;
|
|
608
643
|
await e.transport.pushRebind({
|
|
609
644
|
anonymousId: id.anonymousId,
|
|
@@ -703,12 +738,30 @@ export const UserGist = {
|
|
|
703
738
|
const e = requireEngine();
|
|
704
739
|
await ensureHydrated(e);
|
|
705
740
|
if (!e.consent.allowsPush() || e.resetting)
|
|
706
|
-
return {
|
|
741
|
+
return {
|
|
742
|
+
granted: false,
|
|
743
|
+
status: 'not_determined',
|
|
744
|
+
platform: Platform.OS === 'ios' ? 'ios' : 'android',
|
|
745
|
+
error: 'consent_required',
|
|
746
|
+
};
|
|
707
747
|
const generation = e.resetGeneration;
|
|
708
748
|
const consentVersion = e.consent.get().version;
|
|
709
|
-
const current = () => !e.resetting &&
|
|
710
|
-
|
|
711
|
-
|
|
749
|
+
const current = () => !e.resetting &&
|
|
750
|
+
generation === e.resetGeneration &&
|
|
751
|
+
consentVersion === e.consent.get().version &&
|
|
752
|
+
e.consent.allowsPush();
|
|
753
|
+
const cancelled = () => ({
|
|
754
|
+
granted: false,
|
|
755
|
+
status: 'not_determined',
|
|
756
|
+
platform: Platform.OS === 'ios' ? 'ios' : 'android',
|
|
757
|
+
error: 'session_changed',
|
|
758
|
+
});
|
|
759
|
+
opts = {
|
|
760
|
+
...opts,
|
|
761
|
+
environment: opts?.environment ??
|
|
762
|
+
(await UserGistPushNative.defaultEnvironment()) ??
|
|
763
|
+
defaultEnvironment(),
|
|
764
|
+
};
|
|
712
765
|
await syncNativePushState(e);
|
|
713
766
|
if (!current())
|
|
714
767
|
return cancelled();
|
|
@@ -731,7 +784,7 @@ export const UserGist = {
|
|
|
731
784
|
if (result.granted && result.token) {
|
|
732
785
|
const platform = (result.platform === 'ios' ? 'ios' : 'android');
|
|
733
786
|
await UserGist.registerPushToken(result.token, platform, {
|
|
734
|
-
environment: opts?.environment ??
|
|
787
|
+
environment: opts?.environment ?? defaultEnvironment(),
|
|
735
788
|
});
|
|
736
789
|
}
|
|
737
790
|
return result;
|
|
@@ -811,10 +864,20 @@ export const UserGist = {
|
|
|
811
864
|
debugLog('openSurvey: survey consent not granted; ignoring');
|
|
812
865
|
return;
|
|
813
866
|
}
|
|
867
|
+
const generation = e.resetGeneration;
|
|
868
|
+
const identity = e.identity.get();
|
|
869
|
+
const consentVersion = e.consent.get().version;
|
|
870
|
+
const invitation = await e.surveyInvitations.find(surveyId);
|
|
871
|
+
if (e.resetting || e.resetGeneration !== generation ||
|
|
872
|
+
!e.consent.allowsSurvey() || e.consent.get().version !== consentVersion ||
|
|
873
|
+
e.identity.get().anonymousId !== identity.anonymousId ||
|
|
874
|
+
e.identity.get().externalId !== identity.externalId)
|
|
875
|
+
return;
|
|
814
876
|
e.events.emit('showSurvey', {
|
|
815
877
|
surveyId,
|
|
816
|
-
source: context?.source ?? 'on_demand',
|
|
878
|
+
source: context?.source ?? invitation?.source ?? 'on_demand',
|
|
817
879
|
...(context?.language ? { language: context.language } : {}),
|
|
880
|
+
...(invitation ? { survey: invitation.survey, attempt: invitation.attempt } : {}),
|
|
818
881
|
});
|
|
819
882
|
}
|
|
820
883
|
catch (err) {
|
|
@@ -832,21 +895,28 @@ export const UserGist = {
|
|
|
832
895
|
const e = requireEngine();
|
|
833
896
|
await ensureHydrated(e);
|
|
834
897
|
// Accept URLs of the shape `*://*/s/<token>` or `*survey=<token>`.
|
|
835
|
-
const match = url.match(/\/s\/([A-Za-z0-9._-]+)/) ??
|
|
898
|
+
const match = url.match(/\/s\/([A-Za-z0-9._-]+)/) ??
|
|
899
|
+
url.match(/[?&]survey=([A-Za-z0-9._-]+)/);
|
|
836
900
|
if (!match || !match[1])
|
|
837
901
|
return false;
|
|
838
902
|
const token = match[1];
|
|
903
|
+
const presentationIsValid = e.presentation.validator('survey');
|
|
839
904
|
const id = e.identity.get();
|
|
840
905
|
const resolved = await e.transport.surveyResolveLink({
|
|
841
906
|
token,
|
|
842
907
|
anonymousId: id.anonymousId,
|
|
843
908
|
externalId: id.externalId ?? null,
|
|
844
909
|
});
|
|
910
|
+
if (!presentationIsValid() || !e.consent.allowsSurvey())
|
|
911
|
+
return false;
|
|
845
912
|
if (resolved.consentRequired) {
|
|
846
913
|
debugLog('survey link resolved but consent required — awaiting consent');
|
|
847
914
|
return false;
|
|
848
915
|
}
|
|
849
|
-
e.events.emit('showSurvey', {
|
|
916
|
+
e.events.emit('showSurvey', {
|
|
917
|
+
surveyId: resolved.surveyId,
|
|
918
|
+
source: 'link',
|
|
919
|
+
});
|
|
850
920
|
return true;
|
|
851
921
|
}
|
|
852
922
|
catch (err) {
|
|
@@ -899,6 +969,9 @@ export const UserGist = {
|
|
|
899
969
|
reportError('reportSurveyShown failed', err);
|
|
900
970
|
}
|
|
901
971
|
},
|
|
972
|
+
__internal_surveyOpenFailed(surveyId) {
|
|
973
|
+
engine?.surveyMatcher.cancelPending(surveyId);
|
|
974
|
+
},
|
|
902
975
|
__internal_armedSurveyById(surveyId) {
|
|
903
976
|
// Local-fire fast-path. If the survey-rules-cache already has the
|
|
904
977
|
// full survey content (because the matcher just fired), the
|
|
@@ -919,6 +992,11 @@ export const UserGist = {
|
|
|
919
992
|
const e = requireEngine();
|
|
920
993
|
await ensureHydrated(e);
|
|
921
994
|
const id = e.identity.get();
|
|
995
|
+
const pending = await surveyStore?.findForSurvey(surveyId);
|
|
996
|
+
if (pending?.survey &&
|
|
997
|
+
Date.now() - pending.startedAt <
|
|
998
|
+
pending.survey.saveResumeWindowSeconds * 1000)
|
|
999
|
+
return pending.survey;
|
|
922
1000
|
return await e.transport.surveyGet({
|
|
923
1001
|
surveyId,
|
|
924
1002
|
anonymousId: id.anonymousId,
|
|
@@ -931,52 +1009,169 @@ export const UserGist = {
|
|
|
931
1009
|
return null;
|
|
932
1010
|
}
|
|
933
1011
|
},
|
|
934
|
-
async __internal_createAttempt(surveyId, source, language) {
|
|
1012
|
+
async __internal_createAttempt(surveyId, source, language, presentationId, content, preparedAttempt) {
|
|
1013
|
+
const active = requireEngine();
|
|
1014
|
+
const identity = active.identity.get();
|
|
1015
|
+
const key = JSON.stringify([
|
|
1016
|
+
active.resetGeneration,
|
|
1017
|
+
identity.anonymousId,
|
|
1018
|
+
identity.externalId,
|
|
1019
|
+
surveyId,
|
|
1020
|
+
language,
|
|
1021
|
+
presentationId,
|
|
1022
|
+
preparedAttempt?.attemptId,
|
|
1023
|
+
]);
|
|
1024
|
+
const pending = surveyStarts.get(key);
|
|
1025
|
+
if (pending)
|
|
1026
|
+
return pending;
|
|
1027
|
+
const operation = (async () => {
|
|
1028
|
+
try {
|
|
1029
|
+
const e = requireEngine();
|
|
1030
|
+
await ensureHydrated(e);
|
|
1031
|
+
const id = e.identity.get();
|
|
1032
|
+
const generation = e.resetGeneration;
|
|
1033
|
+
const consentVersion = e.consent.get().version;
|
|
1034
|
+
const current = () => !e.resetting &&
|
|
1035
|
+
e.resetGeneration === generation &&
|
|
1036
|
+
e.consent.allowsSurvey() &&
|
|
1037
|
+
e.consent.get().version === consentVersion &&
|
|
1038
|
+
e.identity.get().anonymousId === id.anonymousId &&
|
|
1039
|
+
e.identity.get().externalId === id.externalId;
|
|
1040
|
+
if (!current())
|
|
1041
|
+
return null;
|
|
1042
|
+
const saved = await surveyStore?.findForSurvey(surveyId);
|
|
1043
|
+
if (!current())
|
|
1044
|
+
return null;
|
|
1045
|
+
// Resuming a durable device session preserves its original content and
|
|
1046
|
+
// answers. Never replace it with a stale network snapshot.
|
|
1047
|
+
if (!preparedAttempt &&
|
|
1048
|
+
(!presentationId || saved?.attemptId === presentationId) &&
|
|
1049
|
+
saved?.survey &&
|
|
1050
|
+
Date.now() - saved.startedAt <
|
|
1051
|
+
saved.survey.saveResumeWindowSeconds * 1000) {
|
|
1052
|
+
if (saved.startRequest)
|
|
1053
|
+
await e.mutations.enqueue('survey-start', 'survey', { surveyId, body: saved.startRequest }, `survey-start:${saved.attemptId}`);
|
|
1054
|
+
if (!current())
|
|
1055
|
+
return null;
|
|
1056
|
+
void flushMutations(e);
|
|
1057
|
+
return {
|
|
1058
|
+
attemptId: saved.attemptId,
|
|
1059
|
+
startQuestionId: saved.survey.flow.startQuestionId,
|
|
1060
|
+
currentQuestionId: saved.currentQuestionId,
|
|
1061
|
+
snapshot: saved.snapshot,
|
|
1062
|
+
resumed: true,
|
|
1063
|
+
resolvedContent: saved.survey,
|
|
1064
|
+
};
|
|
1065
|
+
}
|
|
1066
|
+
const armed = !language && !presentationId
|
|
1067
|
+
? e.surveyRules.getById(surveyId)
|
|
1068
|
+
: null;
|
|
1069
|
+
const grant = armed?.localStart &&
|
|
1070
|
+
Date.parse(armed.localStart.expiresAt) > Date.now()
|
|
1071
|
+
? armed.localStart
|
|
1072
|
+
: null;
|
|
1073
|
+
const body = {
|
|
1074
|
+
presentationId,
|
|
1075
|
+
anonymousId: id.anonymousId,
|
|
1076
|
+
externalId: id.externalId ?? null,
|
|
1077
|
+
source,
|
|
1078
|
+
...(language ? { language } : {}),
|
|
1079
|
+
sdkVersion: `rn-${USERGIST_SDK_VERSION}`,
|
|
1080
|
+
platform: e.context.platform(),
|
|
1081
|
+
...(presentationId || grant || source === 'triggered' || source === 'scheduled'
|
|
1082
|
+
? { resume: false, clientAttemptId: presentationId ?? generateEventId() }
|
|
1083
|
+
: { resume: true }),
|
|
1084
|
+
...(grant
|
|
1085
|
+
? {
|
|
1086
|
+
localStart: {
|
|
1087
|
+
token: grant.token,
|
|
1088
|
+
startedAt: new Date().toISOString(),
|
|
1089
|
+
},
|
|
1090
|
+
}
|
|
1091
|
+
: {}),
|
|
1092
|
+
};
|
|
1093
|
+
const res = preparedAttempt ??
|
|
1094
|
+
(grant && armed
|
|
1095
|
+
? {
|
|
1096
|
+
attemptId: body.clientAttemptId,
|
|
1097
|
+
startQuestionId: armed.survey.flow.startQuestionId,
|
|
1098
|
+
currentQuestionId: armed.survey.flow.startQuestionId,
|
|
1099
|
+
progressSnapshot: {},
|
|
1100
|
+
resumed: false,
|
|
1101
|
+
resolvedContent: armed.survey,
|
|
1102
|
+
}
|
|
1103
|
+
: await e.transport.surveyCreateAttempt(surveyId, body));
|
|
1104
|
+
if (!current())
|
|
1105
|
+
return null;
|
|
1106
|
+
await surveyStore?.upsert({
|
|
1107
|
+
anonymousId: id.anonymousId,
|
|
1108
|
+
externalId: id.externalId,
|
|
1109
|
+
survey: res.resolvedContent ?? content,
|
|
1110
|
+
...(grant && !preparedAttempt ? { startRequest: body } : {}),
|
|
1111
|
+
surveyId,
|
|
1112
|
+
attemptId: res.attemptId,
|
|
1113
|
+
startedAt: Date.now(),
|
|
1114
|
+
currentQuestionId: res.currentQuestionId,
|
|
1115
|
+
snapshot: (res.progressSnapshot ??
|
|
1116
|
+
{}),
|
|
1117
|
+
language: language ?? null,
|
|
1118
|
+
});
|
|
1119
|
+
if (!current())
|
|
1120
|
+
return null;
|
|
1121
|
+
if (presentationId) {
|
|
1122
|
+
await e.surveyInvitations.remove(presentationId);
|
|
1123
|
+
if (!current())
|
|
1124
|
+
return null;
|
|
1125
|
+
}
|
|
1126
|
+
if (grant && !preparedAttempt) {
|
|
1127
|
+
await e.mutations.enqueue('survey-start', 'survey', { surveyId, body }, `survey-start:${res.attemptId}`);
|
|
1128
|
+
if (!current())
|
|
1129
|
+
return null;
|
|
1130
|
+
void flushMutations(e);
|
|
1131
|
+
}
|
|
1132
|
+
return {
|
|
1133
|
+
attemptId: res.attemptId,
|
|
1134
|
+
startQuestionId: res.startQuestionId,
|
|
1135
|
+
currentQuestionId: res.currentQuestionId,
|
|
1136
|
+
snapshot: (res.progressSnapshot ??
|
|
1137
|
+
{}),
|
|
1138
|
+
resumed: res.resumed,
|
|
1139
|
+
resolvedContent: res.resolvedContent,
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
1142
|
+
catch (err) {
|
|
1143
|
+
reportError('createAttempt failed', err);
|
|
1144
|
+
return null;
|
|
1145
|
+
}
|
|
1146
|
+
})();
|
|
1147
|
+
surveyStarts.set(key, operation);
|
|
935
1148
|
try {
|
|
936
|
-
|
|
937
|
-
await ensureHydrated(e);
|
|
938
|
-
// Give a previously deferred completion a chance to reach the server
|
|
939
|
-
// before asking it to create or resume another attempt.
|
|
940
|
-
await flushMutations(e);
|
|
941
|
-
const id = e.identity.get();
|
|
942
|
-
const res = await e.transport.surveyCreateAttempt(surveyId, {
|
|
943
|
-
anonymousId: id.anonymousId,
|
|
944
|
-
externalId: id.externalId ?? null,
|
|
945
|
-
source,
|
|
946
|
-
...(language ? { language } : {}),
|
|
947
|
-
sdkVersion: `rn-${USERGIST_SDK_VERSION}`,
|
|
948
|
-
});
|
|
949
|
-
await surveyStore?.upsert({
|
|
950
|
-
surveyId,
|
|
951
|
-
attemptId: res.attemptId,
|
|
952
|
-
startedAt: Date.now(),
|
|
953
|
-
currentQuestionId: res.currentQuestionId,
|
|
954
|
-
snapshot: (res.progressSnapshot ?? {}),
|
|
955
|
-
language: language ?? null,
|
|
956
|
-
});
|
|
957
|
-
return {
|
|
958
|
-
attemptId: res.attemptId,
|
|
959
|
-
startQuestionId: res.startQuestionId,
|
|
960
|
-
currentQuestionId: res.currentQuestionId,
|
|
961
|
-
snapshot: (res.progressSnapshot ?? {}),
|
|
962
|
-
resumed: res.resumed,
|
|
963
|
-
};
|
|
1149
|
+
return await operation;
|
|
964
1150
|
}
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
1151
|
+
finally {
|
|
1152
|
+
if (surveyStarts.get(key) === operation)
|
|
1153
|
+
surveyStarts.delete(key);
|
|
968
1154
|
}
|
|
969
1155
|
},
|
|
970
1156
|
async __internal_saveProgress(attemptId, currentQuestionId, snapshot) {
|
|
971
1157
|
// Persist the resume point before attempting the network. Progress is
|
|
972
1158
|
// best-effort transport; losing connectivity must not lose local answers.
|
|
1159
|
+
const e = requireEngine();
|
|
1160
|
+
if (e.resetting || !e.consent.allowsSurvey())
|
|
1161
|
+
return;
|
|
1162
|
+
const generation = e.resetGeneration;
|
|
1163
|
+
const pending = (await surveyStore?.list())?.find((a) => a.attemptId === attemptId);
|
|
1164
|
+
if (!pending || e.resetting || e.resetGeneration !== generation)
|
|
1165
|
+
return;
|
|
973
1166
|
await surveyStore?.updateProgress(attemptId, currentQuestionId, snapshot);
|
|
974
1167
|
try {
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
1168
|
+
if (e.resetting || e.resetGeneration !== generation)
|
|
1169
|
+
return;
|
|
1170
|
+
await e.mutations.enqueue('survey-progress', 'survey', {
|
|
1171
|
+
attemptId,
|
|
1172
|
+
body: { currentQuestionId, progressSnapshot: snapshot },
|
|
979
1173
|
});
|
|
1174
|
+
void flushMutations(e);
|
|
980
1175
|
}
|
|
981
1176
|
catch (err) {
|
|
982
1177
|
// Completion can win a race with an in-flight progress PATCH, making a
|
|
@@ -1016,7 +1211,9 @@ export const UserGist = {
|
|
|
1016
1211
|
throw new Error('Survey submission was rejected by the server');
|
|
1017
1212
|
}
|
|
1018
1213
|
if (outcome === 'deferred') {
|
|
1019
|
-
debugLog('survey completion saved locally; delivery deferred', {
|
|
1214
|
+
debugLog('survey completion saved locally; delivery deferred', {
|
|
1215
|
+
attemptId,
|
|
1216
|
+
});
|
|
1020
1217
|
}
|
|
1021
1218
|
// Once the completion is durably queued, the attempt must not reopen as
|
|
1022
1219
|
// resumable work. The mutation queue owns delivery from this point.
|
|
@@ -1096,7 +1293,9 @@ export const UserGist = {
|
|
|
1096
1293
|
submitRequest(title, description, callback) {
|
|
1097
1294
|
try {
|
|
1098
1295
|
const e = requireEngine();
|
|
1099
|
-
if (typeof title !== 'string' ||
|
|
1296
|
+
if (typeof title !== 'string' ||
|
|
1297
|
+
title.length === 0 ||
|
|
1298
|
+
title.length > 120) {
|
|
1100
1299
|
callback?.(new Error('title required, max 120 chars'));
|
|
1101
1300
|
return;
|
|
1102
1301
|
}
|
|
@@ -1382,4 +1581,97 @@ export const UserGist = {
|
|
|
1382
1581
|
}
|
|
1383
1582
|
},
|
|
1384
1583
|
};
|
|
1584
|
+
/** Registration and invalidation share an ordered lane. Automatic retries may
|
|
1585
|
+
* consume a retained token, but must never recreate an invalidated candidate. */
|
|
1586
|
+
async function registerPushDevice(token, platform, opts, retry = false) {
|
|
1587
|
+
try {
|
|
1588
|
+
if (!token)
|
|
1589
|
+
return;
|
|
1590
|
+
const e = requireEngine();
|
|
1591
|
+
if (e.resetting)
|
|
1592
|
+
return;
|
|
1593
|
+
const generation = e.resetGeneration;
|
|
1594
|
+
await serializePushRegistration(async () => {
|
|
1595
|
+
await ensureHydrated(e);
|
|
1596
|
+
if (e.resetting || generation !== e.resetGeneration)
|
|
1597
|
+
return;
|
|
1598
|
+
const environment = opts?.environment ?? (await UserGistPushNative.defaultEnvironment()) ??
|
|
1599
|
+
(e.config.environment === 'production' ? 'production' : 'sandbox');
|
|
1600
|
+
if (e.resetting || generation !== e.resetGeneration)
|
|
1601
|
+
return;
|
|
1602
|
+
const descriptor = { token, platform, environment };
|
|
1603
|
+
const stored = await e.storage.getJson(STORAGE_KEYS.pushDevice);
|
|
1604
|
+
if (JSON.stringify(stored) !== JSON.stringify(descriptor)) {
|
|
1605
|
+
if (retry)
|
|
1606
|
+
return;
|
|
1607
|
+
await e.storage.setJsonStrict(STORAGE_KEYS.pushDevice, descriptor);
|
|
1608
|
+
}
|
|
1609
|
+
e.pushTokenAvailable = true;
|
|
1610
|
+
pushSubscriptionChanged(e);
|
|
1611
|
+
if (!e.consent.allowsPush())
|
|
1612
|
+
return;
|
|
1613
|
+
await drainRevocations(e);
|
|
1614
|
+
if (await hasPendingRevocations(e) || e.resetting || generation !== e.resetGeneration || !e.consent.allowsPush())
|
|
1615
|
+
return;
|
|
1616
|
+
const id = e.identity.get();
|
|
1617
|
+
const registrationKey = [id.anonymousId, id.externalId ?? '', platform, environment, token].join(':');
|
|
1618
|
+
if (e.lastPushRegistrationKey === registrationKey && Date.now() - e.lastPushRegistrationAt < 24 * 60 * 60_000)
|
|
1619
|
+
return;
|
|
1620
|
+
// Persist before delivery so logout still owns a token if its response is lost.
|
|
1621
|
+
e.lastPushToken = token;
|
|
1622
|
+
await e.storage.setJson(STORAGE_KEYS.pushToken, token);
|
|
1623
|
+
if (e.resetting || generation !== e.resetGeneration || !e.consent.allowsPush())
|
|
1624
|
+
return;
|
|
1625
|
+
const registration = await e.transport.pushRegisterToken({
|
|
1626
|
+
anonymousId: id.anonymousId, externalId: id.externalId ?? null,
|
|
1627
|
+
token, platform, environment, timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
1628
|
+
sdkVersion: `rn-${USERGIST_SDK_VERSION}`, optIn: true,
|
|
1629
|
+
});
|
|
1630
|
+
if (e.resetting || generation !== e.resetGeneration || !e.consent.allowsPush())
|
|
1631
|
+
return;
|
|
1632
|
+
if (!(registration && typeof registration === 'object' && 'registered' in registration && registration.registered === true)) {
|
|
1633
|
+
pushSubscriptionChanged(e);
|
|
1634
|
+
return;
|
|
1635
|
+
}
|
|
1636
|
+
e.lastPushRegistrationKey = registrationKey;
|
|
1637
|
+
e.lastPushRegistrationAt = Date.now();
|
|
1638
|
+
pushSubscriptionChanged(e);
|
|
1639
|
+
debugLog('push token registered with server');
|
|
1640
|
+
});
|
|
1641
|
+
}
|
|
1642
|
+
catch (err) {
|
|
1643
|
+
reportError('registerPushToken failed', err);
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
async function invalidatePushDevice(token, retainDevice = false) {
|
|
1647
|
+
try {
|
|
1648
|
+
if (!token)
|
|
1649
|
+
return;
|
|
1650
|
+
const e = requireEngine();
|
|
1651
|
+
const generation = e.resetGeneration;
|
|
1652
|
+
await serializePushRegistration(async () => {
|
|
1653
|
+
await ensureHydrated(e);
|
|
1654
|
+
if (e.resetting || generation !== e.resetGeneration)
|
|
1655
|
+
return;
|
|
1656
|
+
const saved = await e.storage.getJson(STORAGE_KEYS.pushDevice);
|
|
1657
|
+
if (saved?.token === token) {
|
|
1658
|
+
// Retire the automatic retry candidate before invalidating it remotely.
|
|
1659
|
+
if (!retainDevice)
|
|
1660
|
+
await e.storage.setJsonStrict(STORAGE_KEYS.pushDevice, null);
|
|
1661
|
+
e.pushTokenAvailable = retainDevice;
|
|
1662
|
+
e.lastPushRegistrationKey = null;
|
|
1663
|
+
e.lastPushRegistrationAt = 0;
|
|
1664
|
+
}
|
|
1665
|
+
if (e.lastPushToken === token) {
|
|
1666
|
+
await e.storage.setJsonStrict(STORAGE_KEYS.pushToken, null);
|
|
1667
|
+
e.lastPushToken = null;
|
|
1668
|
+
}
|
|
1669
|
+
pushSubscriptionChanged(e);
|
|
1670
|
+
await e.transport.pushInvalidateToken({ anonymousId: e.identity.get().anonymousId, token });
|
|
1671
|
+
});
|
|
1672
|
+
}
|
|
1673
|
+
catch (err) {
|
|
1674
|
+
reportError('invalidatePushToken failed', err);
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1385
1677
|
//# sourceMappingURL=UserGist.js.map
|