feat(eventsnap): UX review batch-3 — feed virtualization, a11y/UX fixes, shared primitives
Frontend - Feed: DOM-windowing via @tanstack/svelte-virtual (new VirtualFeed). The window virtualizer keeps the sticky header, pull-to-refresh, infinite-scroll sentinel and bottom nav working. List = dynamic measured heights keyed by upload id + anchorTo:start so an SSE prepend doesn't jump a scrolled reader; grid = measured square rows. Drops the content-visibility band-aid and the old FeedGrid. measureElement(null) on row unmount prevents ResizeObserver retention; stable option callbacks + guarded setOptions avoid O(n) re-measure on like/comment patches. - Global: viewport-fit=cover (activates safe-area insets), lang=de, PWA manifest + maskable icon + apple-touch metas, prefers-reduced-motion, safe-area-top headers. - Feed reactivity: like/comment SSE patch the single card in place; upload-processed debounced in-place merge; IntersectionObserver leak fixed; shared 60s clock. - CLS: list cards reserve the skeleton aspect box. - Destructive actions (promote/demote/unban, gallery release) routed through ConfirmSheet (host + admin). - Export OOM: streamed download via single-use ticket instead of res.blob(). - Shared primitives: IconButton (44px hit area), scrollLock action; UploadSheet a11y. - Polish: api timeout + non-JSON guard, focus-trap offsetParent fix, pull-to-refresh passive:false + guards, diashow keyboard a11y, Toaster assertive errors, dark-mode gaps, aria-pressed on like/chip state, CameraCapture mic-on-video + retry, ConfirmSheet spinner, upload beforeunload warning, emoji->SVG icons. Backend - social.rs: like/comment SSE broadcasts now carry the fresh count so feed clients patch one card in place instead of refetching page 1. - admin.rs / main.rs: supporting changes for the above. Verified: svelte-check 0 errors, frontend build clean, /feed SSR 200. Runtime feed scroll-feel validation still owed (documented in FOLLOWUPS.md). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { focusTrap } from '$lib/actions/focus-trap';
|
||||
import { scrollLock } from '$lib/actions/scroll-lock';
|
||||
|
||||
interface Props {
|
||||
open: boolean;
|
||||
@@ -50,6 +51,7 @@
|
||||
class="fixed inset-0 z-50 bg-black/40"
|
||||
aria-label="Schließen"
|
||||
onclick={onCancel}
|
||||
disabled={busy}
|
||||
tabindex="-1"
|
||||
></button>
|
||||
<div
|
||||
@@ -60,6 +62,7 @@
|
||||
aria-labelledby={titleId}
|
||||
data-testid="confirm-sheet"
|
||||
use:focusTrap={{ onclose: onCancel }}
|
||||
use:scrollLock
|
||||
>
|
||||
<div class="mb-4 flex justify-center">
|
||||
<div class="h-1 w-10 rounded-full bg-gray-300 dark:bg-gray-600"></div>
|
||||
@@ -77,17 +80,21 @@
|
||||
onclick={handleConfirm}
|
||||
disabled={busy}
|
||||
data-testid="confirm-sheet-confirm"
|
||||
class="mb-3 w-full rounded-xl py-3 text-sm font-semibold text-white transition disabled:opacity-60 {tone === 'danger'
|
||||
class="mb-3 flex w-full items-center justify-center gap-2 rounded-xl py-3 text-sm font-semibold text-white transition disabled:opacity-60 {tone === 'danger'
|
||||
? 'bg-red-600 hover:bg-red-700 active:bg-red-700 dark:bg-red-500 dark:hover:bg-red-400 dark:active:bg-red-400'
|
||||
: 'bg-blue-600 hover:bg-blue-700 active:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-400 dark:active:bg-blue-400'}"
|
||||
>
|
||||
{#if busy}
|
||||
<span class="inline-block h-4 w-4 animate-spin rounded-full border-2 border-white/40 border-t-white" aria-hidden="true"></span>
|
||||
{/if}
|
||||
{confirmLabel}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onclick={onCancel}
|
||||
disabled={busy}
|
||||
data-testid="confirm-sheet-cancel"
|
||||
class="w-full rounded-xl border border-gray-200 py-3 text-sm font-medium text-gray-700 transition hover:bg-gray-50 active:bg-gray-100 dark:border-gray-700 dark:text-gray-300 dark:hover:bg-gray-800 dark:active:bg-gray-800"
|
||||
class="w-full rounded-xl border border-gray-200 py-3 text-sm font-medium text-gray-700 transition hover:bg-gray-50 active:bg-gray-100 disabled:opacity-60 dark:border-gray-700 dark:text-gray-300 dark:hover:bg-gray-800 dark:active:bg-gray-800"
|
||||
>
|
||||
{cancelLabel}
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user