@rejourneyco/react-native 1.0.0 → 1.0.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/README.md +29 -0
- package/android/src/main/java/com/rejourney/RejourneyModuleImpl.kt +12 -1
- package/android/src/main/java/com/rejourney/capture/CaptureEngine.kt +25 -1
- package/android/src/main/java/com/rejourney/capture/CaptureHeuristics.kt +70 -32
- package/android/src/main/java/com/rejourney/core/Constants.kt +4 -4
- package/android/src/newarch/java/com/rejourney/RejourneyModule.kt +7 -0
- package/ios/Capture/RJCaptureEngine.m +74 -5
- package/ios/Capture/RJCaptureHeuristics.h +7 -5
- package/ios/Capture/RJCaptureHeuristics.m +138 -112
- package/ios/Core/Rejourney.mm +35 -10
- package/lib/commonjs/index.js +8 -14
- package/lib/commonjs/sdk/autoTracking.js +21 -48
- package/lib/module/index.js +8 -14
- package/lib/module/sdk/autoTracking.js +21 -48
- package/lib/typescript/NativeRejourney.d.ts +1 -0
- package/lib/typescript/sdk/autoTracking.d.ts +1 -2
- package/package.json +17 -3
- package/src/NativeRejourney.ts +2 -0
- package/src/index.ts +8 -15
- package/src/sdk/autoTracking.ts +26 -54
|
@@ -20,7 +20,7 @@ static const NSTimeInterval kRJQuietScrollSeconds = 0.2;
|
|
|
20
20
|
static const NSTimeInterval kRJQuietBounceSeconds = 0.2;
|
|
21
21
|
static const NSTimeInterval kRJQuietRefreshSeconds = 0.22;
|
|
22
22
|
static const NSTimeInterval kRJQuietMapSeconds = 0.55;
|
|
23
|
-
static const NSTimeInterval kRJQuietTransitionSeconds = 0.
|
|
23
|
+
static const NSTimeInterval kRJQuietTransitionSeconds = 0.1;
|
|
24
24
|
static const NSTimeInterval kRJQuietKeyboardSeconds = 0.25;
|
|
25
25
|
static const NSTimeInterval kRJQuietAnimationSeconds = 0.25;
|
|
26
26
|
|
|
@@ -204,7 +204,8 @@ typedef struct {
|
|
|
204
204
|
|
|
205
205
|
- (void)recordTouchEventAtTime:(NSTimeInterval)time {
|
|
206
206
|
self.lastTouchTime = time;
|
|
207
|
-
[self scheduleBonusCaptureAfterDelay:kRJBonusInteractionDelaySeconds
|
|
207
|
+
[self scheduleBonusCaptureAfterDelay:kRJBonusInteractionDelaySeconds
|
|
208
|
+
now:time];
|
|
208
209
|
}
|
|
209
210
|
|
|
210
211
|
- (void)recordInteractionEventAtTime:(NSTimeInterval)time {
|
|
@@ -221,12 +222,11 @@ typedef struct {
|
|
|
221
222
|
|
|
222
223
|
- (void)recordNavigationEventAtTime:(NSTimeInterval)time {
|
|
223
224
|
self.lastTransitionTime = time;
|
|
224
|
-
[self scheduleBonusCaptureAfterDelay:kRJBonusTransitionDelaySeconds
|
|
225
|
-
now:time];
|
|
225
|
+
[self scheduleBonusCaptureAfterDelay:kRJBonusTransitionDelaySeconds now:time];
|
|
226
226
|
}
|
|
227
227
|
|
|
228
228
|
- (void)recordRenderedSignature:(nullable NSString *)signature
|
|
229
|
-
|
|
229
|
+
atTime:(NSTimeInterval)time {
|
|
230
230
|
self.lastRenderedSignature = signature.length > 0 ? signature : nil;
|
|
231
231
|
self.lastRenderedTime = time;
|
|
232
232
|
if (self.pendingKeyframes > 0) {
|
|
@@ -262,8 +262,8 @@ typedef struct {
|
|
|
262
262
|
}
|
|
263
263
|
self.lastObservedSignatureTime = now;
|
|
264
264
|
self.lastSignatureChurnTime = now;
|
|
265
|
-
self.lastObservedSignature =
|
|
266
|
-
|
|
265
|
+
self.lastObservedSignature =
|
|
266
|
+
currentSignature.length > 0 ? currentSignature : nil;
|
|
267
267
|
} else if (self.lastSignatureChurnTime > 0 &&
|
|
268
268
|
(now - self.lastSignatureChurnTime) >
|
|
269
269
|
kRJSignatureChurnWindowSeconds) {
|
|
@@ -296,15 +296,15 @@ typedef struct {
|
|
|
296
296
|
}
|
|
297
297
|
|
|
298
298
|
[self updateScrollActiveState:scanResult.scrollActive
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
299
|
+
refreshActive:scanResult.refreshActive
|
|
300
|
+
mapActive:scanResult.mapActive
|
|
301
|
+
now:now];
|
|
302
302
|
|
|
303
303
|
BOOL blockingAnimation = NO;
|
|
304
304
|
if (scanResult.hasAnyAnimations) {
|
|
305
305
|
self.lastAnimationAreaRatio = scanResult.animationAreaRatio;
|
|
306
|
-
blockingAnimation =
|
|
307
|
-
|
|
306
|
+
blockingAnimation =
|
|
307
|
+
(scanResult.animationAreaRatio >= kRJAnimationSmallAreaAllowed);
|
|
308
308
|
} else {
|
|
309
309
|
self.lastAnimationAreaRatio = 0.0;
|
|
310
310
|
}
|
|
@@ -335,88 +335,113 @@ typedef struct {
|
|
|
335
335
|
[self probeAnimatedViewsAtTime:now];
|
|
336
336
|
}
|
|
337
337
|
|
|
338
|
-
- (RJCaptureHeuristicsDecision *)
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
338
|
+
- (RJCaptureHeuristicsDecision *)
|
|
339
|
+
decisionForSignature:(nullable NSString *)signature
|
|
340
|
+
now:(NSTimeInterval)now
|
|
341
|
+
hasLastFrame:(BOOL)hasLastFrame
|
|
342
|
+
importance:(RJCaptureImportance)importance {
|
|
342
343
|
RJCaptureHeuristicsDecision *decision =
|
|
343
344
|
[[RJCaptureHeuristicsDecision alloc] init];
|
|
344
345
|
|
|
345
346
|
NSTimeInterval earliestSafeTime = now;
|
|
346
347
|
RJCaptureHeuristicsReason blockerReason = RJCaptureHeuristicsReasonRenderNow;
|
|
347
348
|
|
|
349
|
+
// Check importance to potentially bypass heuristics
|
|
350
|
+
BOOL isUrgent = (importance == RJCaptureImportanceHigh ||
|
|
351
|
+
importance == RJCaptureImportanceCritical);
|
|
352
|
+
|
|
348
353
|
// Active touch/gesture input should be avoided to keep input latency smooth.
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
354
|
+
// CRITICAL updates bypass this.
|
|
355
|
+
if (!isUrgent) {
|
|
356
|
+
[self considerBlockerSince:self.lastTouchTime
|
|
357
|
+
quietInterval:kRJQuietTouchSeconds
|
|
358
|
+
now:now
|
|
359
|
+
reason:RJCaptureHeuristicsReasonDeferTouch
|
|
360
|
+
earliestTime:&earliestSafeTime
|
|
361
|
+
chosenReason:&blockerReason];
|
|
362
|
+
}
|
|
355
363
|
|
|
356
364
|
// Scroll motion (dragging or deceleration) is a high-jank period.
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
365
|
+
// Even urgent captures should respect scroll to avoid visible hitching,
|
|
366
|
+
// unless CRITICAL
|
|
367
|
+
if (importance != RJCaptureImportanceCritical) {
|
|
368
|
+
[self considerBlockerSince:self.lastScrollTime
|
|
369
|
+
quietInterval:kRJQuietScrollSeconds
|
|
370
|
+
now:now
|
|
371
|
+
reason:RJCaptureHeuristicsReasonDeferScroll
|
|
372
|
+
earliestTime:&earliestSafeTime
|
|
373
|
+
chosenReason:&blockerReason];
|
|
374
|
+
}
|
|
363
375
|
|
|
364
376
|
// Rubber-band bounce and inset animations are visually sensitive.
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
377
|
+
if (!isUrgent) {
|
|
378
|
+
[self considerBlockerSince:self.lastBounceTime
|
|
379
|
+
quietInterval:kRJQuietBounceSeconds
|
|
380
|
+
now:now
|
|
381
|
+
reason:RJCaptureHeuristicsReasonDeferBounce
|
|
382
|
+
earliestTime:&earliestSafeTime
|
|
383
|
+
chosenReason:&blockerReason];
|
|
384
|
+
}
|
|
371
385
|
|
|
372
386
|
// Pull-to-refresh animations should finish before rendering.
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
387
|
+
if (!isUrgent) {
|
|
388
|
+
[self considerBlockerSince:self.lastRefreshTime
|
|
389
|
+
quietInterval:kRJQuietRefreshSeconds
|
|
390
|
+
now:now
|
|
391
|
+
reason:RJCaptureHeuristicsReasonDeferRefresh
|
|
392
|
+
earliestTime:&earliestSafeTime
|
|
393
|
+
chosenReason:&blockerReason];
|
|
394
|
+
}
|
|
379
395
|
|
|
380
396
|
// Interactive transitions (swipe back, drag-to-dismiss) are hitch-sensitive.
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
397
|
+
// KEY FIX: Urgent captures (NAVIGATION) must bypass this!
|
|
398
|
+
if (!isUrgent) {
|
|
399
|
+
[self considerBlockerSince:self.lastTransitionTime
|
|
400
|
+
quietInterval:kRJQuietTransitionSeconds
|
|
401
|
+
now:now
|
|
402
|
+
reason:RJCaptureHeuristicsReasonDeferTransition
|
|
403
|
+
earliestTime:&earliestSafeTime
|
|
404
|
+
chosenReason:&blockerReason];
|
|
405
|
+
}
|
|
387
406
|
|
|
388
407
|
// Keyboard frame animations can stutter; wait for settle.
|
|
389
408
|
if (self.keyboardAnimating) {
|
|
390
409
|
self.lastKeyboardTime = now;
|
|
391
410
|
}
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
411
|
+
if (!isUrgent) {
|
|
412
|
+
[self considerBlockerSince:self.lastKeyboardTime
|
|
413
|
+
quietInterval:kRJQuietKeyboardSeconds
|
|
414
|
+
now:now
|
|
415
|
+
reason:RJCaptureHeuristicsReasonDeferKeyboard
|
|
416
|
+
earliestTime:&earliestSafeTime
|
|
417
|
+
chosenReason:&blockerReason];
|
|
418
|
+
}
|
|
398
419
|
|
|
399
420
|
// Map camera or tile motion is visually obvious; avoid rendering mid-flight.
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
421
|
+
// Maps are special; even CRITICAL captures might want to wait for map settle
|
|
422
|
+
// if possible.
|
|
423
|
+
if (importance != RJCaptureImportanceCritical) {
|
|
424
|
+
[self considerBlockerSince:self.lastMapTime
|
|
425
|
+
quietInterval:kRJQuietMapSeconds
|
|
426
|
+
now:now
|
|
427
|
+
reason:RJCaptureHeuristicsReasonDeferMap
|
|
428
|
+
earliestTime:&earliestSafeTime
|
|
429
|
+
chosenReason:&blockerReason];
|
|
406
430
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
431
|
+
if (self.mapSettleUntil > now && self.mapSettleUntil > earliestSafeTime) {
|
|
432
|
+
earliestSafeTime = self.mapSettleUntil;
|
|
433
|
+
blockerReason = RJCaptureHeuristicsReasonDeferMap;
|
|
434
|
+
}
|
|
410
435
|
}
|
|
411
436
|
|
|
412
437
|
// Large-area animations (Lottie, shimmer, etc.) are very noticeable.
|
|
413
|
-
if (self.animationBlocking) {
|
|
438
|
+
if (self.animationBlocking && !isUrgent) {
|
|
414
439
|
[self considerBlockerSince:self.lastAnimationTime
|
|
415
440
|
quietInterval:kRJQuietAnimationSeconds
|
|
416
441
|
now:now
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
442
|
+
reason:RJCaptureHeuristicsReasonDeferBigAnimation
|
|
443
|
+
earliestTime:&earliestSafeTime
|
|
444
|
+
chosenReason:&blockerReason];
|
|
420
445
|
}
|
|
421
446
|
|
|
422
447
|
if (earliestSafeTime > now) {
|
|
@@ -426,26 +451,27 @@ typedef struct {
|
|
|
426
451
|
return decision;
|
|
427
452
|
}
|
|
428
453
|
|
|
429
|
-
BOOL signatureChanged =
|
|
430
|
-
|
|
454
|
+
BOOL signatureChanged =
|
|
455
|
+
(signature.length == 0 ||
|
|
456
|
+
![signature isEqualToString:self.lastRenderedSignature]);
|
|
431
457
|
BOOL stale = (self.lastRenderedTime <= 0 ||
|
|
432
458
|
(now - self.lastRenderedTime) > self.maxStaleSeconds);
|
|
433
459
|
BOOL bonusDue = (self.bonusCaptureTime > 0 && now >= self.bonusCaptureTime);
|
|
434
|
-
BOOL keyframeDue =
|
|
435
|
-
|
|
436
|
-
|
|
460
|
+
BOOL keyframeDue =
|
|
461
|
+
bonusDue && self.pendingKeyframes > 0 &&
|
|
462
|
+
(now - self.lastKeyframeRenderTime) >= kRJKeyframeSpacingSeconds;
|
|
437
463
|
BOOL staleOnly = stale && hasLastFrame && !signatureChanged && !keyframeDue;
|
|
438
464
|
BOOL suppressStaleRender =
|
|
439
465
|
staleOnly &&
|
|
440
466
|
(self.hasVideoSurface || self.hasWebSurface || self.hasCameraSurface);
|
|
441
467
|
|
|
442
|
-
if (suppressStaleRender) {
|
|
468
|
+
if (suppressStaleRender && !isUrgent) {
|
|
443
469
|
decision.action = RJCaptureHeuristicsActionReuseLast;
|
|
444
470
|
decision.reason = RJCaptureHeuristicsReasonReuseSignatureUnchanged;
|
|
445
471
|
return decision;
|
|
446
472
|
}
|
|
447
473
|
|
|
448
|
-
if (!hasLastFrame || signatureChanged || stale || keyframeDue) {
|
|
474
|
+
if (!hasLastFrame || signatureChanged || stale || keyframeDue || isUrgent) {
|
|
449
475
|
decision.action = RJCaptureHeuristicsActionRenderNow;
|
|
450
476
|
decision.reason = RJCaptureHeuristicsReasonRenderNow;
|
|
451
477
|
return decision;
|
|
@@ -516,7 +542,8 @@ typedef struct {
|
|
|
516
542
|
UIViewController *topVC = [self topViewControllerForWindow:window];
|
|
517
543
|
if (topVC != self.lastTopVC) {
|
|
518
544
|
self.lastTransitionTime = now;
|
|
519
|
-
[self scheduleBonusCaptureAfterDelay:kRJBonusTransitionDelaySeconds
|
|
545
|
+
[self scheduleBonusCaptureAfterDelay:kRJBonusTransitionDelaySeconds
|
|
546
|
+
now:now];
|
|
520
547
|
self.lastTopVC = topVC;
|
|
521
548
|
}
|
|
522
549
|
id<UIViewControllerTransitionCoordinator> coordinator =
|
|
@@ -552,7 +579,7 @@ typedef struct {
|
|
|
552
579
|
#pragma mark - Scroll Tracking
|
|
553
580
|
|
|
554
581
|
- (void)updateTrackedScrollViews:(NSArray<NSValue *> *)scrollViewPointers
|
|
555
|
-
|
|
582
|
+
now:(NSTimeInterval)now {
|
|
556
583
|
if (!scrollViewPointers) {
|
|
557
584
|
return;
|
|
558
585
|
}
|
|
@@ -563,18 +590,18 @@ typedef struct {
|
|
|
563
590
|
continue;
|
|
564
591
|
}
|
|
565
592
|
[self evaluateScrollView:scrollView
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
593
|
+
now:now
|
|
594
|
+
scrollActive:NULL
|
|
595
|
+
refreshActive:NULL];
|
|
569
596
|
}
|
|
570
|
-
|
|
571
597
|
}
|
|
572
598
|
|
|
573
599
|
- (void)probeScrollViewsAtTime:(NSTimeInterval)now {
|
|
574
600
|
BOOL anyScrollActive = NO;
|
|
575
601
|
BOOL anyRefreshActive = NO;
|
|
576
602
|
|
|
577
|
-
NSArray<UIScrollView *> *scrollViews =
|
|
603
|
+
NSArray<UIScrollView *> *scrollViews =
|
|
604
|
+
[[self.scrollSamples keyEnumerator] allObjects];
|
|
578
605
|
if (!scrollViews) {
|
|
579
606
|
scrollViews = @[];
|
|
580
607
|
}
|
|
@@ -583,21 +610,21 @@ typedef struct {
|
|
|
583
610
|
continue;
|
|
584
611
|
}
|
|
585
612
|
[self evaluateScrollView:scrollView
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
613
|
+
now:now
|
|
614
|
+
scrollActive:&anyScrollActive
|
|
615
|
+
refreshActive:&anyRefreshActive];
|
|
589
616
|
}
|
|
590
617
|
|
|
591
618
|
[self updateScrollActiveState:anyScrollActive
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
619
|
+
refreshActive:anyRefreshActive
|
|
620
|
+
mapActive:self.mapActive
|
|
621
|
+
now:now];
|
|
595
622
|
}
|
|
596
623
|
|
|
597
624
|
- (void)evaluateScrollView:(UIScrollView *)scrollView
|
|
598
625
|
now:(NSTimeInterval)now
|
|
599
|
-
|
|
600
|
-
|
|
626
|
+
scrollActive:(BOOL *)scrollActive
|
|
627
|
+
refreshActive:(BOOL *)refreshActive {
|
|
601
628
|
RJScrollViewSample *sample = [self.scrollSamples objectForKey:scrollView];
|
|
602
629
|
if (!sample) {
|
|
603
630
|
sample = [[RJScrollViewSample alloc] init];
|
|
@@ -609,8 +636,9 @@ typedef struct {
|
|
|
609
636
|
|
|
610
637
|
BOOL tracking = scrollView.isTracking || scrollView.isDragging ||
|
|
611
638
|
scrollView.isDecelerating;
|
|
612
|
-
BOOL offsetMoved =
|
|
613
|
-
|
|
639
|
+
BOOL offsetMoved =
|
|
640
|
+
(fabs(offset.x - sample.contentOffset.x) > kRJScrollEpsilon ||
|
|
641
|
+
fabs(offset.y - sample.contentOffset.y) > kRJScrollEpsilon);
|
|
614
642
|
BOOL zoomMoved = fabs(zoomScale - sample.zoomScale) > kRJZoomEpsilon;
|
|
615
643
|
BOOL isScrolling = tracking || offsetMoved || zoomMoved;
|
|
616
644
|
|
|
@@ -635,8 +663,8 @@ typedef struct {
|
|
|
635
663
|
}
|
|
636
664
|
|
|
637
665
|
BOOL refreshVisible = [self isRefreshActiveForScrollView:scrollView
|
|
638
|
-
|
|
639
|
-
|
|
666
|
+
offset:offset
|
|
667
|
+
inset:inset];
|
|
640
668
|
if (refreshVisible) {
|
|
641
669
|
// Pull-to-refresh is active or settling.
|
|
642
670
|
self.lastRefreshTime = now;
|
|
@@ -660,8 +688,8 @@ typedef struct {
|
|
|
660
688
|
}
|
|
661
689
|
CGFloat topLimit = -inset.top - kRJScrollEpsilon;
|
|
662
690
|
CGFloat bottomLimit = scrollView.contentSize.height -
|
|
663
|
-
scrollView.bounds.size.height +
|
|
664
|
-
|
|
691
|
+
scrollView.bounds.size.height + inset.bottom +
|
|
692
|
+
kRJScrollEpsilon;
|
|
665
693
|
|
|
666
694
|
if (offset.y < topLimit || offset.y > bottomLimit) {
|
|
667
695
|
return YES;
|
|
@@ -669,8 +697,8 @@ typedef struct {
|
|
|
669
697
|
|
|
670
698
|
CGFloat leftLimit = -inset.left - kRJScrollEpsilon;
|
|
671
699
|
CGFloat rightLimit = scrollView.contentSize.width -
|
|
672
|
-
scrollView.bounds.size.width +
|
|
673
|
-
|
|
700
|
+
scrollView.bounds.size.width + inset.right +
|
|
701
|
+
kRJScrollEpsilon;
|
|
674
702
|
if (offset.x < leftLimit || offset.x > rightLimit) {
|
|
675
703
|
return YES;
|
|
676
704
|
}
|
|
@@ -690,8 +718,8 @@ typedef struct {
|
|
|
690
718
|
return YES;
|
|
691
719
|
}
|
|
692
720
|
|
|
693
|
-
CGFloat triggerOffset =
|
|
694
|
-
|
|
721
|
+
CGFloat triggerOffset =
|
|
722
|
+
-scrollView.adjustedContentInset.top - kRJScrollEpsilon;
|
|
695
723
|
if (offset.y < triggerOffset) {
|
|
696
724
|
return YES;
|
|
697
725
|
}
|
|
@@ -709,9 +737,9 @@ typedef struct {
|
|
|
709
737
|
}
|
|
710
738
|
|
|
711
739
|
- (void)updateScrollActiveState:(BOOL)scrollActive
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
740
|
+
refreshActive:(BOOL)refreshActive
|
|
741
|
+
mapActive:(BOOL)mapActive
|
|
742
|
+
now:(NSTimeInterval)now {
|
|
715
743
|
if (self.scrollActive && !scrollActive) {
|
|
716
744
|
[self scheduleBonusCaptureAfterDelay:kRJBonusScrollDelaySeconds now:now];
|
|
717
745
|
}
|
|
@@ -734,7 +762,7 @@ typedef struct {
|
|
|
734
762
|
#pragma mark - Map Tracking
|
|
735
763
|
|
|
736
764
|
- (void)updateTrackedMapViews:(NSArray<NSValue *> *)mapViewPointers
|
|
737
|
-
|
|
765
|
+
now:(NSTimeInterval)now {
|
|
738
766
|
if (!mapViewPointers) {
|
|
739
767
|
return;
|
|
740
768
|
}
|
|
@@ -763,9 +791,9 @@ typedef struct {
|
|
|
763
791
|
self.lastMapTime = now;
|
|
764
792
|
}
|
|
765
793
|
[self updateScrollActiveState:self.scrollActive
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
794
|
+
refreshActive:self.refreshActive
|
|
795
|
+
mapActive:anyMapActive || self.mapActive
|
|
796
|
+
now:now];
|
|
769
797
|
}
|
|
770
798
|
|
|
771
799
|
- (BOOL)updateMapStateForView:(UIView *)view atTime:(NSTimeInterval)now {
|
|
@@ -836,8 +864,7 @@ typedef struct {
|
|
|
836
864
|
|
|
837
865
|
- (void)probeAnimatedViewsAtTime:(NSTimeInterval)now {
|
|
838
866
|
if (self.churnBlocking) {
|
|
839
|
-
if ((now - self.lastSignatureChurnTime) <
|
|
840
|
-
kRJSignatureChurnWindowSeconds) {
|
|
867
|
+
if ((now - self.lastSignatureChurnTime) < kRJSignatureChurnWindowSeconds) {
|
|
841
868
|
self.lastAnimationTime = now;
|
|
842
869
|
return;
|
|
843
870
|
}
|
|
@@ -863,15 +890,14 @@ typedef struct {
|
|
|
863
890
|
self.lastAnimationTime = now;
|
|
864
891
|
} else {
|
|
865
892
|
self.animationBlocking = NO;
|
|
866
|
-
[self scheduleBonusCaptureAfterDelay:kRJBonusAnimationDelaySeconds
|
|
867
|
-
now:now];
|
|
893
|
+
[self scheduleBonusCaptureAfterDelay:kRJBonusAnimationDelaySeconds now:now];
|
|
868
894
|
}
|
|
869
895
|
}
|
|
870
896
|
|
|
871
897
|
#pragma mark - Bonus Capture
|
|
872
898
|
|
|
873
899
|
- (void)scheduleBonusCaptureAfterDelay:(NSTimeInterval)delay
|
|
874
|
-
|
|
900
|
+
now:(NSTimeInterval)now {
|
|
875
901
|
if (self.pendingKeyframes < kRJMaxPendingKeyframes) {
|
|
876
902
|
self.pendingKeyframes += 1;
|
|
877
903
|
}
|
|
@@ -886,9 +912,9 @@ typedef struct {
|
|
|
886
912
|
- (void)considerBlockerSince:(NSTimeInterval)timestamp
|
|
887
913
|
quietInterval:(NSTimeInterval)quietInterval
|
|
888
914
|
now:(NSTimeInterval)now
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
915
|
+
reason:(RJCaptureHeuristicsReason)reason
|
|
916
|
+
earliestTime:(NSTimeInterval *)earliestTime
|
|
917
|
+
chosenReason:(RJCaptureHeuristicsReason *)chosenReason {
|
|
892
918
|
if (timestamp <= 0) {
|
|
893
919
|
return;
|
|
894
920
|
}
|
package/ios/Core/Rejourney.mm
CHANGED
|
@@ -943,33 +943,54 @@ RCT_EXPORT_METHOD(setDebugMode : (BOOL)enabled resolve : (
|
|
|
943
943
|
|
|
944
944
|
RCT_EXPORT_METHOD(setUserIdentity : (NSString *)userId resolve : (
|
|
945
945
|
RCTPromiseResolveBlock)resolve reject : (RCTPromiseRejectBlock)reject) {
|
|
946
|
-
dispatch_async(
|
|
946
|
+
dispatch_async(self.stateQueue, ^{
|
|
947
947
|
@try {
|
|
948
948
|
NSString *safeUserId = userId.length > 0 ? userId : @"anonymous";
|
|
949
949
|
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
950
|
+
// KEY CHANGE: Persist directly to NSUserDefaults (Native Storage)
|
|
951
|
+
[[NSUserDefaults standardUserDefaults] setObject:safeUserId
|
|
952
|
+
forKey:@"rj_user_identity"];
|
|
953
|
+
[[NSUserDefaults standardUserDefaults] synchronize];
|
|
953
954
|
|
|
955
|
+
self.userId = safeUserId;
|
|
954
956
|
if (self.uploadManager) {
|
|
955
957
|
self.uploadManager.userId = safeUserId;
|
|
956
958
|
}
|
|
957
959
|
|
|
958
|
-
RJLogDebug(@"User identity updated: %@", safeUserId);
|
|
960
|
+
RJLogDebug(@"User identity updated and persisted: %@", safeUserId);
|
|
959
961
|
|
|
960
962
|
if (self.isRecording) {
|
|
961
|
-
|
|
962
|
-
|
|
963
|
+
// Log event for tracking
|
|
964
|
+
NSMutableDictionary *event = [NSMutableDictionary new];
|
|
965
|
+
event[@"type"] = @"user_identity_changed";
|
|
966
|
+
event[@"timestamp"] = @([RJWindowUtils currentTimestampMillis]);
|
|
967
|
+
event[@"userId"] = safeUserId;
|
|
968
|
+
|
|
969
|
+
// Helper to log event safely
|
|
970
|
+
if (self.eventBuffer) {
|
|
971
|
+
[self.eventBuffer appendEvent:event];
|
|
972
|
+
}
|
|
973
|
+
if (self.sessionEvents && !self.isShuttingDown) {
|
|
974
|
+
[self.sessionEvents addObject:event];
|
|
975
|
+
}
|
|
963
976
|
}
|
|
964
977
|
|
|
965
|
-
resolve
|
|
978
|
+
if (resolve)
|
|
979
|
+
resolve(@{@"success" : @YES});
|
|
966
980
|
} @catch (NSException *exception) {
|
|
967
|
-
|
|
968
|
-
|
|
981
|
+
if (resolve)
|
|
982
|
+
resolve(@{@"success" : @NO});
|
|
969
983
|
}
|
|
970
984
|
});
|
|
971
985
|
}
|
|
972
986
|
|
|
987
|
+
RCT_EXPORT_METHOD(getUserIdentity : (RCTPromiseResolveBlock)
|
|
988
|
+
resolve reject : (RCTPromiseRejectBlock)reject) {
|
|
989
|
+
NSString *userId =
|
|
990
|
+
[[NSUserDefaults standardUserDefaults] stringForKey:@"rj_user_identity"];
|
|
991
|
+
resolve(userId ?: [NSNull null]);
|
|
992
|
+
}
|
|
993
|
+
|
|
973
994
|
#pragma mark - RJTouchInterceptorDelegate
|
|
974
995
|
|
|
975
996
|
- (void)touchInterceptorDidDetectInteractionStart {
|
|
@@ -2345,6 +2366,10 @@ RCT_EXPORT_METHOD(setUserIdentity : (NSString *)userId resolve : (
|
|
|
2345
2366
|
self.uploadManager.totalBackgroundTimeMs = 0;
|
|
2346
2367
|
|
|
2347
2368
|
// Preserve user identity
|
|
2369
|
+
if (!self.userId) {
|
|
2370
|
+
self.userId = [[NSUserDefaults standardUserDefaults]
|
|
2371
|
+
stringForKey:@"rj_user_identity"];
|
|
2372
|
+
}
|
|
2348
2373
|
self.uploadManager.userId = self.userId ?: @"anonymous";
|
|
2349
2374
|
}
|
|
2350
2375
|
|
package/lib/commonjs/index.js
CHANGED
|
@@ -276,7 +276,6 @@ function getAutoTracking() {
|
|
|
276
276
|
}
|
|
277
277
|
|
|
278
278
|
// State
|
|
279
|
-
const USER_IDENTITY_KEY = '@rejourney_user_identity';
|
|
280
279
|
let _isInitialized = false;
|
|
281
280
|
let _isRecording = false;
|
|
282
281
|
let _initializationFailed = false;
|
|
@@ -291,22 +290,17 @@ let _lastScrollOffset = 0;
|
|
|
291
290
|
const SCROLL_THROTTLE_MS = 100;
|
|
292
291
|
|
|
293
292
|
// Helper to save/load user identity
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
if (identity) {
|
|
298
|
-
await AsyncStorage.setItem(USER_IDENTITY_KEY, identity);
|
|
299
|
-
} else {
|
|
300
|
-
await AsyncStorage.removeItem(USER_IDENTITY_KEY);
|
|
301
|
-
}
|
|
302
|
-
} catch (e) {
|
|
303
|
-
// Ignore storage errors
|
|
304
|
-
}
|
|
293
|
+
// NOW HANDLED NATIVELY - No-op on JS side to avoid unnecessary bridge calls
|
|
294
|
+
async function persistUserIdentity(_identity) {
|
|
295
|
+
// Native module handles persistence automatically in setUserIdentity
|
|
305
296
|
}
|
|
306
297
|
async function loadPersistedUserIdentity() {
|
|
307
298
|
try {
|
|
308
|
-
const
|
|
309
|
-
|
|
299
|
+
const nativeModule = getRejourneyNative();
|
|
300
|
+
if (!nativeModule) return null;
|
|
301
|
+
|
|
302
|
+
// NATIVE STORAGE: Read directly from SharedPreferences/NSUserDefaults
|
|
303
|
+
return await nativeModule.getUserIdentity();
|
|
310
304
|
} catch (e) {
|
|
311
305
|
return null;
|
|
312
306
|
}
|