Skip to content

Commit

Permalink
feat: ✨ (llm)add new accounts list to wallet centric screen
Browse files Browse the repository at this point in the history
  • Loading branch information
LucasWerey committed Dec 18, 2024
1 parent 8223e21 commit a52bd8e
Show file tree
Hide file tree
Showing 13 changed files with 141 additions and 37 deletions.
5 changes: 5 additions & 0 deletions .changeset/fifty-toes-sit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"live-mobile": minor
---

Implementation of the new accounts list inside wallet centric screen.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CryptoOrTokenCurrency } from "@ledgerhq/types-cryptoassets";
import { AccountLike, ProtoNFT } from "@ledgerhq/types-live";
import { Account, AccountLike, ProtoNFT, TokenAccount } from "@ledgerhq/types-live";
import { ScreenName } from "~/const";

export type AccountsNavigatorParamList = {
Expand Down Expand Up @@ -39,5 +39,6 @@ export type AccountsNavigatorParamList = {
showHeader?: boolean;
canAddAccount?: boolean;
isSyncEnabled?: boolean;
specificAccounts?: Account[] | TokenAccount[];
};
};
1 change: 1 addition & 0 deletions apps/ledger-live-mobile/src/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -3963,6 +3963,7 @@
},
"addAccounts": {
"addNew": "Add new",
"seeAllAccounts": "See all accounts",
"addNewOrExisting": "Add new or existing account",
"supportLinks": {
"segwit_or_native_segwit": "Segwit or Native segwit?"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,31 @@ import { Flex, Tag, Text } from "@ledgerhq/native-ui";
import ParentCurrencyIcon from "~/components/ParentCurrencyIcon";
import BigNumber from "bignumber.js";
import CounterValue from "~/components/CounterValue";
import { Account, DerivationMode } from "@ledgerhq/types-live";
import { Account, DerivationMode, TokenAccount } from "@ledgerhq/types-live";
import { useMaybeAccountName } from "~/reducers/wallet";
import { getTagDerivationMode } from "@ledgerhq/coin-framework/lib/derivation";
import { formatAddress } from "LLM/features/Accounts/utils/formatAddress";
import {
getParentAccount,
isTokenAccount as isTokenAccountChecker,
} from "@ledgerhq/live-common/account/index";
import { useSelector } from "react-redux";
import { accountsSelector } from "~/reducers/accounts";

interface AccountItemProps {
account: Account;
account: Account | TokenAccount;
balance: BigNumber;
}

const AccountItem: React.FC<AccountItemProps> = ({ account, balance }) => {
const currency = account.currency;
const allAccount = useSelector(accountsSelector);
const isTokenAccount = isTokenAccountChecker(account);
const currency = isTokenAccount ? account.token.parentCurrency : account.currency;
const accountName = useMaybeAccountName(account);
const formattedAddress = formatAddress(account.freshAddress);
const parentAccount = getParentAccount(account, allAccount);
const formattedAddress = formatAddress(
isTokenAccount ? parentAccount.freshAddress : account.freshAddress,
);
const tag =
account.type === "Account" &&
account?.derivationMode !== undefined &&
Expand All @@ -26,7 +37,7 @@ const AccountItem: React.FC<AccountItemProps> = ({ account, balance }) => {

return (
<>
<Flex flex={1} flexShrink={1} testID={`accountItem-${accountName}`}>
<Flex flex={1} rowGap={2} flexShrink={1} testID={`accountItem-${accountName}`}>
<Flex flexDirection="row" columnGap={8} alignItems="center" maxWidth="70%">
<Text
numberOfLines={1}
Expand All @@ -47,7 +58,7 @@ const AccountItem: React.FC<AccountItemProps> = ({ account, balance }) => {
<Text numberOfLines={1} variant="body" color="neutral.c70" flexShrink={1}>
{formattedAddress}
</Text>
<ParentCurrencyIcon forceIconScale={1.5} currency={currency} size={20} />
<ParentCurrencyIcon forceIconScale={2} currency={currency} size={20} />
</Flex>
</Flex>
<Flex justifyContent="center" alignItems="flex-end">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useCallback, useMemo } from "react";
import { FlashList, FlashListProps } from "@shopify/flash-list";
import useAccountsListViewModel, { type Props } from "./useAccountsListViewModel";
import { Account } from "@ledgerhq/types-live";
import { Account, TokenAccount } from "@ledgerhq/types-live";
import { Flex } from "@ledgerhq/native-ui";
import { Pressable } from "react-native";
import AccountItem from "./components/AccountItem";
Expand All @@ -15,11 +15,13 @@ type ViewProps = ReturnType<typeof useAccountsListViewModel>;

const View: React.FC<ViewProps> = ({ accountsToDisplay, isSyncEnabled, onAccountPress }) => {
const List = useMemo(() => {
return isSyncEnabled ? globalSyncRefreshControl<FlashListProps<Account>>(FlashList) : FlashList;
return isSyncEnabled
? globalSyncRefreshControl<FlashListProps<Account | TokenAccount>>(FlashList)
: FlashList;
}, [isSyncEnabled]);

const renderItem = useCallback(
({ item }: { item: Account }) => (
({ item }: { item: Account | TokenAccount }) => (
<Pressable
style={({ pressed }: { pressed: boolean }) => [
{ opacity: pressed ? 0.5 : 1.0, marginVertical: 12 },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface Props {
sourceScreenName?: ScreenName;
isSyncEnabled?: boolean;
limitNumberOfAccounts?: number;
specificAccounts?: Account[] | TokenAccount[];
}

export type NavigationProp = BaseNavigationComposite<
Expand All @@ -31,12 +32,13 @@ const useAccountsListViewModel = ({
sourceScreenName,
isSyncEnabled = false,
limitNumberOfAccounts,
specificAccounts,
}: Props) => {
const startNavigationTTITimer = useStartProfiler();
const navigation = useNavigation<NavigationProp>();
const accounts = useSelector(accountsSelector);
const walletState = useSelector(walletSelector);
const accountsToDisplay = accounts.slice(0, limitNumberOfAccounts);
const accountsToDisplay = specificAccounts || accounts.slice(0, limitNumberOfAccounts);

const refreshAccountsOrdering = useRefreshAccountsOrdering();
useFocusEffect(refreshAccountsOrdering);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,19 @@ const StyledPressable = styled(Pressable)`

type Props = {
sourceScreenName: string;
onClick?: () => void;
};

const AddAccountButton: React.FC<Props> = ({ sourceScreenName }) => {
const AddAccountButton: React.FC<Props> = ({ sourceScreenName, onClick }) => {
const { t } = useTranslation();
const [isAddModalOpened, setAddModalOpened] = useState<boolean>(false);

const openAddModal = () => {
track("button_clicked", { button: "Add a new account", page: sourceScreenName });
if (onClick) {
onClick();
return;
}
setAddModalOpened(true);
};
const closeAddModal = () => setAddModalOpened(false);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from "react";
import React, { useCallback } from "react";
import AccounstListView from "LLM/features/Accounts/components/AccountsListView";
import { ScreenName } from "~/const";
import { NavigatorName, ScreenName } from "~/const";
import { ReactNavigationPerformanceView } from "@shopify/react-native-performance-navigation";
import { StackNavigatorProps } from "~/components/RootNavigator/types/helpers";
import SafeAreaView from "~/components/SafeAreaView";
Expand All @@ -14,6 +14,12 @@ import { RefreshMedium } from "@ledgerhq/icons-ui/nativeLegacy";
import { useSelector } from "react-redux";
import Spinning from "~/components/Spinning";
import { isUpToDateSelector } from "~/reducers/accounts";
import { Account, TokenAccount } from "@ledgerhq/types-live";
import {
getAccountCurrency,
isTokenAccount as isTokenAccountChecker,
} from "@ledgerhq/live-common/account/index";
import { useNavigation } from "@react-navigation/native";

type Props = StackNavigatorProps<AccountsListNavigator, ScreenName.AccountsList>;

Expand All @@ -24,19 +30,52 @@ export default function AccountsList({ route }: Props) {
const showHeader = params?.showHeader;
const sourceScreenName = params?.sourceScreenName;
const isSyncEnabled = params?.isSyncEnabled;
const specificAccounts = params?.specificAccounts;
const navigation = useNavigation();

const isTokenAccount = specificAccounts && isTokenAccountChecker(specificAccounts[0]);
const ticker = specificAccounts
? isTokenAccount
? (specificAccounts[0] as TokenAccount).token.ticker
: (specificAccounts[0] as Account).currency.ticker
: undefined;

const isUpToDate = useSelector(isUpToDateSelector);
const globalSyncState = useGlobalSyncState();
const syncPending = globalSyncState.pending && !isUpToDate;

const onAddAccount = useCallback(() => {
if (!specificAccounts) return;

const currency = getAccountCurrency(specificAccounts?.[0]);

if (currency && currency.type === "TokenCurrency") {
navigation.navigate(NavigatorName.AddAccounts, {
screen: undefined,
params: {
token: currency,
},
});
} else {
navigation.navigate(NavigatorName.AddAccounts, {
screen: undefined,
currency,
});
}
}, [navigation, specificAccounts]);

const onClick = specificAccounts ? onAddAccount : undefined;

return (
<>
<TrackScreen event="Accounts" />
<ReactNavigationPerformanceView screenName={ScreenName.AccountsList} interactive>
<SafeAreaView edges={["left", "right"]} isFlex style={{ marginHorizontal: 16 }}>
<SafeAreaView edges={["left", "right", "bottom"]} isFlex style={{ marginHorizontal: 16 }}>
{showHeader && (
<Text variant="h1Inter" fontSize={28} paddingY={2}>
{t("accounts.title")}
{ticker
? t("accounts.cryptoAccountsTitle", { currencyTicker: ticker })
: t("accounts.title")}
</Text>
)}
{syncPending && (
Expand All @@ -49,8 +88,12 @@ export default function AccountsList({ route }: Props) {
</Text>
</Flex>
)}
{canAddAccount && <AddAccountButton sourceScreenName="Accounts" />}
<AccounstListView sourceScreenName={sourceScreenName} isSyncEnabled={isSyncEnabled} />
{canAddAccount && <AddAccountButton sourceScreenName="Accounts" onClick={onClick} />}
<AccounstListView
sourceScreenName={sourceScreenName}
isSyncEnabled={isSyncEnabled}
specificAccounts={specificAccounts}
/>
</SafeAreaView>
</ReactNavigationPerformanceView>
</>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Account, TokenAccount } from "@ledgerhq/types-live";
import { ScreenName } from "~/const";

export type AccountsListNavigator = {
Expand All @@ -6,5 +7,6 @@ export type AccountsListNavigator = {
showHeader?: boolean;
canAddAccount?: boolean;
isSyncEnabled?: boolean;
specificAccounts?: Account[] | TokenAccount[];
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export default function AssetsList({ route }: Props) {
<>
<TrackScreen event="Accounts" />
<ReactNavigationPerformanceView screenName={ScreenName.AssetsList} interactive>
<SafeAreaView edges={["left", "right"]} isFlex style={{ marginHorizontal: 16 }}>
<SafeAreaView edges={["left", "right", "bottom"]} isFlex style={{ marginHorizontal: 16 }}>
{showHeader && (
<Text variant="h1Inter" fontSize={28} paddingY={2}>
{t("assets.title")}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import { NavigatorName, ScreenName } from "~/const";
import { track } from "~/analytics";
import { AccountsNavigatorParamList } from "~/components/RootNavigator/types/AccountsNavigator";
import { BaseComposite, StackNavigatorProps } from "~/components/RootNavigator/types/helpers";
import AccountsList from "LLM/features/Accounts/components/AccountsListView";
import { FeatureToggle, useFeature } from "@ledgerhq/live-common/featureFlags/index";
import AddAccountButton from "~/newArch/features/Accounts/components/AddAccountButton";

const NB_MAX_ACCOUNTS_TO_DISPLAY = 3;

Expand All @@ -19,11 +22,13 @@ type ListProps = {
accounts: Account[] | TokenAccount[];
currencyId: string;
currencyTicker: string;
onAddAccount: () => void;
};

const AccountsSection = ({ accounts, currencyId, currencyTicker }: ListProps) => {
const AccountsSection = ({ accounts, currencyId, currencyTicker, onAddAccount }: ListProps) => {
const navigation = useNavigation<Navigation["navigation"]>();
const { t } = useTranslation();
const llmAccountListUI = useFeature("llmAccountListUI");

const accountsToDisplay = useMemo(
() => accounts.slice(0, NB_MAX_ACCOUNTS_TO_DISPLAY),
Expand All @@ -46,26 +51,50 @@ const AccountsSection = ({ accounts, currencyId, currencyTicker }: ListProps) =>
track("button_clicked", {
button: "See All",
});
navigation.navigate(NavigatorName.Accounts, {
screen: ScreenName.Accounts,
params: {
currencyId,
currencyTicker,
},
});
}, [navigation, currencyId, currencyTicker]);
if (llmAccountListUI) {
navigation.navigate(NavigatorName.Accounts, {
screen: ScreenName.AccountsList,
params: {
sourceScreenName: ScreenName.Asset,
showHeader: true,
canAddAccount: true,
isSyncEnabled: true,
specificAccounts: accounts as Account[],
},
});
} else {
navigation.navigate(NavigatorName.Accounts, {
screen: ScreenName.Accounts,
params: {
currencyId,
currencyTicker,
},
});
}
}, [llmAccountListUI, navigation, accounts, currencyId, currencyTicker]);

return (
<>
<FlatList<Account | TokenAccount>
data={accountsToDisplay}
renderItem={renderItem}
keyExtractor={item => item.id}
contentContainerStyle={{ flex: 1 }}
/>
<FeatureToggle
featureId="llmAccountListUI"
fallback={
<FlatList<Account | TokenAccount>
data={accountsToDisplay}
renderItem={renderItem}
keyExtractor={item => item.id}
contentContainerStyle={{ flex: 1 }}
/>
}
>
<AccountsList
limitNumberOfAccounts={NB_MAX_ACCOUNTS_TO_DISPLAY}
specificAccounts={accountsToDisplay}
/>
<AddAccountButton sourceScreenName={ScreenName.Asset} onClick={onAddAccount} />
</FeatureToggle>
{accounts.length > NB_MAX_ACCOUNTS_TO_DISPLAY ? (
<Button type="shade" size="large" outline mt={6} onPress={goToAccountsScreen}>
{t("common.seeAllWithNumber", { number: accounts.length })}
<Button type="shade" size="large" outline mt={4} onPress={goToAccountsScreen}>
{t("addAccounts.seeAllAccounts")}
</Button>
) : null}
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ const AssetScreen = ({ route }: NavigationProps) => {
accounts={cryptoAccounts as Account[] | TokenAccount[]}
currencyId={currency.id}
currencyTicker={currency.ticker}
onAddAccount={onAddAccount}
/>
</SectionContainer>,
<AssetMarketSection currency={currency} key="AssetMarketSection" />,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useTranslation } from "react-i18next";
import proxyStyled from "@ledgerhq/native-ui/components/styled";
import { FlexBoxProps } from "@ledgerhq/native-ui/components/Layout/Flex/index";
import { NavigationProp } from "@react-navigation/native";
import { useFeature } from "@ledgerhq/live-common/featureFlags/index";

type Props = {
title: React.ReactNode;
Expand Down Expand Up @@ -36,6 +37,7 @@ const SectionTitle = ({
containerProps,
}: Props) => {
const { t } = useTranslation();
const llmAccountListUI = useFeature("llmAccountListUI");
const onLinkPress = useCallback(() => {
if (onSeeAllPress) {
onSeeAllPress();
Expand All @@ -55,7 +57,7 @@ const SectionTitle = ({
<Text variant="small" fontWeight="semiBold" color="neutral.c70" uppercase flexShrink={1}>
{title}
</Text>
{onSeeAllPress || navigatorName ? (
{(onSeeAllPress || navigatorName) && !llmAccountListUI?.enabled ? (
<StyledTouchableOpacity onPress={onLinkPress}>
<TextLink onPress={onLinkPress} type={"color"}>
{seeMoreText || t("common.seeAll")}
Expand Down

0 comments on commit a52bd8e

Please sign in to comment.