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;