Sergei 8bc9649146 WellNuo Lite v1.0.0 - simplified version for App Store review
- Removed voice input features
- Simplified profile page (only legal links and logout)
- Chat with AI context working
- Auto-select first beneficiary
- Dashboard WebView intact

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-24 17:13:13 -08:00

280 lines
8.3 KiB
TypeScript

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 * as SecureStore from 'expo-secure-store';
import { useAuth } from '@/contexts/AuthContext';
import { AppColors, FontSizes, Spacing } from '@/constants/theme';
const DASHBOARD_URL = 'https://react.eluxnetworks.net/dashboard';
export default function HomeScreen() {
const { user } = useAuth();
const webViewRef = useRef<WebView>(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [canGoBack, setCanGoBack] = useState(false);
const [authToken, setAuthToken] = useState<string | null>(null);
const [userName, setUserName] = useState<string | null>(null);
const [userId, setUserId] = useState<string | null>(null);
const [isTokenLoaded, setIsTokenLoaded] = useState(false);
// Load credentials 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('Home: Loaded credentials for WebView:', { hasToken: !!token, user, uid });
} catch (err) {
console.error('Failed to load credentials:', err);
} finally {
setIsTokenLoaded(true);
}
};
loadCredentials();
}, []);
// JavaScript to inject auth token into localStorage
// Web app expects auth2 as JSON: {username, token, user_id}
const injectedJavaScript = authToken
? `
(function() {
try {
var authData = {
username: '${userName || ''}',
token: '${authToken}',
user_id: ${userId || 'null'}
};
localStorage.setItem('auth2', JSON.stringify(authData));
console.log('Auth injected:', authData.username);
} catch(e) {
console.error('Failed to inject token:', e);
}
})();
true;
`
: '';
const handleRefresh = () => {
setError(null);
setIsLoading(true);
webViewRef.current?.reload();
};
const handleWebViewBack = () => {
if (canGoBack) {
webViewRef.current?.goBack();
}
};
const handleNavigationStateChange = (navState: any) => {
setCanGoBack(navState.canGoBack);
};
const handleError = () => {
setError('Failed to load dashboard. Please check your internet connection.');
setIsLoading(false);
};
// Wait for token to load
if (!isTokenLoaded) {
return (
<SafeAreaView style={styles.container} edges={['top']}>
<View style={styles.header}>
<View>
<Text style={styles.greeting}>Hello, {user?.user_name || 'User'}</Text>
<Text style={styles.headerTitle}>Dashboard</Text>
</View>
</View>
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color={AppColors.primary} />
<Text style={styles.loadingText}>Preparing dashboard...</Text>
</View>
</SafeAreaView>
);
}
if (error) {
return (
<SafeAreaView style={styles.container} edges={['top']}>
<View style={styles.header}>
<View>
<Text style={styles.greeting}>Hello, {user?.user_name || 'User'}</Text>
<Text style={styles.headerTitle}>Dashboard</Text>
</View>
<TouchableOpacity style={styles.refreshButton} onPress={handleRefresh}>
<Ionicons name="refresh" size={22} color={AppColors.primary} />
</TouchableOpacity>
</View>
<View style={styles.errorContainer}>
<Ionicons name="cloud-offline-outline" size={64} color={AppColors.textMuted} />
<Text style={styles.errorTitle}>Connection Error</Text>
<Text style={styles.errorText}>{error}</Text>
<TouchableOpacity style={styles.retryButton} onPress={handleRefresh}>
<Text style={styles.retryButtonText}>Try Again</Text>
</TouchableOpacity>
</View>
</SafeAreaView>
);
}
return (
<SafeAreaView style={styles.container} edges={['top']}>
{/* Header */}
<View style={styles.header}>
<View>
<Text style={styles.greeting}>Hello, {user?.user_name || 'User'}</Text>
<Text style={styles.headerTitle}>Dashboard</Text>
</View>
<View style={styles.headerActions}>
{canGoBack && (
<TouchableOpacity style={styles.actionButton} onPress={handleWebViewBack}>
<Ionicons name="chevron-back" size={22} color={AppColors.primary} />
</TouchableOpacity>
)}
<TouchableOpacity style={styles.actionButton} onPress={handleRefresh}>
<Ionicons name="refresh" size={22} color={AppColors.primary} />
</TouchableOpacity>
</View>
</View>
{/* WebView Dashboard */}
<View style={styles.webViewContainer}>
<WebView
ref={webViewRef}
source={{ uri: DASHBOARD_URL }}
style={styles.webView}
onLoadStart={() => setIsLoading(true)}
onLoadEnd={() => setIsLoading(false)}
onError={handleError}
onHttpError={handleError}
onNavigationStateChange={handleNavigationStateChange}
javaScriptEnabled={true}
domStorageEnabled={true}
startInLoadingState={true}
scalesPageToFit={true}
allowsBackForwardNavigationGestures={true}
injectedJavaScriptBeforeContentLoaded={injectedJavaScript}
injectedJavaScript={injectedJavaScript}
renderLoading={() => (
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color={AppColors.primary} />
<Text style={styles.loadingText}>Loading dashboard...</Text>
</View>
)}
/>
{isLoading && (
<View style={styles.loadingOverlay}>
<ActivityIndicator size="large" color={AppColors.primary} />
</View>
)}
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: AppColors.background,
},
header: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingHorizontal: Spacing.lg,
paddingVertical: Spacing.md,
backgroundColor: AppColors.background,
borderBottomWidth: 1,
borderBottomColor: AppColors.border,
},
greeting: {
fontSize: FontSizes.sm,
color: AppColors.textSecondary,
},
headerTitle: {
fontSize: FontSizes.xl,
fontWeight: '700',
color: AppColors.textPrimary,
},
headerActions: {
flexDirection: 'row',
alignItems: 'center',
},
actionButton: {
padding: Spacing.xs,
marginLeft: Spacing.xs,
},
refreshButton: {
padding: Spacing.xs,
},
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,
},
errorContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: Spacing.xl,
},
errorTitle: {
fontSize: FontSizes.lg,
fontWeight: '600',
color: AppColors.textPrimary,
marginTop: Spacing.md,
},
errorText: {
fontSize: FontSizes.base,
color: AppColors.textSecondary,
textAlign: 'center',
marginTop: Spacing.xs,
},
retryButton: {
marginTop: Spacing.lg,
paddingHorizontal: Spacing.xl,
paddingVertical: Spacing.md,
backgroundColor: AppColors.primary,
borderRadius: 8,
},
retryButtonText: {
color: AppColors.white,
fontSize: FontSizes.base,
fontWeight: '600',
},
});