mirror of
https://github.com/OpenSquawk/OpenSquawk
synced 2026-05-13 01:46:08 +08:00
Handle Hotjar consent via local storage
This commit is contained in:
97
app/app.vue
97
app/app.vue
@@ -5,12 +5,27 @@
|
||||
</v-app>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { useRoute } from '#imports';
|
||||
import { computed, onMounted, watch } from 'vue';
|
||||
import { useHotjar, useRoute, useState } from '#imports';
|
||||
import { useAuthStore } from '~/stores/auth';
|
||||
import { HOTJAR_LOCAL_STORAGE_KEY, useCookieConsent } from '~/composables/useCookieConsent';
|
||||
|
||||
const HOTJAR_ID = 6522897;
|
||||
const HOTJAR_SCRIPT_VERSION = 6;
|
||||
const HOTJAR_INIT_DELAY = 500;
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
hj?: ((...args: unknown[]) => void) & { q?: unknown[][] };
|
||||
_hjOptOut?: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
const route = useRoute();
|
||||
const authStore = useAuthStore();
|
||||
const { hasConsent, analyticsEnabled } = useCookieConsent();
|
||||
const { initialize } = useHotjar();
|
||||
const hotjarInitialized = useState('hotjar-initialized', () => false);
|
||||
|
||||
const showCookieBanner = computed(() => {
|
||||
if (!authStore.isAuthenticated) {
|
||||
@@ -19,4 +34,82 @@ const showCookieBanner = computed(() => {
|
||||
|
||||
return route.path === '/';
|
||||
});
|
||||
|
||||
const setHotjarOptOut = (optOut: boolean) => {
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
window._hjOptOut = optOut;
|
||||
|
||||
if (optOut && typeof window.hj === 'function') {
|
||||
try {
|
||||
window.hj('event', 'cookie_consent_opt_out');
|
||||
} catch {
|
||||
// ignore errors when notifying Hotjar of opt-out
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const persistLocalPreference = (value: 'granted' | 'denied' | null) => {
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (value === null) {
|
||||
window.localStorage.removeItem(HOTJAR_LOCAL_STORAGE_KEY);
|
||||
return;
|
||||
}
|
||||
|
||||
window.localStorage.setItem(HOTJAR_LOCAL_STORAGE_KEY, value);
|
||||
};
|
||||
|
||||
const scheduleHotjarInitialization = () => {
|
||||
if (hotjarInitialized.value || typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
hotjarInitialized.value = true;
|
||||
|
||||
window.setTimeout(() => {
|
||||
initialize(HOTJAR_ID, HOTJAR_SCRIPT_VERSION);
|
||||
}, HOTJAR_INIT_DELAY);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
const storedPreference = window.localStorage.getItem(HOTJAR_LOCAL_STORAGE_KEY);
|
||||
|
||||
if (storedPreference === 'granted') {
|
||||
setHotjarOptOut(false);
|
||||
scheduleHotjarInitialization();
|
||||
}
|
||||
|
||||
if (storedPreference === 'denied') {
|
||||
setHotjarOptOut(true);
|
||||
}
|
||||
|
||||
watch(
|
||||
[() => hasConsent.value, () => analyticsEnabled.value],
|
||||
([consent, enabled]) => {
|
||||
if (!consent) {
|
||||
persistLocalPreference(null);
|
||||
setHotjarOptOut(true);
|
||||
return;
|
||||
}
|
||||
|
||||
const localValue = enabled ? 'granted' : 'denied';
|
||||
persistLocalPreference(localValue);
|
||||
setHotjarOptOut(!enabled);
|
||||
|
||||
if (enabled) {
|
||||
scheduleHotjarInitialization();
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -14,6 +14,7 @@ type CookieConsentValue = {
|
||||
const CONSENT_COOKIE_NAME = 'osq-cookie-consent';
|
||||
const CONSENT_VERSION = 1;
|
||||
const SIX_MONTHS_IN_SECONDS = 60 * 60 * 24 * 180;
|
||||
export const HOTJAR_LOCAL_STORAGE_KEY = 'osq-hotjar-consent';
|
||||
|
||||
export const useCookieConsent = () => {
|
||||
const consentCookie = useCookie<CookieConsentValue | null>(CONSENT_COOKIE_NAME, {
|
||||
@@ -37,6 +38,24 @@ export const useCookieConsent = () => {
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
if (process.client) {
|
||||
watch(
|
||||
consentState,
|
||||
(value) => {
|
||||
if (!value) {
|
||||
window.localStorage.removeItem(HOTJAR_LOCAL_STORAGE_KEY);
|
||||
return;
|
||||
}
|
||||
|
||||
window.localStorage.setItem(
|
||||
HOTJAR_LOCAL_STORAGE_KEY,
|
||||
value.preferences.analytics ? 'granted' : 'denied'
|
||||
);
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
}
|
||||
|
||||
const hasConsent = computed(() => consentState.value !== null);
|
||||
|
||||
const analyticsEnabled = computed(() => {
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
import { defineNuxtPlugin } from '#app';
|
||||
import { watch } from 'vue';
|
||||
import { useCookieConsent } from '~/composables/useCookieConsent';
|
||||
import { useHotjar } from '#imports';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
hj?: ((...args: unknown[]) => void) & { q?: unknown[][] };
|
||||
_hjOptOut?: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
const setHotjarOptOut = (optOut: boolean) => {
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
window._hjOptOut = optOut;
|
||||
|
||||
if (optOut && typeof window.hj === 'function') {
|
||||
try {
|
||||
window.hj('event', 'cookie_consent_opt_out');
|
||||
} catch {
|
||||
// ignore errors from Hotjar when shutting down
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default defineNuxtPlugin(() => {
|
||||
if (!import.meta.client) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { initialize } = useHotjar();
|
||||
const hasInitialized = useState('hotjar-initialized', () => false);
|
||||
const { analyticsEnabled } = useCookieConsent();
|
||||
|
||||
watch(
|
||||
() => analyticsEnabled.value,
|
||||
(enabled) => {
|
||||
if (enabled) {
|
||||
setHotjarOptOut(false);
|
||||
|
||||
if (!hasInitialized.value) {
|
||||
initialize();
|
||||
hasInitialized.value = true;
|
||||
}
|
||||
} else {
|
||||
setHotjarOptOut(true);
|
||||
hasInitialized.value = false;
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user