@amplifyup/sdk 0.1.19 → 0.1.21

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/dist/react.mjs CHANGED
@@ -369,6 +369,15 @@ var AmplifyUpSDK = class {
369
369
  this.componentViewObservers = [];
370
370
  this.componentViewSeen = /* @__PURE__ */ new Set();
371
371
  this.pageEngagementActive = false;
372
+ /** Path of the last page() / routeChange — avoids duplicate SPA fires. */
373
+ this.lastTrackedPath = null;
374
+ this.historyHooksInstalled = false;
375
+ this.pageEngagementState = {
376
+ reached: /* @__PURE__ */ new Set(),
377
+ visibleMs: 0,
378
+ visibleSince: null,
379
+ lastDwellSecondsSent: 0
380
+ };
372
381
  this.adapter = adapter || new AWSAdapter();
373
382
  if (typeof window !== "undefined") {
374
383
  this.isVisualEditorMode = this.detectVisualEditorMode();
@@ -541,7 +550,9 @@ var AmplifyUpSDK = class {
541
550
  this.initialPageSent = true;
542
551
  const url = window.location.pathname;
543
552
  this.page(url);
553
+ this.lastTrackedPath = url;
544
554
  this.setupPageEngagement();
555
+ this.installHistoryHooks();
545
556
  }
546
557
  }
547
558
  if (this.config?.debug && this.initialized) {
@@ -584,16 +595,18 @@ var AmplifyUpSDK = class {
584
595
  }
585
596
  /**
586
597
  * Automatic page-level Scroll Depth + Time on Page (dwell heartbeat).
598
+ * State lives on the instance so SPA route changes can reset milestones.
587
599
  */
588
600
  setupPageEngagement() {
589
601
  if (typeof window === "undefined" || typeof document === "undefined") return;
590
602
  if (this.isPreviewMode() || this.pageEngagementActive) return;
591
603
  this.pageEngagementActive = true;
592
604
  const milestones = [25, 50, 75, 100];
593
- const reached = /* @__PURE__ */ new Set();
594
- let visibleMs = 0;
595
- let visibleSince = document.visibilityState === "visible" ? Date.now() : null;
596
- let lastDwellSecondsSent = 0;
605
+ const state = this.pageEngagementState;
606
+ state.reached = /* @__PURE__ */ new Set();
607
+ state.visibleMs = 0;
608
+ state.visibleSince = document.visibilityState === "visible" ? Date.now() : null;
609
+ state.lastDwellSecondsSent = 0;
597
610
  let scrollRaf = null;
598
611
  const path = () => this.currentPath();
599
612
  const measureScrollPercent = () => {
@@ -615,8 +628,8 @@ var AmplifyUpSDK = class {
615
628
  if (this.isPreviewMode()) return;
616
629
  const pct = measureScrollPercent();
617
630
  for (const milestone of milestones) {
618
- if (pct >= milestone && !reached.has(milestone)) {
619
- reached.add(milestone);
631
+ if (pct >= milestone && !state.reached.has(milestone)) {
632
+ state.reached.add(milestone);
620
633
  this.track("Scroll Depth", {
621
634
  percentage: milestone,
622
635
  path: path()
@@ -632,28 +645,29 @@ var AmplifyUpSDK = class {
632
645
  });
633
646
  };
634
647
  const accumulateVisible = () => {
635
- if (visibleSince == null) return;
636
- visibleMs += Date.now() - visibleSince;
637
- visibleSince = Date.now();
648
+ if (state.visibleSince == null) return;
649
+ state.visibleMs += Date.now() - state.visibleSince;
650
+ state.visibleSince = Date.now();
638
651
  };
639
652
  const emitDwell = (force = false) => {
640
653
  if (this.isPreviewMode()) return;
641
654
  accumulateVisible();
642
- const seconds = Math.floor(visibleMs / 1e3);
655
+ const seconds = Math.floor(state.visibleMs / 1e3);
643
656
  if (seconds <= 0) return;
644
- if (!force && seconds <= lastDwellSecondsSent) return;
645
- lastDwellSecondsSent = seconds;
657
+ if (!force && seconds <= state.lastDwellSecondsSent) return;
658
+ state.lastDwellSecondsSent = seconds;
646
659
  this.track("Time on Page", {
647
660
  seconds,
648
- path: path()
661
+ path: this.lastTrackedPath || path()
649
662
  });
650
663
  };
664
+ this._emitPageDwell = emitDwell;
651
665
  const onVisibility = () => {
652
666
  if (document.visibilityState === "visible") {
653
- visibleSince = Date.now();
667
+ state.visibleSince = Date.now();
654
668
  } else {
655
669
  emitDwell(true);
656
- visibleSince = null;
670
+ state.visibleSince = null;
657
671
  }
658
672
  };
659
673
  const onPageHide = () => {
@@ -680,11 +694,64 @@ var AmplifyUpSDK = class {
680
694
  }
681
695
  previousCleanup?.();
682
696
  this.pageEngagementActive = false;
697
+ delete this._emitPageDwell;
683
698
  };
684
699
  if (this.config?.debug) {
685
700
  console.log("[AmplifyUp SDK] Page engagement tracking enabled");
686
701
  }
687
702
  }
703
+ resetEngagementForNewPage() {
704
+ const emit = this._emitPageDwell;
705
+ emit?.(true);
706
+ const state = this.pageEngagementState;
707
+ state.reached = /* @__PURE__ */ new Set();
708
+ state.visibleMs = 0;
709
+ state.lastDwellSecondsSent = 0;
710
+ state.visibleSince = typeof document !== "undefined" && document.visibilityState === "visible" ? Date.now() : null;
711
+ this.componentViewSeen.clear();
712
+ }
713
+ /**
714
+ * SPA / soft navigation: fire a page view and reset engagement.
715
+ * No-op when the path has not changed. Safe for React Strict Mode remounts.
716
+ */
717
+ routeChange(url, properties) {
718
+ if (this.isPreviewMode()) return;
719
+ if (!this.isInitialized()) return;
720
+ if (typeof window === "undefined") return;
721
+ const path = url || window.location.pathname || "/";
722
+ if (path === this.lastTrackedPath) return;
723
+ this.resetEngagementForNewPage();
724
+ this.page(path, properties);
725
+ this.lastTrackedPath = path;
726
+ if (!this.pageEngagementActive) {
727
+ this.setupPageEngagement();
728
+ }
729
+ if (this.config?.debug) {
730
+ console.log("[AmplifyUp SDK] Route change page view", path);
731
+ }
732
+ }
733
+ /**
734
+ * Patch history.pushState / replaceState + popstate for non-React SPAs.
735
+ * React/Next apps should also call routeChange from the provider.
736
+ */
737
+ installHistoryHooks() {
738
+ if (typeof window === "undefined" || this.historyHooksInstalled) return;
739
+ this.historyHooksInstalled = true;
740
+ const notify = () => {
741
+ queueMicrotask(() => this.routeChange(window.location.pathname));
742
+ };
743
+ const wrap = (method) => {
744
+ const original = history[method].bind(history);
745
+ history[method] = (...args) => {
746
+ const result = original(...args);
747
+ notify();
748
+ return result;
749
+ };
750
+ };
751
+ wrap("pushState");
752
+ wrap("replaceState");
753
+ window.addEventListener("popstate", notify);
754
+ }
688
755
  /**
689
756
  * Automatic Component View when component hosts enter the viewport.
690
757
  */
@@ -759,6 +826,7 @@ var AmplifyUpSDK = class {
759
826
  ...properties
760
827
  };
761
828
  this.adapter.page(pageProperties);
829
+ this.lastTrackedPath = url;
762
830
  }
763
831
  /**
764
832
  * Track an event
@@ -3003,6 +3071,7 @@ function AmplifyUpProvider({ children, config }) {
3003
3071
  return;
3004
3072
  }
3005
3073
  const sdk = getSDK();
3074
+ sdk.routeChange(actualRoute);
3006
3075
  const isPreviewContext = isComposerPreview();
3007
3076
  const page = await sdk.fetchPageConfig(actualRoute, isPreviewContext);
3008
3077
  if (!page) {