@amplifyup/sdk 0.1.24 → 0.1.26

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
@@ -58,7 +58,10 @@ var AWSAdapter = class {
58
58
  headers: {
59
59
  "Content-Type": "application/json"
60
60
  },
61
- body: JSON.stringify(eventData)
61
+ body: JSON.stringify(eventData),
62
+ // Keep the request alive through tab close so the single Time on Page
63
+ // flush on pagehide is not cancelled with the document.
64
+ keepalive: true
62
65
  });
63
66
  if (!response.ok) {
64
67
  if (this.config.debug) {
@@ -517,6 +520,8 @@ async function fetchContent(options) {
517
520
 
518
521
  // src/index.ts
519
522
  var SESSION_IDLE_GAP_MS = 30 * 60 * 1e3;
523
+ var DWELL_ACTIVITY_IDLE_MS = 2 * 60 * 1e3;
524
+ var DWELL_POLL_MS = 5e3;
520
525
  function readPreviewQueryOverrides() {
521
526
  if (typeof window === "undefined") return {};
522
527
  const params = new URLSearchParams(window.location.search);
@@ -585,9 +590,13 @@ var AmplifyUpSDK = class {
585
590
  reached: /* @__PURE__ */ new Set(),
586
591
  visibleMs: 0,
587
592
  visibleSince: null,
588
- lastDwellSecondsSent: 0,
593
+ /** True after Time on Page has been sent for the current visit. */
594
+ dwellEmitted: false,
589
595
  /** When the tab last became hidden — used for idle-resume re-page. */
590
- hiddenAt: null
596
+ hiddenAt: null,
597
+ /** Last user interaction — freezes dwell when stale while visible. */
598
+ lastActivityAt: 0,
599
+ activityIdle: false
591
600
  };
592
601
  this.adapter = adapter || new AWSAdapter();
593
602
  if (typeof window !== "undefined") {
@@ -810,8 +819,10 @@ var AmplifyUpSDK = class {
810
819
  return window.location.pathname || "/";
811
820
  }
812
821
  /**
813
- * Automatic page-level Scroll Depth + Time on Page (dwell heartbeat).
814
- * State lives on the instance so SPA route changes can reset milestones.
822
+ * Automatic page-level Scroll Depth + Time on Page.
823
+ * Time on Page fires once per visit when the visit ends (route change,
824
+ * tab close, or 30-minute idle resume). Hidden tabs pause the clock;
825
+ * visible-but-idle tabs freeze after DWELL_ACTIVITY_IDLE_MS.
815
826
  */
816
827
  setupPageEngagement() {
817
828
  if (typeof window === "undefined" || typeof document === "undefined") return;
@@ -822,8 +833,10 @@ var AmplifyUpSDK = class {
822
833
  state.reached = /* @__PURE__ */ new Set();
823
834
  state.visibleMs = 0;
824
835
  state.visibleSince = document.visibilityState === "visible" ? Date.now() : null;
825
- state.lastDwellSecondsSent = 0;
836
+ state.dwellEmitted = false;
826
837
  state.hiddenAt = document.visibilityState === "hidden" ? Date.now() : null;
838
+ state.lastActivityAt = Date.now();
839
+ state.activityIdle = false;
827
840
  let scrollRaf = null;
828
841
  const path = () => this.currentPath();
829
842
  const measureScrollPercent = () => {
@@ -855,6 +868,7 @@ var AmplifyUpSDK = class {
855
868
  }
856
869
  };
857
870
  const onScroll = () => {
871
+ noteActivity();
858
872
  if (scrollRaf != null) return;
859
873
  scrollRaf = window.requestAnimationFrame(() => {
860
874
  scrollRaf = null;
@@ -866,24 +880,49 @@ var AmplifyUpSDK = class {
866
880
  state.visibleMs += Date.now() - state.visibleSince;
867
881
  state.visibleSince = Date.now();
868
882
  };
869
- const emitDwell = (_force = false) => {
870
- if (this.isPreviewMode()) return;
883
+ const freezeActivityIdle = () => {
884
+ if (state.activityIdle) return;
871
885
  accumulateVisible();
886
+ state.visibleSince = null;
887
+ state.activityIdle = true;
888
+ };
889
+ const noteActivity = () => {
890
+ state.lastActivityAt = Date.now();
891
+ if (state.activityIdle) {
892
+ state.activityIdle = false;
893
+ if (document.visibilityState === "visible" && state.visibleSince == null) {
894
+ state.visibleSince = Date.now();
895
+ }
896
+ }
897
+ };
898
+ const emitDwell = (reason) => {
899
+ if (this.isPreviewMode()) return;
900
+ if (state.dwellEmitted) return;
901
+ if (!state.activityIdle) {
902
+ accumulateVisible();
903
+ }
872
904
  const seconds = Math.floor(state.visibleMs / 1e3);
873
905
  if (seconds <= 0) return;
874
- if (seconds <= state.lastDwellSecondsSent) return;
875
- state.lastDwellSecondsSent = seconds;
906
+ state.dwellEmitted = true;
876
907
  this.track("Time on Page", {
877
908
  seconds,
878
- path: this.lastTrackedPath || path()
909
+ path: this.lastTrackedPath || path(),
910
+ reason
879
911
  });
880
912
  };
881
- this._emitPageDwell = emitDwell;
913
+ const evaluateIdle = () => {
914
+ if (document.visibilityState !== "visible") return;
915
+ if (Date.now() - state.lastActivityAt >= DWELL_ACTIVITY_IDLE_MS) {
916
+ freezeActivityIdle();
917
+ }
918
+ };
919
+ this._emitPageDwell = () => emitDwell("route");
882
920
  const onVisibility = () => {
883
921
  if (document.visibilityState === "visible") {
884
922
  const idleMs = state.hiddenAt != null ? Date.now() - state.hiddenAt : 0;
885
923
  state.hiddenAt = null;
886
924
  if (idleMs >= SESSION_IDLE_GAP_MS) {
925
+ emitDwell("idle-resume");
887
926
  this.resetEngagementQuiet();
888
927
  const resumePath = path();
889
928
  this.page(resumePath, { resumed: true });
@@ -896,35 +935,51 @@ var AmplifyUpSDK = class {
896
935
  );
897
936
  }
898
937
  }
938
+ noteActivity();
899
939
  state.visibleSince = Date.now();
940
+ state.activityIdle = false;
900
941
  } else {
901
- emitDwell(true);
942
+ accumulateVisible();
902
943
  state.hiddenAt = Date.now();
903
944
  state.visibleSince = null;
945
+ state.activityIdle = false;
904
946
  }
905
947
  };
906
948
  const onPageHide = () => {
907
- emitDwell(true);
949
+ emitDwell("unload");
908
950
  if (state.hiddenAt == null) {
909
951
  state.hiddenAt = Date.now();
910
952
  }
911
953
  state.visibleSince = null;
912
954
  };
955
+ const activityEvents = [
956
+ "pointerdown",
957
+ "keydown",
958
+ "scroll",
959
+ "touchstart"
960
+ ];
961
+ const onActivity = () => noteActivity();
913
962
  window.addEventListener("scroll", onScroll, { passive: true });
914
963
  document.addEventListener("visibilitychange", onVisibility);
915
964
  window.addEventListener("pagehide", onPageHide);
965
+ for (const evt of activityEvents) {
966
+ if (evt === "scroll") continue;
967
+ window.addEventListener(evt, onActivity, { passive: true });
968
+ }
916
969
  checkScroll();
917
- const heartbeat = setInterval(() => {
918
- if (document.visibilityState === "visible") {
919
- emitDwell(false);
920
- }
921
- }, 15e3);
922
- this.trackingIntervals.push(heartbeat);
970
+ const idlePoll = setInterval(() => {
971
+ evaluateIdle();
972
+ }, DWELL_POLL_MS);
973
+ this.trackingIntervals.push(idlePoll);
923
974
  const previousCleanup = this.trackingCleanup;
924
975
  this.trackingCleanup = () => {
925
976
  window.removeEventListener("scroll", onScroll);
926
977
  document.removeEventListener("visibilitychange", onVisibility);
927
978
  window.removeEventListener("pagehide", onPageHide);
979
+ for (const evt of activityEvents) {
980
+ if (evt === "scroll") continue;
981
+ window.removeEventListener(evt, onActivity);
982
+ }
928
983
  if (scrollRaf != null) {
929
984
  window.cancelAnimationFrame(scrollRaf);
930
985
  scrollRaf = null;
@@ -943,13 +998,15 @@ var AmplifyUpSDK = class {
943
998
  emit?.(true);
944
999
  this.resetEngagementQuiet();
945
1000
  }
946
- /** Reset dwell/scroll state without emitting (idle resume already flushed on hide). */
1001
+ /** Reset dwell/scroll state without emitting (idle resume already flushed). */
947
1002
  resetEngagementQuiet() {
948
1003
  const state = this.pageEngagementState;
949
1004
  state.reached = /* @__PURE__ */ new Set();
950
1005
  state.visibleMs = 0;
951
- state.lastDwellSecondsSent = 0;
1006
+ state.dwellEmitted = false;
952
1007
  state.hiddenAt = null;
1008
+ state.lastActivityAt = Date.now();
1009
+ state.activityIdle = false;
953
1010
  state.visibleSince = typeof document !== "undefined" && document.visibilityState === "visible" ? Date.now() : null;
954
1011
  this.componentViewSeen.clear();
955
1012
  }