Files
TREK/client/src/utils/fileShare.ts
T
Maurice 73237ffedb feat(tours): bike tours in the planner, CyclOSM, and the planned cards' last points (BIK-01 to BIK-06, RTE-01, IO-01, LIB-02)
The tour type is a dropdown; a bike tour picks its profile, routes for it,
says when a point lies off every way, and shows what it runs on: surfaces,
kinds of way and, for a mountain bike, the trail grades, each pointed at on
the map. A type's own map (OpenHikingMap for a hike, CyclOSM for a bike tour)
is offered while a tour of that type is open, with an admin switch each.

From the planned cards: a hike avoids steep climbs, steps or ferries, nothing
else; a saved tour can be shared where the platform takes GPX files; and the
tours rail adds a tour from one menu: plan new, import GPX, or take one over
from another trip.
2026-10-19 21:33:49 +02:00

38 lines
1.6 KiB
TypeScript

/**
* Handing a file to the system's share sheet (the Web Share API), where the
* platform takes files of that kind. Chrome takes only an allow-list of file
* types, Safari and the phones most, so whether a share is offered is asked
* per file name and type, never assumed from `navigator.share` alone.
*/
/** Whether this browser would hand a file of this name and type to the share sheet. */
export function canShareFile(name: string, type: string): boolean {
if (typeof navigator === 'undefined' || typeof navigator.share !== 'function') return false;
if (typeof navigator.canShare !== 'function') return false;
try {
return navigator.canShare({ files: [new File([''], name, { type })] });
} catch {
// A browser that cannot even build the question cannot share the file either.
return false;
}
}
/**
* 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.
*/
export type FileShareOutcome = 'shared' | 'cancelled' | 'needs-gesture' | '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> {
try {
await navigator.share({ files: [file], title });
return 'shared';
} catch (error: unknown) {
if (error instanceof DOMException && error.name === 'AbortError') return 'cancelled';
if (error instanceof DOMException && error.name === 'NotAllowedError') return 'needs-gesture';
return 'failed';
}
}