diff --git a/apps/mobile/package.json b/apps/mobile/package.json index dbc91299ab..1c1683f82f 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -78,6 +78,7 @@ "react-native-ble-plx": "^2.0.3", "react-native-code-push": "^8.2.1", "react-native-device-info": "^10.11.0", + "react-native-draggable-flatlist": "^4.0.1", "react-native-exception-handler": "^2.10.10", "react-native-fs": "^2.20.0", "react-native-gesture-handler": "2.16.0", diff --git a/apps/mobile/src/languages/en.json b/apps/mobile/src/languages/en.json index c49bbef1e5..cd4bebb006 100644 --- a/apps/mobile/src/languages/en.json +++ b/apps/mobile/src/languages/en.json @@ -329,6 +329,9 @@ "page.migration.welcome.restart-modal.title": "Keplr will automatically restart", "page.migration.welcome.restart-modal.paragraph": "Keplr will be closed in {seconds}s...", "page.wallet.title": "Select Wallet", + "page.wallet.input.search.placeholder": "Search wallet name or address", + "page.wallet.search-empty-view-title": "Oops!", + "page.wallet.search-empty-view-paragraph": "No Result Found", "page.wallet.add-wallet-button": "Add Wallet", "page.wallet.recovery-phrase-title": "Recovery Phrase", "page.wallet.connect-with-social-account-title": "Connected with {social} Account", diff --git a/apps/mobile/src/languages/ko.json b/apps/mobile/src/languages/ko.json index f88135d902..4446221c05 100644 --- a/apps/mobile/src/languages/ko.json +++ b/apps/mobile/src/languages/ko.json @@ -326,6 +326,9 @@ "page.migration.welcome.restart-modal.title": "케플러 앱이 자동으로 재시작됩니다.", "page.migration.welcome.restart-modal.paragraph": "{seconds}초 후 재시작돼요.", "page.wallet.title": "지갑 선택", + "page.wallet.input.search.placeholder": "지갑 이름이나 주소로 검색", + "page.wallet.search-empty-view-title": "앗!", + "page.wallet.search-empty-view-paragraph": "검색 결과가 없어요", "page.wallet.add-wallet-button": "지갑 추가", "page.wallet.recovery-phrase-title": "복구 문구 기반", "page.wallet.connect-with-social-account-title": "{social} 계정으로 연결", diff --git a/apps/mobile/src/screen/wallet/select/index.tsx b/apps/mobile/src/screen/wallet/select/index.tsx index a03f5481d1..a75f9f883a 100644 --- a/apps/mobile/src/screen/wallet/select/index.tsx +++ b/apps/mobile/src/screen/wallet/select/index.tsx @@ -15,12 +15,10 @@ import {Box} from '../../../components/box'; import {XAxis, YAxis} from '../../../components/axis'; import {useStyle} from '../../../styles'; import {Gutter} from '../../../components/gutter'; -import {Stack} from '../../../components/stack'; import {Column, Columns} from '../../../components/column'; -import {CheckIcon, CopyOutlineIcon} from '../../../components/icon'; +import {CopyOutlineIcon} from '../../../components/icon'; import {Button} from '../../../components/button'; import {App, AppCoinType} from '@keplr-wallet/ledger-cosmos'; -import {PageWithScrollView} from '../../../components/page'; import {StyleSheet, Text} from 'react-native'; import {StackActions, useNavigation} from '@react-navigation/native'; import {useIntl} from 'react-intl'; @@ -32,24 +30,78 @@ import {RNMessageRequesterInternal} from '../../../router'; import * as Clipboard from 'expo-clipboard'; import {MenuModal, ModalMenuItem} from '../../../components/modal/menu-modal'; import {TouchableWithoutFeedback} from 'react-native-gesture-handler'; +import { + NestableDraggableFlatList, + NestableScrollContainer, +} from 'react-native-draggable-flatlist'; +import {SearchTextInput} from '../../../components/input/search-text-input'; +import {EmptyView, EmptyViewText} from '../../../components/empty-view'; +import {IconProps} from '../../../components/icon/types.ts'; +import Svg, {Rect} from 'react-native-svg'; +import Reanimated, { + useAnimatedStyle, + useSharedValue, + withSpring, +} from 'react-native-reanimated'; +import {defaultSpringConfig} from '../../../styles/spring.ts'; export const WalletSelectScreen: FunctionComponent = observer(() => { const {keyRingStore} = useStore(); const navigation = useNavigation(); const intl = useIntl(); + const style = useStyle(); const timerRef = useRef(null); const [isOpenMenuModal, setIsOpenMenuModal] = useState(false); const [modalMenuItems, setModalMenuItems] = useState([]); + const [searchText, setSearchText] = useState(''); + const [debounceSearchText, setDebounceSearchText] = useState(''); + useEffect(() => { + const timer = setTimeout(() => { + setDebounceSearchText(searchText); + }, 300); + + return () => { + clearTimeout(timer); + }; + }, [searchText]); + + const [searchedKeyInfos, setSearchedKeyInfos] = useState< + KeyInfo[] | undefined + >(undefined); + useEffect(() => { + if (debounceSearchText.trim().length === 0) { + setSearchedKeyInfos(undefined); + return; + } + + let exposed = false; + + keyRingStore + .searchKeyRings(debounceSearchText) + .then(keyInfos => { + if (!exposed) { + setSearchedKeyInfos(keyInfos); + } + }) + .catch(console.log); + + return () => { + exposed = true; + }; + }, [debounceSearchText, keyRingStore]); + + const keyInfos = searchedKeyInfos ?? keyRingStore.keyInfos; + const mnemonicKeys = useMemo(() => { - return keyRingStore.keyInfos.filter(keyInfo => { + return keyInfos.filter(keyInfo => { return keyInfo.type === 'mnemonic'; }); - }, [keyRingStore.keyInfos]); + }, [keyInfos]); const socialPrivateKeyInfos = useMemo(() => { - return keyRingStore.keyInfos.filter(keyInfo => { + return keyInfos.filter(keyInfo => { if ( keyInfo.type === 'private-key' && typeof keyInfo.insensitive === 'object' && @@ -66,28 +118,28 @@ export const WalletSelectScreen: FunctionComponent = observer(() => { return false; }); - }, [keyRingStore.keyInfos]); + }, [keyInfos]); const privateKeyInfos = useMemo(() => { - return keyRingStore.keyInfos.filter(keyInfo => { + return keyInfos.filter(keyInfo => { return ( keyInfo.type === 'private-key' && !socialPrivateKeyInfos.some(k => k.id === keyInfo.id) ); }); - }, [keyRingStore.keyInfos, socialPrivateKeyInfos]); + }, [keyInfos, socialPrivateKeyInfos]); const ledgerKeys = useMemo(() => { - return keyRingStore.keyInfos.filter(keyInfo => { + return keyInfos.filter(keyInfo => { return keyInfo.type === 'ledger'; }); - }, [keyRingStore.keyInfos]); + }, [keyInfos]); const keystoneKeys = useMemo(() => { - return keyRingStore.keyInfos.filter(keyInfo => { + return keyInfos.filter(keyInfo => { return keyInfo.type === 'keystone'; }); - }, [keyRingStore.keyInfos]); + }, [keyInfos]); const unknownKeys = useMemo(() => { const knownKeys = mnemonicKeys @@ -95,11 +147,11 @@ export const WalletSelectScreen: FunctionComponent = observer(() => { .concat(privateKeyInfos) .concat(socialPrivateKeyInfos) .concat(keystoneKeys); - return keyRingStore.keyInfos.filter(keyInfo => { + return keyInfos.filter(keyInfo => { return !knownKeys.find(k => k.id === keyInfo.id); }); }, [ - keyRingStore.keyInfos, + keyInfos, ledgerKeys, mnemonicKeys, privateKeyInfos, @@ -165,90 +217,135 @@ export const WalletSelectScreen: FunctionComponent = observer(() => { }, [isOpenMenuModal]); return ( - - - -