From 55250419011b7d0c6f3dc97b07c2a3a5be73167d Mon Sep 17 00:00:00 2001 From: ZGaetano Date: Tue, 2 Jun 2026 18:40:16 -0400 Subject: [PATCH] =?UTF-8?q?feat/fix:=20visuals.jsx=20=E2=80=94=20growing?= =?UTF-8?q?=20migrate=20flow=20+=20deltacast=20cleanup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- services/web-ui/public/visuals.jsx | 41 ++++++++++++------------------ 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/services/web-ui/public/visuals.jsx b/services/web-ui/public/visuals.jsx index 5382aa6..9e8170d 100644 --- a/services/web-ui/public/visuals.jsx +++ b/services/web-ui/public/visuals.jsx @@ -25,10 +25,6 @@ function AssetThumb({ asset, size = 'md' }) { ); } - // Live/recording assets: once the capture sidecar has published a poster - // thumbnail (first frame of the recording), show that static frame instead - // of the HLS "connecting…" player. Until the poster exists (the brief window - // before the first segment is grabbed), fall back to the live HLS preview. if (asset.status === 'live' && asset.id) { if (asset.thumbnail_s3_key || thumbUrl) { const altText = asset.name ? `Thumbnail for ${asset.name}` : 'Live recording thumbnail'; @@ -37,7 +33,6 @@ function AssetThumb({ asset, size = 'md' }) { {thumbUrl ? {altText} : } - {/* Keep the pulsing LIVE border so it still reads as recording */}
); @@ -45,6 +40,19 @@ function AssetThumb({ asset, size = 'md' }) { return ; } + if (asset.status === 'pending_migration') { + return ( +
+
+ + Awaiting migration +
+
+ ); + } + const altText = asset.name ? `Thumbnail for ${asset.name}` : 'Asset thumbnail'; return (
@@ -55,10 +63,6 @@ function AssetThumb({ asset, size = 'md' }) { ); } -// Muted inline HLS preview for a live/recording asset tile. Attaches hls.js -// (or native HLS on Safari) to show the live feed inside the library card. -// Shows a "connecting…" spinner while the manifest loads, falls back to a -// placeholder with a record icon if hls.js is unavailable or playback fails. function LiveThumb({ assetId, aspect }) { const videoRef = React.useRef(null); const [ready, setReady] = React.useState(false); @@ -129,7 +133,6 @@ function LiveThumb({ assetId, aspect }) { autoPlay style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} /> - {/* Pulsing red border while connecting or playing, matches LIVE badge colour */}
{!ready && !failed && (
; @@ -250,19 +254,6 @@ function Elapsed({ seconds, live = false }) { return {String(h).padStart(2,'0')}:{String(m).padStart(2,'0')}:{String(s).padStart(2,'0')}; } -// ───────────────────────────────────────────────────────────────────────── -// ConfirmModal + useConfirm — in-page replacement for window.confirm(). -// -// Usage in a component: -// const [confirm, confirmModal] = useConfirm(); -// ... -// if (!(await confirm({ title: 'Delete user?', message: '…' }))) return; -// ... -// return (<>{confirmModal} ...rest of UI... ); -// -// confirm(opts) returns a Promise. Options: -// title, message, confirmLabel (default 'Delete'), cancelLabel ('Cancel'), -// danger (default true → red confirm button). function ConfirmModal({ title, message, confirmLabel = 'Delete', cancelLabel = 'Cancel', danger = true, onConfirm, onCancel }) { React.useEffect(() => { const onKey = (e) => { @@ -283,7 +274,7 @@ function ConfirmModal({ title, message, confirmLabel = 'Delete', cancelLabel = '
{typeof message === 'string' ? message.split('\n').map((line, i) => ( -
{line || ' '}
+
{line || ' '}
)) :
{message}
}
@@ -297,7 +288,7 @@ function ConfirmModal({ title, message, confirmLabel = 'Delete', cancelLabel = ' } function useConfirm() { - const [state, setState] = React.useState(null); // { opts, resolve } | null + const [state, setState] = React.useState(null); const confirm = React.useCallback((opts) => { return new Promise((resolve) => {