Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/frontend/account/cookie-consent-settings.tsx
14422 views
1
/*
2
* This file is part of CoCalc: Copyright © 2026 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
import { Alert, Button, Space } from "antd";
7
import { useEffect, useState } from "react";
8
9
import { Panel } from "@cocalc/frontend/antd-bootstrap";
10
import { useTypedRedux } from "@cocalc/frontend/app-framework";
11
import { Icon } from "@cocalc/frontend/components";
12
import { TimeAgo } from "@cocalc/frontend/components/time-ago";
13
import {
14
COOKIE_CATEGORIES,
15
ConsentSnapshot,
16
getConsentSnapshot,
17
onConsentChange,
18
showPreferences,
19
} from "@cocalc/frontend/cookie-consent";
20
import {
21
revokeYouTubeConsent,
22
useYouTubeConsent,
23
} from "@cocalc/frontend/cookie-consent/youtube";
24
import { COLORS } from "@cocalc/util/theme";
25
26
// Visual style mirrors project Settings → Features (project-capabilites.tsx)
27
// — green check-square for accepted, red minus-square for declined, one row
28
// per category. The list is driven by COOKIE_CATEGORIES, so future
29
// categories show up here automatically.
30
function CategoryStatus({
31
accepted,
32
label,
33
}: {
34
accepted: boolean;
35
label: string;
36
}) {
37
return (
38
<div>
39
<Icon
40
name={accepted ? "check-square" : "minus-square"}
41
style={{ color: accepted ? COLORS.BS_GREEN_D : COLORS.BS_RED }}
42
/>{" "}
43
{label}
44
</div>
45
);
46
}
47
48
export function CookieConsentSettings(): React.JSX.Element | null {
49
const cookieBannerEnabled = useTypedRedux(
50
"customize",
51
"cookie_banner_enabled",
52
);
53
const [snap, setSnap] = useState<ConsentSnapshot | null>(() =>
54
getConsentSnapshot(),
55
);
56
// YouTube consent is stored in a dedicated cookie, parallel to the v3
57
// banner (see frontend/cookie-consent/youtube.ts). We surface it in
58
// this same panel because users naturally come here looking for "all
59
// cookie-style consents I've granted on this site".
60
const ytAllowed = useYouTubeConsent();
61
62
useEffect(() => onConsentChange(setSnap), []);
63
64
if (!cookieBannerEnabled) return null;
65
66
return (
67
<Panel
68
size="small"
69
header={
70
<>
71
<Icon name="lock" /> Cookie preferences
72
</>
73
}
74
>
75
{snap == null ? (
76
<Alert
77
type="warning"
78
showIcon
79
message="You have not yet acknowledged the cookie banner."
80
/>
81
) : (
82
<Space direction="vertical" size="small" style={{ width: "100%" }}>
83
{COOKIE_CATEGORIES.map((c) => (
84
<CategoryStatus
85
key={c.key}
86
accepted={!!snap[c.key]}
87
label={c.label}
88
/>
89
))}
90
<CategoryStatus
91
accepted={ytAllowed}
92
label="Embedded YouTube videos"
93
/>
94
{snap.timestamp && (
95
<div style={{ color: COLORS.GRAY }}>
96
Last updated: <TimeAgo date={snap.timestamp} />
97
</div>
98
)}
99
</Space>
100
)}
101
<div style={{ marginTop: 12 }}>
102
<Space wrap>
103
<Button onClick={() => showPreferences()}>
104
<Icon name="cog" /> Manage cookie preferences
105
</Button>
106
{ytAllowed && (
107
<Button
108
danger
109
onClick={() => revokeYouTubeConsent()}
110
title="Block embedded YouTube videos again. They will show a click-to-load placeholder."
111
>
112
<Icon name="youtube" /> Revoke YouTube embed consent
113
</Button>
114
)}
115
</Space>
116
</div>
117
</Panel>
118
);
119
}
120
121