import React, { useState, useRef, useEffect } from 'react'; import { View, Text, StyleSheet, ActivityIndicator, TouchableOpacity } from 'react-native'; import { WebView } from 'react-native-webview'; import { Ionicons } from '@expo/vector-icons'; import { SafeAreaView } from 'react-native-safe-area-context'; import { useLocalSearchParams, router } from 'expo-router'; import * as SecureStore from 'expo-secure-store'; import { useBeneficiary } from '@/contexts/BeneficiaryContext'; import { AppColors, BorderRadius, FontSizes, Spacing } from '@/constants/theme'; import { FullScreenError } from '@/components/ui/ErrorMessage'; // Use dev.kresoja.net for MobileAppLogin support // After MobileAppLogin() is called on /login, it auto-redirects to /dashboard const LOGIN_URL = 'https://dev.kresoja.net/login'; export default function BeneficiaryDashboardScreen() { const { id } = useLocalSearchParams<{ id: string }>(); const { currentBeneficiary, setCurrentBeneficiary } = useBeneficiary(); const webViewRef = useRef(null); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const [canGoBack, setCanGoBack] = useState(false); const [authToken, setAuthToken] = useState(null); const [userName, setUserName] = useState(null); const [userId, setUserId] = useState(null); const [isTokenLoaded, setIsTokenLoaded] = useState(false); const [hasCalledMobileLogin, setHasCalledMobileLogin] = useState(false); const beneficiaryName = currentBeneficiary?.name || 'Dashboard'; // Load token, username, and userId from SecureStore useEffect(() => { const loadCredentials = async () => { try { const token = await SecureStore.getItemAsync('accessToken'); const user = await SecureStore.getItemAsync('userName'); const uid = await SecureStore.getItemAsync('userId'); setAuthToken(token); setUserName(user); setUserId(uid); console.log('Loaded credentials for WebView:', { hasToken: !!token, user, uid }); } catch (err) { console.error('Failed to load credentials:', err); } finally { setIsTokenLoaded(true); } }; loadCredentials(); }, []); // JavaScript to call MobileAppLogin after page loads // MobileAppLogin sets is_mobile=1, saves auth2, and redirects to /dashboard // This hides desktop navigation (login/logout/dashboard buttons) const getMobileLoginScript = () => { if (!authToken) return ''; return ` (function() { try { // Wait for window.MobileAppLogin to be available var checkInterval = setInterval(function() { if (typeof window.MobileAppLogin === 'function') { clearInterval(checkInterval); console.log('MobileAppLogin found, calling with auth data...'); var authData = { username: '${userName || ''}', token: '${authToken}', user_id: ${userId || 'null'} }; window.MobileAppLogin(authData); console.log('MobileAppLogin called successfully'); } }, 100); // Timeout after 5 seconds setTimeout(function() { clearInterval(checkInterval); console.log('MobileAppLogin timeout - function not found'); }, 5000); } catch(e) { console.error('Failed to call MobileAppLogin:', e); } })(); true; `; }; const handleRefresh = () => { setError(null); setIsLoading(true); setHasCalledMobileLogin(false); // Reset to call MobileAppLogin again webViewRef.current?.reload(); }; const handleWebViewBack = () => { if (canGoBack) { webViewRef.current?.goBack(); } }; const handleNavigationStateChange = (navState: any) => { setCanGoBack(navState.canGoBack); // Auto-relogin when session expires (redirected to /login) // If we're on /login and MobileAppLogin was already called, session expired const url = navState.url || ''; const isOnLoginPage = url.includes('/login'); if (isOnLoginPage && hasCalledMobileLogin && authToken) { console.log('[Dashboard] Session expired, re-authenticating...'); // Reset and call MobileAppLogin again setHasCalledMobileLogin(false); setTimeout(() => { setHasCalledMobileLogin(true); webViewRef.current?.injectJavaScript(getMobileLoginScript()); }, 1000); } }; const handleError = () => { setError('Failed to load dashboard. Please check your internet connection.'); setIsLoading(false); }; const handleGoBack = () => { router.back(); }; // Wait for token to load before showing WebView if (!isTokenLoaded) { return ( {beneficiaryName} Preparing dashboard... ); } if (error) { return ( {beneficiaryName} ); } return ( {/* Header */} {currentBeneficiary && ( {currentBeneficiary.name.charAt(0).toUpperCase()} )} {beneficiaryName} {currentBeneficiary?.relationship && ( {currentBeneficiary.relationship} )} {canGoBack && ( )} {/* WebView */} setIsLoading(true)} onLoadEnd={() => { setIsLoading(false); // Call MobileAppLogin only once after first load if (!hasCalledMobileLogin && authToken) { setHasCalledMobileLogin(true); // Small delay to ensure React app has mounted setTimeout(() => { webViewRef.current?.injectJavaScript(getMobileLoginScript()); }, 500); } }} onError={handleError} onHttpError={handleError} onNavigationStateChange={handleNavigationStateChange} javaScriptEnabled={true} domStorageEnabled={true} startInLoadingState={true} scalesPageToFit={true} allowsBackForwardNavigationGestures={true} renderLoading={() => ( Loading dashboard... )} /> {isLoading && ( )} {/* Bottom Quick Actions */} { if (currentBeneficiary) { setCurrentBeneficiary(currentBeneficiary); } router.push('/(tabs)/chat'); }} > Ask Julia router.push(`./`)} > Details ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: AppColors.background, }, header: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: Spacing.md, paddingVertical: Spacing.sm, backgroundColor: AppColors.background, borderBottomWidth: 1, borderBottomColor: AppColors.border, }, backButton: { padding: Spacing.xs, }, headerCenter: { flex: 1, flexDirection: 'row', alignItems: 'center', marginLeft: Spacing.sm, }, avatarSmall: { width: 36, height: 36, borderRadius: BorderRadius.full, backgroundColor: AppColors.primaryLight, justifyContent: 'center', alignItems: 'center', marginRight: Spacing.sm, }, avatarText: { fontSize: FontSizes.base, fontWeight: '600', color: AppColors.white, }, headerTitle: { fontSize: FontSizes.lg, fontWeight: '700', color: AppColors.textPrimary, }, headerSubtitle: { fontSize: FontSizes.xs, color: AppColors.textSecondary, }, headerActions: { flexDirection: 'row', alignItems: 'center', }, actionButton: { padding: Spacing.xs, marginLeft: Spacing.xs, }, placeholder: { width: 32, }, webViewContainer: { flex: 1, }, webView: { flex: 1, }, loadingContainer: { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, justifyContent: 'center', alignItems: 'center', backgroundColor: AppColors.background, }, loadingOverlay: { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, justifyContent: 'center', alignItems: 'center', backgroundColor: 'rgba(255,255,255,0.8)', }, loadingText: { marginTop: Spacing.md, fontSize: FontSizes.base, color: AppColors.textSecondary, }, bottomBar: { flexDirection: 'row', justifyContent: 'space-around', paddingVertical: Spacing.sm, paddingHorizontal: Spacing.lg, backgroundColor: AppColors.background, borderTopWidth: 1, borderTopColor: AppColors.border, }, quickAction: { alignItems: 'center', padding: Spacing.sm, }, quickActionText: { fontSize: FontSizes.xs, color: AppColors.primary, marginTop: Spacing.xs, }, });