From 6ff364aacf8e993adf0ec56d4cec6ad508a09ab0 Mon Sep 17 00:00:00 2001 From: Helmut Date: Fri, 29 May 2026 04:56:52 +0200 Subject: [PATCH] feat(ui): warn before share + surface failures for large files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Web Share fails silently when iOS' share sheet refuses the payload, typically because the file exceeds the platform's soft size limit (~50–100 MB depending on iOS version). The previous patch logged to the console but the user saw nothing — staring at a button that 'does nothing' is poor UX. Adds two layers of feedback: 1. Pre-flight size check (SHARE_SIZE_WARN_BYTES = 80 MB, conservative relative to iOS' actual limit) with a confirm() dialog before the fetch. Avoids spending bandwidth pulling a 150 MB blob into the browser only for navigator.canShare to reject it. 2. Surfaces canShare-rejection AND share()-failure as a visible alert() suggesting the user fall back to the download link next to the share button. Tested locally with files from 0.7 MB up to 150.7 MB: small files share unchanged, the 150 MB file now produces a pre-flight warning the user can dismiss, and any subsequent rejection produces a clear alert instead of silently no-op'ing. --- ui/src/app/app.ts | 42 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/ui/src/app/app.ts b/ui/src/app/app.ts index 03448ee..089b78f 100644 --- a/ui/src/app/app.ts +++ b/ui/src/app/app.ts @@ -1203,10 +1203,28 @@ export class App implements AfterViewInit, OnInit, OnDestroy { && typeof navigator.canShare === 'function'; } + // Conservative warning threshold for the share sheet — iOS' actual + // refusal limit varies between ~50 MB (older versions) and ~150 MB + // (recent ones). 80 MB warns the user before the time-wasting fetch+ + // copy of a too-large file that the platform will then reject. + private static readonly SHARE_SIZE_WARN_BYTES = 80 * 1024 * 1024; + async shareDownload(download: Download): Promise { if (!this.canShareDownloads()) { return; } + // Pre-flight size check: warn the user about the iOS share-sheet + // soft-fail on large files, before we spend time fetching the whole + // file into memory only to have navigator.canShare reject it. + if (download.size && download.size > App.SHARE_SIZE_WARN_BYTES) { + const sizeMb = Math.round(download.size / 1024 / 1024); + const proceed = window.confirm( + `This file is ${sizeMb} MB. iOS' share sheet often refuses files ` + + `larger than ~100 MB and the share will silently fail. ` + + `Try anyway? (Use the download button instead if it fails.)` + ); + if (!proceed) return; + } try { const response = await fetch(this.buildDownloadLink(download)); if (!response.ok) { @@ -1218,20 +1236,28 @@ export class App implements AfterViewInit, OnInit, OnDestroy { }); const payload: ShareData = { files: [file], title: download.title }; if (!navigator.canShare(payload)) { - // File type not shareable on this platform (e.g. desktop browsers, - // or some MIME types iOS refuses). Bail out so the user can still - // use the regular download button right next to this one. + // The platform refused the payload — most commonly because the + // file is too large for the iOS share sheet, or the MIME type + // isn't accepted. Tell the user so they can fall back to the + // download button right next to this one instead of staring at + // a button that quietly did nothing. console.warn('navigator.canShare rejected payload for', download.filename); + window.alert( + `Your device's share sheet doesn't accept this file ` + + `(most likely because it's too large). ` + + `Please use the download button instead.` + ); return; } await navigator.share(payload); } catch (err: any) { // AbortError = user dismissed the share sheet → silent no-op. - // Other errors (network, file too big, …) get logged but we don't - // surface a UI error: the regular download link remains a fallback. - if (err?.name !== 'AbortError') { - console.error('Share failed:', err); - } + if (err?.name === 'AbortError') return; + console.error('Share failed:', err); + window.alert( + `Share failed: ${err?.message || 'unknown error'}. ` + + `Please use the download button instead.` + ); } }