fix: close the last three guest-facing dead ends (items 7-9)
C1 — a failed page-append silently ended infinite scroll `loadMore`'s catch showed a toast and changed no state, unlike every sibling error path in the file. `nextCursor` survived so the feed was still technically paginable, but the IntersectionObserver only fires on a CHANGE: after a failed append nothing scrolls and no rows are added, so it never re-fires. One 429 or wifi blip and the guest concluded the gallery was 20 photos. Now leaves a retry control at the sentinel — a toast that fades in 5s is not an affordance — resuming from the untouched cursor. C2 — the export page reported downloads that never happened `downloadFile` toasted 'Download gestartet' the instant it assigned the iframe's src, before a single byte existed. Since the iframe swallows errors BY DESIGN (a top-level navigation to a 404 would unload the PWA), a failure produced a green success message, a consumed single-use ticket, and one of only three daily slots spent — repeatable until the day's allowance was gone, on the screen that is the whole point of the app. Root cause is two sources of truth: `export_status` reports `done` from `export_job` and enables the button, while the download resolves through `export_current.file_path` plus a `Path::exists()`. They can legitimately disagree. `export_ticket` now takes a `kind` and calls the existing `resolve_export_file` BEFORE charging the rate slot, so a missing archive fails honestly on a plain fetch that `toastError` already renders. Not the HEAD probe ruled out elsewhere: it reads the same indexed row the download will read and touches no ticket, so it cannot consume anything. The parameter is optional, so an older client degrades to today's behaviour rather than breaking. C3 — the WhatsApp journey could dead-end with no error at all The join link travels through guest group chats, and a link tapped inside one opens in that app's browser, where the file picker and getUserMedia both depend on the host app having wired them up. When they aren't, the buttons do nothing — no error, nothing to act on. Two targeted changes rather than a UI rebuild: the camera error panel now offers "Aus Galerie wählen" (its advice to change "Browsereinstellungen" refers to settings that do not exist in a webview, so retrying could never help those guests), and the sheet carries a standing one-line hint to open the link in Safari or Chrome. Deliberately no user-agent sniffing: a sniff list is wrong for every browser it has not heard of, while a quiet standing hint costs one line and is never wrong. The hint lives in UploadSheet rather than the root layout because both layout banners are gated on `$showBottomNav`, which `/upload` turns off — one there would never render on the composer. Verified: 151/151 backend tests against a live Postgres, clippy clean, 58/58 vitest, svelte-check 0 errors, eslint clean, both builds. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -156,6 +156,12 @@
|
||||
oncapture={handleCapture}
|
||||
onclose={handleCameraClose}
|
||||
onready={handleCameraReady}
|
||||
onpickfile={() => {
|
||||
// Close the camera first, then open the OS picker — the sheet is still mounted
|
||||
// behind it, so this returns the guest to a working path instead of a dead end.
|
||||
showCamera = false;
|
||||
openGallery();
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
@@ -288,6 +294,20 @@
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">Jetzt aufnehmen</p>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<!-- The in-app-browser escape hatch.
|
||||
The join link travels through WhatsApp groups, and a link tapped inside one opens
|
||||
in that app's own browser rather than Safari or Chrome. There, the file picker and
|
||||
`getUserMedia` both depend on the host app having wired them up, and when they are
|
||||
not wired up the buttons above simply do nothing — no error, nothing to act on,
|
||||
with no operator to ask. Deliberately NOT user-agent sniffing: a sniff list is
|
||||
wrong for browsers it has never heard of, whereas a quiet standing hint costs a
|
||||
line of text and is never wrong. It lives here rather than in the root layout
|
||||
because both layout banners are gated on `$showBottomNav`, which `/upload` turns
|
||||
off — a banner there would never render on the composer. -->
|
||||
<p class="px-1 pt-1 text-center text-xs text-gray-500 dark:text-gray-400">
|
||||
Nichts passiert beim Tippen? Öffne den Link in Safari oder Chrome.
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
<!-- Queue access. The FAB badge is the ONLY signal a guest gets that an upload is
|
||||
|
||||
Reference in New Issue
Block a user