import React, { useEffect } from 'react'; import { ActivityIndicator, Image, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; import { useRouter } from 'expo-router'; import { parseShareIntent, ShareIntentModule } from 'expo-share-intent'; import * as ExpoLinking from 'expo-linking'; import { SHARE_INTENT_KEY, storeSharedImageUri } from '../utils/shareHandoff'; import { resolveSharedImageUri, summarizeShareIntent } from '../utils/shareIntent'; const SHARE_INTENT_SCHEME = 'greenlens'; const SHARE_INTENT_OPTIONS = { scheme: SHARE_INTENT_SCHEME, }; const isShareIntentUrl = (url: string | null | undefined) => Boolean(url?.includes(`${SHARE_INTENT_SCHEME}://dataUrl=`)); export default function ShareIntentCallbackScreen() { const router = useRouter(); const [isWaiting, setIsWaiting] = React.useState(true); const [failureDetails, setFailureDetails] = React.useState(null); const [previewUri, setPreviewUri] = React.useState(null); const pendingKeyRef = React.useRef(null); const getIntentCalledRef = React.useRef(false); const settledRef = React.useRef(false); const linkingUrl = ExpoLinking.useLinkingURL(); useEffect(() => { const fallback = setTimeout(() => { if (settledRef.current) return; setIsWaiting(false); setFailureDetails((current) => current || 'Keine Antwort von der Share Extension.'); }, 15000); return () => clearTimeout(fallback); }, []); useEffect(() => { const showFailure = (message: string) => { settledRef.current = true; ShareIntentModule?.clearShareIntent(SHARE_INTENT_KEY); setPreviewUri(null); setIsWaiting(false); setFailureDetails(message); }; const changeSubscription = ShareIntentModule?.addListener('onChange', async (event) => { try { setIsWaiting(true); setFailureDetails(null); const shareIntent = parseShareIntent(event.value, SHARE_INTENT_OPTIONS); if (__DEV__) { console.debug('[ShareIntentCallback]', summarizeShareIntent(shareIntent)); } const resolved = await resolveSharedImageUri(shareIntent); if (!resolved) { showFailure('Die Quelle hat keinen nutzbaren Bildanhang oder Bild-Link geliefert.'); return; } settledRef.current = true; const sharedImageKey = storeSharedImageUri(resolved.uri); ShareIntentModule?.clearShareIntent(SHARE_INTENT_KEY); if (resolved.requiresConfirmation) { pendingKeyRef.current = sharedImageKey; setIsWaiting(false); setPreviewUri(resolved.uri); return; } router.replace({ pathname: '/scanner', params: { sharedImageKey }, }); } catch (error) { console.error('[ShareIntentCallback] failed to parse share intent', error); showFailure('Die Share-Daten konnten nicht gelesen werden.'); } }); const errorSubscription = ShareIntentModule?.addListener('onError', (event) => { console.error('[ShareIntentCallback] native error', event.value); showFailure(event.value || 'Die Share Extension hat einen Fehler gemeldet.'); }); const url = linkingUrl || ExpoLinking.getLinkingURL(); if (url && isShareIntentUrl(url) && !getIntentCalledRef.current) { getIntentCalledRef.current = true; ShareIntentModule?.getShareIntent(url); } return () => { changeSubscription?.remove(); errorSubscription?.remove(); }; }, [linkingUrl, router]); return ( {isWaiting ? ( ) : previewUri ? ( Pflanze gefunden Ist das die Pflanze, die du scannen möchtest? Wenn das nicht die Pflanze ist, tippe „Scanner öffnen" und teile das Bild direkt in Safari. { if (pendingKeyRef.current) { router.replace({ pathname: '/scanner', params: { sharedImageKey: pendingKeyRef.current } }); } }} > Diese Pflanze scannen router.replace('/scanner')}> Scanner öffnen ) : ( Bild konnte nicht geladen werden. Tippe und halte das Bild in Safari oder Instagram, wähle „Bild teilen" und teile es direkt mit GreenLens. {failureDetails ? ( {failureDetails} ) : null} router.replace('/scanner')}> Scanner öffnen )} ); } const styles = StyleSheet.create({ container: { flex: 1, alignItems: 'center', justifyContent: 'center', backgroundColor: '#111813', padding: 24, }, messageBox: { alignItems: 'center', gap: 14, }, title: { color: '#F4F7F1', fontSize: 18, fontWeight: '700', textAlign: 'center', }, body: { color: '#CAD3CB', fontSize: 14, lineHeight: 20, textAlign: 'center', }, detail: { color: '#8F9A91', fontSize: 12, lineHeight: 17, textAlign: 'center', }, previewImage: { width: '100%', aspectRatio: 4 / 3, borderRadius: 16, backgroundColor: '#1E2820', }, button: { marginTop: 8, borderRadius: 12, backgroundColor: '#D7F5A2', paddingHorizontal: 18, paddingVertical: 12, }, buttonText: { color: '#111813', fontSize: 14, fontWeight: '800', }, hint: { color: '#8F9A91', fontSize: 11, lineHeight: 16, textAlign: 'center', marginTop: -6, }, buttonSecondary: { marginTop: 4, borderRadius: 12, paddingHorizontal: 18, paddingVertical: 12, }, buttonSecondaryText: { color: '#8F9A91', fontSize: 14, fontWeight: '600', textAlign: 'center', }, });