import React, { useRef } from 'react'; import { Platform, StyleSheet, View, ActivityIndicator } from 'react-native'; import { WebView } from 'react-native-webview'; import type { WebViewMessageEvent } from 'react-native-webview'; import type { ApacuanaConfig } from '../types'; // onPermissionRequest is not typed in react-native-webview@13.x but exists at runtime // on Android to grant camera/microphone access from getUserMedia() calls inside the WebView. const androidCameraProps = Platform.OS === 'android' ? ({ onPermissionRequest: (request: any) => request.grant(request.resources), } as any) : {}; interface WebViewLivenessProps { sessionId: string; onComplete: () => void; onError: (error: Error) => void; onCancel: () => void; config: ApacuanaConfig; } const WebViewLiveness: React.FC = ({ sessionId, onComplete, onError, onCancel, config, }) => { const webViewRef = useRef(null); const urlEnv = config.apiUrl === 'https://api.dev.apacuana.3dlinkweb.com' ? 'dev' : config.apiUrl === 'https://api.apacuana.pre.3dlinkweb.com' ? 'staging' : 'production'; const baseUrl = urlEnv === 'dev' ? 'https://web.onbo.dev.apacuana.3dlinkweb.com/register/fl-sdk/' : urlEnv === 'staging' ? 'https://web.onbo.pre.apacuana.3dlinkweb.com/register/fl-sdk/' : 'https://web.onbo.production.apacuana.3dlinkweb.com/register/fl-sdk/'; const handleWebViewMessage = (event: WebViewMessageEvent) => { try { const data = JSON.parse(event.nativeEvent.data); console.log('Mensaje recibido desde WebView:', data.type); switch (data.type) { // 1. La página web nos avisa que está lista para recibir datos case 'WEB_PAGE_READY': if (webViewRef.current) { console.log('Página web lista. Enviando configuración...', config); const message = { type: 'CONFIG_DATA', payload: { customerId: config.customerId, secretKey: config.secretKey, apiKey: config.apiKey, apiUrl: config.apiUrl, integrationType: config.integrationType, sessionId: sessionId, verificationId: config.verificationId, encryptionKey: config.encryptionKey, }, }; webViewRef.current.postMessage(JSON.stringify(message)); } break; // 2. La página web nos envía el resultado exitoso case 'LIVENESS_SUCCESS': onComplete(); break; case 'LIVENESS_ERROR': onCancel(); break; } } catch (error) { console.error('Error al procesar el mensaje de la WebView', error); onError(error as Error); } }; return ( ( )} mediaPlaybackRequiresUserAction={false} allowsInlineMediaPlayback={true} domStorageEnabled={true} mediaCapturePermissionGrantType="grant" androidLayerType="hardware" {...androidCameraProps} /> ); }; const styles = StyleSheet.create({ container: { flex: 1, width: '100%', alignSelf: 'stretch', }, webview: { flex: 1, width: '100%', borderRadius: 8, overflow: 'hidden', }, loader: { ...StyleSheet.absoluteFillObject, // backgroundColor: 'rgba(0,0,0,0.7)', }, }); export default WebViewLiveness;