@convex-internal/web-analytics 2.0.1 → 2.1.0
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/README.md +11 -7
- package/dist/react/ConsentBanner.js +13 -2
- package/dist/svelte/WebAnalytics.svelte +13 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -31,13 +31,8 @@ capture("www-login", undefined, {
|
|
|
31
31
|
|
|
32
32
|
### Consent banner
|
|
33
33
|
|
|
34
|
-
The banner
|
|
35
|
-
`
|
|
36
|
-
`{ decision: "accepted" | "declined" }` just before the choice is saved.
|
|
37
|
-
|
|
38
|
-
The banner stays hidden while Global Privacy Control is on, because that
|
|
39
|
-
resolves `consent` to `"declined"` rather than `"undecided"`. GPC does not
|
|
40
|
-
write the cookie; see [Cookie behavior](#cookie-behavior).
|
|
34
|
+
The banner is visible when `consent` is `"undecided"`. It is hidden while
|
|
35
|
+
`consent` is `"unknown"`, `"consented"`, or `"declined"`.
|
|
41
36
|
|
|
42
37
|
Theme with `--wa-banner-*` variables on `.wa-banner`:
|
|
43
38
|
|
|
@@ -66,6 +61,15 @@ Theme with `--wa-banner-*` variables on `.wa-banner`:
|
|
|
66
61
|
|
|
67
62
|
Buttons default to 80% opacity and go to 100% on hover or keyboard focus.
|
|
68
63
|
|
|
64
|
+
### Consent events
|
|
65
|
+
|
|
66
|
+
`cookie_consent_shown` is recorded when the banner appears.
|
|
67
|
+
`cookie_consent_decided` is recorded with
|
|
68
|
+
`{ decision: "accepted" | "declined", source: "banner" }` just before the
|
|
69
|
+
choice is saved, and with `{ decision: "declined", source: "gpc" }` when
|
|
70
|
+
the GPC preference is applied. Historic events without `source` are banner
|
|
71
|
+
decisions.
|
|
72
|
+
|
|
69
73
|
### Cookie behavior
|
|
70
74
|
|
|
71
75
|
Once the cookie has been read, `consent` is resolved in this order:
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useEffect, useRef } from "react";
|
|
4
|
-
import { bannerCopy } from "../strings.js";
|
|
5
4
|
import { capture } from "../capture.js";
|
|
5
|
+
import { hasGPC } from "../consent.js";
|
|
6
|
+
import { bannerCopy } from "../strings.js";
|
|
6
7
|
import { useConsent } from "./useConsent.js";
|
|
7
8
|
import "../banner.css";
|
|
8
9
|
export function ConsentBanner() {
|
|
9
10
|
const { consent, setConsent } = useConsent();
|
|
10
11
|
const shouldShow = consent === "undecided";
|
|
11
12
|
const wasShowing = useRef(false);
|
|
13
|
+
const recordedGPCDecision = useRef(false);
|
|
12
14
|
useEffect(() => {
|
|
13
15
|
if (shouldShow && !wasShowing.current) {
|
|
14
16
|
// Captures once when the banner becomes visible, not on later renders.
|
|
@@ -16,13 +18,22 @@ export function ConsentBanner() {
|
|
|
16
18
|
}
|
|
17
19
|
wasShowing.current = shouldShow;
|
|
18
20
|
}, [shouldShow]);
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (consent === "declined" && hasGPC() && !recordedGPCDecision.current) {
|
|
23
|
+
recordedGPCDecision.current = true;
|
|
24
|
+
capture("cookie_consent_decided", {
|
|
25
|
+
decision: "declined",
|
|
26
|
+
source: "gpc",
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}, [consent]);
|
|
19
30
|
if (!shouldShow) {
|
|
20
31
|
return null;
|
|
21
32
|
}
|
|
22
33
|
const decide = (decision) => {
|
|
23
34
|
try {
|
|
24
35
|
// Captures before the cookie write so decline is captured cookieless.
|
|
25
|
-
capture("cookie_consent_decided", { decision });
|
|
36
|
+
capture("cookie_consent_decided", { decision, source: "banner" });
|
|
26
37
|
}
|
|
27
38
|
finally {
|
|
28
39
|
setConsent(decision === "accepted" ? "consented" : "declined");
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
let isReady = $state(false);
|
|
11
11
|
let cookieValue = $state<boolean | undefined>(undefined);
|
|
12
|
+
let recordedGPCDecision = false;
|
|
12
13
|
|
|
13
14
|
const consent = $derived(resolveConsent(isReady, cookieValue, hasGPC()));
|
|
14
15
|
const shouldShowBanner = $derived(consent === "undecided");
|
|
@@ -60,6 +61,17 @@
|
|
|
60
61
|
wasShowingBanner = shouldShowBanner;
|
|
61
62
|
});
|
|
62
63
|
|
|
64
|
+
$effect(() => {
|
|
65
|
+
if (consent === "declined" && hasGPC() && !recordedGPCDecision) {
|
|
66
|
+
// GPC hides the banner, so record the decline here instead of a click.
|
|
67
|
+
recordedGPCDecision = true;
|
|
68
|
+
capture("cookie_consent_decided", {
|
|
69
|
+
decision: "declined",
|
|
70
|
+
source: "gpc",
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
63
75
|
function setConsent(status: "consented" | "declined") {
|
|
64
76
|
setCookie(COOKIE_NAME, status === "consented" ? "true" : "false");
|
|
65
77
|
cookieValue = status === "consented";
|
|
@@ -68,7 +80,7 @@
|
|
|
68
80
|
function decide(decision: "accepted" | "declined") {
|
|
69
81
|
try {
|
|
70
82
|
// Captures before the cookie write so decline is captured cookieless.
|
|
71
|
-
capture("cookie_consent_decided", { decision });
|
|
83
|
+
capture("cookie_consent_decided", { decision, source: "banner" });
|
|
72
84
|
} finally {
|
|
73
85
|
setConsent(decision === "accepted" ? "consented" : "declined");
|
|
74
86
|
}
|