From b9568489e277e1f46b6ef16454252b1ebfbbdf59 Mon Sep 17 00:00:00 2001 From: Kurdistan Tech Ministry Date: Thu, 15 Jan 2026 08:09:55 +0300 Subject: [PATCH] =?UTF-8?q?feat(wallet):=20Token=20search,=20settings=20ve?= =?UTF-8?q?=20backup=20iyile=C5=9Ftirmeleri?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WalletScreen: - Token arama modalı eklendi (isim/sembol ile ara) - Token settings modalı (görünürlük yönetimi) - Backup, token settings'den kaldırıldı (Settings'e taşındı) SettingsScreen: - "Backup Recovery Phrase" Network & Security altına eklendi - NetworkType tüm seçenekleri içerecek şekilde düzeltildi Düzeltmeler: - Image type declarations (png, jpg, svg vb.) - KurdistanColors'a mor, şîn, gewr eklendi - ValidatorSelectionSheet export edildi - @pezkuwi/extension-inject devDependency olarak eklendi --- mobile/package.json | 2 + mobile/src/components/index.ts | 1 + mobile/src/screens/SettingsScreen.tsx | 131 ++++++++++++++- mobile/src/screens/WalletScreen.tsx | 228 +++++++++++++++++++++++++- mobile/src/types/images.d.ts | 34 ++++ shared/theme/colors.ts | 6 +- 6 files changed, 397 insertions(+), 5 deletions(-) create mode 100644 mobile/src/types/images.d.ts diff --git a/mobile/package.json b/mobile/package.json index 382ab93a..ee3353ab 100644 --- a/mobile/package.json +++ b/mobile/package.json @@ -88,6 +88,8 @@ "@pezkuwi/util-crypto": "14.0.11" }, "devDependencies": { + "@pezkuwi/extension-dapp": "0.62.14", + "@pezkuwi/extension-inject": "0.62.14", "@expo/ngrok": "^4.1.0", "@testing-library/jest-native": "^5.4.3", "@testing-library/react-native": "^13.3.3", diff --git a/mobile/src/components/index.ts b/mobile/src/components/index.ts index 246b56df..6b84a306 100644 --- a/mobile/src/components/index.ts +++ b/mobile/src/components/index.ts @@ -17,3 +17,4 @@ export { TokenSelector } from './TokenSelector'; export type { Token } from './TokenSelector'; export { default as PezkuwiWebView } from './PezkuwiWebView'; export type { PezkuwiWebViewProps } from './PezkuwiWebView'; +export { ValidatorSelectionSheet } from './ValidatorSelectionSheet'; diff --git a/mobile/src/screens/SettingsScreen.tsx b/mobile/src/screens/SettingsScreen.tsx index dfc7b267..6aa91ca9 100644 --- a/mobile/src/screens/SettingsScreen.tsx +++ b/mobile/src/screens/SettingsScreen.tsx @@ -16,6 +16,8 @@ import { 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 } from '@react-navigation/native'; import { KurdistanColors } from '../theme/colors'; import { useTheme } from '../contexts/ThemeContext'; @@ -154,7 +156,7 @@ const SettingsScreen: React.FC = () => { const { isDarkMode, toggleDarkMode, colors, fontSize, setFontSize } = useTheme(); const { isBiometricEnabled, enableBiometric, disableBiometric, biometricType, autoLockTimer, setAutoLockTimer } = useBiometricAuth(); const { signOut, user } = useAuth(); - const { currentNetwork, switchNetwork } = usePezkuwi(); + const { currentNetwork, switchNetwork, selectedAccount } = usePezkuwi(); // Profile State (Supabase) const [profile, setProfile] = useState({ @@ -171,6 +173,8 @@ const SettingsScreen: React.FC = () => { 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(''); @@ -272,7 +276,7 @@ const SettingsScreen: React.FC = () => { }; // 5. Network Switcher - const handleNetworkChange = async (network: 'pezkuwi' | 'bizinikiwi') => { + const handleNetworkChange = async (network: 'pezkuwi' | 'dicle' | 'zagros' | 'bizinikiwi' | 'zombienet') => { await switchNetwork(network); setShowNetworkModal(false); @@ -307,6 +311,36 @@ const SettingsScreen: React.FC = () => { 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 ( @@ -409,6 +443,14 @@ const SettingsScreen: React.FC = () => { onPress={() => setShowAutoLockModal(true)} testID="auto-lock-button" /> + + {/* SUPPORT */} @@ -642,6 +684,44 @@ const SettingsScreen: React.FC = () => { + {/* 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 + + + + + ); }; @@ -799,6 +879,53 @@ const styles = StyleSheet.create({ 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; \ No newline at end of file diff --git a/mobile/src/screens/WalletScreen.tsx b/mobile/src/screens/WalletScreen.tsx index 9d046690..ff1dc872 100644 --- a/mobile/src/screens/WalletScreen.tsx +++ b/mobile/src/screens/WalletScreen.tsx @@ -119,6 +119,10 @@ const WalletScreen: React.FC = () => { const [networkSelectorVisible, setNetworkSelectorVisible] = useState(false); const [walletSelectorVisible, setWalletSelectorVisible] = useState(false); const [addTokenModalVisible, setAddTokenModalVisible] = useState(false); + const [tokenSearchVisible, setTokenSearchVisible] = useState(false); + const [tokenSearchQuery, setTokenSearchQuery] = useState(''); + const [tokenSettingsVisible, setTokenSettingsVisible] = useState(false); + const [hiddenTokens, setHiddenTokens] = useState([]); const [recipientAddress, setRecipientAddress] = useState(''); const [sendAmount, setSendAmount] = useState(''); const [walletName, setWalletName] = useState(''); @@ -701,13 +705,13 @@ const WalletScreen: React.FC = () => { Tokens - + setTokenSearchVisible(true)}> 🔍 setAddTokenModalVisible(true)}> - + setTokenSettingsVisible(true)}> ⚙️ @@ -1100,6 +1104,116 @@ const WalletScreen: React.FC = () => { + {/* Token Search Modal */} + setTokenSearchVisible(false)}> + + + 🔍 Search Tokens + + + {tokens + .filter(t => + !hiddenTokens.includes(t.symbol) && + (t.symbol.toLowerCase().includes(tokenSearchQuery.toLowerCase()) || + t.name.toLowerCase().includes(tokenSearchQuery.toLowerCase())) + ) + .map((token) => ( + { + setTokenSearchVisible(false); + setTokenSearchQuery(''); + handleTokenPress(token); + }} + > + + + {token.symbol} + {token.name} + + {balances[token.symbol] || '0.00'} + + ))} + {tokens.filter(t => + !hiddenTokens.includes(t.symbol) && + (t.symbol.toLowerCase().includes(tokenSearchQuery.toLowerCase()) || + t.name.toLowerCase().includes(tokenSearchQuery.toLowerCase())) + ).length === 0 && ( + No tokens found + )} + + { + setTokenSearchVisible(false); + setTokenSearchQuery(''); + }}> + Close + + + + + + {/* Token Settings Modal */} + setTokenSettingsVisible(false)}> + + + ⚙️ Token Settings + Manage your token visibility + + + {tokens.map((token) => { + const isHidden = hiddenTokens.includes(token.symbol); + return ( + + + + {token.symbol} + {token.name} + + { + if (isHidden) { + setHiddenTokens(prev => prev.filter(s => s !== token.symbol)); + } else { + setHiddenTokens(prev => [...prev, token.symbol]); + } + }} + > + {isHidden ? '👁️‍🗨️' : '👁️'} + + + ); + })} + + + + { + setTokenSettingsVisible(false); + setAddTokenModalVisible(true); + }} + > + + Add Custom Token + + + + setTokenSettingsVisible(false)}> + Done + + + + + ); }; @@ -1798,6 +1912,116 @@ const styles = StyleSheet.create({ deleteWalletIcon: { fontSize: 18, }, + // Token Search Modal + tokenSearchResults: { + width: '100%', + maxHeight: 300, + marginBottom: 16, + }, + tokenSearchItem: { + flexDirection: 'row', + alignItems: 'center', + backgroundColor: '#F8F9FA', + padding: 12, + borderRadius: 12, + marginBottom: 8, + }, + tokenSearchLogo: { + width: 40, + height: 40, + marginRight: 12, + }, + tokenSearchSymbol: { + fontSize: 16, + fontWeight: '600', + color: '#333', + }, + tokenSearchName: { + fontSize: 12, + color: '#999', + marginTop: 2, + }, + tokenSearchBalance: { + fontSize: 16, + fontWeight: '600', + color: KurdistanColors.kesk, + }, + noTokensFound: { + textAlign: 'center', + color: '#999', + paddingVertical: 32, + }, + // Token Settings Modal + tokenSettingsSubtitle: { + color: '#666', + fontSize: 14, + marginBottom: 16, + textAlign: 'center', + }, + tokenSettingsList: { + width: '100%', + maxHeight: 300, + marginBottom: 16, + }, + tokenSettingsItem: { + flexDirection: 'row', + alignItems: 'center', + backgroundColor: '#F8F9FA', + padding: 12, + borderRadius: 12, + marginBottom: 8, + }, + tokenSettingsLogo: { + width: 40, + height: 40, + marginRight: 12, + }, + tokenSettingsSymbol: { + fontSize: 16, + fontWeight: '600', + color: '#333', + }, + tokenSettingsName: { + fontSize: 12, + color: '#999', + marginTop: 2, + }, + tokenVisibilityToggle: { + width: 44, + height: 44, + borderRadius: 22, + backgroundColor: 'rgba(0, 143, 67, 0.1)', + justifyContent: 'center', + alignItems: 'center', + }, + tokenVisibilityHidden: { + backgroundColor: 'rgba(156, 163, 175, 0.2)', + }, + tokenVisibilityText: { + fontSize: 20, + }, + tokenSettingsActions: { + width: '100%', + marginBottom: 16, + }, + tokenSettingsOption: { + flexDirection: 'row', + alignItems: 'center', + backgroundColor: 'rgba(0, 143, 67, 0.05)', + padding: 16, + borderRadius: 12, + borderWidth: 1, + borderColor: 'rgba(0, 143, 67, 0.2)', + }, + tokenSettingsOptionIcon: { + fontSize: 20, + marginRight: 12, + }, + tokenSettingsOptionText: { + fontSize: 16, + fontWeight: '500', + color: KurdistanColors.kesk, + }, }); export default WalletScreen; \ No newline at end of file diff --git a/mobile/src/types/images.d.ts b/mobile/src/types/images.d.ts new file mode 100644 index 00000000..1a622e06 --- /dev/null +++ b/mobile/src/types/images.d.ts @@ -0,0 +1,34 @@ +/** + * Type declarations for image imports + */ + +declare module '*.png' { + const content: number; + export default content; +} + +declare module '*.jpg' { + const content: number; + export default content; +} + +declare module '*.jpeg' { + const content: number; + export default content; +} + +declare module '*.gif' { + const content: number; + export default content; +} + +declare module '*.svg' { + import { SvgProps } from 'react-native-svg'; + const content: React.FC; + export default content; +} + +declare module '*.webp' { + const content: number; + export default content; +} diff --git a/shared/theme/colors.ts b/shared/theme/colors.ts index 798f78ea..ac6f4128 100644 --- a/shared/theme/colors.ts +++ b/shared/theme/colors.ts @@ -2,13 +2,17 @@ * Shared theme colors for all platforms */ -// Kurdistan Flag Colors +// Kurdistan Flag Colors + Extended Palette export const KurdistanColors = { kesk: '#00A94F', // Green - Primary sor: '#EE2A35', // Red - Accent zer: '#FFD700', // Gold - Secondary spi: '#FFFFFF', // White - Background reş: '#000000', // Black - Text + // Extended colors + mor: '#9C27B0', // Purple - For special actions + şîn: '#2196F3', // Blue - For info/links + gewr: '#9E9E9E', // Gray - For disabled states }; // Light theme color palette