@phygitallabs/tapquest-core 6.7.12 → 6.7.13
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/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +5 -26
- package/dist/index.d.ts +5 -26
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/modules/data-tracking/{GtagConsentSync.tsx → DataTrackingConsentSync.tsx} +14 -43
- package/src/modules/data-tracking/hooks/index.ts +82 -11
- package/src/modules/data-tracking/index.ts +1 -2
- package/src/providers/ServicesProvider.tsx +2 -4
- package/src/modules/data-tracking/GtagGtmScriptToggle.tsx +0 -73
package/package.json
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { usePhygitalConsent } from "@phygitallabs/phygital-consent";
|
|
4
|
-
import posthog from "posthog-js";
|
|
5
4
|
import { useEffect } from "react";
|
|
6
|
-
import {
|
|
5
|
+
import { useDataTracking } from "./hooks";
|
|
7
6
|
|
|
8
7
|
type GtagConsent = (
|
|
9
8
|
command: "consent",
|
|
@@ -19,27 +18,6 @@ const GTAG_CONSENT_DENIED = {
|
|
|
19
18
|
ad_personalization: "denied",
|
|
20
19
|
} as const;
|
|
21
20
|
|
|
22
|
-
// const GTAG_CONSENT_GRANTED_ANALYTICS = {
|
|
23
|
-
// analytics_storage: "granted",
|
|
24
|
-
// } as const;
|
|
25
|
-
|
|
26
|
-
const GTAG_CONSENT_GRANTED_ADS = {
|
|
27
|
-
ad_storage: "granted",
|
|
28
|
-
ad_user_data: "granted",
|
|
29
|
-
ad_personalization: "granted",
|
|
30
|
-
} as const;
|
|
31
|
-
|
|
32
|
-
function pushConsentUpdate(params: Record<string, string>) {
|
|
33
|
-
if (typeof window === "undefined") return;
|
|
34
|
-
const w = window as Window & { gtag?: GtagConsent; dataLayer?: unknown[] };
|
|
35
|
-
if (typeof w.gtag === "function") {
|
|
36
|
-
w.gtag("consent", "update", params);
|
|
37
|
-
} else {
|
|
38
|
-
w.dataLayer = w.dataLayer || [];
|
|
39
|
-
(w.dataLayer as unknown[]).push(["consent", "update", params]);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
21
|
/**
|
|
44
22
|
* Syncs cookie consent to GA, GTM, PostHog, OpenReplay, and Google AdSense.
|
|
45
23
|
* - On mount: sets gtag/GTM consent default to denied.
|
|
@@ -48,14 +26,14 @@ function pushConsentUpdate(params: Record<string, string>) {
|
|
|
48
26
|
* Must be used under PhygitalConsentProvider; for OpenReplay also under SessionReplayProvider.
|
|
49
27
|
* Renders nothing.
|
|
50
28
|
*/
|
|
51
|
-
export function
|
|
29
|
+
export function DataTrackingConsentSync() {
|
|
52
30
|
const consent = usePhygitalConsent() as {
|
|
53
31
|
isAnalyticsAllowed?: boolean;
|
|
54
32
|
isAdvertisingAllowed?: boolean;
|
|
55
33
|
};
|
|
56
34
|
const isAnalyticsAllowed = consent.isAnalyticsAllowed ?? false;
|
|
57
35
|
const isAdvertisingAllowed = consent.isAdvertisingAllowed ?? false;
|
|
58
|
-
const {
|
|
36
|
+
const { togglePostHog, toggleOpenReplay, toggleGoogleAdSense, pauseGoogleAdSense } = useDataTracking();
|
|
59
37
|
|
|
60
38
|
// Default: deny all consent (run once on mount)
|
|
61
39
|
useEffect(() => {
|
|
@@ -69,33 +47,26 @@ export function GtagConsentSync() {
|
|
|
69
47
|
}
|
|
70
48
|
}, []);
|
|
71
49
|
|
|
72
|
-
//
|
|
73
|
-
// useEffect(() => {
|
|
74
|
-
// if (!isAnalyticsAllowed) return;
|
|
75
|
-
// pushConsentUpdate({ ...GTAG_CONSENT_GRANTED_ANALYTICS });
|
|
76
|
-
// }, [isAnalyticsAllowed]);
|
|
77
|
-
|
|
78
|
-
// Google AdSense: grant ad consent when user accepted advertising
|
|
50
|
+
// Google AdSense: grant ad consent when allowed, revoke (pause) when denied
|
|
79
51
|
useEffect(() => {
|
|
80
|
-
if (
|
|
81
|
-
|
|
82
|
-
|
|
52
|
+
if (isAdvertisingAllowed) {
|
|
53
|
+
toggleGoogleAdSense();
|
|
54
|
+
} else {
|
|
55
|
+
pauseGoogleAdSense();
|
|
56
|
+
}
|
|
57
|
+
}, [isAdvertisingAllowed, toggleGoogleAdSense, pauseGoogleAdSense]);
|
|
83
58
|
|
|
84
59
|
// PostHog: opt in when analytics allowed
|
|
85
60
|
useEffect(() => {
|
|
86
61
|
if (!isAnalyticsAllowed) return;
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
ph.opt_in_capturing();
|
|
90
|
-
}
|
|
91
|
-
}, [isAnalyticsAllowed]);
|
|
62
|
+
togglePostHog();
|
|
63
|
+
}, [isAnalyticsAllowed, togglePostHog]);
|
|
92
64
|
|
|
93
65
|
// OpenReplay: init and start when analytics allowed
|
|
94
66
|
useEffect(() => {
|
|
95
67
|
if (!isAnalyticsAllowed) return;
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}, [isAnalyticsAllowed, initTracker, startTracking]);
|
|
68
|
+
toggleOpenReplay();
|
|
69
|
+
}, [isAnalyticsAllowed, toggleOpenReplay]);
|
|
99
70
|
|
|
100
71
|
return null;
|
|
101
72
|
}
|
|
@@ -21,6 +21,11 @@ const GTAG_ADS_GRANTED = {
|
|
|
21
21
|
ad_user_data: "granted",
|
|
22
22
|
ad_personalization: "granted",
|
|
23
23
|
} as const;
|
|
24
|
+
const GTAG_ADS_DENIED = {
|
|
25
|
+
ad_storage: "denied",
|
|
26
|
+
ad_user_data: "denied",
|
|
27
|
+
ad_personalization: "denied",
|
|
28
|
+
} as const;
|
|
24
29
|
|
|
25
30
|
// GTM
|
|
26
31
|
const pushEventToDataLayer = (event: string, data: Record<string, any>) => {
|
|
@@ -59,21 +64,75 @@ function useDataTracking() {
|
|
|
59
64
|
const isAnalyticsAllowed = consent.isAnalyticsAllowed ?? false;
|
|
60
65
|
const isAdvertisingAllowed = consent.isAdvertisingAllowed ?? false;
|
|
61
66
|
|
|
62
|
-
/**
|
|
63
|
-
const
|
|
64
|
-
if (!isAnalyticsAllowed || typeof window === "undefined") return;
|
|
65
|
-
const w = window as Window & {
|
|
67
|
+
/** Load GA script and grant analytics consent. No-op if analytics consent is denied or script already loaded. */
|
|
68
|
+
const loadGA = (gaId: string) => {
|
|
69
|
+
if (!isAnalyticsAllowed || typeof window === "undefined" || !gaId) return;
|
|
70
|
+
const w = window as Window & {
|
|
71
|
+
gtag?: (...args: unknown[]) => void;
|
|
72
|
+
dataLayer?: unknown[];
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// Load gtag.js script if not already present
|
|
76
|
+
const scriptId = `ga-script-${gaId}`;
|
|
77
|
+
if (!document.getElementById(scriptId)) {
|
|
78
|
+
const script = document.createElement("script");
|
|
79
|
+
script.id = scriptId;
|
|
80
|
+
script.async = true;
|
|
81
|
+
script.src = `https://www.googletagmanager.com/gtag/js?id=${encodeURIComponent(gaId)}`;
|
|
82
|
+
document.head.appendChild(script);
|
|
83
|
+
|
|
84
|
+
// Initialize gtag
|
|
85
|
+
w.dataLayer = w.dataLayer || [];
|
|
86
|
+
w.gtag = function () {
|
|
87
|
+
// eslint-disable-next-line prefer-rest-params
|
|
88
|
+
(w.dataLayer as unknown[]).push(arguments);
|
|
89
|
+
};
|
|
90
|
+
w.gtag("js", new Date());
|
|
91
|
+
w.gtag("config", gaId);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Grant analytics consent
|
|
66
95
|
if (typeof w.gtag === "function") {
|
|
67
96
|
w.gtag("consent", "update", { ...GTAG_ANALYTICS_GRANTED });
|
|
68
|
-
} else if (Array.isArray(w.dataLayer)) {
|
|
69
|
-
w.dataLayer.push(["consent", "update", { ...GTAG_ANALYTICS_GRANTED }]);
|
|
70
97
|
}
|
|
71
98
|
};
|
|
72
99
|
|
|
73
|
-
/**
|
|
74
|
-
const
|
|
75
|
-
if (!isAnalyticsAllowed || typeof window === "undefined") return;
|
|
100
|
+
/** Load GTM script and grant analytics consent. No-op if analytics consent is denied or script already loaded. */
|
|
101
|
+
const loadGTM = (gtmId: string) => {
|
|
102
|
+
if (!isAnalyticsAllowed || typeof window === "undefined" || !gtmId) return;
|
|
76
103
|
const w = window as Window & { gtag?: GtagConsent; dataLayer?: unknown[] };
|
|
104
|
+
|
|
105
|
+
// Load GTM script if not already present
|
|
106
|
+
const scriptId = `gtm-script-${gtmId}`;
|
|
107
|
+
if (!document.getElementById(scriptId)) {
|
|
108
|
+
w.dataLayer = w.dataLayer || [];
|
|
109
|
+
(w.dataLayer as Record<string, unknown>[]).push({
|
|
110
|
+
"gtm.start": new Date().getTime(),
|
|
111
|
+
event: "gtm.js",
|
|
112
|
+
});
|
|
113
|
+
const script = document.createElement("script");
|
|
114
|
+
script.id = scriptId;
|
|
115
|
+
script.async = true;
|
|
116
|
+
script.src = `https://www.googletagmanager.com/gtm.js?id=${encodeURIComponent(gtmId)}`;
|
|
117
|
+
document.head.appendChild(script);
|
|
118
|
+
|
|
119
|
+
// Inject <noscript> iframe fallback into <body>
|
|
120
|
+
const noscriptId = `gtm-noscript-${gtmId}`;
|
|
121
|
+
if (!document.getElementById(noscriptId)) {
|
|
122
|
+
const noscript = document.createElement("noscript");
|
|
123
|
+
noscript.id = noscriptId;
|
|
124
|
+
const iframe = document.createElement("iframe");
|
|
125
|
+
iframe.src = `https://www.googletagmanager.com/ns.html?id=${encodeURIComponent(gtmId)}`;
|
|
126
|
+
iframe.height = "0";
|
|
127
|
+
iframe.width = "0";
|
|
128
|
+
iframe.style.display = "none";
|
|
129
|
+
iframe.style.visibility = "hidden";
|
|
130
|
+
noscript.appendChild(iframe);
|
|
131
|
+
document.body.insertBefore(noscript, document.body.firstChild);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Grant analytics consent
|
|
77
136
|
if (typeof w.gtag === "function") {
|
|
78
137
|
w.gtag("consent", "update", { ...GTAG_ANALYTICS_GRANTED });
|
|
79
138
|
} else {
|
|
@@ -108,6 +167,17 @@ function useDataTracking() {
|
|
|
108
167
|
}
|
|
109
168
|
};
|
|
110
169
|
|
|
170
|
+
/** Pause Google AdSense by revoking ad consent (sets ad_storage, ad_user_data, ad_personalization to denied). */
|
|
171
|
+
const pauseGoogleAdSense = () => {
|
|
172
|
+
if (typeof window === "undefined") return;
|
|
173
|
+
const w = window as Window & { gtag?: GtagConsent; dataLayer?: unknown[] };
|
|
174
|
+
if (typeof w.gtag === "function") {
|
|
175
|
+
w.gtag("consent", "update", { ...GTAG_ADS_DENIED });
|
|
176
|
+
} else if (Array.isArray(w.dataLayer)) {
|
|
177
|
+
w.dataLayer.push(["consent", "update", { ...GTAG_ADS_DENIED }]);
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
|
|
111
181
|
const trackEvent = (
|
|
112
182
|
eventName: string,
|
|
113
183
|
eventData: Record<string, any>,
|
|
@@ -150,11 +220,12 @@ function useDataTracking() {
|
|
|
150
220
|
trackEvent,
|
|
151
221
|
trackUserIdentify,
|
|
152
222
|
trackLogoutEvent,
|
|
153
|
-
|
|
154
|
-
|
|
223
|
+
loadGA,
|
|
224
|
+
loadGTM,
|
|
155
225
|
togglePostHog,
|
|
156
226
|
toggleOpenReplay,
|
|
157
227
|
toggleGoogleAdSense,
|
|
228
|
+
pauseGoogleAdSense,
|
|
158
229
|
};
|
|
159
230
|
}
|
|
160
231
|
|
|
@@ -5,8 +5,7 @@ import { RewardServiceProvider } from "@phygitallabs/reward";
|
|
|
5
5
|
import { AchievementServiceProvider } from "@phygitallabs/achievement";
|
|
6
6
|
import { GenerateCertificateServiceProvider } from "@phygitallabs/generate-certificate";
|
|
7
7
|
import { PhygitalConsentProvider } from "@phygitallabs/phygital-consent";
|
|
8
|
-
import {
|
|
9
|
-
import { GtagGtmScriptToggleFromConsent } from "../modules/data-tracking";
|
|
8
|
+
import { DataTrackingConsentSync } from "../modules/data-tracking";
|
|
10
9
|
|
|
11
10
|
import serviceApiUrl from "../constants/service";
|
|
12
11
|
import { APIConfig, ServiceConfig } from "../types/service";
|
|
@@ -152,8 +151,7 @@ export const ServicesProvider: React.FC<ServicesProviderProps> = ({
|
|
|
152
151
|
},
|
|
153
152
|
}}
|
|
154
153
|
>
|
|
155
|
-
<
|
|
156
|
-
<GtagGtmScriptToggleFromConsent/>
|
|
154
|
+
<DataTrackingConsentSync />
|
|
157
155
|
{children}
|
|
158
156
|
</PhygitalConsentProvider>
|
|
159
157
|
</GenerateCertificateServiceProvider>
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { usePhygitalConsent } from "@phygitallabs/phygital-consent";
|
|
4
|
-
import { useEffect } from "react";
|
|
5
|
-
|
|
6
|
-
/** Selectors for gtag / GTM scripts in the DOM */
|
|
7
|
-
const SCRIPT_SELECTORS =
|
|
8
|
-
'script[src*="googletagmanager.com"], script[src*="google-analytics.com"], script[src*="gtag/js"]';
|
|
9
|
-
|
|
10
|
-
const GTM_SCRIPT_SELECTOR = 'script[src*="googletagmanager.com/gtm.js"]';
|
|
11
|
-
const GTAG_SCRIPT_SELECTOR =
|
|
12
|
-
'script[src*="gtag/js"], script[src*="googletagmanager.com/gtag"]';
|
|
13
|
-
|
|
14
|
-
/** Detect if gtag/GTM scripts are present in the DOM */
|
|
15
|
-
export function detectGtagGtmInDom(): {
|
|
16
|
-
gtag: boolean;
|
|
17
|
-
gtm: boolean;
|
|
18
|
-
any: boolean;
|
|
19
|
-
gtagScripts: HTMLScriptElement[];
|
|
20
|
-
gtmScripts: HTMLScriptElement[];
|
|
21
|
-
} {
|
|
22
|
-
if (typeof document === "undefined") {
|
|
23
|
-
return { gtag: false, gtm: false, any: false, gtagScripts: [], gtmScripts: [] };
|
|
24
|
-
}
|
|
25
|
-
const gtagScripts = Array.from(document.querySelectorAll<HTMLScriptElement>(GTAG_SCRIPT_SELECTOR));
|
|
26
|
-
const gtmScripts = Array.from(document.querySelectorAll<HTMLScriptElement>(GTM_SCRIPT_SELECTOR));
|
|
27
|
-
return {
|
|
28
|
-
gtag: gtagScripts.length > 0,
|
|
29
|
-
gtm: gtmScripts.length > 0,
|
|
30
|
-
any: gtagScripts.length > 0 || gtmScripts.length > 0,
|
|
31
|
-
gtagScripts,
|
|
32
|
-
gtmScripts,
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export interface GtagGtmScriptToggleProps {
|
|
37
|
-
/** When true, scripts stay. When false, detected GA/GTM scripts are removed from DOM. */
|
|
38
|
-
enabled?: boolean;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Detects GA/GTM scripts in the DOM and removes them when enabled is false.
|
|
43
|
-
* When enabled is true, does nothing (scripts remain if already in the page).
|
|
44
|
-
* No config: only detect and toggle (remove) based on condition.
|
|
45
|
-
* Renders nothing.
|
|
46
|
-
*/
|
|
47
|
-
export function GtagGtmScriptToggle({ enabled = false }: GtagGtmScriptToggleProps) {
|
|
48
|
-
useEffect(() => {
|
|
49
|
-
if (typeof document === "undefined") return;
|
|
50
|
-
|
|
51
|
-
if (!enabled) {
|
|
52
|
-
const toRemove = document.querySelectorAll<HTMLScriptElement>(SCRIPT_SELECTORS);
|
|
53
|
-
toRemove.forEach((el) => {
|
|
54
|
-
el.parentNode?.removeChild(el);
|
|
55
|
-
});
|
|
56
|
-
const w = window as Window & { gtag?: unknown; dataLayer?: unknown[] };
|
|
57
|
-
if (w.dataLayer && Array.isArray(w.dataLayer)) {
|
|
58
|
-
w.dataLayer.length = 0;
|
|
59
|
-
}
|
|
60
|
-
w.gtag = undefined;
|
|
61
|
-
}
|
|
62
|
-
}, [enabled]);
|
|
63
|
-
|
|
64
|
-
return null;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/** Reads consent from PhygitalConsentProvider; removes GA/GTM scripts when analytics not allowed. */
|
|
68
|
-
export function GtagGtmScriptToggleFromConsent() {
|
|
69
|
-
const consent = usePhygitalConsent() as { isAnalyticsAllowed?: boolean };
|
|
70
|
-
const enabled = consent.isAnalyticsAllowed ?? false;
|
|
71
|
-
|
|
72
|
-
return <GtagGtmScriptToggle enabled={enabled} />;
|
|
73
|
-
}
|