From e00ef2726319ce1b94114bcba682c03e33f47537 Mon Sep 17 00:00:00 2001 From: Mounir Hamzaoui Date: Thu, 9 Jan 2025 16:59:21 +0100 Subject: [PATCH] feat: Add success & warning screen for add account v2 --- .../src/components/SelectableAccountsList.tsx | 175 +++++++----------- .../src/const/navigation.ts | 1 + .../families/hedera/NoAssociatedAccounts.tsx | 57 +++++- .../src/locales/en/common.json | 26 ++- .../newArch/features/Accounts/Navigator.tsx | 22 +++ .../AccountListDrawer/CustomHeader/index.tsx | 43 +++++ .../components/AccountListDrawer/index.tsx | 76 ++++++++ .../components/AccountListEmpty/index.tsx | 11 ++ .../CustomHeader/index.tsx | 40 ++++ .../AccountQuickActionsDrawer/index.tsx | 58 ++++++ .../useAccountQuickActionDrawerViewModel.ts | 73 ++++++++ .../components/AccountItem/index.tsx | 30 ++- .../AccountItem/useAccountItemModel.ts | 10 +- .../components/AddFundsButton/index.tsx | 71 +++++++ .../VerticalGradientBackground/index.tsx | 25 +++ .../Accounts/screens/AddAccount/types.ts | 19 +- .../screens/AddAccountSuccess/index.tsx | 142 +++++++------- .../screens/AddAccountWarning/index.tsx | 127 +++++++++++++ .../NoAssociatedAccountsView/index.tsx | 73 ++++++++ .../CanCreateAccountAlert/index.tsx | 59 ++++++ .../ScanDeviceAccountsFooter/index.tsx | 2 +- .../screens/ScanDeviceAccounts/index.tsx | 72 +++---- .../useScanDeviceAccountsViewModel.ts | 68 ++++--- .../utils/getAccountListKeyExtractor.ts | 5 + .../tests/getAccountListKeyExtractor.test.ts | 13 ++ .../src/screens/SkipSelectDevice.tsx | 2 +- libs/live-wallet/src/store.ts | 2 +- 27 files changed, 1020 insertions(+), 282 deletions(-) create mode 100644 apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountListDrawer/CustomHeader/index.tsx create mode 100644 apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountListDrawer/index.tsx create mode 100644 apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountListEmpty/index.tsx create mode 100644 apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountQuickActionsDrawer/CustomHeader/index.tsx create mode 100644 apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountQuickActionsDrawer/index.tsx create mode 100644 apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountQuickActionsDrawer/useAccountQuickActionDrawerViewModel.ts create mode 100644 apps/ledger-live-mobile/src/newArch/features/Accounts/components/AddFundsButton/index.tsx create mode 100644 apps/ledger-live-mobile/src/newArch/features/Accounts/components/VerticalGradientBackground/index.tsx create mode 100644 apps/ledger-live-mobile/src/newArch/features/Accounts/screens/AddAccountWarning/index.tsx create mode 100644 apps/ledger-live-mobile/src/newArch/features/Accounts/screens/NoAssociatedAccountsView/index.tsx create mode 100644 apps/ledger-live-mobile/src/newArch/features/Accounts/screens/ScanDeviceAccounts/components/CanCreateAccountAlert/index.tsx create mode 100644 apps/ledger-live-mobile/src/newArch/features/Accounts/utils/getAccountListKeyExtractor.ts create mode 100644 apps/ledger-live-mobile/src/newArch/features/Accounts/utils/tests/getAccountListKeyExtractor.test.ts diff --git a/apps/ledger-live-mobile/src/components/SelectableAccountsList.tsx b/apps/ledger-live-mobile/src/components/SelectableAccountsList.tsx index 98badfc9d40f..dfbfab57226b 100644 --- a/apps/ledger-live-mobile/src/components/SelectableAccountsList.tsx +++ b/apps/ledger-live-mobile/src/components/SelectableAccountsList.tsx @@ -30,7 +30,7 @@ import AccountItem from "LLM/features/Accounts/components/AccountsListView/compo import { useFeature } from "@ledgerhq/live-common/featureFlags/index"; import { BaseComposite, StackNavigatorProps } from "./RootNavigator/types/helpers"; -const ANIMATION_DURATION = 200; +import useAnimatedStyle from "LLM/features/Accounts/screens/ScanDeviceAccounts/components/ScanDeviceAccountsFooter/useAnimatedStyle"; const selectAllHitSlop = { top: 16, @@ -89,57 +89,23 @@ const SelectableAccountsList = ({ }, [accounts, onUnselectAllProp]); const areAllSelected = accounts.every(a => selectedIds.indexOf(a.id) > -1); - const translateY = useRef(new Animated.Value(50)).current; - const opacity = useRef(new Animated.Value(0)).current; - const scale = useRef(new Animated.Value(0.8)).current; const llmNetworkBasedAddAccountFlow = useFeature("llmNetworkBasedAddAccountFlow"); - useEffect(() => { - if (!llmNetworkBasedAddAccountFlow?.enabled) return; - Animated.parallel([ - Animated.timing(translateY, { - toValue: 0, - duration: ANIMATION_DURATION, - useNativeDriver: true, - }), - Animated.timing(opacity, { - toValue: 1, - duration: ANIMATION_DURATION, - useNativeDriver: true, - }), - Animated.timing(scale, { - toValue: 1, - duration: ANIMATION_DURATION, - useNativeDriver: true, - }), - ]).start(); - }, [translateY, opacity, scale, llmNetworkBasedAddAccountFlow?.enabled]); - - const animatedSelectableAccount = useMemo( - () => ({ - transform: [{ translateY }, { scale }], - opacity, - }), - [translateY, scale, opacity], - ); - const renderSelectableAccount = useCallback( ({ item, index }: { index: number; item: Account }) => llmNetworkBasedAddAccountFlow?.enabled ? ( - - -1} - isDisabled={isDisabled} - onPress={onPressAccount} - useFullBalance={useFullBalance} - /> - + -1} + isDisabled={isDisabled} + onPress={onPressAccount} + useFullBalance={useFullBalance} + /> ) : ( 0; - + const { animatedSelectableAccount } = useAnimatedStyle(); const inner = ( - - {llmNetworkBasedAddAccountFlow?.enabled ? ( - - - - ) : ( - - - - - - - ) : null - } - /> - - )} + + + {llmNetworkBasedAddAccountFlow?.enabled ? ( + + + + ) : ( + + + + + + + ) : null + } + /> + + )} - {!isDisabled && ( - - - - )} - + {!isDisabled && ( + + + + )} + + ); if (isDisabled) return inner; diff --git a/apps/ledger-live-mobile/src/const/navigation.ts b/apps/ledger-live-mobile/src/const/navigation.ts index 0f0a5dd975fd..abfa4fdc986f 100644 --- a/apps/ledger-live-mobile/src/const/navigation.ts +++ b/apps/ledger-live-mobile/src/const/navigation.ts @@ -542,6 +542,7 @@ export enum ScreenName { SelectAccounts = "SelectAccounts", ScanDeviceAccounts = "ScanDeviceAccounts", AddAccountsWarning = "AddAccountsWarning", + NoAssociatedAccounts = "NoAssociatedAccounts", } export enum NavigatorName { diff --git a/apps/ledger-live-mobile/src/families/hedera/NoAssociatedAccounts.tsx b/apps/ledger-live-mobile/src/families/hedera/NoAssociatedAccounts.tsx index a13737079b7d..ec6a5f5d1d1b 100644 --- a/apps/ledger-live-mobile/src/families/hedera/NoAssociatedAccounts.tsx +++ b/apps/ledger-live-mobile/src/families/hedera/NoAssociatedAccounts.tsx @@ -1,11 +1,14 @@ import React, { useCallback } from "react"; import i18next from "i18next"; -import { useTheme } from "@react-navigation/native"; -import { StyleSheet, Linking, Text } from "react-native"; +import { StyleSheet, Linking } from "react-native"; import { urls } from "~/utils/urls"; import Touchable, { Props as TouchableProps } from "~/components/Touchable"; import LText from "~/components/LText"; import ExternalLink from "~/icons/ExternalLink"; +import { Flex, Icons, Text, Button } from "@ledgerhq/native-ui"; +import { useFeature } from "@ledgerhq/live-common/featureFlags/index"; +import { useTheme } from "styled-components/native"; +import { useTranslation } from "react-i18next"; type Props = { style?: { @@ -16,10 +19,12 @@ type Props = { // "no associated accounts" text when adding/importing accounts function NoAssociatedAccounts({ style }: Props) { const { colors } = useTheme(); - const c = colors.live; + const { t } = useTranslation(); + const c = colors.primary.c100; + const llmNetworkBasedAddAccountFlow = useFeature("llmNetworkBasedAddAccountFlow"); const fontSize = 13; const onPress = useCallback(() => Linking.openURL(urls.hedera.supportArticleLink), []); - return ( + return !llmNetworkBasedAddAccountFlow?.enabled ? ( {i18next.t("hedera.createHederaAccountHelp.text") as React.ReactNode} - {i18next.t("hedera.createHederaAccountHelp.link") as React.ReactNode} + {t("hedera.createHederaAccountHelp.link") as React.ReactNode} + ) : ( + <> + + {t("hedera.createHederaAccountHelp.title")} + + + {t("hedera.createHederaAccountHelp.description")} + + + + ); } @@ -44,5 +68,28 @@ const styles = StyleSheet.create({ alignItems: "center", flexWrap: "wrap", }, + title: { + marginTop: 32, + fontSize: 24, + textAlign: "center", + width: "100%", + fontWeight: 600, + fontStyle: "normal", + lineHeight: 32.4, + letterSpacing: 0.75, + }, + desc: { + marginTop: 16, + marginBottom: 32, + fontSize: 14, + width: "100%", + lineHeight: 23.8, + fontWeight: 500, + textAlign: "center", + alignSelf: "stretch", + }, + cta: { + textTransform: "capitalize", + }, }); export default NoAssociatedAccounts; diff --git a/apps/ledger-live-mobile/src/locales/en/common.json b/apps/ledger-live-mobile/src/locales/en/common.json index e611dace1e0a..6617020073c8 100644 --- a/apps/ledger-live-mobile/src/locales/en/common.json +++ b/apps/ledger-live-mobile/src/locales/en/common.json @@ -99,7 +99,8 @@ "id": "ID {{ value }}", "times": "x{{ value }}" }, - "quote": "Quote" + "quote": "Quote", + "tryAgain": "Try again" }, "errors": { "ReplacementTransactionUnderpriced": { @@ -4075,18 +4076,29 @@ "title": "New account" }, "imported": { - "title": "Account already in the Portfolio ({{length}})", - "title_plural": "Accounts already in the Portfolio ({{length}})" + "title": "Account already in the Portfolio ({{count}})", + "title_plural": "Accounts already in the Portfolio ({{count}})" }, "migrate": { "title": "Accounts to update" } - } + }, + "retryScanning": "Try Again" }, "addAccountsSuccess": { "ctaAddFunds": "Add funds to my account", "ctaClose": "Close" - } + }, + "addAccountsWarning": { + "cantCreateAccount": { + "title": "We can’t add a new account", + "body": "A new account cannot be added before receiving assets on your {{accountName}} account." + }, + "noAccountToCreate": { + "title": "We couldn’t add a new {{currency}} account" + } + }, + "chooseAccountToFund": "Choose an account to add funds" }, "DeviceAction": { "stayInTheAppPlz": "Stay in the Ledger Live app and keep your Ledger nearby.", @@ -6569,7 +6581,9 @@ "name": "hedera", "createHederaAccountHelp": { "text": "Please refer to this support article for", - "link": "how to create a Hedera account" + "link": "How to create a Hedera account ?", + "title": "We can’t create a new Hedera account", + "description": "In the Hedera ecosystem, only a select number of services can create new accounts. While Ledger does not currently offer this onboarding service, alternatives are available. You will be able to add your Hedera account on Ledger Live once it’s created." }, "currentAddress": { "messageIfVirtual": "Your {{name}} address cannot be confirmed on your Ledger device. Use at your own risk." diff --git a/apps/ledger-live-mobile/src/newArch/features/Accounts/Navigator.tsx b/apps/ledger-live-mobile/src/newArch/features/Accounts/Navigator.tsx index cb53e8bdf5b0..24e8ded4bf33 100644 --- a/apps/ledger-live-mobile/src/newArch/features/Accounts/Navigator.tsx +++ b/apps/ledger-live-mobile/src/newArch/features/Accounts/Navigator.tsx @@ -15,6 +15,8 @@ import AccountsList from "LLM/features/Accounts/screens/AccountsList"; import { NavigationHeaderBackButton } from "~/components/NavigationHeaderBackButton"; import AddAccountsSuccess from "./screens/AddAccountSuccess"; import SelectAccounts from "./screens/SelectAccounts"; +import AddAccountsWarning from "./screens/AddAccountWarning"; +import NoAssociatedAccountsView from "./screens/NoAssociatedAccountsView"; export default function Navigator() { const { colors } = useTheme(); @@ -58,6 +60,7 @@ export default function Navigator() { component={ScanDeviceAccounts} options={{ headerTitle: "", + headerTransparent: true, }} /> {/* Select Accounts */} @@ -85,6 +88,25 @@ export default function Navigator() { options={{ headerTitle: "", headerLeft: () => null, + headerTransparent: true, + }} + /> + null, + headerTransparent: true, + }} + /> + null, + headerTransparent: true, }} /> diff --git a/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountListDrawer/CustomHeader/index.tsx b/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountListDrawer/CustomHeader/index.tsx new file mode 100644 index 000000000000..a514ad324d11 --- /dev/null +++ b/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountListDrawer/CustomHeader/index.tsx @@ -0,0 +1,43 @@ +import React from "react"; +import { Flex, Icons, Text } from "@ledgerhq/native-ui"; +import { TouchableOpacity } from "react-native"; + +type Props = { + onClose: () => void; + backgroundColor: string; + iconColor: string; + title: string; +}; + +const CustomHeader = ({ onClose, backgroundColor, iconColor, title }: Props) => { + return ( + + + {title} + + + + + + + + + + ); +}; + +export default CustomHeader; diff --git a/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountListDrawer/index.tsx b/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountListDrawer/index.tsx new file mode 100644 index 000000000000..3707a2f1c75c --- /dev/null +++ b/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountListDrawer/index.tsx @@ -0,0 +1,76 @@ +import React, { useCallback } from "react"; +import QueuedDrawer from "~/components/QueuedDrawer"; +import { Flex } from "@ledgerhq/native-ui"; +import { FlatList, ListRenderItemInfo, TouchableOpacity, View } from "react-native"; +import { Account, TokenAccount } from "@ledgerhq/types-live"; +import AccountItem from "../AccountsListView/components/AccountItem"; +import CustomHeader from "./CustomHeader"; +import { useTheme } from "styled-components/native"; +import getAccountListKeyExtractor from "../../utils/getAccountListKeyExtractor"; +import AccountListEmpty from "../AccountListEmpty"; +import { useTranslation } from "react-i18next"; + +type AccountListDrawerProps = { + isOpen: boolean; + onClose: () => void; + onBack: () => void; + data: (Account | TokenAccount)[]; + onPressAccount: (account: Account | TokenAccount) => void; +}; + +const AccountListDrawer = ({ + isOpen, + onClose, + onBack, + data, + onPressAccount, +}: AccountListDrawerProps) => { + const { colors } = useTheme(); + const { t } = useTranslation(); + + const renderItem = useCallback( + ({ item: account }: ListRenderItemInfo) => { + return ( + onPressAccount(account)}> + + + + + ); + }, + [onPressAccount], + ); + + const keyExtractor = useCallback(getAccountListKeyExtractor, []); + + return ( + ( + + )} + > + + } + style={{ paddingHorizontal: 16 }} + ListEmptyComponent={} + /> + + + ); +}; + +export default AccountListDrawer; diff --git a/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountListEmpty/index.tsx b/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountListEmpty/index.tsx new file mode 100644 index 000000000000..a0c63a7f9b98 --- /dev/null +++ b/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountListEmpty/index.tsx @@ -0,0 +1,11 @@ +import { Text } from "@ledgerhq/native-ui"; +import React from "react"; +import { Trans } from "react-i18next"; + +export default function AccountListEmpty() { + return ( + + + + ); +} diff --git a/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountQuickActionsDrawer/CustomHeader/index.tsx b/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountQuickActionsDrawer/CustomHeader/index.tsx new file mode 100644 index 000000000000..e57643d7db8a --- /dev/null +++ b/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountQuickActionsDrawer/CustomHeader/index.tsx @@ -0,0 +1,40 @@ +import React from "react"; +import { Flex, Icons } from "@ledgerhq/native-ui"; +import { TouchableOpacity } from "react-native"; +import AccountItem from "../../AccountsListView/components/AccountItem"; +import { AccountLike } from "@ledgerhq/types-live"; + +type Props = { + account: AccountLike | null; + onClose: () => void; + backgroundColor: string; + iconColor: string; +}; + +const CustomHeader = ({ account, onClose, backgroundColor, iconColor }: Props) => + !account ? null : ( + + + + + + + + + + + ); + +export default CustomHeader; diff --git a/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountQuickActionsDrawer/index.tsx b/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountQuickActionsDrawer/index.tsx new file mode 100644 index 000000000000..1f0d5d3705c1 --- /dev/null +++ b/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountQuickActionsDrawer/index.tsx @@ -0,0 +1,58 @@ +import React from "react"; +import QueuedDrawer from "~/components/QueuedDrawer"; +import { Box, Flex } from "@ledgerhq/native-ui"; +import { Account, TokenAccount } from "@ledgerhq/types-live"; + +import useAccountQuickActionDrawerViewModel from "./useAccountQuickActionDrawerViewModel"; +import TransferButton from "~/components/TransferButton"; +import { CryptoOrTokenCurrency } from "@ledgerhq/types-cryptoassets"; +import CustomHeader from "./CustomHeader"; +import { useTheme } from "styled-components/native"; + +type AccountListDrawerProps = { + isOpen: boolean; + onClose: () => void; + onBack: () => void; + account: Account | TokenAccount | null; + currency: CryptoOrTokenCurrency; +}; + +const AccountQuickActionsDrawer = ({ + isOpen, + onClose, + onBack, + account, + currency, +}: AccountListDrawerProps) => { + const { actions } = useAccountQuickActionDrawerViewModel({ + accounts: account ? [account as Account] : [], + currency, + }); + const { colors } = useTheme(); + + return ( + ( + + )} + > + + {actions.map((button, index) => ( + + + + ))} + + + ); +}; + +export default AccountQuickActionsDrawer; diff --git a/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountQuickActionsDrawer/useAccountQuickActionDrawerViewModel.ts b/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountQuickActionsDrawer/useAccountQuickActionDrawerViewModel.ts new file mode 100644 index 000000000000..c26f2367daac --- /dev/null +++ b/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountQuickActionsDrawer/useAccountQuickActionDrawerViewModel.ts @@ -0,0 +1,73 @@ +import { Account, TokenAccount } from "@ledgerhq/types-live"; + +import useQuickActions from "~/hooks/useQuickActions"; +import { useNavigation } from "@react-navigation/core"; +import { useTranslation } from "react-i18next"; +import { CryptoOrTokenCurrency } from "@ledgerhq/types-cryptoassets"; +import { BaseNavigatorStackParamList } from "~/components/RootNavigator/types/BaseNavigator"; +import { StackNavigatorNavigation } from "~/components/RootNavigator/types/helpers"; +import { IconType } from "@ledgerhq/native-ui/components/Icon/type"; +import { StyleProp, ViewStyle } from "react-native"; +import { track } from "~/analytics"; + +type ActionItem = { + title: string; + description: string; + tag?: string; + Icon: IconType; + onPress?: (() => void) | null; + onDisabledPress?: () => void; + disabled?: boolean; + event?: string; + eventProperties?: Parameters[1]; + style?: StyleProp; + testID?: string; +}; + +export default function useAccountQuickActionDrawerViewModel({ + currency, + accounts, +}: { + currency: CryptoOrTokenCurrency; + accounts: Account[] | TokenAccount[]; +}) { + const { + quickActionsList: { RECEIVE, BUY }, + } = useQuickActions({ currency, accounts }); + const navigation = useNavigation>(); + const { t } = useTranslation(); + + const actions: ActionItem[] = [ + RECEIVE && { + eventProperties: { + button: "transfer_receive", + //page, TODO: add it in the analytics ticket + drawer: "trade", + }, + title: t("transfer.receive.title"), + description: t("transfer.receive.description"), + onPress: () => + navigation.navigate(...(RECEIVE && RECEIVE.route)), + Icon: RECEIVE.icon, + disabled: RECEIVE.disabled, + testID: "transfer-receive-button", + }, + BUY && { + eventProperties: { + button: "transfer_buy", + //page, TODO: add it in the analytics ticket + drawer: "trade", + }, + title: t("transfer.buy.title"), + description: t("transfer.buy.description"), + Icon: BUY.icon, + onPress: () => navigation.navigate(...BUY.route), + disabled: BUY.disabled, + testID: "transfer-buy-button", + }, + ].filter((v: T | undefined): v is T => !!v); + + return { + actions, + }; +} diff --git a/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountsListView/components/AccountItem/index.tsx b/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountsListView/components/AccountItem/index.tsx index 17ca67ece392..db016caf36a9 100644 --- a/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountsListView/components/AccountItem/index.tsx +++ b/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountsListView/components/AccountItem/index.tsx @@ -3,10 +3,21 @@ import useAccountItemModel, { AccountItemProps } from "./useAccountItemModel"; import { Flex, Tag, Text } from "@ledgerhq/native-ui"; import CounterValue from "~/components/CounterValue"; import ParentCurrencyIcon from "~/components/ParentCurrencyIcon"; +import CurrencyUnitValue from "~/components/CurrencyUnitValue"; +import { Unit } from "@ledgerhq/types-cryptoassets"; type ViewProps = ReturnType; -const View: React.FC = ({ accountName, balance, formattedAddress, tag, currency }) => ( +const View: React.FC = ({ + accountName, + balance, + formattedAddress, + tag, + currency, + unit, + showUnit, + hideBalanceInfo, +}) => ( <> @@ -32,11 +43,18 @@ const View: React.FC = ({ accountName, balance, formattedAddress, tag - - - - - + {!hideBalanceInfo && ( + + + + + {showUnit && ( + + + + )} + + )} ); diff --git a/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountsListView/components/AccountItem/useAccountItemModel.ts b/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountsListView/components/AccountItem/useAccountItemModel.ts index b166c68b0540..dafc836a987f 100644 --- a/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountsListView/components/AccountItem/useAccountItemModel.ts +++ b/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountsListView/components/AccountItem/useAccountItemModel.ts @@ -6,6 +6,7 @@ import { import { Account, DerivationMode, TokenAccount } from "@ledgerhq/types-live"; import BigNumber from "bignumber.js"; import { useSelector } from "react-redux"; +import { useMaybeAccountUnit } from "~/hooks"; import { formatAddress } from "~/newArch/features/Accounts/utils/formatAddress"; import { accountsSelector } from "~/reducers/accounts"; import { useMaybeAccountName } from "~/reducers/wallet"; @@ -13,13 +14,17 @@ import { useMaybeAccountName } from "~/reducers/wallet"; export interface AccountItemProps { account: Account | TokenAccount; balance: BigNumber; + showUnit?: boolean; + hideBalanceInfo?: boolean; } -const useAccountItemModel = ({ account, balance }: AccountItemProps) => { +const useAccountItemModel = ({ account, balance, showUnit, hideBalanceInfo }: AccountItemProps) => { const allAccount = useSelector(accountsSelector); const isTokenAccount = isTokenAccountChecker(account); const currency = isTokenAccount ? account.token.parentCurrency : account.currency; const accountName = useMaybeAccountName(account); + const unit = useMaybeAccountUnit(account); + const parentAccount = getParentAccount(account, allAccount); const formattedAddress = formatAddress( isTokenAccount ? parentAccount.freshAddress : account.freshAddress, @@ -37,6 +42,9 @@ const useAccountItemModel = ({ account, balance }: AccountItemProps) => { formattedAddress, tag, currency, + unit, + showUnit, + hideBalanceInfo, }; }; diff --git a/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AddFundsButton/index.tsx b/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AddFundsButton/index.tsx new file mode 100644 index 000000000000..a65d6c5c6bad --- /dev/null +++ b/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AddFundsButton/index.tsx @@ -0,0 +1,71 @@ +import React, { useCallback, useState } from "react"; + +import { Button } from "@ledgerhq/native-ui"; +import { useTranslation } from "react-i18next"; +import { track } from "~/analytics"; +import { Account, AccountLike } from "@ledgerhq/types-live"; + +import { CryptoOrTokenCurrency } from "@ledgerhq/types-cryptoassets"; +import AccountListDrawer from "../AccountListDrawer"; +import AccountQuickActionsDrawer from "../AccountQuickActionsDrawer"; + +export default function AddFundsButton({ + accounts, + currency, +}: { + accounts: Account[]; + currency: CryptoOrTokenCurrency; +}) { + const { t } = useTranslation(); + const [isAccountListDrawerOpen, setIsAccountListDrawerOpen] = useState(false); + const [isAccountQuickActionsDrawerOpen, setIsAccountQuickActionsDrawerOpen] = + useState(false); + const [selectedAccount, setSelectedAccount] = useState( + accounts.length === 1 ? accounts[0] : null, + ); + const openFundOrAccountListDrawer = () => { + track("button_clicked", { button: "Add a new account" }); + if (accounts.length === 1) { + setIsAccountQuickActionsDrawerOpen(true); + } else setIsAccountListDrawerOpen(true); + }; + + const closeAccountListDrawer = () => setIsAccountListDrawerOpen(false); + + const handleOnSelectAccoount = useCallback((account: AccountLike) => { + closeAccountListDrawer(); + setSelectedAccount(account); + setIsAccountQuickActionsDrawerOpen(true); + }, []); + + const handleOnCloseQuickActions = () => { + setIsAccountQuickActionsDrawerOpen(false); + }; + + return ( + <> + + + + + ); +} diff --git a/apps/ledger-live-mobile/src/newArch/features/Accounts/components/VerticalGradientBackground/index.tsx b/apps/ledger-live-mobile/src/newArch/features/Accounts/components/VerticalGradientBackground/index.tsx new file mode 100644 index 000000000000..f70abd8ddcec --- /dev/null +++ b/apps/ledger-live-mobile/src/newArch/features/Accounts/components/VerticalGradientBackground/index.tsx @@ -0,0 +1,25 @@ +import React from "react"; +import { StyleSheet } from "react-native"; +import Svg, { Rect, Defs, LinearGradient, Stop } from "react-native-svg"; + +export default function VerticalGradientBackground({ stopColor }: { stopColor: string }) { + return ( + + + + + + + + + + ); +} diff --git a/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/AddAccount/types.ts b/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/AddAccount/types.ts index 4ce5be48b8f8..39b830fd86cf 100644 --- a/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/AddAccount/types.ts +++ b/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/AddAccount/types.ts @@ -1,8 +1,8 @@ import { CryptoOrTokenCurrency } from "@ledgerhq/types-cryptoassets"; import { ScreenName } from "~/const"; import { Device } from "@ledgerhq/types-devices"; -import { AccountLikeEnhanced } from "../ScanDeviceAccounts/types"; import { Account } from "@ledgerhq/types-live"; +import { Props as TouchableProps } from "~/components/Touchable"; type CommonParams = { context?: "addAccounts" | "receiveFunds"; @@ -19,8 +19,21 @@ export type NetworkBasedAddAccountNavigator = { [ScreenName.SelectAccounts]: CommonParams & { createTokenAccount?: boolean; }; + [ScreenName.AddAccountsSuccess]: CommonParams & { - fundedAccounts: AccountLikeEnhanced[]; - accountsWithZeroBalance: AccountLikeEnhanced[]; + accountsToAdd: Account[]; + }; + [ScreenName.AddAccountsWarning]: CommonParams & { + emptyAccount?: Account; + emptyAccountName?: string; + }; + [ScreenName.NoAssociatedAccounts]: { + CustomNoAssociatedAccounts: ({ + style, + }: { + style?: { + paddingHorizontal?: TouchableProps["style"]; + }; + }) => JSX.Element; }; }; diff --git a/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/AddAccountSuccess/index.tsx b/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/AddAccountSuccess/index.tsx index 3113338cb956..31418641182d 100644 --- a/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/AddAccountSuccess/index.tsx +++ b/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/AddAccountSuccess/index.tsx @@ -1,10 +1,16 @@ -import { Button, Flex, Icons, Text } from "@ledgerhq/native-ui"; +import { Button, Flex, Icons, rgba, Text } from "@ledgerhq/native-ui"; import React, { useCallback } from "react"; import { useTranslation } from "react-i18next"; -import { FlatList, ListRenderItemInfo, StyleSheet, TouchableOpacity, View } from "react-native"; -import { useTheme } from "@react-navigation/native"; +import { + Animated, + FlatList, + ListRenderItemInfo, + StyleSheet, + TouchableOpacity, + View, +} from "react-native"; +import { useTheme } from "styled-components/native"; import { ScreenName } from "~/const"; -import { rgba } from "../../../../../colors"; import { TrackScreen } from "~/analytics"; import type { BaseComposite, StackNavigatorProps } from "~/components/RootNavigator/types/helpers"; import AccountItem from "../../components/AccountsListView/components/AccountItem"; @@ -14,8 +20,11 @@ import { useSafeAreaInsets } from "react-native-safe-area-context"; import SafeAreaView from "~/components/SafeAreaView"; import Circle from "~/components/Circle"; import { NetworkBasedAddAccountNavigator } from "../AddAccount/types"; - -// TODO: Add business logic (0 funded account warning section) and Poolish (UI + gradient like figma design) & add integration tests in the following ticket https://ledgerhq.atlassian.net/browse/LIVE-13983 +import VerticalGradientBackground from "../../components/VerticalGradientBackground"; +import { getCurrencyColor } from "@ledgerhq/live-common/currencies/index"; +import { useNavigation } from "@react-navigation/core"; +import useAnimatedStyle from "../ScanDeviceAccounts/components/ScanDeviceAccountsFooter/useAnimatedStyle"; +import AddFundsButton from "../../components/AddFundsButton"; type Props = BaseComposite< StackNavigatorProps @@ -25,56 +34,70 @@ export default function AddAccountsSuccess({ route }: Props) { const { colors } = useTheme(); const { t } = useTranslation(); const insets = useSafeAreaInsets(); + const navigation = useNavigation(); + const { animatedSelectableAccount } = useAnimatedStyle(); + + const goToAccounts = useCallback( + (accountId: string) => () => { + navigation.navigate(ScreenName.Account, { + accountId, + }); + }, + [navigation], + ); - const { currency, fundedAccounts } = route.params || {}; + const { currency, accountsToAdd } = route.params || {}; const renderItem = useCallback( ({ item }: ListRenderItemInfo) => ( - - - - - - + + + + + + + + ), - [colors.primary], + [colors.primary, goToAccounts, animatedSelectableAccount], ); const keyExtractor = useCallback((item: AccountLikeEnhanced) => item?.id, []); + const statusColor = colors.neutral.c100; + return ( + - + - + - {t("addAccounts.added", { count: fundedAccounts.length })} + {t("addAccounts.added", { count: accountsToAdd.length })} + + + } + style={{ paddingHorizontal: 16 }} + /> - - - + @@ -85,34 +108,8 @@ export default function AddAccountsSuccess({ route }: Props) { const styles = StyleSheet.create({ root: { - flex: 1, - paddingHorizontal: 20, - alignItems: "center", - justifyContent: "center", - }, - currencySuccess: { - width: 80, - height: 80, - borderRadius: 40, - alignItems: "center", - justifyContent: "center", - }, - outer: { - position: "absolute", - top: -5, - right: -5, - width: 34, - height: 34, - borderRadius: 100, - flexDirection: "row", - alignItems: "center", - justifyContent: "center", - }, - inner: { - width: 26, - height: 26, - borderRadius: 100, - flexDirection: "row", + marginTop: 100, + marginBottom: 20, alignItems: "center", justifyContent: "center", }, @@ -120,19 +117,6 @@ const styles = StyleSheet.create({ marginTop: 32, fontSize: 24, }, - desc: { - marginTop: 16, - marginBottom: 32, - marginHorizontal: 32, - textAlign: "center", - fontSize: 14, - }, - buttonsContainer: { - alignSelf: "stretch", - }, - button: { - marginBottom: 16, - }, iconWrapper: { height: 72, width: 72, diff --git a/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/AddAccountWarning/index.tsx b/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/AddAccountWarning/index.tsx new file mode 100644 index 000000000000..7f8d27811bac --- /dev/null +++ b/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/AddAccountWarning/index.tsx @@ -0,0 +1,127 @@ +import React, { useCallback } from "react"; +import { Button, Flex, Icons, rgba, Text } from "@ledgerhq/native-ui"; +import { useTranslation } from "react-i18next"; +import { Animated, StyleSheet, TouchableOpacity, View } from "react-native"; +import { useTheme } from "styled-components/native"; +import { ScreenName } from "~/const"; +import type { BaseComposite, StackNavigatorProps } from "~/components/RootNavigator/types/helpers"; +import AccountItem from "../../components/AccountsListView/components/AccountItem"; +import { Account } from "@ledgerhq/types-live"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; +import SafeAreaView from "~/components/SafeAreaView"; +import Circle from "~/components/Circle"; +import { NetworkBasedAddAccountNavigator } from "../AddAccount/types"; +import VerticalGradientBackground from "../../components/VerticalGradientBackground"; +import BigNumber from "bignumber.js"; +import { useNavigation } from "@react-navigation/core"; +import useAnimatedStyle from "../ScanDeviceAccounts/components/ScanDeviceAccountsFooter/useAnimatedStyle"; +import AddFundsButton from "../../components/AddFundsButton"; +type Props = BaseComposite< + StackNavigatorProps +>; + +export default function AddAccountsWarning({ route }: Props) { + const { colors } = useTheme(); + const { t } = useTranslation(); + const insets = useSafeAreaInsets(); + const navigation = useNavigation(); + + const { animatedSelectableAccount } = useAnimatedStyle(); + + const goToAccounts = useCallback( + (accountId: string) => () => { + navigation.navigate(ScreenName.Account, { + accountId, + }); + }, + [navigation], + ); + const { emptyAccount, emptyAccountName, currency } = route.params || {}; + + const statusColor = colors.warning.c70; + + return ( + + + + + + + + + <> + + {t("addAccounts.addAccountsWarning.cantCreateAccount.title", { + accountName: emptyAccountName, + })} + + + + {t("addAccounts.addAccountsWarning.cantCreateAccount.body", { + accountName: emptyAccountName, + })} + + + + + + + + + + + + + + + + + + + ); +} + +const styles = StyleSheet.create({ + root: { + paddingHorizontal: 20, + alignItems: "center", + justifyContent: "center", + marginTop: 50, + }, + title: { + marginTop: 32, + fontSize: 24, + textAlign: "center", + width: "100%", + fontWeight: 600, + fontStyle: "normal", + lineHeight: 32.4, + letterSpacing: 0.75, + }, + desc: { + marginTop: 16, + marginBottom: 32, + marginHorizontal: 32, + textAlign: "center", + fontSize: 14, + }, + iconWrapper: { + height: 72, + width: 72, + borderRadius: 50, + alignItems: "center", + justifyContent: "center", + }, +}); diff --git a/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/NoAssociatedAccountsView/index.tsx b/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/NoAssociatedAccountsView/index.tsx new file mode 100644 index 000000000000..3218989a70fc --- /dev/null +++ b/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/NoAssociatedAccountsView/index.tsx @@ -0,0 +1,73 @@ +import React from "react"; +import { Flex, Icons, rgba, Button } from "@ledgerhq/native-ui"; +import { StyleSheet, View } from "react-native"; +import { useTheme } from "styled-components/native"; +import SafeAreaView from "~/components/SafeAreaView"; +import Circle from "~/components/Circle"; +import VerticalGradientBackground from "LLM/features/Accounts/components/VerticalGradientBackground"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { useTranslation } from "react-i18next"; +import { BaseComposite, StackNavigatorProps } from "~/components/RootNavigator/types/helpers"; +import { NetworkBasedAddAccountNavigator } from "../AddAccount/types"; +import { ScreenName } from "~/const"; + +type Props = BaseComposite< + StackNavigatorProps +>; +export default function NoAssociatedAccountsView({ route }: Props) { + const { colors } = useTheme(); + const insets = useSafeAreaInsets(); + const { t } = useTranslation(); + const { CustomNoAssociatedAccounts } = route.params || {}; + + const statusColor = colors.primary.c70; + return ( + + + + + + + + + {} + + + + + + ); +} + +const styles = StyleSheet.create({ + famillyComponentContainer: { + paddingHorizontal: 20, + alignItems: "center", + justifyContent: "center", + }, + iconWrapper: { + height: 72, + width: 72, + borderRadius: 50, + alignItems: "center", + justifyContent: "center", + display: "flex", + }, +}); diff --git a/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/ScanDeviceAccounts/components/CanCreateAccountAlert/index.tsx b/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/ScanDeviceAccounts/components/CanCreateAccountAlert/index.tsx new file mode 100644 index 000000000000..dbc3b1deb0c3 --- /dev/null +++ b/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/ScanDeviceAccounts/components/CanCreateAccountAlert/index.tsx @@ -0,0 +1,59 @@ +import { Flex, Icons, rgba, Text } from "@ledgerhq/native-ui"; +import React from "react"; +import { useTranslation } from "react-i18next"; +import { Animated, StyleSheet } from "react-native"; +import { View } from "react-native-animatable"; +import { useTheme } from "styled-components/native"; +import Circle from "~/components/Circle"; +import VerticalGradientBackground from "~/newArch/features/Accounts/components/VerticalGradientBackground"; +import useAnimatedStyle from "../ScanDeviceAccountsFooter/useAnimatedStyle"; + +export function CantCreateAccountAlert({ currencyName }: { currencyName: string }) { + const { colors } = useTheme(); + const { t } = useTranslation(); + const { animatedSelectableAccount: animatedContainer } = useAnimatedStyle(); + return ( + + + + + + + + + + + {t("addAccounts.addAccountsWarning.noAccountToCreate.title", { + replace: { + currency: currencyName, + }, + })} + + + + ); +} + +const styles = StyleSheet.create({ + cantCreateAccountTitle: { + marginTop: 32, + fontSize: 24, + textAlign: "center", + width: "100%", + fontWeight: 600, + fontStyle: "normal", + lineHeight: 32.4, + letterSpacing: 0.75, + }, + iconWrapper: { + height: 72, + width: 72, + borderRadius: 50, + alignItems: "center", + justifyContent: "center", + }, + cantCreateAccount: { + flex: 1, + justifyContent: "center", + }, +}); diff --git a/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/ScanDeviceAccounts/components/ScanDeviceAccountsFooter/index.tsx b/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/ScanDeviceAccounts/components/ScanDeviceAccountsFooter/index.tsx index cb844d333f0d..0610cbbb2827 100644 --- a/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/ScanDeviceAccounts/components/ScanDeviceAccountsFooter/index.tsx +++ b/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/ScanDeviceAccounts/components/ScanDeviceAccountsFooter/index.tsx @@ -34,7 +34,7 @@ const ScanDeviceAccountsFooter = ({