mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-22 06:56:35 +00:00
fix(tours): open the GPX share sheet inside the tap
Sharing a tour fetched the GPX file first and only then called navigator.share, so the tap that asked had often run out by then. The retry path kept that file for one more tap only, and a tap while a sheet was still open made the browser throw InvalidStateError, which read as a failed share. The share button now fetches the file ahead where sharing is offered (not while the planner has unsaved changes), and every tap hands a fresh File built from the kept bytes to the sheet before anything is awaited. A file a tap had to fetch is kept for the next tap. A tap while a sheet is open is let be, and InvalidStateError and a closed sheet stay quiet; only a real failure still says the download is left.
This commit is contained in:
@@ -41,13 +41,15 @@ describe('shareFile (IO-01)', () => {
|
||||
expect(share).toHaveBeenCalledWith({ files: [file], title: 'Kramer' });
|
||||
});
|
||||
|
||||
it('tells a closed sheet, a refused tap and any other failure apart', async () => {
|
||||
it('tells a closed sheet, a refused tap, an open sheet and any other failure apart', async () => {
|
||||
const share = vi.fn();
|
||||
stubShare(share, () => true);
|
||||
share.mockRejectedValueOnce(new DOMException('closed', 'AbortError'));
|
||||
await expect(shareFile(gpx(), 'Kramer')).resolves.toBe('cancelled');
|
||||
share.mockRejectedValueOnce(new DOMException('no activation', 'NotAllowedError'));
|
||||
await expect(shareFile(gpx(), 'Kramer')).resolves.toBe('needs-gesture');
|
||||
share.mockRejectedValueOnce(new DOMException('earlier share still open', 'InvalidStateError'));
|
||||
await expect(shareFile(gpx(), 'Kramer')).resolves.toBe('busy');
|
||||
share.mockRejectedValueOnce(new DOMException('bad data', 'DataError'));
|
||||
await expect(shareFile(gpx(), 'Kramer')).resolves.toBe('failed');
|
||||
share.mockRejectedValueOnce(new Error('odd'));
|
||||
|
||||
@@ -20,9 +20,11 @@ export function canShareFile(name: string, type: string): boolean {
|
||||
/**
|
||||
* How a share ended: `cancelled` when the sheet was closed without a target,
|
||||
* `needs-gesture` when the browser refused because the tap that asked lies too
|
||||
* far back (Safari, after the file had to be fetched first), `failed` otherwise.
|
||||
* far back (Safari, after the file had to be fetched first), `busy` when an
|
||||
* earlier sheet is still open (the browser's InvalidStateError), `failed`
|
||||
* otherwise.
|
||||
*/
|
||||
export type FileShareOutcome = 'shared' | 'cancelled' | 'needs-gesture' | 'failed';
|
||||
export type FileShareOutcome = 'shared' | 'cancelled' | 'needs-gesture' | 'busy' | 'failed';
|
||||
|
||||
/** Opens the share sheet with the file; call it straight from the tap that asks for it. */
|
||||
export async function shareFile(file: File, title: string): Promise<FileShareOutcome> {
|
||||
@@ -32,6 +34,7 @@ export async function shareFile(file: File, title: string): Promise<FileShareOut
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof DOMException && error.name === 'AbortError') return 'cancelled';
|
||||
if (error instanceof DOMException && error.name === 'NotAllowedError') return 'needs-gesture';
|
||||
if (error instanceof DOMException && error.name === 'InvalidStateError') return 'busy';
|
||||
return 'failed';
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user