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
+33 -7
View File
@@ -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<void> {
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') {
if (err?.name === 'AbortError') return;
console.error('Share failed:', err);
}
window.alert(
`Share failed: ${err?.message || 'unknown error'}. ` +
`Please use the download button instead.`
);
}
}