From d21f3a8ab3b978b3a3a71274430a2c475f2e7a93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Kr=C3=BChlmann?= Date: Mon, 5 Jan 2026 13:00:35 +0100 Subject: [PATCH] Add cookie disclaimer --- src/main/resources/static/css/styles.css | 87 +++++++++++++++++++ .../resources/static/js/cookie-consent.js | 86 ++++++++++++++++++ src/main/resources/templates/admin.html | 1 + src/main/resources/templates/broadcast.html | 1 + src/main/resources/templates/channels.html | 1 + src/main/resources/templates/dashboard.html | 1 + src/main/resources/templates/index.html | 1 + src/main/resources/templates/settings.html | 1 + 8 files changed, 179 insertions(+) create mode 100644 src/main/resources/static/js/cookie-consent.js diff --git a/src/main/resources/static/css/styles.css b/src/main/resources/static/css/styles.css index 9fd866d..ca11804 100644 --- a/src/main/resources/static/css/styles.css +++ b/src/main/resources/static/css/styles.css @@ -2063,3 +2063,90 @@ button:disabled:hover { background: #60a5fa; box-shadow: 0 0 0 4px rgba(96, 165, 250, 0.2); } + +.cookie-consent { + position: fixed; + bottom: 16px; + left: 16px; + right: 16px; + max-width: 520px; + margin-left: auto; + padding: 16px 52px 14px 18px; + display: grid; + grid-template-columns: 1fr auto; + gap: 12px; + background: linear-gradient(135deg, rgba(124, 58, 237, 0.25), rgba(59, 130, 246, 0.18)); + border: 1px solid rgba(148, 163, 184, 0.35); + border-radius: 14px; + box-shadow: 0 18px 40px rgba(0, 0, 0, 0.45); + color: #e2e8f0; + z-index: 11000; + backdrop-filter: blur(4px); + transition: + opacity 150ms ease, + transform 150ms ease; +} + +.cookie-consent-exit { + opacity: 0; + transform: translateY(6px); +} + +.cookie-consent-body { + display: flex; + flex-direction: column; + gap: 6px; + padding-right: 18px; +} + +.cookie-consent-copy { + margin: 0; + line-height: 1.45; + color: #cbd5e1; +} + +.cookie-consent-copy a { + color: #a5b4fc; + text-decoration: underline; +} + +.cookie-consent-actions { + display: flex; + align-items: center; + gap: 8px; + flex-wrap: wrap; + justify-content: flex-end; +} + +.cookie-consent-close { + position: absolute; + top: 10px; + right: 10px; + background: transparent; + border: 1px solid rgba(226, 232, 240, 0.3); + color: #e2e8f0; + border-radius: 50%; + width: 32px; + height: 32px; + font-size: 18px; + font-weight: 700; + line-height: 1; + cursor: pointer; + transition: all 120ms ease; +} + +.cookie-consent-close:hover { + background: rgba(255, 255, 255, 0.08); + border-color: rgba(226, 232, 240, 0.45); +} + +@media (max-width: 640px) { + .cookie-consent { + grid-template-columns: 1fr; + padding: 16px; + } + + .cookie-consent-actions { + justify-content: flex-start; + } +} diff --git a/src/main/resources/static/js/cookie-consent.js b/src/main/resources/static/js/cookie-consent.js new file mode 100644 index 0000000..e5b95b9 --- /dev/null +++ b/src/main/resources/static/js/cookie-consent.js @@ -0,0 +1,86 @@ +(() => { + const CONSENT_STORAGE_KEY = "imgfloat.cookie-consent.dismissed"; + const COOKIE_MAX_AGE_SECONDS = 60 * 60 * 24 * 365; // 1 year + + const readConsentCookie = () => { + return document.cookie + .split("; ") + .find((entry) => entry.startsWith(`${CONSENT_STORAGE_KEY}=`)) + ?.split("=")[1]; + }; + + const persistDismissal = () => { + try { + window.localStorage.setItem(CONSENT_STORAGE_KEY, "true"); + } catch { } + document.cookie = `${CONSENT_STORAGE_KEY}=true; max-age=${COOKIE_MAX_AGE_SECONDS}; path=/; SameSite=Lax`; + }; + + const hasDismissed = () => { + try { + if (window.localStorage.getItem(CONSENT_STORAGE_KEY) === "true") { + return true; + } + } catch { } + return readConsentCookie() === "true"; + }; + + const shouldShowBanner = () => { + if (!document.body) { + return false; + } + if (document.body.classList.contains("broadcast-body")) { + return false; + } + return !hasDismissed(); + }; + + const dismissBanner = (banner) => { + if (!banner) { + return; + } + persistDismissal(); + banner.classList.add("cookie-consent-exit"); + window.setTimeout(() => banner.remove(), 180); + }; + + const renderBanner = () => { + if (!shouldShowBanner()) { + return; + } + + const banner = document.createElement("section"); + banner.className = "cookie-consent"; + banner.setAttribute("role", "dialog"); + banner.setAttribute("aria-live", "polite"); + banner.setAttribute("aria-label", "Cookie usage notice"); + + banner.innerHTML = ` + + + + `; + + banner.querySelectorAll("[data-consent-action]").forEach((element) => { + element.addEventListener("click", () => dismissBanner(banner)); + }); + + document.body.appendChild(banner); + }; + + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", renderBanner); + } else { + renderBanner(); + } +})(); diff --git a/src/main/resources/templates/admin.html b/src/main/resources/templates/admin.html index 8e0f052..e948270 100644 --- a/src/main/resources/templates/admin.html +++ b/src/main/resources/templates/admin.html @@ -300,6 +300,7 @@ const UPLOAD_LIMIT_BYTES = /*[[${uploadLimitBytes}]]*/ 0; const SETTINGS = /*[[${settingsJson}]]*/; + diff --git a/src/main/resources/templates/broadcast.html b/src/main/resources/templates/broadcast.html index fe21b6a..7253db2 100644 --- a/src/main/resources/templates/broadcast.html +++ b/src/main/resources/templates/broadcast.html @@ -12,6 +12,7 @@ + diff --git a/src/main/resources/templates/channels.html b/src/main/resources/templates/channels.html index b1231c6..7074cf1 100644 --- a/src/main/resources/templates/channels.html +++ b/src/main/resources/templates/channels.html @@ -39,6 +39,7 @@ + diff --git a/src/main/resources/templates/dashboard.html b/src/main/resources/templates/dashboard.html index aeb6d0b..de23bc0 100644 --- a/src/main/resources/templates/dashboard.html +++ b/src/main/resources/templates/dashboard.html @@ -156,6 +156,7 @@ + diff --git a/src/main/resources/templates/settings.html b/src/main/resources/templates/settings.html index ec06995..2952d26 100644 --- a/src/main/resources/templates/settings.html +++ b/src/main/resources/templates/settings.html @@ -248,6 +248,7 @@ +