Skip to content
57 linesCodeBlameRaw
1/**
2 * Asking a visitor from the EU, EEA, UK or Switzerland before site
3 * analytics runs (lib/analytics-consent.ts): both answers are one click,
4 * and nothing is fetched or stored for analytics until they agree. The
5 * footer's "Cookie choices" asks again (components/footer.tsx).
6 */
7import { useEffect, useState, useSyncExternalStore } from "react";
8import { Link } from "react-router";
9
10import { analyticsHere, choice, choose, consentRequired, onChoice } from "../lib/analytics-consent";
11import { Button } from "./ui/button";
12
13/** Whether this visitor is asked at all, known only once the page is in the browser. */
14export function useAsksFirst(): boolean {
15 const [asks, setAsks] = useState(false);
16 useEffect(() => setAsks(analyticsHere() && consentRequired()), []);
17 return asks;
18}
19
20export function AnalyticsConsent() {
21 const asks = useAsksFirst();
22 const chosen = useSyncExternalStore(onChoice, choice, () => null);
23 if (!asks || chosen) return null;
24 return (
25 <section
26 aria-label="Analytics cookie"
27 className="fixed inset-x-3 bottom-3 z-[60] max-md:bottom-[calc(var(--tabbar-h)+0.75rem)] max-md:in-data-[keyboard=open]:bottom-3 mx-auto max-w-lg rounded-2xl bg-surface p-4 text-sm shadow-2xl shadow-black/50 ring-1 ring-line-strong sm:inset-x-auto sm:right-4 sm:bottom-4"
28 >
29 <p className="leading-6 text-fg-soft">
30 May g1t count how its pages are used? It sets one analytics cookie, and names inside your workspaces are
31 removed before anything is sent.{" "}
32 <Link to="/policies/privacy#how-the-site-is-used" className="text-accent hover:underline">
33 What is sent
34 </Link>
35 </p>
36 <div className="mt-3 flex gap-2">
37 <Button
38 type="button"
39 onClick={() => choose("yes")}
40 size="inline"
41 className="flex-1 rounded-lg px-3 py-2"
42 >
43 Allow
44 </Button>
45 <Button
46 type="button"
47 onClick={() => choose("no")}
48 variant="ghost"
49 size="inline"
50 className="flex-1 rounded-lg px-3 py-2 text-fg ring-1 ring-line-strong"
51 >
52 No thanks
53 </Button>
54 </div>
55 </section>
56 );
57}