Add cookie disclaimer

This commit is contained in:
2026-01-05 13:00:35 +01:00
parent 65488ae59e
commit d21f3a8ab3
8 changed files with 179 additions and 0 deletions

View File

@@ -2063,3 +2063,90 @@ button:disabled:hover {
background: #60a5fa; background: #60a5fa;
box-shadow: 0 0 0 4px rgba(96, 165, 250, 0.2); 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;
}
}

View File

@@ -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 = `
<div class="cookie-consent-body">
<p class="eyebrow subtle">Cookies & privacy</p>
<p class="cookie-consent-copy">
Imgfloat uses essential cookies to keep you signed in. By continuing to use the site, you agree to this use.
</p>
</div>
<div class="cookie-consent-actions">
<button type="button" class="button" data-consent-action="accept">Accept</button>
<button type="button" class="button ghost" data-consent-action="dismiss">Dismiss</button>
</div>
<button type="button" class="cookie-consent-close" aria-label="Close cookie notice" data-consent-action="dismiss">
&times;
</button>
`;
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();
}
})();

View File

@@ -300,6 +300,7 @@
const UPLOAD_LIMIT_BYTES = /*[[${uploadLimitBytes}]]*/ 0; const UPLOAD_LIMIT_BYTES = /*[[${uploadLimitBytes}]]*/ 0;
const SETTINGS = /*[[${settingsJson}]]*/; const SETTINGS = /*[[${settingsJson}]]*/;
</script> </script>
<script src="/js/cookie-consent.js"></script>
<script src="/js/toast.js"></script> <script src="/js/toast.js"></script>
<script src="/js/admin.js"></script> <script src="/js/admin.js"></script>
</body> </body>

View File

@@ -12,6 +12,7 @@
<script th:inline="javascript"> <script th:inline="javascript">
const broadcaster = /*[[${broadcaster}]]*/ ""; const broadcaster = /*[[${broadcaster}]]*/ "";
</script> </script>
<script src="/js/cookie-consent.js"></script>
<script src="/js/toast.js"></script> <script src="/js/toast.js"></script>
<script src="/js/broadcast.js"></script> <script src="/js/broadcast.js"></script>
</body> </body>

View File

@@ -39,6 +39,7 @@
</section> </section>
</main> </main>
</div> </div>
<script src="/js/cookie-consent.js"></script>
<script src="/js/landing.js"></script> <script src="/js/landing.js"></script>
</body> </body>
</html> </html>

View File

@@ -156,6 +156,7 @@
</div> </div>
</section> </section>
</div> </div>
<script src="/js/cookie-consent.js"></script>
<script src="/js/toast.js"></script> <script src="/js/toast.js"></script>
<script src="/js/downloads.js"></script> <script src="/js/downloads.js"></script>
<script th:inline="javascript"> <script th:inline="javascript">

View File

@@ -90,6 +90,7 @@
</div> </div>
</footer> </footer>
</div> </div>
<script src="/js/cookie-consent.js"></script>
<script src="/js/downloads.js"></script> <script src="/js/downloads.js"></script>
</body> </body>
</html> </html>

View File

@@ -248,6 +248,7 @@
<script th:inline="javascript"> <script th:inline="javascript">
const serverRenderedSettings = /*[[${settingsJson}]]*/; const serverRenderedSettings = /*[[${settingsJson}]]*/;
</script> </script>
<script src="/js/cookie-consent.js"></script>
<script src="/js/settings.js"></script> <script src="/js/settings.js"></script>
<script src="/js/toast.js"></script> <script src="/js/toast.js"></script>
</body> </body>