mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-11-13 07:40:59 +00:00
7b8bd4b166
A share extension puts TREK in the share sheet of Mail, Safari, Files, Wallet and any app that shares a document, a link, text or a photo. It shows what came in (a Wallet pass, a PDF, a mail, the page as Safari rendered it, text, a photo) and the user's trips with their covers, the next one already picked, and hands both to the app, which opens that trip's booking import with the file in place. "Choose later" leaves the trip question to the app's import sheet as before. - ShareExtension target (add-extension.rb learns "share": JavaScript and the localizations go in as resources), bundle com.liketrek.trek.share, signed in the release lane, its plists linted in native.yml - Safari hands over the rendered page (SharePage.js), so a confirmation behind a sign-in arrives whole; other apps' links are fetched - The item travels through the shared keychain group the widgets already use; the app takes it on com.liketrek.trek://share or when it next becomes active - Feature shareSheet; strings in the app's 28 languages
60 lines
2.3 KiB
TypeScript
60 lines
2.3 KiB
TypeScript
import { create } from 'zustand';
|
|
import { hasNativeFeature, loadNativeCapabilities } from './capabilities';
|
|
import { reportNativeFailure } from './reportFailure';
|
|
import { TrekShell, isUnimplemented, type SharedFilePayload } from './trekShell';
|
|
|
|
/**
|
|
* Booking documents shared to TREK ("Open in TREK" on a PDF, a calendar entry,
|
|
* a Wallet pass or a mail). The native side hands them over as base64
|
|
* (TrekShell.takeSharedFile and the sharedFile event); here they become a File
|
|
* waiting for a trip: NativeImportSheet asks which one, and the trip's booking
|
|
* import opens with the file already chosen (BookingImportModal).
|
|
*/
|
|
interface SharedImportState {
|
|
file: File | null;
|
|
/** The trip the user picked for it; the planner of that trip takes it from here. */
|
|
tripId: number | null;
|
|
setFile: (file: File | null) => void;
|
|
chooseTrip: (tripId: number) => void;
|
|
/** The booking import took the file: the queue is empty again. */
|
|
take: () => File | null;
|
|
}
|
|
|
|
export const useSharedImportStore = create<SharedImportState>((set, get) => ({
|
|
file: null,
|
|
tripId: null,
|
|
setFile: (file) => set({ file, tripId: null }),
|
|
chooseTrip: (tripId) => set({ tripId }),
|
|
take: () => {
|
|
const { file } = get();
|
|
set({ file: null, tripId: null });
|
|
return file;
|
|
},
|
|
}));
|
|
|
|
export function toFile(payload: SharedFilePayload): File {
|
|
const binary = atob(payload.data);
|
|
const bytes = new Uint8Array(binary.length);
|
|
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
return new File([bytes], payload.name || 'booking', { type: payload.mimeType || 'application/octet-stream' });
|
|
}
|
|
|
|
/** Picks up a document shared before the UI ran, and listens for the next ones. */
|
|
export async function startSharedFiles(): Promise<void> {
|
|
await loadNativeCapabilities();
|
|
if (!hasNativeFeature('sharedFiles')) return;
|
|
const accept = (payload: SharedFilePayload | null) => {
|
|
if (!payload?.data) return;
|
|
const store = useSharedImportStore.getState();
|
|
store.setFile(toFile(payload));
|
|
// The share sheet asked for the trip already; NativeImportSheet opens it.
|
|
if (typeof payload.tripId === 'number') store.chooseTrip(payload.tripId);
|
|
};
|
|
void TrekShell.addListener('sharedFile', accept);
|
|
try {
|
|
accept((await TrekShell.takeSharedFile()).file);
|
|
} catch (err) {
|
|
if (!isUnimplemented(err)) reportNativeFailure(err);
|
|
}
|
|
}
|