docs: correct the claims that no longer match the code
Checked each against the implementation and fixed the doc, never the code: - FEATURES claimed the ban modal offers a choice about hiding existing uploads. There is no such choice — a ban always hides. USER_JOURNEYS §9 was already right. - FEATURES claimed hosts may demote other hosts. It is admin-only, enforced in the backend, and the two documents contradicted each other on it. - FEATURES showed the quota widget as guest-facing; it is deliberately staff-only. - The first-visit tour has six steps, not four. - USER_JOURNEYS §12.7 said export downloads are rate-limited per IP. They are per USER — a materially different thing at a shared-NAT venue, where per-IP would have locked out the fourth guest to fetch their keepsake. - §15 still described "Event verlassen"; that button is now Abmelden / Auf allen Geräten abmelden. - §4, §13, §14, §16 and §18 were marked "(planned)" and have shipped. - The lightbox row now describes what exists after this branch: prev/next controls, arrow keys and swipe. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -14,12 +14,12 @@ Status legend: **✓ shipped** · **◐ partial** · **◯ planned** · **✗ ou
|
||||
|---------------------------------------------------------|:-----:|:-----:|:-----:|-----------------------------------------------------------------------|
|
||||
| **Onboarding & sessions** | | | | |
|
||||
| Join via shared event link / QR code | ✓ | ✓ | ✓ | Name-only registration; server issues JWT + 4-digit PIN |
|
||||
| First-visit guided tour (4 steps) | ✓ | ✓ | ✓ | Dismissed once, flag in `localStorage` |
|
||||
| First-visit guided tour (6 steps) | ✓ | ✓ | ✓ | Welcome, upload, hashtags, long-press, hell/dunkel, PIN (+ privacy-note pointer when one is set). Dismissed once, flag in `localStorage` |
|
||||
| Persistent 30-day session | ✓ | ✓ | ✓ | JWT in `localStorage`; refreshed on activity |
|
||||
| Sign in on another device using name + PIN | ✓ | ✓ | ✓ | 3 wrong PINs → 15-min lockout |
|
||||
| "Ich habe bereits einen Account" link on the join page | ✓ | ✓ | ✓ | Small inline link → `/recover` (name + PIN) |
|
||||
| View / copy own PIN any time ("My Account") | ✓ | ✓ | ✓ | Read from `localStorage`; never sent back from the server |
|
||||
| Log out / "Leave event" | ✓ | ✓ | ✓ | Confirmation bottom-sheet; invalidates the session row |
|
||||
| Log out ("Abmelden" / "Auf allen Geräten abmelden") | ✓ | ✓ | ✓ | Confirmation bottom-sheet; `DELETE /session` (this device) or `DELETE /sessions` (all devices). Nothing is deleted — account, PIN and uploads stay |
|
||||
| Rename own display name | ◯ | ◯ | ◯ | Not yet wired; PIN-protected change |
|
||||
| Pick **data mode** (Saver / Original) in My Account | ✓ | ✓ | ✓ | Saver = compressed (default). Original = full files + data-usage warning. Applies to feed and diashow. Per-device, in `localStorage` |
|
||||
| Read the **Datenschutzhinweis** (privacy note) | ✓ | ✓ | ✓ | Free text set by Admin during setup; rendered preformatted in My Account; first-visit guide briefly points to it |
|
||||
@@ -42,7 +42,7 @@ Status legend: **✓ shipped** · **◐ partial** · **◯ planned** · **✗ ou
|
||||
| 3-column grid feed with toggle | ✓ | ✓ | ✓ | Video play badges, duration |
|
||||
| Search & autocomplete (uploader + hashtag) | ✓ | ✓ | ✓ | Grid view; derived in-memory, no extra API calls |
|
||||
| Active filter chips (OR within type, AND across types) | ✓ | ✓ | ✓ | Multiple hashtags = OR; uploader + hashtag = AND |
|
||||
| Fullscreen lightbox with swipe | ✓ | ✓ | ✓ | Swipe navigates the filtered set |
|
||||
| Fullscreen lightbox with prev/next navigation | ✓ | ✓ | ✓ | Navigates the *filtered* set. On-screen prev/next controls, ← / → arrow keys, and swipe left/right on touch |
|
||||
| Like / unlike any post | ✓ | ✓ | ✓ | Single toggle; SSE `like-update` |
|
||||
| Read comments on any post | ✓ | ✓ | ✓ | |
|
||||
| Add a comment | ✓ | ✓ | ✓ | Hashtags in comments also parsed |
|
||||
@@ -56,11 +56,11 @@ Status legend: **✓ shipped** · **◐ partial** · **◯ planned** · **✗ ou
|
||||
| | | | | |
|
||||
| **Moderation (Host)** | | | | |
|
||||
| List all event users | | ✓ | ✓ | Includes upload count, total bytes |
|
||||
| Ban / unban a user | | ✓ | ✓ | Modal asks: hide their existing uploads, or keep visible? |
|
||||
| Ban / unban a user | | ✓ | ✓ | Ban **always hides** their existing uploads — no opt-out, the modal is a plain confirm. Unban restores them. Both invalidate and rebuild a released keepsake. See USER_JOURNEYS §9/§10 |
|
||||
| Delete any upload | | ✓ | ✓ | |
|
||||
| Delete any comment | | ✓ | ✓ | |
|
||||
| Promote guest to Host | | ✓ | ✓ | |
|
||||
| Demote Host to guest | | ✓ | ✓ | Hosts may demote other Hosts. Cannot demote self. Admins cannot be demoted by hosts. |
|
||||
| Demote Host to guest | | | ✓ | **Admin only** — the backend 403s a Host demoting a peer Host (F1: it would launder past the ban / PIN-reset peer guards). Nobody may change their own role; Admins cannot be demoted at all. The button is hidden for non-admin Hosts |
|
||||
| Reset a guest's PIN (Host) / any non-admin PIN (Admin) | | ✓ | ✓ | New PIN shown once in modal; Host shows/shares it with the guest |
|
||||
| Lock new uploads ("Event schließen") | | ✓ | ✓ | Likes + comments + browsing remain open |
|
||||
| Unlock new uploads | | ✓ | ✓ | |
|
||||
@@ -78,18 +78,18 @@ Status legend: **✓ shipped** · **◐ partial** · **◯ planned** · **✗ ou
|
||||
| Edit compression-worker concurrency | | | ✓ | |
|
||||
| Edit **Datenschutzhinweis** (privacy note, free text) | | | ✓ | Plain text, whitespace + newlines preserved, no HTML. SSE `event-updated` broadcasts edits live. |
|
||||
| Inspect export job list & progress | | | ✓ | |
|
||||
| Low-disk alert (< 10 GB free) | | | ◯ | Planned |
|
||||
| Low-disk alert | | ✓ | ✓ | Red banner at the top of the Host dashboard when `disk_low`, naming free space and the keepsake's required bytes. Fails closed to "not low" on an unreadable mount |
|
||||
| Event banner / cover image | | | ◯ | DB column exists, no UI |
|
||||
| | | | | |
|
||||
| **Quota visibility (Guest-facing)** | | | | |
|
||||
| Show current per-user quota estimate | ✓ | ✓ | ✓ | "Du hast X MB von Y MB genutzt." in My Account and on the upload screen. Computed from the live formula. Hidden when quota enforcement is toggled off |
|
||||
| **Quota visibility (staff-only)** | | | | |
|
||||
| Show current per-user quota estimate | | ✓ | ✓ | **Deliberately not shown to guests** — the storage widget in My Account and on the upload screen is gated on host/admin. Computed from the live formula; also hidden when quota enforcement is toggled off |
|
||||
| | | | | |
|
||||
| **Export** | | | | |
|
||||
| Wait at locked export page until released | ✓ | ✓ | ✓ | Friendly "not yet available" copy |
|
||||
| Download `Gallery.zip` (full-quality originals) | ✓ | ✓ | ✓ | Streamed via `async-zip`; `Photos/` + `Videos/` folders |
|
||||
| Download `Memories.zip` (offline HTML viewer) | ✓ | ✓ | ✓ | Self-contained SvelteKit-static app + `data.json` + `media/` |
|
||||
| HTML-export in-app guide modal before download | ✓ | ✓ | ✓ | Explains: unzip first, open `index.html` |
|
||||
| Per-IP export download rate limit (3 / day) | ✓ | ✓ | ✓ | |
|
||||
| Per-**user** export download rate limit (3 / day) | ✓ | ✓ | ✓ | Keyed on the user id, not the IP — a venue behind one NAT would otherwise share a 3/day budget across every guest |
|
||||
| | | | | |
|
||||
| **Banned guest** (subset) | | | | |
|
||||
| Cannot upload, like, or comment | ✗ | | | Returns HTTP 403 |
|
||||
@@ -143,7 +143,7 @@ email, no password, no account portal.
|
||||
PIN-recovery form for that account ("Already taken — sign in instead, or pick another
|
||||
name"). The join page also surfaces an explicit **"Ich habe bereits einen Account"**
|
||||
link routing to `/recover` for users who already know they want to sign in.
|
||||
- **PIN reset by Host / Admin.** Planned. If a guest loses their PIN and `localStorage` is
|
||||
- **PIN reset by Host / Admin.** If a guest loses their PIN and `localStorage` is
|
||||
gone everywhere, a Host (for guests) or Admin (for hosts and guests) can hit a
|
||||
**PIN zurücksetzen** action in the user list. A fresh PIN is generated server-side, its
|
||||
bcrypt stored, and the plaintext is shown **once** in a modal to the requesting
|
||||
@@ -153,9 +153,13 @@ email, no password, no account portal.
|
||||
Admins can.
|
||||
- **Roles.** `guest` (default), `host`, `admin`. The Admin role is seeded from the
|
||||
`ADMIN_PASSWORD_HASH` env var; admins log in at `/admin/login` with a password (separate
|
||||
JWT, 1-day expiry, in `sessionStorage`). Hosts are guests promoted by an admin. **Hosts
|
||||
may also demote other Hosts to guests** (planned) — but never themselves, to avoid
|
||||
locking the event out of moderation. Admins can demote anyone except admins.
|
||||
JWT, 1-day expiry, in `sessionStorage`). Hosts are guests promoted by a host or an admin.
|
||||
**Only an Admin may change a Host's role** — a plain Host may promote guests but may not
|
||||
demote a peer Host, because demoting one would then let them ban or PIN-reset (→
|
||||
`/recover` takeover) that ex-peer, the guards keying off the target's *current* role (F1).
|
||||
Nobody may change their own role, so the event can't be locked out of moderation, and
|
||||
Admins are un-demotable and un-bannable. Enforced in `handlers::host::set_role`; the
|
||||
button is hidden for non-admin Hosts.
|
||||
|
||||
### 2.2 Posting pipeline
|
||||
|
||||
@@ -188,8 +192,9 @@ The upload pipeline is built for flaky mobile networks:
|
||||
the loaded uploads, so typing never hits the server.
|
||||
- **Filter chips** — multiple hashtags combine with OR; multiple uploaders combine with OR;
|
||||
hashtag + uploader combine with AND. Matches the redesign concept exactly.
|
||||
- **Lightbox** — fullscreen view, swipe navigates the *filtered* set, with embedded
|
||||
like/comment UI.
|
||||
- **Lightbox** — fullscreen view with embedded like/comment UI. Navigation across the
|
||||
*filtered* set is available three ways: on-screen prev/next controls, the ← / → arrow
|
||||
keys, and a left/right swipe on touch.
|
||||
- **Real-time** — SSE delivers `new-upload`, `upload-processed`, `like-update`,
|
||||
`new-comment`, `upload-deleted`, `event-closed`/`event-opened`, `export-progress`,
|
||||
`export-available`. Client pauses SSE on `visibilitychange: hidden` and reopens on visible.
|
||||
@@ -197,15 +202,18 @@ The upload pipeline is built for flaky mobile networks:
|
||||
### 2.4 Host / Admin tooling
|
||||
|
||||
- **Host dashboard** — three collapsible sections: Stats, Event-Einstellungen,
|
||||
Nutzerverwaltung. Ban modal asks explicitly whether to hide the user's existing uploads
|
||||
from the public feed. Promote/demote, lock/unlock, release-gallery are one-tap.
|
||||
- **Admin dashboard** — same dashboard plus three more inner tabs (Stats, Config, Export,
|
||||
Nutzerverwaltung. The ban modal is a plain confirm: banning **always hides** the user's
|
||||
existing uploads, there is no keep-visible option. Promote/demote/unban/release are each
|
||||
behind a confirmation sheet that spells out the consequence, not one-tap.
|
||||
- **Admin dashboard** — same user list plus four inner tabs (Stats, Config, Export,
|
||||
Nutzer). Config form covers per-file limits, rate limits, quota tolerance, estimated
|
||||
guest count, and compression concurrency — all stored in the `config` table and read on
|
||||
guest count and the colour theme — all stored in the `config` table and read on
|
||||
each request, so changes take effect without a restart. Disk widget pulls from the
|
||||
`sysinfo` crate live.
|
||||
`sysinfo` crate live. The Export tab mirrors the host dashboard's release controls:
|
||||
the release button is disabled once released, the live keepsake status (progress /
|
||||
ready / failure reason) is shown, and a rebuild is offered as the recovery path.
|
||||
|
||||
### 2.5 Data mode (planned)
|
||||
### 2.5 Data mode
|
||||
|
||||
Each device picks a **data mode** in My Account; the setting lives in `localStorage` so a
|
||||
guest can be on Saver on their phone and Original on their laptop.
|
||||
@@ -218,7 +226,7 @@ guest can be on Saver on their phone and Original on their laptop.
|
||||
Applies uniformly to the live app's feed/lightbox **and** the diashow. The viewer (offline
|
||||
HTML export) is unaffected — it's already a snapshot of pre-bundled media variants.
|
||||
|
||||
### 2.6 Rate limits and quotas — toggleable (planned)
|
||||
### 2.6 Rate limits and quotas — toggleable
|
||||
|
||||
The Admin Config tab gains explicit on/off toggles in addition to the numeric inputs:
|
||||
|
||||
@@ -230,13 +238,14 @@ The Admin Config tab gains explicit on/off toggles in addition to the numeric in
|
||||
- **Per-area quota switch.** Storage-bytes quota and upload-count quota can be disabled
|
||||
independently.
|
||||
|
||||
When a feature is toggled off, the relevant UI in the guest-facing app should adapt: e.g.
|
||||
the "Du hast X von Y MB genutzt" widget hides itself when storage quota is disabled. The
|
||||
quota estimate is computed from the same formula the server uses
|
||||
(`(free_disk × tolerance) / max(active_uploaders, 1)`) — surfaced in My Account *and* on
|
||||
the upload preview screen so guests know before they pick files.
|
||||
When a feature is toggled off, the dependent UI adapts: the "Speicher: X / Y" widget hides
|
||||
itself when the storage quota is disabled. The quota estimate is computed from the same
|
||||
formula the server uses (`(free_disk × tolerance) / max(active_uploaders, 1)`) and is
|
||||
surfaced in My Account *and* on the upload preview screen — but **only to hosts and
|
||||
admins**. Guests never see it: the number moves as other people upload and as disk frees
|
||||
up, which reads as a broken or unfair limit to someone who can't see why.
|
||||
|
||||
### 2.7 Privacy note (Datenschutzhinweis, planned)
|
||||
### 2.7 Privacy note (Datenschutzhinweis)
|
||||
|
||||
Admin sets a free-text **Datenschutzhinweis** during instance setup (Admin Dashboard →
|
||||
Config). It's stored as a single config key (plain text, whitespace and newlines
|
||||
|
||||
@@ -19,8 +19,9 @@ can do what" overview, see [FEATURES.md](FEATURES.md). For manual QA, see
|
||||
this PIN is the only way to sign in on another device. PIN is also written to
|
||||
`localStorage`.
|
||||
6. Guest taps **Weiter zur Galerie** → lands in the feed (`/feed`).
|
||||
7. The **first-visit onboarding overlay** appears: dismissible steps (welcome, upload,
|
||||
hashtags, PIN, and a brief pointer to the **Datenschutzhinweis** in My Account).
|
||||
7. The **first-visit onboarding overlay** appears: six dismissible steps (welcome, upload,
|
||||
hashtags, long-press for more actions, hell/dunkel design pick, and PIN — the PIN step
|
||||
also points at the **Datenschutzhinweis** in My Account when the admin has set one).
|
||||
`localStorage('eventsnap_guide_seen') = 'true'` after dismiss.
|
||||
8. Guest sees the bottom nav: **🏠 Feed · [📷+ FAB] · 👤 Account**.
|
||||
|
||||
@@ -41,7 +42,7 @@ can do what" overview, see [FEATURES.md](FEATURES.md). For manual QA, see
|
||||
5. Wrong PIN: up to 3 attempts. After the third, the account is locked for 15 minutes
|
||||
(`pin_locked_until` is set; further attempts return HTTP 429 with a localized message).
|
||||
|
||||
## 4. PIN forgotten — Host or Admin resets it (planned)
|
||||
## 4. PIN forgotten — Host or Admin resets it
|
||||
|
||||
The PIN is visible in **My Account** as long as `localStorage` is intact on at least one
|
||||
of the user's devices. If lost everywhere, the user asks a Host (or Admin) for a reset.
|
||||
@@ -126,14 +127,23 @@ the Host can clean up later).
|
||||
3. **Event settings** — toggle to lock new uploads (likes / comments / browsing stay open;
|
||||
broadcasts `event-closed` SSE so all clients show a "uploads are locked" banner).
|
||||
4. **Galerie freigeben** — releases the export. Enqueues two export jobs (ZIP + HTML
|
||||
viewer). Progress is visible in the Admin dashboard's Export tab; SSE
|
||||
`export-progress` keeps it live; `export-available` notifies all guests when ready.
|
||||
viewer). Progress is visible on the Host dashboard *and* in the Admin dashboard's Export
|
||||
tab; SSE `export-progress` keeps it live; `export-available` notifies all guests when
|
||||
ready. Once released the button is disabled and reads *"Galerie bereits freigegeben"* —
|
||||
a second release is a 409. If a keepsake half fails, both dashboards show the reason and
|
||||
offer **Erneut versuchen** (rebuild); a ready keepsake can also be rebuilt behind a
|
||||
confirm, during which guest downloads are briefly unavailable.
|
||||
5. **Nutzerverwaltung** — search users; per-user controls:
|
||||
- **Sperren** opens a confirmation modal. Banning **always hides** the user's existing
|
||||
uploads (a banned user's content is "gone" everywhere) — there is no opt-out. Submitting
|
||||
calls `POST /host/users/{id}/ban` (no body).
|
||||
- **Entsperren** lifts the ban. Same authority boundary as ban (below): a plain Host may
|
||||
only unban Guests; only an Admin may unban a Host.
|
||||
- **Entsperren** lifts the ban — and does two further things the confirm sheet now spells
|
||||
out, because both are visible to the whole party: it clears `uploads_hidden`, so **all**
|
||||
of that user's previously hidden uploads reappear in the gallery, the diashow and the
|
||||
export; and it invalidates and rebuilds a released keepsake (`invalidate_and_arm`), so
|
||||
every guest's download is unavailable for as long as that takes. A ban does the mirror
|
||||
image of both. Same authority boundary as ban: a plain Host may only unban Guests; only
|
||||
an Admin may unban a Host.
|
||||
- **Host** promotes a guest to host (Hosts and Admins may do this).
|
||||
- **Degradieren** — demote a Host back to guest. **Only an Admin may change a Host's
|
||||
role.** A plain Host may *not* demote a peer Host: doing so would let them then ban or
|
||||
@@ -181,14 +191,18 @@ the Host can clean up later).
|
||||
- **Stats**: live counts and disk-usage widget (via `sysinfo`).
|
||||
- **Config**: per-file limits (image MB / video MB), rate limits (upload / feed /
|
||||
export), quota tolerance, estimated guest count, compression-worker concurrency,
|
||||
plus the **Datenschutzhinweis** free-text editor and **on/off toggles** for the rate
|
||||
limiters and quotas (planned — see §16). Whitelist on the server side rejects
|
||||
unknown keys. Values are read from the `config` table on each request — no restart
|
||||
needed.
|
||||
- **Export**: list of past export jobs with status badges (pending / running / done /
|
||||
failed) and progress bars; refresh button re-polls.
|
||||
- **Nutzer**: same user list as Host, with the additional Demote action and (planned)
|
||||
PIN-reset on host rows.
|
||||
plus the colour-theme picker, the **Datenschutzhinweis** free-text editor and
|
||||
**on/off toggles** for the rate limiters and quotas (see §18). Whitelist on the
|
||||
server side rejects unknown keys. Values are read from the `config` table on each
|
||||
request — no restart needed.
|
||||
- **Export**: the gallery-release control plus the list of export jobs with status
|
||||
badges (pending / running / done / failed) and progress bars; refresh button
|
||||
re-polls. The release control mirrors the Host dashboard exactly — disabled once
|
||||
released (a second tap would only 409), live keepsake status with progress and the
|
||||
failure reason, and a **Neu erstellen / Erneut versuchen** rebuild as the recovery
|
||||
path for a failed or stale keepsake.
|
||||
- **Nutzer**: same user list as Host, with the additional Demote action (admin-only,
|
||||
see §9) and PIN-reset on host rows.
|
||||
|
||||
## 12. Releasing the export and downloading
|
||||
|
||||
@@ -224,11 +238,14 @@ the Host can clean up later).
|
||||
buttons. Tapping the HTML download first shows an in-app guide modal explaining:
|
||||
"Entpacke die ZIP, öffne `index.html`". Tapping **Herunterladen** triggers the
|
||||
browser download.
|
||||
7. Downloads are rate-limited per IP (default 3 / day).
|
||||
7. Downloads are rate-limited **per user** (default 3 / day), keyed on the user id in
|
||||
`enforce_export_rate` — deliberately not per IP. At a venue where a hundred guests share
|
||||
one NAT'd wifi, a per-IP budget would be exhausted by the third person to tap
|
||||
**Herunterladen** and lock everyone else out of their own keepsake.
|
||||
|
||||
## 13. Diashow (planned)
|
||||
## 13. Diashow
|
||||
|
||||
See [CONCEPT_DIASHOW.md](CONCEPT_DIASHOW.md). Summary of the planned flow:
|
||||
See [CONCEPT_DIASHOW.md](CONCEPT_DIASHOW.md). Summary of the flow:
|
||||
|
||||
1. User taps a **Diashow / Präsentation** action (feed header on tablet/desktop, Account
|
||||
on mobile).
|
||||
@@ -240,7 +257,7 @@ See [CONCEPT_DIASHOW.md](CONCEPT_DIASHOW.md). Summary of the planned flow:
|
||||
immediately.
|
||||
6. Tap or Escape reveals an overlay (pause, dwell selector, exit).
|
||||
|
||||
## 14. Picking a data mode (planned)
|
||||
## 14. Picking a data mode
|
||||
|
||||
1. Guest opens **My Account** → scrolls to **Datennutzung**.
|
||||
2. Two options: **Datensparer (empfohlen)** and **Original**. Saver is the default.
|
||||
@@ -252,14 +269,24 @@ See [CONCEPT_DIASHOW.md](CONCEPT_DIASHOW.md). Summary of the planned flow:
|
||||
5. The viewer (offline HTML export) is unaffected — it already ships with its own pre-
|
||||
bundled `_thumb` / `_full` variants.
|
||||
|
||||
## 15. Leaving an event
|
||||
## 15. Signing out
|
||||
|
||||
1. User opens **My Account** → taps **🚪 Event verlassen**.
|
||||
2. Bottom-sheet confirmation: "Event verlassen?" with **Abmelden** and **Bleiben**.
|
||||
3. Confirming calls `DELETE /api/v1/session` (invalidates the session row), clears the JWT
|
||||
and PIN from `localStorage`, and redirects to the join page.
|
||||
The wording matters here: nothing is *left* and nothing is deleted. The account, the PIN
|
||||
and every uploaded photo survive — the user signs back in with name + PIN whenever they
|
||||
like. ("Event verlassen" read as leaving for good, which the confirmation sheet then
|
||||
contradicted by saying you can come back.)
|
||||
|
||||
## 16. Reading the Datenschutzhinweis (planned)
|
||||
1. User opens **My Account** → Konto section. Two separate actions:
|
||||
- **Abmelden** — this device only (`DELETE /api/v1/session`).
|
||||
- **Auf allen Geräten abmelden** — every session of theirs (`DELETE /api/v1/sessions`),
|
||||
for a lost or borrowed phone.
|
||||
2. Bottom-sheet confirmation naming which of the two it is, with **Abmelden** / **Abbrechen**.
|
||||
3. Confirming invalidates the session row(s), wipes the local JWT, PIN and the IndexedDB
|
||||
upload queue (so the next guest on a shared phone doesn't inherit pending uploads), and
|
||||
redirects to the join page. The session-delete call is best-effort: the token is gone
|
||||
locally either way, so a network failure never traps the user on the page.
|
||||
|
||||
## 16. Reading the Datenschutzhinweis
|
||||
|
||||
1. User opens **My Account** → scrolls to **Datenschutzhinweis**.
|
||||
2. The note is rendered inside a preformatted block (`<pre>`-style: monospace, whitespace
|
||||
@@ -270,11 +297,15 @@ See [CONCEPT_DIASHOW.md](CONCEPT_DIASHOW.md). Summary of the planned flow:
|
||||
4. Admin sets / edits the note in **Admin Dashboard → Config → Datenschutzhinweis**: a
|
||||
tall textarea with a save button. Saved to a single `config` key.
|
||||
|
||||
## 17. Mobile-first gestures (planned)
|
||||
## 17. Mobile-first gestures (partly shipped)
|
||||
|
||||
EventSnap's UI is mobile-first; gestures replace explicit buttons where they're more
|
||||
ergonomic. Buttons are always present as fallback for desktop and accessibility.
|
||||
|
||||
Shipped today: long-press context sheets on posts and comments, and lightbox navigation
|
||||
(on-screen prev/next controls, ← / → arrow keys, and left/right swipe). The remaining rows
|
||||
below are still planned.
|
||||
|
||||
| Gesture | Action |
|
||||
|-------------------------------------------|-------------------------------------------------------|
|
||||
| Long-press on a post (own) | Bottom sheet → Löschen, Original anzeigen, Teilen |
|
||||
@@ -294,7 +325,7 @@ Inspiration: Instagram (double-tap heart, swipe stories), WhatsApp (long-press f
|
||||
context), Telegram (swipe-to-reply on messages — could inform comment threads if those
|
||||
land).
|
||||
|
||||
## 18. Admin toggles a rate limit or quota off (planned)
|
||||
## 18. Admin toggles a rate limit or quota off
|
||||
|
||||
1. Admin opens **Admin Dashboard → Config**.
|
||||
2. **Rate-Limits** section: a master switch and per-endpoint switches (upload / feed /
|
||||
@@ -305,8 +336,9 @@ land).
|
||||
limiter entirely.
|
||||
5. **Quoten** section mirrors the pattern: master toggle plus per-area toggles (storage
|
||||
bytes / upload count).
|
||||
6. When the storage-quota toggle is off, the **"X von Y MB genutzt"** widget in the
|
||||
guest's My Account and upload screen hides itself (no quota → no number to show).
|
||||
6. When the storage-quota toggle is off, the **"Speicher: X / Y"** widget in My Account and
|
||||
on the upload screen hides itself (no quota → no number to show). That widget is
|
||||
staff-only in any case — guests never see it.
|
||||
|
||||
Suggested defaults at deploy time: all toggles **on**, sensible numeric limits.
|
||||
Toggling off is the explicit escape hatch for testing or trusted internal events.
|
||||
|
||||
Reference in New Issue
Block a user