From adc0e151670f3f7d2c8a86f4f61b1d002133a576 Mon Sep 17 00:00:00 2001 From: Emelia Smith Date: Fri, 10 Oct 2025 10:43:48 +0200 Subject: [PATCH] [Glitch] Add support for displaying link previews for Admin UI Port 81350c7cfb217349d0d73b6250faaf05c054fd08 to glitch-soc Signed-off-by: Claire --- .../flavours/glitch/entrypoints/admin.tsx | 41 ++++++++++++++ .../flavours/glitch/styles/admin.scss | 55 +++++++++++++++++++ 2 files changed, 96 insertions(+) diff --git a/app/javascript/flavours/glitch/entrypoints/admin.tsx b/app/javascript/flavours/glitch/entrypoints/admin.tsx index 2b97ef08f4..0c2126e5a8 100644 --- a/app/javascript/flavours/glitch/entrypoints/admin.tsx +++ b/app/javascript/flavours/glitch/entrypoints/admin.tsx @@ -1,6 +1,7 @@ import { createRoot } from 'react-dom/client'; import Rails from '@rails/ujs'; +import { decode, ValidationError } from 'blurhash'; import ready from 'flavours/glitch/ready'; @@ -362,6 +363,46 @@ ready(() => { document.querySelectorAll('[data-admin-component]').forEach((element) => { void mountReactComponent(element); }); + + document + .querySelectorAll('canvas[data-blurhash]') + .forEach((canvas) => { + const blurhash = canvas.dataset.blurhash; + if (blurhash) { + try { + // decode returns a Uint8ClampedArray not Uint8ClampedArray + const pixels = decode( + blurhash, + 32, + 32, + ) as Uint8ClampedArray; + const ctx = canvas.getContext('2d'); + const imageData = new ImageData(pixels, 32, 32); + + ctx?.putImageData(imageData, 0, 0); + } catch (err) { + if (err instanceof ValidationError) { + // ignore blurhash validation errors + return; + } + + throw err; + } + } + }); + + document + .querySelectorAll('.preview-card') + .forEach((previewCard) => { + const spoilerButton = previewCard.querySelector('.spoiler-button'); + if (!spoilerButton) { + return; + } + + spoilerButton.addEventListener('click', () => { + previewCard.classList.toggle('preview-card--image-visible'); + }); + }); }).catch((reason: unknown) => { throw reason; }); diff --git a/app/javascript/flavours/glitch/styles/admin.scss b/app/javascript/flavours/glitch/styles/admin.scss index 82ea9e8b1e..5bf9a5a206 100644 --- a/app/javascript/flavours/glitch/styles/admin.scss +++ b/app/javascript/flavours/glitch/styles/admin.scss @@ -1990,6 +1990,61 @@ a.sparkline { display: list-item; } } + + .preview-card { + position: relative; + max-width: 566px; + + .status-card__image { + &--video { + aspect-ratio: 16 / 9; + } + + &--large { + aspect-ratio: 1.91 / 1; + } + + aspect-ratio: 1; + } + + .spoiler-button__overlay__label { + outline: 1px solid var(--media-outline-color); + } + + .hide-button { + // Toggled to appear when the preview-card is unblurred: + display: none; + position: absolute; + top: 5px; + right: 5px; + color: $white; + border: 0; + outline: 1px solid var(--media-outline-color); + background-color: color.change($black, $alpha: 0.45); + backdrop-filter: $backdrop-blur-filter; + padding: 3px 12px; + border-radius: 99px; + font-size: 14px; + font-weight: 700; + line-height: 20px; + + &:hover, + &:focus { + background-color: color.change($black, $alpha: 0.9); + } + } + + &.preview-card--image-visible { + .hide-button { + display: block; + } + + .spoiler-button__overlay, + .status-card__image-preview { + display: none; + } + } + } } .admin {