fix(user-flow): close offline-queue, export, session, ban & realtime gaps
Comprehensive user-flow review across guest/host/admin roles, then fixes with
e2e regression guards. Highlights:
- Offline upload queue auto-resumes on reconnect: network errors keep items
pending (not error), 4xx are terminal (no infinite retry), quota exhaustion
returns a distinct 413; queue cap + dedup.
- Export lifecycle: release atomically locks uploads; reopen invalidates and
re-release regenerates the keepsake; workers claim their job atomically so a
reopen->re-release can't corrupt the ZIP; startup re-spawns interrupted
exports.
- Sessions slide on activity (no 30-day cliff); JWT expiry deferred to the
revocable session row; sign-out-everywhere + revoke-on-PIN-reset.
- Ban always hides content (v_feed / find_visible_media / export filter
is_banned) but stays a read-only ban per USER_JOURNEYS §10 — sessions are
not revoked, read access + keepsake download preserved.
- Realtime: server-clock SSE delta cursor; event-closed/opened drive the UI
live; feed_delta rate-limited; like returns {liked, like_count} to fix
multi-device drift; lightbox live comments; diashow delta backfill.
- Forgotten-PIN in-app request flow; simultaneous same-name join returns 409;
quota increment is transactional; operator floor.
Adds e2e/specs/10-flow-review/ (offline resume, export integrity, deterministic
anti-race guard) and updates existing specs for the new contracts.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -127,7 +127,6 @@
|
||||
|
||||
// Ban modal state
|
||||
let banTarget = $state<UserSummary | null>(null);
|
||||
let banHideUploads = $state(false);
|
||||
let banSubmitting = $state(false);
|
||||
|
||||
// PIN reset state — `pinModal` holds the freshly-issued plaintext PIN. We forget it
|
||||
@@ -254,14 +253,13 @@
|
||||
|
||||
function openBanModal(user: UserSummary) {
|
||||
banTarget = user;
|
||||
banHideUploads = false;
|
||||
}
|
||||
|
||||
async function confirmBan() {
|
||||
if (!banTarget) return;
|
||||
banSubmitting = true;
|
||||
try {
|
||||
await api.post(`/host/users/${banTarget.id}/ban`, { hide_uploads: banHideUploads });
|
||||
await api.post(`/host/users/${banTarget.id}/ban`, {});
|
||||
toast(`${banTarget.display_name} wurde gesperrt.`, 'success');
|
||||
banTarget = null;
|
||||
users = await api.get<UserSummary[]>('/host/users');
|
||||
@@ -416,17 +414,14 @@
|
||||
{/if}
|
||||
</Modal>
|
||||
|
||||
<!-- Ban modal — checkbox-bearing, so uses the Modal shell instead of ConfirmSheet. -->
|
||||
<!-- Ban modal — ban always hides now, so this is a plain confirm (no checkbox). -->
|
||||
<Modal open={banTarget !== null} titleId="admin-ban-modal-title" onClose={() => (banTarget = null)}>
|
||||
{#if banTarget}
|
||||
<h2 id="admin-ban-modal-title" class="mb-1 text-lg font-bold text-gray-900 dark:text-gray-100">Benutzer sperren</h2>
|
||||
<p class="mb-4 text-sm text-gray-600 dark:text-gray-400">
|
||||
Was soll mit den Uploads von <strong>{banTarget.display_name}</strong> passieren?
|
||||
<strong>{banTarget.display_name}</strong> wird gesperrt: alle Uploads verschwinden aus
|
||||
Galerie, Diashow und Export, und die Sitzung wird beendet. Rückgängig machbar über „Entsperren“.
|
||||
</p>
|
||||
<label class="mb-4 flex cursor-pointer items-center gap-3 rounded-lg border border-gray-200 p-3 dark:border-gray-700">
|
||||
<input type="checkbox" bind:checked={banHideUploads} class="h-4 w-4 rounded border-gray-300 text-red-600 focus:ring-red-500 dark:border-gray-600" />
|
||||
<span class="text-sm text-gray-700 dark:text-gray-300">Uploads aus der Galerie ausblenden</span>
|
||||
</label>
|
||||
<div class="flex gap-2">
|
||||
<button onclick={() => (banTarget = null)} class="flex-1 rounded-lg border border-gray-300 py-2 text-sm text-gray-700 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">Abbrechen</button>
|
||||
<button onclick={confirmBan} disabled={banSubmitting} class="flex-1 rounded-lg bg-red-600 py-2 text-sm font-medium text-white hover:bg-red-700 active:bg-red-700 disabled:opacity-50 dark:bg-red-500 dark:hover:bg-red-400 dark:active:bg-red-400">
|
||||
|
||||
Reference in New Issue
Block a user