Skip to content

Commit 034a7a9

Browse files
committed
Fix mobile share doing nothing; test canShare on the real file
The empty/type-less canShare probe (new File([], 'test')) returns false on current iOS WebKit, so the share was never attempted and it fell through to window.open('_blank'), which iOS blocks — hence "nothing happens". Now fetch the file first and test canShare against the real blob (type forced to video/mp4 when missing), which iOS accepts. Failures are now surfaced as toasts instead of a silent console.warn, so issues are visible on the phone. Falls back to an <a download> object-URL when the Share API is unavailable (e.g. Firefox/iOS).
1 parent 467afa0 commit 034a7a9

1 file changed

Lines changed: 29 additions & 13 deletions

File tree

src/lib/components/download/DownloadVersionPicker.svelte

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -54,26 +54,42 @@
5454
}
5555
5656
async function triggerDownload(id: string) {
57-
// On mobile, open the native share sheet so users can "Save to Photos".
58-
// Check canShare *synchronously* up front (inside the tap gesture) — this
59-
// matches the original working implementation. Then fetch the file and
60-
// share it; the button stays mounted throughout so iOS keeps the share
61-
// activation alive across the single fetch.
62-
const canShareFiles =
63-
isMobileDevice() && navigator.canShare?.({ files: [new File([], 'test')] });
64-
if (!canShareFiles) {
57+
// Desktop: open/download in a new tab (existing behavior).
58+
if (!isMobileDevice()) {
6559
window.open(fileUrl(id), '_blank');
6660
return;
6761
}
62+
63+
// Mobile: fetch the real file, then open the native share sheet so users can
64+
// "Save to Photos". canShare must be tested against the *real* file with a
65+
// real MIME type — an empty/type-less probe returns false on iOS WebKit,
66+
// which is why the old code silently fell through to a blocked window.open.
67+
// Failures surface as toasts so they're visible on the phone.
6868
try {
6969
const res = await fetch(fileUrl(id));
70-
if (!res.ok) throw new Error('fetch failed');
70+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
7171
const blob = await res.blob();
72-
const name = filenameFromResponse(res) || 'download';
73-
const file = new File([blob], name, { type: blob.type });
74-
await navigator.share({ files: [file] });
72+
const name = filenameFromResponse(res) || 'video.mp4';
73+
const file = new File([blob], name, { type: blob.type || 'video/mp4' });
74+
75+
if (navigator.share && navigator.canShare?.({ files: [file] })) {
76+
await navigator.share({ files: [file] });
77+
return;
78+
}
79+
80+
// Share unsupported (e.g. Firefox/iOS) → fall back to a real download.
81+
addToast('error', 'Share sheet unavailable on this browser — downloading instead');
82+
const objUrl = URL.createObjectURL(blob);
83+
const a = document.createElement('a');
84+
a.href = objUrl;
85+
a.download = name;
86+
document.body.appendChild(a);
87+
a.click();
88+
a.remove();
89+
setTimeout(() => URL.revokeObjectURL(objUrl), 10_000);
7590
} catch (e: any) {
76-
if (e.name !== 'AbortError') console.warn('Share failed:', e);
91+
if (e.name === 'AbortError') return; // user dismissed the share sheet
92+
addToast('error', `Couldn't share: ${e.name || e.message || e}`);
7793
}
7894
}
7995

0 commit comments

Comments
 (0)