H11: UploadSheet now honors the modal contract — role=dialog/aria-modal +
accessible name, a focus-trap/Escape/focus-restore $effect mirroring
ContextSheet, and inert + pointer-events-none when closed so its controls leave
the tab order and AT tree.
H12: a layout-level $effect toasts each upload-queue item the first time it
transitions to 'error', so a banned/locked/over-quota guest is actually told
why their photo didn't post instead of the composer silently closing.
M10: api.ts now redirects to /join after clearAuth() on a 401 (guarded against
loops on the auth screens), so a 401 no longer strands the user on a dead page.
M11: ban and event-lock are pushed to guests. A new uploadsLocked store is
seeded from /me/context (now exposes uploads_locked) and kept live by
event-closed/opened SSE; the feed shows a banner and the FAB is disabled when
locked. A user-banned SSE event force-logs-out the targeted user.
M19: feed and export pages track a distinct loadError and render an "Erneut
versuchen" retry card instead of collapsing a fetch failure into "empty" /
"not released". Light-mode empty-state text bumped to gray-500 for contrast.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wires up everything from the previous commits into actual UI surfaces, and
applies Tailwind dark: variants throughout. All pages now support the
'system' / 'light' / 'dark' preference set in the onboarding step or in
Mein Konto → Design.
Layout & nav:
- routes/+layout.svelte: initTheme(), global pin-reset SSE handler that
filters by user_id and calls clearPin(), one-shot /me/context fetch
on boot to hydrate privacyNote + quota.
- components/BottomNav.svelte: dark variants on the frosted-glass bar.
- components/UploadSheet.svelte: dark variants on backdrop, sheet,
source buttons.
- components/OnboardingGuide.svelte: new "Helles oder dunkles Design?"
step (3-option custom-radio grid), reactive currentStep with proper
type narrowing, dark variants throughout. Privacy-note nudge appears
on the PIN step only when one is configured.
Feed:
- routes/feed/+page.svelte: diashow entry icon (tablet/desktop only),
long-press → ContextSheet (Löschen for own posts, Original anzeigen
for all), upload-deleted + feed-delta SSE handlers, dark variants on
header, search, autocomplete, filter chips, empty states.
- components/FeedListCard.svelte: long-press wireup, double-tap-to-like,
data-mode-aware mediaSrc via pickMediaUrl, kebab fallback for desktop,
isOwn prop, dark variants.
- components/FeedGrid.svelte: long-press wireup, dark variants.
- components/LightboxModal.svelte: data-mode-aware src, double-tap heart
burst, dark variants on card / comments / input.
- components/HashtagChips.svelte: dark variants.
Account:
- routes/account/+page.svelte: theme picker (3-button radio grid), data
mode picker (with confirm sheet for Original), live quota widget,
preformatted Datenschutzhinweis block, diashow tile (mobile only),
pin now sourced from the $currentPin store so a global pin-reset
clears it live, clearQueue() on explicit logout, dark variants
across every card + both bottom sheets.
Upload:
- routes/upload/+page.svelte: per-user quota progress bar above the
submit button, dark variants.
Host & Admin:
- routes/host/+page.svelte: PIN-reset confirm + one-time PIN modal,
hosts may demote other hosts, canResetPinFor() helper, dark variants
on all cards, modals, stats, toast.
- routes/admin/+page.svelte: Config form rebuilt as CONFIG_GROUPS with
per-field kind (number / bool / text), renders toggles for the
rate-limit + quota switches and a textarea for the privacy_note;
Nutzer tab gains PIN reset + hosts-may-demote-hosts wiring; same
one-time PIN modal; dark variants everywhere.
- routes/admin/login/+page.svelte: dark variants.
Join / Recover / Export:
- routes/join/+page.svelte: rename inline link to
"Ich habe bereits einen Account", dark variants.
- routes/recover/+page.svelte: dark variants.
- routes/export/+page.svelte: dark variants on status cards + HTML
guide modal.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Persistent bottom tab bar (Feed · FAB · Account) on all authenticated pages
- Upload FAB triggers bottom sheet (Galerie / Kamera) → navigates to composer
- Upload page redesigned as full-screen composer with thumbnail strip, textarea,
quick-tag chips, sticky submit button; bottom nav suppressed while composing
- Slim upload progress bar above bottom nav driven by queue state
- Feed: list/grid view toggle; list = chronological full-width FeedListCard;
grid = 3-col with search bar, autocomplete from loaded posts, filter chips
- Account page: role-gated dashboard links (Host / Admin); Konto section with
leave-confirm bottom sheet; no more per-page header nav icons
- Host dashboard: back arrow, collapsible sections, 2-col stats, user search
- Admin dashboard: back arrow, inner tab bar (Stats/Config/Export/Nutzer),
stacked config inputs with sticky save, new Nutzer tab
- BottomNav hidden on unauthenticated pages via isAuthenticated store
- FeedGrid: threeCol prop; OnboardingGuide upload step updated for FAB
- Concept docs added to docs/
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend:
- AppConfig, AppError, AppState modules for shared infrastructure
- JWT creation/verification with HS256 (jsonwebtoken crate)
- Session management: SHA-256 token hashing, DB-backed sessions
- Auth middleware: AuthUser, RequireHost, RequireAdmin extractors
- POST /api/v1/join: name-only registration, 4-digit PIN + bcrypt hash
- POST /api/v1/recover: PIN-based recovery with 3-attempt lockout (15 min)
- POST /api/v1/admin/login: bcrypt password verification
- DELETE /api/v1/session: logout (session invalidation)
- Migration 006: user PIN lockout columns (failed_pin_attempts, pin_locked_until)
- Models: Event, User (with role enum), Session with all CRUD methods
Frontend:
- api.ts: typed fetch wrapper with automatic Bearer token injection
- auth.ts: JWT/PIN localStorage management with Svelte store
- /join: name entry form with PIN display modal and copy button
- /recover: name + PIN recovery form with saved PIN pre-fill
- /feed: placeholder gallery page with logout
- Root layout: auth initialization on mount
- Root page: redirect to /join or /feed based on auth state
All responses use German language strings as specified.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>