From b8ccd7a9bc544bd87820733417a07cedfd1042c0 Mon Sep 17 00:00:00 2001 From: Patrick Roza Date: Sat, 25 Jul 2026 08:03:03 +0200 Subject: [PATCH] Keep older dev clients usable without Expo sharing --- .../sharing/IncomingShareProvider.tsx | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/apps/mobile/src/features/sharing/IncomingShareProvider.tsx b/apps/mobile/src/features/sharing/IncomingShareProvider.tsx index 04d371e5d2f..286b4a3a0fd 100644 --- a/apps/mobile/src/features/sharing/IncomingShareProvider.tsx +++ b/apps/mobile/src/features/sharing/IncomingShareProvider.tsx @@ -1,12 +1,7 @@ +import { requireOptionalNativeModule } from "expo"; import Constants from "expo-constants"; import * as Crypto from "expo-crypto"; -import { - clearSharedPayloads, - getResolvedSharedPayloadsAsync, - getSharedPayloads, - type ResolvedSharePayload, - type SharePayload, -} from "expo-sharing"; +import type { ResolvedSharePayload, SharePayload } from "expo-sharing"; import React, { useCallback, useEffect, useEffectEvent, useMemo, useRef, useState } from "react"; import { Alert, AppState, Platform } from "react-native"; @@ -22,6 +17,17 @@ import { writeIncomingShareDraft, } from "./incoming-share-storage"; +type ExpoSharingModule = { + readonly getSharedPayloads: () => SharePayload[]; + readonly getResolvedSharedPayloadsAsync: () => Promise; + readonly clearSharedPayloads: () => void; +}; + +// A development client can connect to Metro after the JS checkout gains a new +// native dependency. Keep the app usable in that state; rebuilding the client +// enables incoming sharing without changing this bundle. +const expoSharing = requireOptionalNativeModule("ExpoSharing"); + type IncomingShareContextValue = { readonly pendingShare: IncomingShareDraft | null; readonly isLoading: boolean; @@ -39,6 +45,9 @@ type IncomingShareContextValue = { const IncomingShareContext = React.createContext(null); function receiveSharingEnabled(): boolean { + if (expoSharing === null) { + return false; + } if (Platform.OS === "android") { return true; } @@ -49,8 +58,11 @@ function receiveSharingEnabled(): boolean { } async function resolvedPayloadsForImages(): Promise> { + if (expoSharing === null) { + return []; + } try { - return await getResolvedSharedPayloadsAsync(); + return await expoSharing.getResolvedSharedPayloadsAsync(); } catch (error) { // iOS already gives the containing app a copied file:// URL, so raw // payloads remain usable. Android normally resolves content:// into a @@ -121,8 +133,8 @@ const incomingShareInbox = new IncomingShareInbox({ loadDrafts: loadIncomingShareDrafts, writeDraft: writeIncomingShareDraft, removeDraft: removeIncomingShareDraft, - getPayloads: getSharedPayloads, - clearPayloads: clearSharedPayloads, + getPayloads: () => expoSharing?.getSharedPayloads() ?? [], + clearPayloads: () => expoSharing?.clearSharedPayloads(), buildDraft: async ({ payloads, id, createdAt }) => { const cleanupUris = new Set(); const resolvedPayloads = payloads.some((payload) => payload.shareType === "image")