SEO
This commit is contained in:
@@ -11,6 +11,7 @@ import * as ImageManipulator from 'expo-image-manipulator';
|
||||
import * as Haptics from 'expo-haptics';
|
||||
import * as AppleAuthentication from 'expo-apple-authentication';
|
||||
import Constants from 'expo-constants';
|
||||
import { ShareIntentModule } from 'expo-share-intent';
|
||||
import { useSafeAnalytics } from '../services/analytics';
|
||||
import { useApp } from '../context/AppContext';
|
||||
import { useColors } from '../constants/Colors';
|
||||
@@ -22,6 +23,7 @@ import { isBackendApiError } from '../services/backend/contracts';
|
||||
import { createIdempotencyKey } from '../utils/idempotency';
|
||||
import { AuthService } from '../services/authService';
|
||||
import { getMockPlantByImage } from '../services/backend/mockCatalog';
|
||||
import { consumeSharedImageUri, SHARE_INTENT_KEY } from '../utils/shareHandoff';
|
||||
|
||||
const HEALTH_CHECK_CREDIT_COST = 2;
|
||||
const DEMO_SCAN_LIMIT = 5;
|
||||
@@ -129,7 +131,7 @@ const getBillingCopy = (language: 'de' | 'en' | 'es') => {
|
||||
};
|
||||
|
||||
export default function ScannerScreen() {
|
||||
const params = useLocalSearchParams<{ mode?: string; plantId?: string; sharedImageUri?: string }>();
|
||||
const params = useLocalSearchParams<{ mode?: string; plantId?: string; sharedImageKey?: string; sharedImageUri?: string }>();
|
||||
const posthog = useSafeAnalytics();
|
||||
const {
|
||||
isDarkMode,
|
||||
@@ -160,6 +162,9 @@ export default function ScannerScreen() {
|
||||
const sharedImageUri = Array.isArray(params.sharedImageUri)
|
||||
? params.sharedImageUri[0]
|
||||
: params.sharedImageUri;
|
||||
const sharedImageKey = Array.isArray(params.sharedImageKey)
|
||||
? params.sharedImageKey[0]
|
||||
: params.sharedImageKey;
|
||||
const hasActiveEntitlement = billingSummary?.entitlement?.plan === 'pro'
|
||||
&& billingSummary?.entitlement?.status === 'active';
|
||||
const isDemoMode = !hasActiveEntitlement;
|
||||
@@ -197,17 +202,10 @@ export default function ScannerScreen() {
|
||||
};
|
||||
}, [isExpoGo]);
|
||||
|
||||
const hasProcessedSharedImage = useRef(false);
|
||||
useEffect(() => {
|
||||
if (!sharedImageUri || hasProcessedSharedImage.current) return;
|
||||
hasProcessedSharedImage.current = true;
|
||||
(async () => {
|
||||
const analysisUri = await resizeForAnalysis(sharedImageUri);
|
||||
setDemoResultVisible(false);
|
||||
setSelectedImage(sharedImageUri);
|
||||
analyzeImage(analysisUri, sharedImageUri);
|
||||
})();
|
||||
}, [sharedImageUri]);
|
||||
const lastProcessedShareToken = useRef<string | null>(null);
|
||||
const sharedAnalysisInFlightToken = useRef<string | null>(null);
|
||||
const resizeForAnalysisRef = useRef<(uri: string) => Promise<string>>(async (uri) => uri);
|
||||
const analyzeImageRef = useRef<(imageUri: string, galleryImageUri?: string) => Promise<void>>(async () => {});
|
||||
|
||||
useEffect(() => {
|
||||
if (!isAnalyzing) {
|
||||
@@ -256,8 +254,8 @@ export default function ScannerScreen() {
|
||||
try {
|
||||
const result = await ImageManipulator.manipulateAsync(
|
||||
uri,
|
||||
[{ resize: { width: 768 } }],
|
||||
{ compress: 0.7, format: ImageManipulator.SaveFormat.JPEG, base64: true },
|
||||
[{ resize: { width: 1280 } }],
|
||||
{ compress: 0.9, format: ImageManipulator.SaveFormat.JPEG, base64: true },
|
||||
);
|
||||
return result.base64 ? `data:image/jpeg;base64,${result.base64}` : result.uri;
|
||||
} catch {
|
||||
@@ -472,6 +470,45 @@ export default function ScannerScreen() {
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
resizeForAnalysisRef.current = resizeForAnalysis;
|
||||
analyzeImageRef.current = analyzeImage;
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const shareToken = sharedImageKey || sharedImageUri;
|
||||
if (!shareToken || isLoadingBilling || isAnalyzing) return;
|
||||
if (lastProcessedShareToken.current === shareToken) return;
|
||||
if (sharedAnalysisInFlightToken.current) return;
|
||||
|
||||
const handoffImageUri = consumeSharedImageUri(sharedImageKey);
|
||||
const nextSharedImageUri = handoffImageUri || sharedImageUri;
|
||||
if (!nextSharedImageUri) return;
|
||||
|
||||
lastProcessedShareToken.current = shareToken;
|
||||
sharedAnalysisInFlightToken.current = shareToken;
|
||||
ShareIntentModule?.clearShareIntent(SHARE_INTENT_KEY);
|
||||
|
||||
let cancelled = false;
|
||||
(async () => {
|
||||
try {
|
||||
const analysisUri = await resizeForAnalysisRef.current(nextSharedImageUri);
|
||||
if (cancelled || sharedAnalysisInFlightToken.current !== shareToken) return;
|
||||
setDemoResultVisible(false);
|
||||
setSelectedImage(analysisUri);
|
||||
await analyzeImageRef.current(analysisUri, nextSharedImageUri);
|
||||
} finally {
|
||||
if (sharedAnalysisInFlightToken.current === shareToken) {
|
||||
sharedAnalysisInFlightToken.current = null;
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [sharedImageKey, sharedImageUri, isLoadingBilling, isAnalyzing]);
|
||||
|
||||
const takePicture = async () => {
|
||||
if (!cameraRef.current || isAnalyzing) return;
|
||||
await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);
|
||||
|
||||
Reference in New Issue
Block a user