@shware/analytics 2.14.4 → 2.15.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.
Files changed (68) hide show
  1. package/dist/hooks/use-app-session-analytics.cjs +68 -0
  2. package/dist/hooks/use-app-session-analytics.cjs.map +1 -0
  3. package/dist/hooks/use-app-session-analytics.d.cts +3 -0
  4. package/dist/hooks/use-app-session-analytics.d.ts +3 -0
  5. package/dist/hooks/use-app-session-analytics.mjs +43 -0
  6. package/dist/hooks/use-app-session-analytics.mjs.map +1 -0
  7. package/dist/{native/analytics.cjs → hooks/use-screen-view-analytics.cjs} +6 -7
  8. package/dist/hooks/use-screen-view-analytics.cjs.map +1 -0
  9. package/dist/{native/analytics.mjs → hooks/use-screen-view-analytics.mjs} +3 -4
  10. package/dist/hooks/use-screen-view-analytics.mjs.map +1 -0
  11. package/dist/hooks/use-web-session-analytics.cjs +130 -0
  12. package/dist/hooks/use-web-session-analytics.cjs.map +1 -0
  13. package/dist/hooks/use-web-session-analytics.d.cts +8 -0
  14. package/dist/hooks/use-web-session-analytics.d.ts +8 -0
  15. package/dist/hooks/use-web-session-analytics.mjs +105 -0
  16. package/dist/hooks/use-web-session-analytics.mjs.map +1 -0
  17. package/dist/native/index.cjs +5 -2
  18. package/dist/native/index.cjs.map +1 -1
  19. package/dist/native/index.d.cts +2 -1
  20. package/dist/native/index.d.ts +2 -1
  21. package/dist/native/index.mjs +3 -1
  22. package/dist/native/index.mjs.map +1 -1
  23. package/dist/next/index.cjs +2 -2
  24. package/dist/next/index.cjs.map +1 -1
  25. package/dist/next/index.mjs +2 -2
  26. package/dist/next/index.mjs.map +1 -1
  27. package/dist/react-router/index.cjs +2 -2
  28. package/dist/react-router/index.cjs.map +1 -1
  29. package/dist/react-router/index.mjs +2 -2
  30. package/dist/react-router/index.mjs.map +1 -1
  31. package/dist/schema/index.cjs +2 -0
  32. package/dist/schema/index.cjs.map +1 -1
  33. package/dist/schema/index.d.cts +2 -0
  34. package/dist/schema/index.d.ts +2 -0
  35. package/dist/schema/index.mjs +2 -0
  36. package/dist/schema/index.mjs.map +1 -1
  37. package/dist/server/ignore-events.cjs +17 -2
  38. package/dist/server/ignore-events.cjs.map +1 -1
  39. package/dist/server/ignore-events.mjs +17 -2
  40. package/dist/server/ignore-events.mjs.map +1 -1
  41. package/dist/setup/index.cjs +14 -2
  42. package/dist/setup/index.cjs.map +1 -1
  43. package/dist/setup/index.d.cts +7 -5
  44. package/dist/setup/index.d.ts +7 -5
  45. package/dist/setup/index.mjs +13 -3
  46. package/dist/setup/index.mjs.map +1 -1
  47. package/dist/track/gtag.cjs.map +1 -1
  48. package/dist/track/gtag.d.cts +7 -8
  49. package/dist/track/gtag.d.ts +7 -8
  50. package/dist/track/gtag.mjs.map +1 -1
  51. package/dist/track/index.cjs +4 -0
  52. package/dist/track/index.cjs.map +1 -1
  53. package/dist/track/index.mjs +5 -1
  54. package/dist/track/index.mjs.map +1 -1
  55. package/dist/track/types.cjs.map +1 -1
  56. package/dist/track/types.d.cts +2 -0
  57. package/dist/track/types.d.ts +2 -0
  58. package/package.json +3 -3
  59. package/dist/hooks/use-session-analytics.cjs +0 -53
  60. package/dist/hooks/use-session-analytics.cjs.map +0 -1
  61. package/dist/hooks/use-session-analytics.d.cts +0 -3
  62. package/dist/hooks/use-session-analytics.d.ts +0 -3
  63. package/dist/hooks/use-session-analytics.mjs +0 -28
  64. package/dist/hooks/use-session-analytics.mjs.map +0 -1
  65. package/dist/native/analytics.cjs.map +0 -1
  66. package/dist/native/analytics.mjs.map +0 -1
  67. /package/dist/{native/analytics.d.cts → hooks/use-screen-view-analytics.d.cts} +0 -0
  68. /package/dist/{native/analytics.d.ts → hooks/use-screen-view-analytics.d.ts} +0 -0
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/hooks/use-app-session-analytics.ts
21
+ var use_app_session_analytics_exports = {};
22
+ __export(use_app_session_analytics_exports, {
23
+ useAppSessionAnalytics: () => useAppSessionAnalytics
24
+ });
25
+ module.exports = __toCommonJS(use_app_session_analytics_exports);
26
+ var import_react = require("react");
27
+ var import_react_native = require("react-native");
28
+ var import_track = require("../track/index.cjs");
29
+ function useAppSessionAnalytics() {
30
+ const isActive = (0, import_react.useRef)(true);
31
+ const startTime = (0, import_react.useRef)(Date.now());
32
+ const accumulatedTime = (0, import_react.useRef)(0);
33
+ const updateAccumulator = (0, import_react.useCallback)(() => {
34
+ const now = Date.now();
35
+ if (isActive.current) {
36
+ const delta = now - startTime.current;
37
+ if (delta > 0) {
38
+ accumulatedTime.current += delta;
39
+ }
40
+ }
41
+ startTime.current = now;
42
+ }, []);
43
+ const sendUserEngagement = (0, import_react.useCallback)(() => {
44
+ updateAccumulator();
45
+ const engagement_time_msec = accumulatedTime.current;
46
+ accumulatedTime.current = 0;
47
+ if (engagement_time_msec <= 0) return;
48
+ (0, import_track.track)("user_engagement", { engagement_time_msec }, { enableThirdPartyTracking: false });
49
+ }, [updateAccumulator]);
50
+ (0, import_react.useEffect)(() => {
51
+ (0, import_track.track)("session_start", {}, { enableThirdPartyTracking: false });
52
+ const subscription = import_react_native.AppState.addEventListener("change", (state) => {
53
+ updateAccumulator();
54
+ if (state === "active" && !isActive.current) {
55
+ isActive.current = true;
56
+ } else if (state !== "active" && isActive.current) {
57
+ isActive.current = false;
58
+ sendUserEngagement();
59
+ }
60
+ });
61
+ return () => subscription.remove();
62
+ }, []);
63
+ }
64
+ // Annotate the CommonJS export names for ESM import in node:
65
+ 0 && (module.exports = {
66
+ useAppSessionAnalytics
67
+ });
68
+ //# sourceMappingURL=use-app-session-analytics.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/hooks/use-app-session-analytics.ts"],"sourcesContent":["import { useCallback, useEffect, useRef } from 'react';\nimport { AppState } from 'react-native';\nimport { track } from '../track/index';\n\nexport function useAppSessionAnalytics() {\n const isActive = useRef(true);\n const startTime = useRef(Date.now());\n const accumulatedTime = useRef(0);\n\n const updateAccumulator = useCallback(() => {\n const now = Date.now();\n if (isActive.current) {\n const delta = now - startTime.current;\n if (delta > 0) {\n accumulatedTime.current += delta;\n }\n }\n startTime.current = now;\n }, []);\n\n const sendUserEngagement = useCallback(() => {\n updateAccumulator();\n const engagement_time_msec = accumulatedTime.current;\n accumulatedTime.current = 0;\n if (engagement_time_msec <= 0) return;\n track('user_engagement', { engagement_time_msec }, { enableThirdPartyTracking: false });\n }, [updateAccumulator]);\n\n useEffect(() => {\n track('session_start', {}, { enableThirdPartyTracking: false });\n\n const subscription = AppState.addEventListener('change', (state) => {\n updateAccumulator();\n // when returning to the foreground from the background\n if (state === 'active' && !isActive.current) {\n isActive.current = true;\n }\n // when entering the background\n else if (state !== 'active' && isActive.current) {\n isActive.current = false;\n sendUserEngagement();\n }\n });\n\n return () => subscription.remove();\n }, []);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAA+C;AAC/C,0BAAyB;AACzB,mBAAsB;AAEf,SAAS,yBAAyB;AACvC,QAAM,eAAW,qBAAO,IAAI;AAC5B,QAAM,gBAAY,qBAAO,KAAK,IAAI,CAAC;AACnC,QAAM,sBAAkB,qBAAO,CAAC;AAEhC,QAAM,wBAAoB,0BAAY,MAAM;AAC1C,UAAM,MAAM,KAAK,IAAI;AACrB,QAAI,SAAS,SAAS;AACpB,YAAM,QAAQ,MAAM,UAAU;AAC9B,UAAI,QAAQ,GAAG;AACb,wBAAgB,WAAW;AAAA,MAC7B;AAAA,IACF;AACA,cAAU,UAAU;AAAA,EACtB,GAAG,CAAC,CAAC;AAEL,QAAM,yBAAqB,0BAAY,MAAM;AAC3C,sBAAkB;AAClB,UAAM,uBAAuB,gBAAgB;AAC7C,oBAAgB,UAAU;AAC1B,QAAI,wBAAwB,EAAG;AAC/B,4BAAM,mBAAmB,EAAE,qBAAqB,GAAG,EAAE,0BAA0B,MAAM,CAAC;AAAA,EACxF,GAAG,CAAC,iBAAiB,CAAC;AAEtB,8BAAU,MAAM;AACd,4BAAM,iBAAiB,CAAC,GAAG,EAAE,0BAA0B,MAAM,CAAC;AAE9D,UAAM,eAAe,6BAAS,iBAAiB,UAAU,CAAC,UAAU;AAClE,wBAAkB;AAElB,UAAI,UAAU,YAAY,CAAC,SAAS,SAAS;AAC3C,iBAAS,UAAU;AAAA,MACrB,WAES,UAAU,YAAY,SAAS,SAAS;AAC/C,iBAAS,UAAU;AACnB,2BAAmB;AAAA,MACrB;AAAA,IACF,CAAC;AAED,WAAO,MAAM,aAAa,OAAO;AAAA,EACnC,GAAG,CAAC,CAAC;AACP;","names":[]}
@@ -0,0 +1,3 @@
1
+ declare function useAppSessionAnalytics(): void;
2
+
3
+ export { useAppSessionAnalytics };
@@ -0,0 +1,3 @@
1
+ declare function useAppSessionAnalytics(): void;
2
+
3
+ export { useAppSessionAnalytics };
@@ -0,0 +1,43 @@
1
+ // src/hooks/use-app-session-analytics.ts
2
+ import { useCallback, useEffect, useRef } from "react";
3
+ import { AppState } from "react-native";
4
+ import { track } from "../track/index.mjs";
5
+ function useAppSessionAnalytics() {
6
+ const isActive = useRef(true);
7
+ const startTime = useRef(Date.now());
8
+ const accumulatedTime = useRef(0);
9
+ const updateAccumulator = useCallback(() => {
10
+ const now = Date.now();
11
+ if (isActive.current) {
12
+ const delta = now - startTime.current;
13
+ if (delta > 0) {
14
+ accumulatedTime.current += delta;
15
+ }
16
+ }
17
+ startTime.current = now;
18
+ }, []);
19
+ const sendUserEngagement = useCallback(() => {
20
+ updateAccumulator();
21
+ const engagement_time_msec = accumulatedTime.current;
22
+ accumulatedTime.current = 0;
23
+ if (engagement_time_msec <= 0) return;
24
+ track("user_engagement", { engagement_time_msec }, { enableThirdPartyTracking: false });
25
+ }, [updateAccumulator]);
26
+ useEffect(() => {
27
+ track("session_start", {}, { enableThirdPartyTracking: false });
28
+ const subscription = AppState.addEventListener("change", (state) => {
29
+ updateAccumulator();
30
+ if (state === "active" && !isActive.current) {
31
+ isActive.current = true;
32
+ } else if (state !== "active" && isActive.current) {
33
+ isActive.current = false;
34
+ sendUserEngagement();
35
+ }
36
+ });
37
+ return () => subscription.remove();
38
+ }, []);
39
+ }
40
+ export {
41
+ useAppSessionAnalytics
42
+ };
43
+ //# sourceMappingURL=use-app-session-analytics.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/hooks/use-app-session-analytics.ts"],"sourcesContent":["import { useCallback, useEffect, useRef } from 'react';\nimport { AppState } from 'react-native';\nimport { track } from '../track/index';\n\nexport function useAppSessionAnalytics() {\n const isActive = useRef(true);\n const startTime = useRef(Date.now());\n const accumulatedTime = useRef(0);\n\n const updateAccumulator = useCallback(() => {\n const now = Date.now();\n if (isActive.current) {\n const delta = now - startTime.current;\n if (delta > 0) {\n accumulatedTime.current += delta;\n }\n }\n startTime.current = now;\n }, []);\n\n const sendUserEngagement = useCallback(() => {\n updateAccumulator();\n const engagement_time_msec = accumulatedTime.current;\n accumulatedTime.current = 0;\n if (engagement_time_msec <= 0) return;\n track('user_engagement', { engagement_time_msec }, { enableThirdPartyTracking: false });\n }, [updateAccumulator]);\n\n useEffect(() => {\n track('session_start', {}, { enableThirdPartyTracking: false });\n\n const subscription = AppState.addEventListener('change', (state) => {\n updateAccumulator();\n // when returning to the foreground from the background\n if (state === 'active' && !isActive.current) {\n isActive.current = true;\n }\n // when entering the background\n else if (state !== 'active' && isActive.current) {\n isActive.current = false;\n sendUserEngagement();\n }\n });\n\n return () => subscription.remove();\n }, []);\n}\n"],"mappings":";AAAA,SAAS,aAAa,WAAW,cAAc;AAC/C,SAAS,gBAAgB;AACzB,SAAS,aAAa;AAEf,SAAS,yBAAyB;AACvC,QAAM,WAAW,OAAO,IAAI;AAC5B,QAAM,YAAY,OAAO,KAAK,IAAI,CAAC;AACnC,QAAM,kBAAkB,OAAO,CAAC;AAEhC,QAAM,oBAAoB,YAAY,MAAM;AAC1C,UAAM,MAAM,KAAK,IAAI;AACrB,QAAI,SAAS,SAAS;AACpB,YAAM,QAAQ,MAAM,UAAU;AAC9B,UAAI,QAAQ,GAAG;AACb,wBAAgB,WAAW;AAAA,MAC7B;AAAA,IACF;AACA,cAAU,UAAU;AAAA,EACtB,GAAG,CAAC,CAAC;AAEL,QAAM,qBAAqB,YAAY,MAAM;AAC3C,sBAAkB;AAClB,UAAM,uBAAuB,gBAAgB;AAC7C,oBAAgB,UAAU;AAC1B,QAAI,wBAAwB,EAAG;AAC/B,UAAM,mBAAmB,EAAE,qBAAqB,GAAG,EAAE,0BAA0B,MAAM,CAAC;AAAA,EACxF,GAAG,CAAC,iBAAiB,CAAC;AAEtB,YAAU,MAAM;AACd,UAAM,iBAAiB,CAAC,GAAG,EAAE,0BAA0B,MAAM,CAAC;AAE9D,UAAM,eAAe,SAAS,iBAAiB,UAAU,CAAC,UAAU;AAClE,wBAAkB;AAElB,UAAI,UAAU,YAAY,CAAC,SAAS,SAAS;AAC3C,iBAAS,UAAU;AAAA,MACrB,WAES,UAAU,YAAY,SAAS,SAAS;AAC/C,iBAAS,UAAU;AACnB,2BAAmB;AAAA,MACrB;AAAA,IACF,CAAC;AAED,WAAO,MAAM,aAAa,OAAO;AAAA,EACnC,GAAG,CAAC,CAAC;AACP;","names":[]}
@@ -17,17 +17,17 @@ var __copyProps = (to, from, except, desc) => {
17
17
  };
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
 
20
- // src/native/analytics.ts
21
- var analytics_exports = {};
22
- __export(analytics_exports, {
20
+ // src/hooks/use-screen-view-analytics.ts
21
+ var use_screen_view_analytics_exports = {};
22
+ __export(use_screen_view_analytics_exports, {
23
23
  useScreenViewAnalytics: () => useScreenViewAnalytics
24
24
  });
25
- module.exports = __toCommonJS(analytics_exports);
25
+ module.exports = __toCommonJS(use_screen_view_analytics_exports);
26
26
  var import_expo_router = require("expo-router");
27
27
  var import_react = require("react");
28
28
  var import_react_native = require("react-native");
29
- var import_use_previous = require("../hooks/use-previous.cjs");
30
29
  var import_track = require("../track/index.cjs");
30
+ var import_use_previous = require("./use-previous.cjs");
31
31
  function useScreenViewAnalytics() {
32
32
  const pathname = (0, import_expo_router.usePathname)();
33
33
  const prevPathname = (0, import_use_previous.usePrevious)(pathname);
@@ -60,10 +60,9 @@ function useScreenViewAnalytics() {
60
60
  });
61
61
  session.current = { start: performance.now(), total: 0, isActive: true };
62
62
  }, [pathname]);
63
- (0, import_react.useEffect)(() => (0, import_track.track)("app_launch", { pathname }, { enableThirdPartyTracking: false }), []);
64
63
  }
65
64
  // Annotate the CommonJS export names for ESM import in node:
66
65
  0 && (module.exports = {
67
66
  useScreenViewAnalytics
68
67
  });
69
- //# sourceMappingURL=analytics.cjs.map
68
+ //# sourceMappingURL=use-screen-view-analytics.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/hooks/use-screen-view-analytics.ts"],"sourcesContent":["import { usePathname } from 'expo-router';\nimport { useEffect, useRef } from 'react';\nimport { AppState } from 'react-native';\nimport { track } from '../track/index';\nimport { usePrevious } from './use-previous';\n\nexport function useScreenViewAnalytics() {\n const pathname = usePathname();\n const prevPathname = usePrevious(pathname);\n const session = useRef({ start: performance.now(), total: 0, isActive: true });\n\n useEffect(() => {\n const subscription = AppState.addEventListener('change', (state) => {\n // when returning to the foreground from the background\n if (state === 'active' && !session.current.isActive) {\n session.current.start = performance.now();\n session.current.isActive = true;\n }\n // when entering the background\n else if (state !== 'active' && session.current.isActive) {\n session.current.total += (performance.now() - session.current.start) / 1000;\n session.current.isActive = false;\n }\n });\n\n return () => subscription.remove();\n }, []);\n\n // when the screen is switched, the duration of the previous screen is recorded\n useEffect(() => {\n if (!prevPathname) {\n session.current = { start: performance.now(), total: 0, isActive: true };\n }\n\n let duration = session.current.total;\n if (session.current.isActive) {\n duration += (performance.now() - session.current.start) / 1000;\n }\n\n track('screen_view', {\n screen_name: pathname,\n screen_class: pathname,\n previous_screen_class: prevPathname ?? undefined,\n previous_screen_class_duration: prevPathname ? Number(duration.toFixed(2)) : undefined,\n });\n\n // reset session\n session.current = { start: performance.now(), total: 0, isActive: true };\n }, [pathname]);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAA4B;AAC5B,mBAAkC;AAClC,0BAAyB;AACzB,mBAAsB;AACtB,0BAA4B;AAErB,SAAS,yBAAyB;AACvC,QAAM,eAAW,gCAAY;AAC7B,QAAM,mBAAe,iCAAY,QAAQ;AACzC,QAAM,cAAU,qBAAO,EAAE,OAAO,YAAY,IAAI,GAAG,OAAO,GAAG,UAAU,KAAK,CAAC;AAE7E,8BAAU,MAAM;AACd,UAAM,eAAe,6BAAS,iBAAiB,UAAU,CAAC,UAAU;AAElE,UAAI,UAAU,YAAY,CAAC,QAAQ,QAAQ,UAAU;AACnD,gBAAQ,QAAQ,QAAQ,YAAY,IAAI;AACxC,gBAAQ,QAAQ,WAAW;AAAA,MAC7B,WAES,UAAU,YAAY,QAAQ,QAAQ,UAAU;AACvD,gBAAQ,QAAQ,UAAU,YAAY,IAAI,IAAI,QAAQ,QAAQ,SAAS;AACvE,gBAAQ,QAAQ,WAAW;AAAA,MAC7B;AAAA,IACF,CAAC;AAED,WAAO,MAAM,aAAa,OAAO;AAAA,EACnC,GAAG,CAAC,CAAC;AAGL,8BAAU,MAAM;AACd,QAAI,CAAC,cAAc;AACjB,cAAQ,UAAU,EAAE,OAAO,YAAY,IAAI,GAAG,OAAO,GAAG,UAAU,KAAK;AAAA,IACzE;AAEA,QAAI,WAAW,QAAQ,QAAQ;AAC/B,QAAI,QAAQ,QAAQ,UAAU;AAC5B,mBAAa,YAAY,IAAI,IAAI,QAAQ,QAAQ,SAAS;AAAA,IAC5D;AAEA,4BAAM,eAAe;AAAA,MACnB,aAAa;AAAA,MACb,cAAc;AAAA,MACd,uBAAuB,gBAAgB;AAAA,MACvC,gCAAgC,eAAe,OAAO,SAAS,QAAQ,CAAC,CAAC,IAAI;AAAA,IAC/E,CAAC;AAGD,YAAQ,UAAU,EAAE,OAAO,YAAY,IAAI,GAAG,OAAO,GAAG,UAAU,KAAK;AAAA,EACzE,GAAG,CAAC,QAAQ,CAAC;AACf;","names":[]}
@@ -1,9 +1,9 @@
1
- // src/native/analytics.ts
1
+ // src/hooks/use-screen-view-analytics.ts
2
2
  import { usePathname } from "expo-router";
3
3
  import { useEffect, useRef } from "react";
4
4
  import { AppState } from "react-native";
5
- import { usePrevious } from "../hooks/use-previous.mjs";
6
5
  import { track } from "../track/index.mjs";
6
+ import { usePrevious } from "./use-previous.mjs";
7
7
  function useScreenViewAnalytics() {
8
8
  const pathname = usePathname();
9
9
  const prevPathname = usePrevious(pathname);
@@ -36,9 +36,8 @@ function useScreenViewAnalytics() {
36
36
  });
37
37
  session.current = { start: performance.now(), total: 0, isActive: true };
38
38
  }, [pathname]);
39
- useEffect(() => track("app_launch", { pathname }, { enableThirdPartyTracking: false }), []);
40
39
  }
41
40
  export {
42
41
  useScreenViewAnalytics
43
42
  };
44
- //# sourceMappingURL=analytics.mjs.map
43
+ //# sourceMappingURL=use-screen-view-analytics.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/hooks/use-screen-view-analytics.ts"],"sourcesContent":["import { usePathname } from 'expo-router';\nimport { useEffect, useRef } from 'react';\nimport { AppState } from 'react-native';\nimport { track } from '../track/index';\nimport { usePrevious } from './use-previous';\n\nexport function useScreenViewAnalytics() {\n const pathname = usePathname();\n const prevPathname = usePrevious(pathname);\n const session = useRef({ start: performance.now(), total: 0, isActive: true });\n\n useEffect(() => {\n const subscription = AppState.addEventListener('change', (state) => {\n // when returning to the foreground from the background\n if (state === 'active' && !session.current.isActive) {\n session.current.start = performance.now();\n session.current.isActive = true;\n }\n // when entering the background\n else if (state !== 'active' && session.current.isActive) {\n session.current.total += (performance.now() - session.current.start) / 1000;\n session.current.isActive = false;\n }\n });\n\n return () => subscription.remove();\n }, []);\n\n // when the screen is switched, the duration of the previous screen is recorded\n useEffect(() => {\n if (!prevPathname) {\n session.current = { start: performance.now(), total: 0, isActive: true };\n }\n\n let duration = session.current.total;\n if (session.current.isActive) {\n duration += (performance.now() - session.current.start) / 1000;\n }\n\n track('screen_view', {\n screen_name: pathname,\n screen_class: pathname,\n previous_screen_class: prevPathname ?? undefined,\n previous_screen_class_duration: prevPathname ? Number(duration.toFixed(2)) : undefined,\n });\n\n // reset session\n session.current = { start: performance.now(), total: 0, isActive: true };\n }, [pathname]);\n}\n"],"mappings":";AAAA,SAAS,mBAAmB;AAC5B,SAAS,WAAW,cAAc;AAClC,SAAS,gBAAgB;AACzB,SAAS,aAAa;AACtB,SAAS,mBAAmB;AAErB,SAAS,yBAAyB;AACvC,QAAM,WAAW,YAAY;AAC7B,QAAM,eAAe,YAAY,QAAQ;AACzC,QAAM,UAAU,OAAO,EAAE,OAAO,YAAY,IAAI,GAAG,OAAO,GAAG,UAAU,KAAK,CAAC;AAE7E,YAAU,MAAM;AACd,UAAM,eAAe,SAAS,iBAAiB,UAAU,CAAC,UAAU;AAElE,UAAI,UAAU,YAAY,CAAC,QAAQ,QAAQ,UAAU;AACnD,gBAAQ,QAAQ,QAAQ,YAAY,IAAI;AACxC,gBAAQ,QAAQ,WAAW;AAAA,MAC7B,WAES,UAAU,YAAY,QAAQ,QAAQ,UAAU;AACvD,gBAAQ,QAAQ,UAAU,YAAY,IAAI,IAAI,QAAQ,QAAQ,SAAS;AACvE,gBAAQ,QAAQ,WAAW;AAAA,MAC7B;AAAA,IACF,CAAC;AAED,WAAO,MAAM,aAAa,OAAO;AAAA,EACnC,GAAG,CAAC,CAAC;AAGL,YAAU,MAAM;AACd,QAAI,CAAC,cAAc;AACjB,cAAQ,UAAU,EAAE,OAAO,YAAY,IAAI,GAAG,OAAO,GAAG,UAAU,KAAK;AAAA,IACzE;AAEA,QAAI,WAAW,QAAQ,QAAQ;AAC/B,QAAI,QAAQ,QAAQ,UAAU;AAC5B,mBAAa,YAAY,IAAI,IAAI,QAAQ,QAAQ,SAAS;AAAA,IAC5D;AAEA,UAAM,eAAe;AAAA,MACnB,aAAa;AAAA,MACb,cAAc;AAAA,MACd,uBAAuB,gBAAgB;AAAA,MACvC,gCAAgC,eAAe,OAAO,SAAS,QAAQ,CAAC,CAAC,IAAI;AAAA,IAC/E,CAAC;AAGD,YAAQ,UAAU,EAAE,OAAO,YAAY,IAAI,GAAG,OAAO,GAAG,UAAU,KAAK;AAAA,EACzE,GAAG,CAAC,QAAQ,CAAC;AACf;","names":[]}
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/hooks/use-web-session-analytics.ts
21
+ var use_web_session_analytics_exports = {};
22
+ __export(use_web_session_analytics_exports, {
23
+ useWebSessionAnalytics: () => useWebSessionAnalytics
24
+ });
25
+ module.exports = __toCommonJS(use_web_session_analytics_exports);
26
+ var import_react = require("react");
27
+ var import_track = require("../track/index.cjs");
28
+ var scrollGap = 500;
29
+ var scrollThreshold = 0.9;
30
+ function useWebSessionAnalytics(pathname) {
31
+ const isActive = (0, import_react.useRef)(true);
32
+ const isFocused = (0, import_react.useRef)(true);
33
+ const isVisible = (0, import_react.useRef)(true);
34
+ const startTime = (0, import_react.useRef)(Date.now());
35
+ const accumulatedTime = (0, import_react.useRef)(0);
36
+ const hasSendScroll = (0, import_react.useRef)(false);
37
+ const lastScrollTime = (0, import_react.useRef)(0);
38
+ const updateAccumulator = (0, import_react.useCallback)(() => {
39
+ const now = Date.now();
40
+ if (isFocused.current && isVisible.current && isActive.current) {
41
+ const delta = now - startTime.current;
42
+ if (delta > 0) {
43
+ accumulatedTime.current += delta;
44
+ }
45
+ }
46
+ startTime.current = now;
47
+ }, []);
48
+ const sendUserEngagement = (0, import_react.useCallback)(() => {
49
+ updateAccumulator();
50
+ const engagement_time_msec = accumulatedTime.current;
51
+ accumulatedTime.current = 0;
52
+ if (engagement_time_msec <= 0) return;
53
+ (0, import_track.sendBeacon)("user_engagement", { engagement_time_msec });
54
+ }, [updateAccumulator]);
55
+ const sendScroll = (0, import_react.useCallback)(() => {
56
+ updateAccumulator();
57
+ const engagement_time_msec = accumulatedTime.current;
58
+ accumulatedTime.current = 0;
59
+ if (engagement_time_msec <= 0) return;
60
+ (0, import_track.track)("scroll", { engagement_time_msec }, { enableThirdPartyTracking: false });
61
+ }, [updateAccumulator]);
62
+ (0, import_react.useEffect)(() => {
63
+ hasSendScroll.current = false;
64
+ }, [pathname]);
65
+ (0, import_react.useEffect)(() => {
66
+ isFocused.current = typeof document !== "undefined" && document.hasFocus();
67
+ isVisible.current = typeof document !== "undefined" && document.visibilityState === "visible";
68
+ startTime.current = Date.now();
69
+ const onFocus = () => {
70
+ updateAccumulator();
71
+ isFocused.current = true;
72
+ };
73
+ const onBlur = () => {
74
+ updateAccumulator();
75
+ isFocused.current = false;
76
+ };
77
+ const onPageShow = () => {
78
+ updateAccumulator();
79
+ isActive.current = true;
80
+ };
81
+ const onPageHide = () => {
82
+ updateAccumulator();
83
+ isActive.current = false;
84
+ sendUserEngagement();
85
+ };
86
+ const onVisibilityChange = () => {
87
+ updateAccumulator();
88
+ if (document.visibilityState === "visible") {
89
+ isVisible.current = true;
90
+ } else {
91
+ isVisible.current = false;
92
+ sendUserEngagement();
93
+ }
94
+ };
95
+ const onScroll = () => {
96
+ const now = Date.now();
97
+ if (now - lastScrollTime.current < scrollGap) return;
98
+ lastScrollTime.current = now;
99
+ if (hasSendScroll.current) return;
100
+ const scrollTop = window.scrollY || document.documentElement.scrollTop;
101
+ const windowHeight = window.innerHeight;
102
+ const docHeight = document.documentElement.scrollHeight;
103
+ if (docHeight === 0) return;
104
+ const scrollPercent = (scrollTop + windowHeight) / docHeight;
105
+ if (scrollPercent < scrollThreshold) return;
106
+ hasSendScroll.current = true;
107
+ sendScroll();
108
+ };
109
+ (0, import_track.track)("session_start", {}, { enableThirdPartyTracking: false });
110
+ window.addEventListener("focus", onFocus, { passive: true });
111
+ window.addEventListener("blur", onBlur, { passive: true });
112
+ window.addEventListener("pageshow", onPageShow, { passive: true });
113
+ window.addEventListener("pagehide", onPageHide, { passive: true });
114
+ window.addEventListener("scroll", onScroll, { passive: true });
115
+ document.addEventListener("visibilitychange", onVisibilityChange, { passive: true });
116
+ return () => {
117
+ window.removeEventListener("focus", onFocus);
118
+ window.removeEventListener("blur", onBlur);
119
+ window.removeEventListener("pageshow", onPageShow);
120
+ window.removeEventListener("pagehide", onPageHide);
121
+ window.removeEventListener("scroll", onScroll);
122
+ document.removeEventListener("visibilitychange", onVisibilityChange);
123
+ };
124
+ }, []);
125
+ }
126
+ // Annotate the CommonJS export names for ESM import in node:
127
+ 0 && (module.exports = {
128
+ useWebSessionAnalytics
129
+ });
130
+ //# sourceMappingURL=use-web-session-analytics.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/hooks/use-web-session-analytics.ts"],"sourcesContent":["/**\n * reference:\n * - Discover how long someone spends engaged on your website or app in Google Analytics](https://support.google.com/analytics/answer/11109416?hl=en)\n */\nimport { useCallback, useEffect, useRef } from 'react';\nimport { sendBeacon, track } from '../track/index';\n\nconst scrollGap = 500;\nconst scrollThreshold = 0.9;\n\n/**\n * 1. send session_start event when the page is loaded\n * 2. send scroll event when the user scrolls more than 90% of the page\n * 3. send user_engagement event when the page is hidden or the user is not focused\n */\nexport function useWebSessionAnalytics(pathname: string) {\n const isActive = useRef(true);\n const isFocused = useRef(true);\n const isVisible = useRef(true);\n\n const startTime = useRef(Date.now());\n const accumulatedTime = useRef(0);\n\n const hasSendScroll = useRef(false);\n const lastScrollTime = useRef(0);\n\n const updateAccumulator = useCallback(() => {\n const now = Date.now();\n if (isFocused.current && isVisible.current && isActive.current) {\n const delta = now - startTime.current;\n if (delta > 0) {\n accumulatedTime.current += delta;\n }\n }\n startTime.current = now;\n }, []);\n\n const sendUserEngagement = useCallback(() => {\n updateAccumulator();\n const engagement_time_msec = accumulatedTime.current;\n accumulatedTime.current = 0;\n if (engagement_time_msec <= 0) return;\n sendBeacon('user_engagement', { engagement_time_msec });\n }, [updateAccumulator]);\n\n const sendScroll = useCallback(() => {\n updateAccumulator();\n const engagement_time_msec = accumulatedTime.current;\n accumulatedTime.current = 0;\n if (engagement_time_msec <= 0) return;\n track('scroll', { engagement_time_msec }, { enableThirdPartyTracking: false });\n }, [updateAccumulator]);\n\n // reset scroll state when the pathname changes, so we can send scroll when the user navigates to\n // a new page\n useEffect(() => {\n hasSendScroll.current = false;\n }, [pathname]);\n\n useEffect(() => {\n isFocused.current = typeof document !== 'undefined' && document.hasFocus();\n isVisible.current = typeof document !== 'undefined' && document.visibilityState === 'visible';\n startTime.current = Date.now();\n\n const onFocus = () => {\n updateAccumulator();\n isFocused.current = true;\n };\n\n const onBlur = () => {\n updateAccumulator();\n isFocused.current = false;\n };\n\n const onPageShow = () => {\n updateAccumulator();\n isActive.current = true;\n };\n\n const onPageHide = () => {\n updateAccumulator();\n isActive.current = false;\n sendUserEngagement();\n };\n\n const onVisibilityChange = () => {\n updateAccumulator();\n if (document.visibilityState === 'visible') {\n isVisible.current = true;\n } else {\n isVisible.current = false;\n sendUserEngagement();\n }\n };\n\n const onScroll = () => {\n const now = Date.now();\n if (now - lastScrollTime.current < scrollGap) return;\n lastScrollTime.current = now;\n if (hasSendScroll.current) return;\n\n // only send scroll when the user has scrolled more than 90% of the page\n const scrollTop = window.scrollY || document.documentElement.scrollTop;\n const windowHeight = window.innerHeight;\n const docHeight = document.documentElement.scrollHeight;\n if (docHeight === 0) return;\n const scrollPercent = (scrollTop + windowHeight) / docHeight;\n if (scrollPercent < scrollThreshold) return;\n hasSendScroll.current = true;\n\n sendScroll();\n };\n\n track('session_start', {}, { enableThirdPartyTracking: false });\n\n window.addEventListener('focus', onFocus, { passive: true });\n window.addEventListener('blur', onBlur, { passive: true });\n window.addEventListener('pageshow', onPageShow, { passive: true });\n window.addEventListener('pagehide', onPageHide, { passive: true });\n window.addEventListener('scroll', onScroll, { passive: true });\n document.addEventListener('visibilitychange', onVisibilityChange, { passive: true });\n\n return () => {\n window.removeEventListener('focus', onFocus);\n window.removeEventListener('blur', onBlur);\n window.removeEventListener('pageshow', onPageShow);\n window.removeEventListener('pagehide', onPageHide);\n window.removeEventListener('scroll', onScroll);\n document.removeEventListener('visibilitychange', onVisibilityChange);\n };\n }, []);\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * 1. send session_start event when the page is loaded
3
+ * 2. send scroll event when the user scrolls more than 90% of the page
4
+ * 3. send user_engagement event when the page is hidden or the user is not focused
5
+ */
6
+ declare function useWebSessionAnalytics(pathname: string): void;
7
+
8
+ export { useWebSessionAnalytics };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * 1. send session_start event when the page is loaded
3
+ * 2. send scroll event when the user scrolls more than 90% of the page
4
+ * 3. send user_engagement event when the page is hidden or the user is not focused
5
+ */
6
+ declare function useWebSessionAnalytics(pathname: string): void;
7
+
8
+ export { useWebSessionAnalytics };
@@ -0,0 +1,105 @@
1
+ // src/hooks/use-web-session-analytics.ts
2
+ import { useCallback, useEffect, useRef } from "react";
3
+ import { sendBeacon, track } from "../track/index.mjs";
4
+ var scrollGap = 500;
5
+ var scrollThreshold = 0.9;
6
+ function useWebSessionAnalytics(pathname) {
7
+ const isActive = useRef(true);
8
+ const isFocused = useRef(true);
9
+ const isVisible = useRef(true);
10
+ const startTime = useRef(Date.now());
11
+ const accumulatedTime = useRef(0);
12
+ const hasSendScroll = useRef(false);
13
+ const lastScrollTime = useRef(0);
14
+ const updateAccumulator = useCallback(() => {
15
+ const now = Date.now();
16
+ if (isFocused.current && isVisible.current && isActive.current) {
17
+ const delta = now - startTime.current;
18
+ if (delta > 0) {
19
+ accumulatedTime.current += delta;
20
+ }
21
+ }
22
+ startTime.current = now;
23
+ }, []);
24
+ const sendUserEngagement = useCallback(() => {
25
+ updateAccumulator();
26
+ const engagement_time_msec = accumulatedTime.current;
27
+ accumulatedTime.current = 0;
28
+ if (engagement_time_msec <= 0) return;
29
+ sendBeacon("user_engagement", { engagement_time_msec });
30
+ }, [updateAccumulator]);
31
+ const sendScroll = useCallback(() => {
32
+ updateAccumulator();
33
+ const engagement_time_msec = accumulatedTime.current;
34
+ accumulatedTime.current = 0;
35
+ if (engagement_time_msec <= 0) return;
36
+ track("scroll", { engagement_time_msec }, { enableThirdPartyTracking: false });
37
+ }, [updateAccumulator]);
38
+ useEffect(() => {
39
+ hasSendScroll.current = false;
40
+ }, [pathname]);
41
+ useEffect(() => {
42
+ isFocused.current = typeof document !== "undefined" && document.hasFocus();
43
+ isVisible.current = typeof document !== "undefined" && document.visibilityState === "visible";
44
+ startTime.current = Date.now();
45
+ const onFocus = () => {
46
+ updateAccumulator();
47
+ isFocused.current = true;
48
+ };
49
+ const onBlur = () => {
50
+ updateAccumulator();
51
+ isFocused.current = false;
52
+ };
53
+ const onPageShow = () => {
54
+ updateAccumulator();
55
+ isActive.current = true;
56
+ };
57
+ const onPageHide = () => {
58
+ updateAccumulator();
59
+ isActive.current = false;
60
+ sendUserEngagement();
61
+ };
62
+ const onVisibilityChange = () => {
63
+ updateAccumulator();
64
+ if (document.visibilityState === "visible") {
65
+ isVisible.current = true;
66
+ } else {
67
+ isVisible.current = false;
68
+ sendUserEngagement();
69
+ }
70
+ };
71
+ const onScroll = () => {
72
+ const now = Date.now();
73
+ if (now - lastScrollTime.current < scrollGap) return;
74
+ lastScrollTime.current = now;
75
+ if (hasSendScroll.current) return;
76
+ const scrollTop = window.scrollY || document.documentElement.scrollTop;
77
+ const windowHeight = window.innerHeight;
78
+ const docHeight = document.documentElement.scrollHeight;
79
+ if (docHeight === 0) return;
80
+ const scrollPercent = (scrollTop + windowHeight) / docHeight;
81
+ if (scrollPercent < scrollThreshold) return;
82
+ hasSendScroll.current = true;
83
+ sendScroll();
84
+ };
85
+ track("session_start", {}, { enableThirdPartyTracking: false });
86
+ window.addEventListener("focus", onFocus, { passive: true });
87
+ window.addEventListener("blur", onBlur, { passive: true });
88
+ window.addEventListener("pageshow", onPageShow, { passive: true });
89
+ window.addEventListener("pagehide", onPageHide, { passive: true });
90
+ window.addEventListener("scroll", onScroll, { passive: true });
91
+ document.addEventListener("visibilitychange", onVisibilityChange, { passive: true });
92
+ return () => {
93
+ window.removeEventListener("focus", onFocus);
94
+ window.removeEventListener("blur", onBlur);
95
+ window.removeEventListener("pageshow", onPageShow);
96
+ window.removeEventListener("pagehide", onPageHide);
97
+ window.removeEventListener("scroll", onScroll);
98
+ document.removeEventListener("visibilitychange", onVisibilityChange);
99
+ };
100
+ }, []);
101
+ }
102
+ export {
103
+ useWebSessionAnalytics
104
+ };
105
+ //# sourceMappingURL=use-web-session-analytics.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/hooks/use-web-session-analytics.ts"],"sourcesContent":["/**\n * reference:\n * - Discover how long someone spends engaged on your website or app in Google Analytics](https://support.google.com/analytics/answer/11109416?hl=en)\n */\nimport { useCallback, useEffect, useRef } from 'react';\nimport { sendBeacon, track } from '../track/index';\n\nconst scrollGap = 500;\nconst scrollThreshold = 0.9;\n\n/**\n * 1. send session_start event when the page is loaded\n * 2. send scroll event when the user scrolls more than 90% of the page\n * 3. send user_engagement event when the page is hidden or the user is not focused\n */\nexport function useWebSessionAnalytics(pathname: string) {\n const isActive = useRef(true);\n const isFocused = useRef(true);\n const isVisible = useRef(true);\n\n const startTime = useRef(Date.now());\n const accumulatedTime = useRef(0);\n\n const hasSendScroll = useRef(false);\n const lastScrollTime = useRef(0);\n\n const updateAccumulator = useCallback(() => {\n const now = Date.now();\n if (isFocused.current && isVisible.current && isActive.current) {\n const delta = now - startTime.current;\n if (delta > 0) {\n accumulatedTime.current += delta;\n }\n }\n startTime.current = now;\n }, []);\n\n const sendUserEngagement = useCallback(() => {\n updateAccumulator();\n const engagement_time_msec = accumulatedTime.current;\n accumulatedTime.current = 0;\n if (engagement_time_msec <= 0) return;\n sendBeacon('user_engagement', { engagement_time_msec });\n }, [updateAccumulator]);\n\n const sendScroll = useCallback(() => {\n updateAccumulator();\n const engagement_time_msec = accumulatedTime.current;\n accumulatedTime.current = 0;\n if (engagement_time_msec <= 0) return;\n track('scroll', { engagement_time_msec }, { enableThirdPartyTracking: false });\n }, [updateAccumulator]);\n\n // reset scroll state when the pathname changes, so we can send scroll when the user navigates to\n // a new page\n useEffect(() => {\n hasSendScroll.current = false;\n }, [pathname]);\n\n useEffect(() => {\n isFocused.current = typeof document !== 'undefined' && document.hasFocus();\n isVisible.current = typeof document !== 'undefined' && document.visibilityState === 'visible';\n startTime.current = Date.now();\n\n const onFocus = () => {\n updateAccumulator();\n isFocused.current = true;\n };\n\n const onBlur = () => {\n updateAccumulator();\n isFocused.current = false;\n };\n\n const onPageShow = () => {\n updateAccumulator();\n isActive.current = true;\n };\n\n const onPageHide = () => {\n updateAccumulator();\n isActive.current = false;\n sendUserEngagement();\n };\n\n const onVisibilityChange = () => {\n updateAccumulator();\n if (document.visibilityState === 'visible') {\n isVisible.current = true;\n } else {\n isVisible.current = false;\n sendUserEngagement();\n }\n };\n\n const onScroll = () => {\n const now = Date.now();\n if (now - lastScrollTime.current < scrollGap) return;\n lastScrollTime.current = now;\n if (hasSendScroll.current) return;\n\n // only send scroll when the user has scrolled more than 90% of the page\n const scrollTop = window.scrollY || document.documentElement.scrollTop;\n const windowHeight = window.innerHeight;\n const docHeight = document.documentElement.scrollHeight;\n if (docHeight === 0) return;\n const scrollPercent = (scrollTop + windowHeight) / docHeight;\n if (scrollPercent < scrollThreshold) return;\n hasSendScroll.current = true;\n\n sendScroll();\n };\n\n track('session_start', {}, { enableThirdPartyTracking: false });\n\n window.addEventListener('focus', onFocus, { passive: true });\n window.addEventListener('blur', onBlur, { passive: true });\n window.addEventListener('pageshow', onPageShow, { passive: true });\n window.addEventListener('pagehide', onPageHide, { passive: true });\n window.addEventListener('scroll', onScroll, { passive: true });\n document.addEventListener('visibilitychange', onVisibilityChange, { passive: true });\n\n return () => {\n window.removeEventListener('focus', onFocus);\n window.removeEventListener('blur', onBlur);\n window.removeEventListener('pageshow', onPageShow);\n window.removeEventListener('pagehide', onPageHide);\n window.removeEventListener('scroll', onScroll);\n document.removeEventListener('visibilitychange', onVisibilityChange);\n };\n }, []);\n}\n"],"mappings":"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","names":[]}
@@ -26,11 +26,13 @@ __export(native_exports, {
26
26
  getProbabilisticFingerprint: () => import_fingerprint.getProbabilisticFingerprint,
27
27
  getTags: () => import_setup.getTags,
28
28
  storage: () => import_setup.storage,
29
- useScreenViewAnalytics: () => import_analytics.useScreenViewAnalytics
29
+ useAppSessionAnalytics: () => import_use_app_session_analytics.useAppSessionAnalytics,
30
+ useScreenViewAnalytics: () => import_use_screen_view_analytics.useScreenViewAnalytics
30
31
  });
31
32
  module.exports = __toCommonJS(native_exports);
32
33
  var import_setup = require("./setup.cjs");
33
- var import_analytics = require("./analytics.cjs");
34
+ var import_use_screen_view_analytics = require("../hooks/use-screen-view-analytics.cjs");
35
+ var import_use_app_session_analytics = require("../hooks/use-app-session-analytics.cjs");
34
36
  var import_fingerprint = require("./fingerprint.cjs");
35
37
  // Annotate the CommonJS export names for ESM import in node:
36
38
  0 && (module.exports = {
@@ -40,6 +42,7 @@ var import_fingerprint = require("./fingerprint.cjs");
40
42
  getProbabilisticFingerprint,
41
43
  getTags,
42
44
  storage,
45
+ useAppSessionAnalytics,
43
46
  useScreenViewAnalytics
44
47
  });
45
48
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/native/index.ts"],"sourcesContent":["export { getDeviceId, getDeviceType, getTags, storage } from './setup';\nexport { useScreenViewAnalytics } from './analytics';\nexport { getDeterministicFingerprint, getProbabilisticFingerprint } from './fingerprint';\n\nexport type { DeterministicFingerprint, ProbabilisticFingerprint } from './fingerprint';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAA6D;AAC7D,uBAAuC;AACvC,yBAAyE;","names":[]}
1
+ {"version":3,"sources":["../../src/native/index.ts"],"sourcesContent":["export { getDeviceId, getDeviceType, getTags, storage } from './setup';\nexport { useScreenViewAnalytics } from '../hooks/use-screen-view-analytics';\nexport { useAppSessionAnalytics } from '../hooks/use-app-session-analytics';\nexport { getDeterministicFingerprint, getProbabilisticFingerprint } from './fingerprint';\n\nexport type { DeterministicFingerprint, ProbabilisticFingerprint } from './fingerprint';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAA6D;AAC7D,uCAAuC;AACvC,uCAAuC;AACvC,yBAAyE;","names":[]}
@@ -1,5 +1,6 @@
1
1
  export { getDeviceId, getDeviceType, getTags, storage } from './setup.cjs';
2
- export { useScreenViewAnalytics } from './analytics.cjs';
2
+ export { useScreenViewAnalytics } from '../hooks/use-screen-view-analytics.cjs';
3
+ export { useAppSessionAnalytics } from '../hooks/use-app-session-analytics.cjs';
3
4
  export { DeterministicFingerprint, ProbabilisticFingerprint, getDeterministicFingerprint, getProbabilisticFingerprint } from './fingerprint.cjs';
4
5
  import '../setup/index.cjs';
5
6
  import '../track/types.cjs';
@@ -1,5 +1,6 @@
1
1
  export { getDeviceId, getDeviceType, getTags, storage } from './setup.js';
2
- export { useScreenViewAnalytics } from './analytics.js';
2
+ export { useScreenViewAnalytics } from '../hooks/use-screen-view-analytics.js';
3
+ export { useAppSessionAnalytics } from '../hooks/use-app-session-analytics.js';
3
4
  export { DeterministicFingerprint, ProbabilisticFingerprint, getDeterministicFingerprint, getProbabilisticFingerprint } from './fingerprint.js';
4
5
  import '../setup/index.js';
5
6
  import '../track/types.js';
@@ -1,6 +1,7 @@
1
1
  // src/native/index.ts
2
2
  import { getDeviceId, getDeviceType, getTags, storage } from "./setup.mjs";
3
- import { useScreenViewAnalytics } from "./analytics.mjs";
3
+ import { useScreenViewAnalytics } from "../hooks/use-screen-view-analytics.mjs";
4
+ import { useAppSessionAnalytics } from "../hooks/use-app-session-analytics.mjs";
4
5
  import { getDeterministicFingerprint, getProbabilisticFingerprint } from "./fingerprint.mjs";
5
6
  export {
6
7
  getDeterministicFingerprint,
@@ -9,6 +10,7 @@ export {
9
10
  getProbabilisticFingerprint,
10
11
  getTags,
11
12
  storage,
13
+ useAppSessionAnalytics,
12
14
  useScreenViewAnalytics
13
15
  };
14
16
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/native/index.ts"],"sourcesContent":["export { getDeviceId, getDeviceType, getTags, storage } from './setup';\nexport { useScreenViewAnalytics } from './analytics';\nexport { getDeterministicFingerprint, getProbabilisticFingerprint } from './fingerprint';\n\nexport type { DeterministicFingerprint, ProbabilisticFingerprint } from './fingerprint';\n"],"mappings":";AAAA,SAAS,aAAa,eAAe,SAAS,eAAe;AAC7D,SAAS,8BAA8B;AACvC,SAAS,6BAA6B,mCAAmC;","names":[]}
1
+ {"version":3,"sources":["../../src/native/index.ts"],"sourcesContent":["export { getDeviceId, getDeviceType, getTags, storage } from './setup';\nexport { useScreenViewAnalytics } from '../hooks/use-screen-view-analytics';\nexport { useAppSessionAnalytics } from '../hooks/use-app-session-analytics';\nexport { getDeterministicFingerprint, getProbabilisticFingerprint } from './fingerprint';\n\nexport type { DeterministicFingerprint, ProbabilisticFingerprint } from './fingerprint';\n"],"mappings":";AAAA,SAAS,aAAa,eAAe,SAAS,eAAe;AAC7D,SAAS,8BAA8B;AACvC,SAAS,8BAA8B;AACvC,SAAS,6BAA6B,mCAAmC;","names":[]}
@@ -39,7 +39,7 @@ var import_script = __toESM(require("next/script"), 1);
39
39
  var import_web_vitals = require("next/web-vitals");
40
40
  var import_use_click_id_persistence = require("../hooks/use-click-id-persistence.cjs");
41
41
  var import_use_page_view_analytics = require("../hooks/use-page-view-analytics.cjs");
42
- var import_use_session_analytics = require("../hooks/use-session-analytics.cjs");
42
+ var import_use_web_session_analytics = require("../hooks/use-web-session-analytics.cjs");
43
43
  var import_track = require("../track/index.cjs");
44
44
  var import_jsx_runtime = require("react/jsx-runtime");
45
45
  function Analytics({
@@ -53,10 +53,10 @@ function Analytics({
53
53
  facebookAppId,
54
54
  reportWebVitals = true
55
55
  }) {
56
- (0, import_use_session_analytics.useSessionAnalytics)();
57
56
  (0, import_use_click_id_persistence.useClickIdPersistence)();
58
57
  const pathname = (0, import_navigation.usePathname)();
59
58
  (0, import_use_page_view_analytics.usePageViewAnalytics)(pathname);
59
+ (0, import_use_web_session_analytics.useWebSessionAnalytics)(pathname);
60
60
  (0, import_web_vitals.useReportWebVitals)((metric) => {
61
61
  if (!reportWebVitals) return;
62
62
  const properties = {