Skip to content

Commit dfc252d

Browse files
authored
feat: track consent (#2668)
1 parent 350ce45 commit dfc252d

2 files changed

Lines changed: 59 additions & 6 deletions

File tree

src/components/Analytics/AnalyticsConsent.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Box, Typography, useMediaQuery, useTheme } from '@mui/material';
22
import React, { useEffect, useState } from 'react';
33
import { CookieConsent as AnalyticsConsentBanner } from 'react-cookie-consent';
44
import { Link } from 'src/components/primitives/Link';
5+
import { CONSENT_KEY } from 'src/store/analyticsSlice';
56
import { useRootStore } from 'src/store/root';
67
import { useShallow } from 'zustand/shallow';
78

@@ -27,7 +28,7 @@ export default function AnalyticsBanner() {
2728
const isMobile = useMediaQuery(breakpoints.down('sm'));
2829

2930
const hasUserMadeChoice =
30-
typeof window !== 'undefined' && localStorage.getItem('userAcceptedAnalytics') !== null;
31+
typeof window !== 'undefined' && localStorage.getItem(CONSENT_KEY) !== null;
3132

3233
// Note: If they have already chosen don't show again unless configured from footer
3334
if (hasUserMadeChoice) return null;
@@ -109,7 +110,7 @@ export default function AnalyticsBanner() {
109110
onAccept={() => {
110111
optInAnalytics();
111112
}}
112-
cookieName="userAcceptedAnalytics"
113+
cookieName={CONSENT_KEY}
113114
>
114115
<Box>
115116
We may employ on-the-spot tracking techniques during your browsing session to collect data

src/store/analyticsSlice.ts

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ const createAppContextPlugin = (context: string) => ({
2121

2222
const AMPLITUDE_API_KEY = process.env.NEXT_PUBLIC_AMPLITUDE_API_KEY || '';
2323

24+
// Consent version - increment this to force all users to re-consent
25+
const CONSENT_VERSION = 'v1';
26+
export const CONSENT_KEY = `userAcceptedAnalytics_${CONSENT_VERSION}`;
27+
const CONSENT_COUNTED_KEY = `analyticsConsentCounted_${CONSENT_VERSION}`;
28+
2429
export type TrackEventProperties = {
2530
[key: string]: string | number | boolean | Date | undefined;
2631
};
@@ -78,7 +83,7 @@ export const createAnalyticsSlice: StateCreator<
7883
eventsTrackingInitialized: false,
7984

8085
initializeEventsTracking: () => {
81-
const userAcceptedAnalytics = localStorage.getItem('userAcceptedAnalytics') === 'true';
86+
const userAcceptedAnalytics = localStorage.getItem(CONSENT_KEY) === 'true';
8287
const isInitialized = get().eventsTrackingInitialized;
8388

8489
if (!AMPLITUDE_API_KEY) return;
@@ -120,18 +125,65 @@ export const createAnalyticsSlice: StateCreator<
120125
}
121126
},
122127
acceptAnalytics: () => {
123-
localStorage.setItem('userAcceptedAnalytics', 'true');
128+
localStorage.setItem(CONSENT_KEY, 'true');
124129
set({ isTrackingEnabled: true, analyticsConfigOpen: false });
125130

126131
get().initializeEventsTracking();
132+
133+
// Only track the consent event once per user per consent version
134+
const alreadyCounted = localStorage.getItem(CONSENT_COUNTED_KEY) === 'true';
135+
if (!alreadyCounted) {
136+
localStorage.setItem(CONSENT_COUNTED_KEY, 'true');
137+
get().trackEvent('analytics_consent_given');
138+
}
127139
},
128140
rejectAnalytics: () => {
129-
localStorage.setItem('userAcceptedAnalytics', 'false');
141+
localStorage.setItem(CONSENT_KEY, 'false');
142+
143+
// Only track the consent event once per user per consent version
144+
const alreadyCounted = localStorage.getItem(CONSENT_COUNTED_KEY) === 'true';
145+
146+
if (!alreadyCounted && AMPLITUDE_API_KEY) {
147+
localStorage.setItem(CONSENT_COUNTED_KEY, 'true');
148+
149+
if (!get().eventsTrackingInitialized) {
150+
// Initialize minimal tracking just for this one event
151+
init(AMPLITUDE_API_KEY, {
152+
autocapture: false,
153+
trackingOptions: {
154+
ipAddress: false,
155+
language: false,
156+
platform: false,
157+
},
158+
});
159+
add(createAppContextPlugin('app'));
160+
set({ eventsTrackingInitialized: true });
161+
}
162+
163+
try {
164+
// Temporarily enable tracking to send the opt-out event
165+
// This ensures the event is sent even if Amplitude is in opt-out mode
166+
setOptOut(false);
167+
168+
track('analytics_consent_declined', {
169+
app_context: 'app',
170+
});
171+
172+
// Immediately disable tracking again
173+
setOptOut(true);
174+
} catch (err) {
175+
console.log('Error tracking opt-out event', err);
176+
}
177+
}
178+
179+
// Now disable all tracking
130180
setOptOut(true);
131181
set({ isTrackingEnabled: false, analyticsConfigOpen: false });
132182
},
133183
setAnalyticsConfigOpen: (value: boolean) => {
134-
localStorage.removeItem('userAcceptedAnalytics');
184+
// Clear the current consent version when reopening analytics config
185+
localStorage.removeItem(CONSENT_KEY);
186+
localStorage.removeItem(CONSENT_COUNTED_KEY);
135187

136188
set({ analyticsConfigOpen: value });
137189
},

0 commit comments

Comments
 (0)