@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.
- package/dist/hooks/use-app-session-analytics.cjs +68 -0
- package/dist/hooks/use-app-session-analytics.cjs.map +1 -0
- package/dist/hooks/use-app-session-analytics.d.cts +3 -0
- package/dist/hooks/use-app-session-analytics.d.ts +3 -0
- package/dist/hooks/use-app-session-analytics.mjs +43 -0
- package/dist/hooks/use-app-session-analytics.mjs.map +1 -0
- package/dist/{native/analytics.cjs → hooks/use-screen-view-analytics.cjs} +6 -7
- package/dist/hooks/use-screen-view-analytics.cjs.map +1 -0
- package/dist/{native/analytics.mjs → hooks/use-screen-view-analytics.mjs} +3 -4
- package/dist/hooks/use-screen-view-analytics.mjs.map +1 -0
- package/dist/hooks/use-web-session-analytics.cjs +130 -0
- package/dist/hooks/use-web-session-analytics.cjs.map +1 -0
- package/dist/hooks/use-web-session-analytics.d.cts +8 -0
- package/dist/hooks/use-web-session-analytics.d.ts +8 -0
- package/dist/hooks/use-web-session-analytics.mjs +105 -0
- package/dist/hooks/use-web-session-analytics.mjs.map +1 -0
- package/dist/native/index.cjs +5 -2
- package/dist/native/index.cjs.map +1 -1
- package/dist/native/index.d.cts +2 -1
- package/dist/native/index.d.ts +2 -1
- package/dist/native/index.mjs +3 -1
- package/dist/native/index.mjs.map +1 -1
- package/dist/next/index.cjs +2 -2
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.mjs +2 -2
- package/dist/next/index.mjs.map +1 -1
- package/dist/react-router/index.cjs +2 -2
- package/dist/react-router/index.cjs.map +1 -1
- package/dist/react-router/index.mjs +2 -2
- package/dist/react-router/index.mjs.map +1 -1
- package/dist/schema/index.cjs +2 -0
- package/dist/schema/index.cjs.map +1 -1
- package/dist/schema/index.d.cts +2 -0
- package/dist/schema/index.d.ts +2 -0
- package/dist/schema/index.mjs +2 -0
- package/dist/schema/index.mjs.map +1 -1
- package/dist/server/ignore-events.cjs +17 -2
- package/dist/server/ignore-events.cjs.map +1 -1
- package/dist/server/ignore-events.mjs +17 -2
- package/dist/server/ignore-events.mjs.map +1 -1
- package/dist/setup/index.cjs +14 -2
- package/dist/setup/index.cjs.map +1 -1
- package/dist/setup/index.d.cts +7 -5
- package/dist/setup/index.d.ts +7 -5
- package/dist/setup/index.mjs +13 -3
- package/dist/setup/index.mjs.map +1 -1
- package/dist/track/gtag.cjs.map +1 -1
- package/dist/track/gtag.d.cts +7 -8
- package/dist/track/gtag.d.ts +7 -8
- package/dist/track/gtag.mjs.map +1 -1
- package/dist/track/index.cjs +4 -0
- package/dist/track/index.cjs.map +1 -1
- package/dist/track/index.mjs +5 -1
- package/dist/track/index.mjs.map +1 -1
- package/dist/track/types.cjs.map +1 -1
- package/dist/track/types.d.cts +2 -0
- package/dist/track/types.d.ts +2 -0
- package/package.json +3 -3
- package/dist/hooks/use-session-analytics.cjs +0 -53
- package/dist/hooks/use-session-analytics.cjs.map +0 -1
- package/dist/hooks/use-session-analytics.d.cts +0 -3
- package/dist/hooks/use-session-analytics.d.ts +0 -3
- package/dist/hooks/use-session-analytics.mjs +0 -28
- package/dist/hooks/use-session-analytics.mjs.map +0 -1
- package/dist/native/analytics.cjs.map +0 -1
- package/dist/native/analytics.mjs.map +0 -1
- /package/dist/{native/analytics.d.cts → hooks/use-screen-view-analytics.d.cts} +0 -0
- /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,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/
|
|
21
|
-
var
|
|
22
|
-
__export(
|
|
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(
|
|
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/
|
|
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":[]}
|
package/dist/native/index.cjs
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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 '
|
|
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":[]}
|
package/dist/native/index.d.cts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { getDeviceId, getDeviceType, getTags, storage } from './setup.cjs';
|
|
2
|
-
export { useScreenViewAnalytics } from '
|
|
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';
|
package/dist/native/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { getDeviceId, getDeviceType, getTags, storage } from './setup.js';
|
|
2
|
-
export { useScreenViewAnalytics } from '
|
|
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';
|
package/dist/native/index.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// src/native/index.ts
|
|
2
2
|
import { getDeviceId, getDeviceType, getTags, storage } from "./setup.mjs";
|
|
3
|
-
import { useScreenViewAnalytics } from "
|
|
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 '
|
|
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":[]}
|
package/dist/next/index.cjs
CHANGED
|
@@ -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
|
|
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 = {
|