feat(ui): warn before share + surface failures for large files

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.
This commit is contained in:
Helmut
2026-05-29 04:56:52 +02:00
parent 39a8948976
commit 6ff364aacf
+34 -8
View File
@@ -1203,10 +1203,28 @@ export class App implements AfterViewInit, OnInit, OnDestroy {
&& typeof navigator.canShare === 'function'; && 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<void> { async shareDownload(download: Download): Promise<void> {
if (!this.canShareDownloads()) { if (!this.canShareDownloads()) {
return; 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 { try {
const response = await fetch(this.buildDownloadLink(download)); const response = await fetch(this.buildDownloadLink(download));
if (!response.ok) { if (!response.ok) {
@@ -1218,20 +1236,28 @@ export class App implements AfterViewInit, OnInit, OnDestroy {
}); });
const payload: ShareData = { files: [file], title: download.title }; const payload: ShareData = { files: [file], title: download.title };
if (!navigator.canShare(payload)) { if (!navigator.canShare(payload)) {
// File type not shareable on this platform (e.g. desktop browsers, // The platform refused the payload — most commonly because the
// or some MIME types iOS refuses). Bail out so the user can still // file is too large for the iOS share sheet, or the MIME type
// use the regular download button right next to this one. // 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); 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; return;
} }
await navigator.share(payload); await navigator.share(payload);
} catch (err: any) { } catch (err: any) {
// AbortError = user dismissed the share sheet → silent no-op. // AbortError = user dismissed the share sheet → silent no-op.
// Other errors (network, file too big, …) get logged but we don't if (err?.name === 'AbortError') return;
// surface a UI error: the regular download link remains a fallback. console.error('Share failed:', err);
if (err?.name !== 'AbortError') { window.alert(
console.error('Share failed:', err); `Share failed: ${err?.message || 'unknown error'}. ` +
} `Please use the download button instead.`
);
} }
} }