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