diff --git a/apps/mobile/src/app/(home)/_layout.tsx b/apps/mobile/src/app/(home)/_layout.tsx index 22370e0bc..04ba90983 100644 --- a/apps/mobile/src/app/(home)/_layout.tsx +++ b/apps/mobile/src/app/(home)/_layout.tsx @@ -163,6 +163,8 @@ export default function StackLayout() { options={{ header: () => NavigationDeveloperConsole }} /> + NavigationBackSimple }} /> + NavigationBackSimple }} /> ); } diff --git a/apps/mobile/src/app/(home)/hardware-wallets.tsx b/apps/mobile/src/app/(home)/hardware-wallets.tsx new file mode 100644 index 000000000..cfb620d18 --- /dev/null +++ b/apps/mobile/src/app/(home)/hardware-wallets.tsx @@ -0,0 +1,107 @@ +import { useRef, useState } from 'react'; +import { ScrollView } from 'react-native-gesture-handler'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; + +import { NotifyUserSheet, OptionData } from '@/components/sheets/notify-user-sheet.layout'; +import { Trans, t } from '@lingui/macro'; +import { useTheme } from '@shopify/restyle'; + +import { + Box, + Cell, + LogoHardwareBitkey, + LogoHardwareFoundation, + LogoHardwareLedger, + LogoHardwareOnekey, + LogoHardwareRyder, + LogoHardwareTrezor, + QuestionCircleIcon, + SheetRef, + Text, + Theme, + TouchableOpacity, +} from '@leather.io/ui/native'; + +export default function HardwareWalletsScreen() { + const { bottom } = useSafeAreaInsets(); + const theme = useTheme(); + const notifyUserSheetRef = useRef(null); + const [optionData, setOptionData] = useState(null); + function onOpenNotificationsSheet(option: OptionData) { + setOptionData(option); + notifyUserSheetRef.current?.present(); + } + function onCloseNotificationsSheet() { + setOptionData(null); + } + function getUnavailableFeatures() { + const UNAVAILABLE_FEATURES = { + bitgo: { title: t`Bitkey`, Icon: LogoHardwareBitkey }, + capsule: { title: t`Ledger`, Icon: LogoHardwareLedger }, + copper: { title: t`OneKey`, Icon: LogoHardwareOnekey }, + fireblocks: { title: t`Passport`, Icon: LogoHardwareFoundation }, + foredefi: { title: t`Ryder`, Icon: LogoHardwareRyder }, + portal: { title: t`Trezor`, Icon: LogoHardwareTrezor }, + }; + + return UNAVAILABLE_FEATURES; + } + return ( + <> + + + + { + // TODO: show some kind of a helper here + }} + p="5" + position="absolute" + right={-theme.spacing['5']} + zIndex={10} + top={theme.spacing['1']} + > + + + + + CONNECT + HARDWARE DEVICE + + + + {Object.entries(getUnavailableFeatures()).map(featureEntry => { + const [featureKey, feature] = featureEntry; + const hardwareWalletName = feature.title; + function onPress() { + onOpenNotificationsSheet({ + title: t`Connect hardware wallet: ${hardwareWalletName}`, + id: featureKey, + }); + } + return ( + + ); + })} + + + + + + + ); +} diff --git a/apps/mobile/src/app/(home)/mpc-wallets.tsx b/apps/mobile/src/app/(home)/mpc-wallets.tsx new file mode 100644 index 000000000..2231a6023 --- /dev/null +++ b/apps/mobile/src/app/(home)/mpc-wallets.tsx @@ -0,0 +1,111 @@ +import { useRef, useState } from 'react'; +import { ScrollView } from 'react-native-gesture-handler'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; + +import { NotifyUserSheet, OptionData } from '@/components/sheets/notify-user-sheet.layout'; +import { Trans, t } from '@lingui/macro'; +import { useTheme } from '@shopify/restyle'; + +import { + Box, + Cell, + LogoMPCBitgo, + LogoMPCCapsule, + LogoMPCCopper, + LogoMPCFireblocks, + LogoMPCFordefi, + LogoMPCPortal, + LogoMPCPrivy, + LogoMPCQredo, + QuestionCircleIcon, + SheetRef, + Text, + Theme, + TouchableOpacity, +} from '@leather.io/ui/native'; + +export default function MPCWalletsScreen() { + const { bottom } = useSafeAreaInsets(); + const theme = useTheme(); + const notifyUserSheetRef = useRef(null); + const [optionData, setOptionData] = useState(null); + function onOpenNotificationsSheet(option: OptionData) { + setOptionData(option); + notifyUserSheetRef.current?.present(); + } + function onCloseNotificationsSheet() { + setOptionData(null); + } + function getUnavailableFeatures() { + const UNAVAILABLE_FEATURES = { + bitgo: { title: t`BitGo`, Icon: LogoMPCBitgo }, + capsule: { title: t`Capsule`, Icon: LogoMPCCapsule }, + copper: { title: t`Copper`, Icon: LogoMPCCopper }, + fireblocks: { title: t`Fireblocks`, Icon: LogoMPCFireblocks }, + foredefi: { title: t`Foredefi`, Icon: LogoMPCFordefi }, + portal: { title: t`Portal`, Icon: LogoMPCPortal }, + privy: { title: t`Privy`, Icon: LogoMPCPrivy }, + qredo: { title: t`Qredo`, Icon: LogoMPCQredo }, + }; + + return UNAVAILABLE_FEATURES; + } + return ( + <> + + + + { + // TODO: show some kind of a helper here + }} + p="5" + position="absolute" + right={-theme.spacing['5']} + zIndex={10} + top={theme.spacing['1']} + > + + + + + CONNECT + MPC WALLET + + + + {Object.entries(getUnavailableFeatures()).map(featureEntry => { + const [featureKey, feature] = featureEntry; + const mpcWalletName = feature.title; + function onPress() { + onOpenNotificationsSheet({ + title: t`Connect MPC wallet: ${mpcWalletName}`, + id: featureKey, + }); + } + return ( + + ); + })} + + + + + + + ); +} diff --git a/apps/mobile/src/assets/unavailable-feature.png b/apps/mobile/src/assets/unavailable-feature.png new file mode 100644 index 000000000..3affbd4aa Binary files /dev/null and b/apps/mobile/src/assets/unavailable-feature.png differ diff --git a/apps/mobile/src/components/add-wallet/add-wallet-sheet.layout.tsx b/apps/mobile/src/components/add-wallet/add-wallet-sheet.layout.tsx index fa2541352..0e9bcd289 100644 --- a/apps/mobile/src/components/add-wallet/add-wallet-sheet.layout.tsx +++ b/apps/mobile/src/components/add-wallet/add-wallet-sheet.layout.tsx @@ -6,9 +6,11 @@ import Animated, { useSharedValue, } from 'react-native-reanimated'; +import { AppRoutes } from '@/routes'; import { t } from '@lingui/macro'; import { useTheme } from '@shopify/restyle'; import { Image } from 'expo-image'; +import { useRouter } from 'expo-router'; import { ArrowRotateClockwiseIcon, @@ -32,32 +34,6 @@ import { AddWalletListItem } from './add-wallet-list-item'; const AnimatedBox = Animated.createAnimatedComponent(Box); -function getUnavailableFeatures(theme: Theme) { - const UNAVAILABLE_FEATURES = { - hardwareWallet: { - title: t`Connect hardware wallet`, - subtitle: t`Ledger, Trezor, Ryder and more`, - icon: , - }, - emailRestore: { - title: t`Create or restore via email`, - subtitle: t`Access custodial wallet`, - icon: , - }, - mpcWallet: { - title: t`Connect MPC wallet`, - subtitle: t`Import existing accounts`, - icon: , - }, - watchOnlyWallet: { - title: t`Create watch-only wallet`, - subtitle: t`No key needed`, - icon: , - }, - }; - return UNAVAILABLE_FEATURES; -} - interface AddWalletSheetBaseProps { addWalletSheetRef: RefObject; } @@ -78,7 +54,7 @@ export function AddWalletSheetLayout({ const [moreOptionsVisible, setMoreOptionsVisible] = useState(false); const animatedIndex = useSharedValue(CLOSED_ANIMATED_SHARED_VALUE); const theme = useTheme(); - + const router = useRouter(); function openOptions() { setMoreOptionsVisible(!moreOptionsVisible); } @@ -87,6 +63,42 @@ export function AddWalletSheetLayout({ marginBottom: interpolate(animatedIndex.value, [-1, 0], [200, 0], Extrapolation.CLAMP), })); + function getUnavailableFeatures(theme: Theme) { + const UNAVAILABLE_FEATURES = { + hardwareWallet: { + title: t`Connect hardware wallet`, + subtitle: t`Ledger, Trezor, Ryder and more`, + icon: , + onPress() { + router.navigate(AppRoutes.HardwareWallets); + addWalletSheetRef.current?.close(); + }, + }, + emailRestore: { + title: t`Create or restore via email`, + subtitle: t`Access custodial wallet`, + icon: , + onPress: undefined, + }, + mpcWallet: { + title: t`Connect MPC wallet`, + subtitle: t`Import existing accounts`, + icon: , + onPress() { + router.navigate(AppRoutes.MpcWallets); + addWalletSheetRef.current?.close(); + }, + }, + watchOnlyWallet: { + title: t`Create watch-only wallet`, + subtitle: t`No key needed`, + icon: , + onPress: undefined, + }, + }; + return UNAVAILABLE_FEATURES; + } + return ( { const [featureKey, feature] = featureEntry; + function fallbackOnPress() { + onOpenNotificationsSheet({ title: feature.title, id: featureKey }); + } return ( - onOpenNotificationsSheet({ title: feature.title, id: featureKey }) - } + onPress={feature.onPress ?? fallbackOnPress} title={feature.title} subtitle={feature.subtitle} icon={feature.icon} diff --git a/apps/mobile/src/components/recover-wallet/recover-wallet-sheet.tsx b/apps/mobile/src/components/recover-wallet/recover-wallet-sheet.tsx index f3b9011ce..13dc93f24 100644 --- a/apps/mobile/src/components/recover-wallet/recover-wallet-sheet.tsx +++ b/apps/mobile/src/components/recover-wallet/recover-wallet-sheet.tsx @@ -6,11 +6,6 @@ import { LockIcon, SheetRef } from '@leather.io/ui/native'; import { InputSheet } from '../sheets/input-sheet.layout'; -export interface OptionData { - title: string; - id: string; -} - interface RecoverWalletSheetProps { recoverWalletSheetRef: RefObject; passphrase: string; diff --git a/apps/mobile/src/components/sheets/input-sheet.layout.tsx b/apps/mobile/src/components/sheets/input-sheet.layout.tsx index c57897f60..94afe9b09 100644 --- a/apps/mobile/src/components/sheets/input-sheet.layout.tsx +++ b/apps/mobile/src/components/sheets/input-sheet.layout.tsx @@ -14,11 +14,6 @@ import { import { TextInput } from '../text-input'; -export interface OptionData { - title: string; - id: string; -} - interface InputSheetProps { sheetRef: RefObject; initialValue: string; diff --git a/apps/mobile/src/components/sheets/notify-user-sheet.layout.tsx b/apps/mobile/src/components/sheets/notify-user-sheet.layout.tsx index 38464046a..7cfbbaa3e 100644 --- a/apps/mobile/src/components/sheets/notify-user-sheet.layout.tsx +++ b/apps/mobile/src/components/sheets/notify-user-sheet.layout.tsx @@ -4,17 +4,16 @@ import { usePushNotifications } from '@/hooks/use-push-notifications'; import { useSettings } from '@/store/settings/settings'; import { t } from '@lingui/macro'; import { useTheme } from '@shopify/restyle'; +import { Image } from 'expo-image'; import { BellAlarmIcon, Box, Button, - CloseIcon, Sheet, SheetRef, Text, Theme, - TouchableOpacity, getButtonTextColor, } from '@leather.io/ui/native'; @@ -48,28 +47,23 @@ export function NotifyUserSheet({ onDismiss={onCloseNotificationsSheet} ref={notifyUserSheetRef} > + - { - notifyUserSheetRef.current?.close(); - }} - p="5" - right={0} - top={0} - zIndex={10} - position="absolute" - > - - - {t`Notify me when ready`} - {t`"${title}" isn't ready yet.`} + {title} + + {t`This feature is not available yet, but we can notify when it’s ready.`} +