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