import React, { useState, useEffect, useCallback } from 'react'; import { View, Text, TouchableOpacity, StyleSheet, SafeAreaView, ScrollView, StatusBar, Alert, Switch, Linking, ActivityIndicator, Modal, TextInput, Platform } from 'react-native'; import AsyncStorage from '@react-native-async-storage/async-storage'; import * as SecureStore from 'expo-secure-store'; import { Clipboard } from 'react-native'; import { useNavigation, NavigationProp } from '@react-navigation/native'; import type { AlertButton } from 'react-native'; import { RootStackParamList } from '../navigation/AppNavigator'; import { KurdistanColors } from '../theme/colors'; // Profile type for Supabase interface UserProfile { id?: string; full_name: string; username: string; bio?: string; notifications_push: boolean; notifications_email: boolean; updated_at?: string; } import { useTheme } from '../contexts/ThemeContext'; import { useBiometricAuth } from '../contexts/BiometricAuthContext'; import { useAuth } from '../contexts/AuthContext'; import { usePezkuwi, NETWORKS } from '../contexts/PezkuwiContext'; import { supabase } from '../lib/supabase'; // Cross-platform alert helper const showAlert = (title: string, message: string, buttons?: AlertButton[]) => { if (Platform.OS === 'web') { if (buttons && buttons.length > 1) { // For confirm dialogs const result = window.confirm(`${title}\n\n${message}`); if (result && buttons[1]?.onPress) { buttons[1].onPress(); } } else { window.alert(`${title}\n\n${message}`); } } else { Alert.alert(title, message, buttons); } }; // Font size options type FontSize = 'small' | 'medium' | 'large'; const FONT_SIZE_OPTIONS: { value: FontSize; label: string; description: string }[] = [ { value: 'small', label: 'Small', description: '87.5% - Compact text' }, { value: 'medium', label: 'Medium', description: '100% - Default size' }, { value: 'large', label: 'Large', description: '112.5% - Easier to read' }, ]; // Auto-lock timer options (in minutes) const AUTO_LOCK_OPTIONS: { value: number; label: string }[] = [ { value: 1, label: '1 minute' }, { value: 5, label: '5 minutes' }, { value: 15, label: '15 minutes' }, { value: 30, label: '30 minutes' }, { value: 60, label: '1 hour' }, ]; // --- COMPONENTS (Internal for simplicity) --- const SectionHeader = ({ title }: { title: string }) => { const { colors } = useTheme(); return ( {title} ); }; const SettingItem = ({ icon, title, subtitle, onPress, showArrow = true, textColor, testID }: { icon: string; title: string; subtitle?: string; onPress: () => void; showArrow?: boolean; textColor?: string; testID?: string; }) => { const { colors } = useTheme(); return ( {icon} {title} {subtitle && {subtitle}} {showArrow && } ); }; const SettingToggle = ({ icon, title, subtitle, value, onToggle, loading = false, testID }: { icon: string; title: string; subtitle?: string; value: boolean; onToggle: (value: boolean) => void; loading?: boolean; testID?: string; }) => { const { colors } = useTheme(); return ( {icon} {title} {subtitle && {subtitle}} {loading ? ( ) : ( )} ); }; // --- MAIN SCREEN --- const SettingsScreen: React.FC = () => { const navigation = useNavigation>(); const { isDarkMode, toggleDarkMode, colors, fontSize, setFontSize } = useTheme(); const { isBiometricEnabled, enableBiometric, disableBiometric, biometricType, autoLockTimer, setAutoLockTimer } = useBiometricAuth(); const { signOut, user } = useAuth(); const { currentNetwork, switchNetwork, selectedAccount } = usePezkuwi(); // Profile State (Supabase) const [profile, setProfile] = useState({ full_name: '', username: '', notifications_push: false, notifications_email: true, }); const [_loadingProfile, setLoadingProfile] = useState(false); const [savingSettings, setSavingSettings] = useState(false); // Modals const [showNetworkModal, setShowNetworkModal] = useState(false); const [showProfileEdit, setShowProfileEdit] = useState(false); const [showFontSizeModal, setShowFontSizeModal] = useState(false); const [showAutoLockModal, setShowAutoLockModal] = useState(false); const [showBackupModal, setShowBackupModal] = useState(false); const [backupMnemonic, setBackupMnemonic] = useState(''); const [editName, setEditName] = useState(''); const [editBio, setEditBio] = useState(''); // 1. Fetch Profile from Supabase const fetchProfile = useCallback(async () => { if (!user) return; setLoadingProfile(true); try { const { data, error } = await supabase .from('profiles') .select('*') .eq('id', user.id) .maybeSingle(); if (error) throw error; if (data) { setProfile(data); setEditName(data.full_name || ''); setEditBio(data.bio || ''); } } catch (_err) { console.log('Error fetching profile:', _err); } finally { setLoadingProfile(false); } }, [user]); useEffect(() => { fetchProfile(); }, [fetchProfile]); // 2. Update Settings in Supabase const updateSetting = async (key: string, value: boolean) => { if (!user) return; setSavingSettings(true); // Optimistic update setProfile((prev) => ({ ...prev, [key]: value })); try { const { error } = await supabase .from('profiles') .update({ [key]: value, updated_at: new Date().toISOString() }) .eq('id', user.id); if (error) throw error; } catch (err) { console.error('Failed to update setting:', err); // Revert on error setProfile((prev) => ({ ...prev, [key]: !value })); showAlert('Error', 'Failed to save setting. Please check your connection.'); } finally { setSavingSettings(false); } }; // 3. Save Profile Info const saveProfileInfo = async () => { if (!user) return; try { const { error } = await supabase .from('profiles') .update({ full_name: editName, bio: editBio, updated_at: new Date().toISOString() }) .eq('id', user.id); if (error) throw error; setProfile((prev) => ({ ...prev, full_name: editName, bio: editBio })); setShowProfileEdit(false); showAlert('Success', 'Profile updated successfully'); } catch { showAlert('Error', 'Failed to update profile'); } }; // 4. Biometric Handler const handleBiometryToggle = async (value: boolean) => { // Biometric not available on web if (Platform.OS === 'web') { showAlert( 'Not Available', 'Biometric authentication is only available on mobile devices.' ); return; } if (value) { const success = await enableBiometric(); if (success) { showAlert('Success', 'Biometric authentication enabled'); } } else { await disableBiometric(); } }; // 5. Network Switcher const handleNetworkChange = async (network: 'pezkuwi' | 'dicle' | 'zagros' | 'bizinikiwi' | 'zombienet') => { await switchNetwork(network); setShowNetworkModal(false); showAlert( 'Network Changed', `Switched to ${NETWORKS[network].displayName}. The app will reconnect automatically.`, [{ text: 'OK' }] ); }; // 6. Font Size Handler const handleFontSizeChange = async (size: FontSize) => { await setFontSize(size); setShowFontSizeModal(false); }; // 7. Auto-Lock Timer Handler const handleAutoLockChange = async (minutes: number) => { await setAutoLockTimer(minutes); setShowAutoLockModal(false); }; // Get display text for current font size const getFontSizeLabel = () => { const option = FONT_SIZE_OPTIONS.find(opt => opt.value === fontSize); return option ? option.label : 'Medium'; }; // Get display text for current auto-lock timer const getAutoLockLabel = () => { const option = AUTO_LOCK_OPTIONS.find(opt => opt.value === autoLockTimer); return option ? option.label : '5 minutes'; }; // 8. Wallet Backup Handler const handleWalletBackup = async () => { if (!selectedAccount) { showAlert('No Wallet', 'Please create or import a wallet first.'); return; } try { // Retrieve mnemonic from secure storage const seedKey = `pezkuwi_seed_${selectedAccount.address}`; let storedMnemonic: string | null = null; if (Platform.OS === 'web') { storedMnemonic = await AsyncStorage.getItem(seedKey); } else { storedMnemonic = await SecureStore.getItemAsync(seedKey); } if (storedMnemonic) { setBackupMnemonic(storedMnemonic); setShowBackupModal(true); } else { showAlert('No Backup', 'Recovery phrase not found. It may have been imported from another device.'); } } catch (error) { console.error('Error retrieving mnemonic:', error); showAlert('Error', 'Failed to retrieve recovery phrase.'); } }; return ( {/* Header */} Settings {/* ACCOUNT SECTION */} setShowProfileEdit(true)} testID="edit-profile-button" /> navigation.navigate('Wallet')} testID="wallet-management-button" /> {/* APP SETTINGS */} setShowFontSizeModal(true)} testID="font-size-button" /> updateSetting('notifications_push', val)} loading={savingSettings} testID="push-notifications" /> updateSetting('notifications_email', val)} loading={savingSettings} testID="email-updates" /> {/* NETWORK & SECURITY */} setShowNetworkModal(true)} testID="network-node-button" /> setShowAutoLockModal(true)} testID="auto-lock-button" /> {/* SUPPORT */} showAlert('Terms', 'Terms of service content...')} testID="terms-of-service-button" /> showAlert('Privacy', 'Privacy policy content...')} testID="privacy-policy-button" /> Linking.openURL('mailto:support@pezkuwichain.io')} testID="help-center-button" /> {/* DEVELOPER OPTIONS (only in DEV) */} {__DEV__ && ( DEVELOPER { showAlert( 'Reset Wallet', 'This will delete all wallet data including saved accounts and keys. Are you sure?', [ { text: 'Cancel', style: 'cancel' }, { text: 'Reset', style: 'destructive', onPress: async () => { try { await AsyncStorage.multiRemove([ '@pezkuwi_wallets', '@pezkuwi_selected_account', '@pezkuwi_selected_network' ]); showAlert('Success', 'Wallet data cleared. Restart the app to see changes.'); } catch { showAlert('Error', 'Failed to clear wallet data'); } } } ] ); }} testID="reset-wallet-button" /> )} {/* LOGOUT */} { showAlert( 'Sign Out', 'Are you sure you want to sign out?', [ { text: 'Cancel', style: 'cancel' }, { text: 'Sign Out', style: 'destructive', onPress: signOut } ] ); }} testID="sign-out-button" /> Pezkuwi Super App v1.0.0 © 2026 Digital Kurdistan {/* NETWORK MODAL */} Select Network handleNetworkChange('pezkuwi')} testID="network-option-mainnet" > {NETWORKS.pezkuwi.displayName} {NETWORKS.pezkuwi.rpcEndpoint} handleNetworkChange('bizinikiwi')} testID="network-option-testnet" > {NETWORKS.bizinikiwi.displayName} {NETWORKS.bizinikiwi.rpcEndpoint} handleNetworkChange('zombienet')} testID="network-option-zombienet" > {NETWORKS.zombienet.displayName} {NETWORKS.zombienet.rpcEndpoint} setShowNetworkModal(false)} testID="network-modal-cancel" > Cancel {/* PROFILE EDIT MODAL */} setShowProfileEdit(false)}> Cancel Edit Profile Save Full Name Bio {/* FONT SIZE MODAL */} Select Font Size {FONT_SIZE_OPTIONS.map((option) => ( handleFontSizeChange(option.value)} testID={`font-size-option-${option.value}`} > {option.label} {option.description} ))} setShowFontSizeModal(false)} testID="font-size-modal-cancel" > Cancel {/* AUTO-LOCK TIMER MODAL */} Auto-Lock Timer Lock app after inactivity {AUTO_LOCK_OPTIONS.map((option) => ( handleAutoLockChange(option.value)} testID={`auto-lock-option-${option.value}`} > {option.label} ))} setShowAutoLockModal(false)} testID="auto-lock-modal-cancel" > Cancel {/* WALLET BACKUP MODAL */} 🔐 Recovery Phrase ⚠️ NEVER share this with anyone! Write it down and store safely. {backupMnemonic} { Clipboard.setString(backupMnemonic); showAlert('Copied', 'Recovery phrase copied to clipboard'); }} > 📋 Copy { setShowBackupModal(false); setBackupMnemonic(''); }} > Done ); }; const styles = StyleSheet.create({ container: { flex: 1, }, header: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingHorizontal: 16, paddingVertical: 12, borderBottomWidth: 1, }, backButton: { width: 40, height: 40, justifyContent: 'center', }, backButtonText: { fontSize: 24, color: KurdistanColors.kesk, fontWeight: 'bold', }, headerTitle: { fontSize: 18, fontWeight: 'bold', }, sectionHeader: { marginTop: 24, marginBottom: 8, paddingHorizontal: 16, }, sectionTitle: { fontSize: 12, fontWeight: '700', letterSpacing: 0.5, }, section: { marginHorizontal: 16, borderRadius: 12, overflow: 'hidden', }, settingItem: { flexDirection: 'row', alignItems: 'center', padding: 16, borderBottomWidth: 1, }, settingIcon: { width: 32, height: 32, borderRadius: 8, justifyContent: 'center', alignItems: 'center', marginRight: 12, }, settingIconText: { fontSize: 18, }, settingContent: { flex: 1, }, settingTitle: { fontSize: 16, fontWeight: '500', }, settingSubtitle: { fontSize: 13, marginTop: 2, }, arrow: { fontSize: 18, }, footer: { alignItems: 'center', marginTop: 32, }, versionText: { fontSize: 13, fontWeight: '600', }, copyright: { fontSize: 11, marginTop: 4, }, // Modal Styles modalOverlay: { flex: 1, backgroundColor: 'rgba(0,0,0,0.5)', justifyContent: 'center', padding: 20, }, modalContent: { borderRadius: 16, padding: 20, }, modalTitle: { fontSize: 18, fontWeight: 'bold', marginBottom: 20, textAlign: 'center', }, modalSubtitle: { fontSize: 13, textAlign: 'center', marginTop: -12, marginBottom: 16, }, networkOption: { padding: 16, borderRadius: 12, backgroundColor: '#f5f5f5', marginBottom: 10, borderWidth: 1, borderColor: '#eee', }, selectedNetwork: { borderColor: KurdistanColors.kesk, backgroundColor: '#e8f5e9', }, networkName: { fontWeight: 'bold', fontSize: 16, color: '#333', }, networkUrl: { fontSize: 12, color: '#666', marginTop: 2, }, cancelButton: { marginTop: 10, padding: 16, alignItems: 'center', }, cancelText: { color: '#FF3B30', fontWeight: '600', fontSize: 16, }, fullModal: { flex: 1, }, label: { fontSize: 14, fontWeight: '600', marginBottom: 8, }, input: { borderWidth: 1, borderRadius: 8, padding: 12, fontSize: 16, }, // Backup Modal Styles warningText: { fontSize: 12, textAlign: 'center', marginBottom: 16, fontWeight: '600', }, mnemonicContainer: { backgroundColor: '#FEF9E7', padding: 16, borderRadius: 12, marginBottom: 16, borderWidth: 1, borderColor: '#F5D76E', }, mnemonicText: { fontSize: 14, lineHeight: 24, textAlign: 'center', }, backupActions: { flexDirection: 'row', justifyContent: 'center', marginBottom: 16, }, copyButton: { backgroundColor: '#F5F5F5', paddingVertical: 12, paddingHorizontal: 24, borderRadius: 8, }, copyButtonText: { fontSize: 16, fontWeight: '600', color: '#333', }, confirmButton: { width: '100%', paddingVertical: 16, borderRadius: 12, alignItems: 'center', }, confirmButtonText: { color: '#FFFFFF', fontSize: 16, fontWeight: '600', }, }); export default SettingsScreen;