Skip to content

Commit

Permalink
feat: ✨ (llm): new accounts and assets lists portfolio
Browse files Browse the repository at this point in the history
  • Loading branch information
LucasWerey committed Dec 11, 2024
1 parent c23401a commit 3c3c2c9
Show file tree
Hide file tree
Showing 23 changed files with 1,405 additions and 146 deletions.
6 changes: 6 additions & 0 deletions .changeset/curvy-ladybugs-collect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"live-mobile": minor
"@ledgerhq/native-ui": minor
---

Create a new tabSelector component inside native ui. Rename the existing one in DrawerTabSelector since it's used only in a drawer. Integration of the new assets/accounts lists in wallet screen
5 changes: 5 additions & 0 deletions apps/ledger-live-mobile/__tests__/jest-setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@ jest.mock("react-native-reanimated", () => {
// Silence the warning: Animated: `useNativeDriver` is not supported because the native animated module is missing
jest.mock("react-native/Libraries/Animated/NativeAnimatedHelper");

jest.mock("~/analytics", () => ({
...jest.requireActual("~/analytics"),
track: jest.fn(),
}));

jest.mock("@react-native-firebase/messaging", () => ({
messaging: jest.fn(() => ({
hasPermission: jest.fn(() => Promise.resolve(true)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,7 @@ export type BaseNavigatorStackParamList = {

[NavigatorName.AnalyticsOptInPrompt]: NavigatorScreenParams<AnalyticsOptInPromptNavigatorParamList>;
[ScreenName.MockedAddAssetButton]: undefined;
[ScreenName.MockedWalletScreen]: undefined;

// WALLET SYNC
[NavigatorName.WalletSync]: NavigatorScreenParams<WalletSyncNavigatorStackParamList>;
Expand Down
1 change: 1 addition & 0 deletions apps/ledger-live-mobile/src/const/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,7 @@ export enum ScreenName {
LedgerSyncDeepLinkHandler = "LedgerSyncDeepLinkHandler",

MockedAddAssetButton = "MockedAddAssetButton",
MockedWalletScreen = "MockedWalletScreen",
GenericLandingPage = "GenericLandingPage",

// Web3Hub
Expand Down
3 changes: 2 additions & 1 deletion apps/ledger-live-mobile/src/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -2342,7 +2342,8 @@
"seeMarket": "See all"
},
"walletBalance": "Wallet balance",
"seelAllAssets": "See All Assets",
"seeAllAssets": "See all assets",
"seeAllAccounts": "See all accounts",
"marketPriceSection": {
"title": "{{currencyTicker}} market price",
"currencyPrice": "1 {{currencyTicker}} price",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { Flex } from "@ledgerhq/native-ui";
import { Pressable } from "react-native";
import AccountItem from "./components/AccountItem";
import globalSyncRefreshControl from "~/components/globalSyncRefreshControl";
import { withDiscreetMode } from "~/context/DiscreetModeContext";
import isEqual from "lodash/isEqual";

const ESTIMED_ITEM_SIZE = 150;

Expand Down Expand Up @@ -50,4 +52,4 @@ const AccountsListView: React.FC<Props> = props => {
return <View {...viewModel} />;
};

export default AccountsListView;
export default React.memo(withDiscreetMode(AccountsListView), isEqual);
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,16 @@ const StyledPressable = styled(Pressable)`
column-gap: 12px;
`;

const AddAccountButton: React.FC = () => {
type Props = {
sourceScreenName: string;
};

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

const openAddModal = () => {
track("button_clicked", { button: "add a new account", page: "Accounts" });
track("button_clicked", { button: "Add a new account", page: sourceScreenName });
setAddModalOpened(true);
};
const closeAddModal = () => setAddModalOpened(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export default function AccountsList({ route }: Props) {
</Text>
</Flex>
)}
{canAddAccount && <AddAccountButton />}
{canAddAccount && <AddAccountButton sourceScreenName="Accounts" />}
<AccounstListView sourceScreenName={sourceScreenName} isSyncEnabled={isSyncEnabled} />
</SafeAreaView>
</ReactNavigationPerformanceView>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import AssetItem from "./components/AssetItem";
import { Asset } from "~/types/asset";
import BigNumber from "bignumber.js";
import globalSyncRefreshControl from "~/components/globalSyncRefreshControl";
import { withDiscreetMode } from "~/context/DiscreetModeContext";
import isEqual from "lodash/isEqual";

const ESTIMED_ITEM_SIZE = 150;

Expand Down Expand Up @@ -53,4 +55,4 @@ const AssetsListView: React.FC<Props> = props => {
return <View {...viewModel} />;
};

export default AssetsListView;
export default React.memo(withDiscreetMode(AssetsListView), isEqual);
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from "react";
import { Flex, TabSelector } from "@ledgerhq/native-ui";
import { DrawerTabSelector, Flex } from "@ledgerhq/native-ui";
import QrCode from "LLM/features/WalletSync/components/Synchronize/QrCode";
import ScanQrCode from "../../components/Synchronize/ScanQrCode";
import { Options, OptionsType } from "LLM/features/WalletSync/types/Activation";
Expand Down Expand Up @@ -59,7 +59,7 @@ const QrCodeMethod = ({

return (
<Flex flexDirection={"column"} alignItems={"center"} rowGap={24} width={"100%"} height={"100%"}>
<TabSelector
<DrawerTabSelector
options={[Options.SCAN, Options.SHOW_QR]}
selectedOption={currentOption}
handleSelectOption={handleSelectOption}
Expand Down
80 changes: 70 additions & 10 deletions apps/ledger-live-mobile/src/screens/Portfolio/PortfolioAssets.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,33 @@
import { Button, IconsLegacy } from "@ledgerhq/native-ui";
import { useNavigation } from "@react-navigation/native";
import React, { useCallback, useMemo } from "react";
import { useTranslation } from "react-i18next";
import { useSelector } from "react-redux";
import { useStartProfiler } from "@shopify/react-native-performance";
import { GestureResponderEvent } from "react-native";
import Animated from "react-native-reanimated";
import { useNavigation } from "@react-navigation/native";
import { Button, IconsLegacy, Box, Flex, TabSelector } from "@ledgerhq/native-ui";
import { useDistribution } from "~/actions/general";
import { TrackScreen } from "~/analytics";
import { track, TrackScreen } from "~/analytics";
import { NavigatorName, ScreenName } from "~/const";
import { Box } from "@ledgerhq/native-ui";
import { blacklistedTokenIdsSelector, discreetModeSelector } from "~/reducers/settings";
import Assets from "./Assets";
import PortfolioQuickActionsBar from "./PortfolioQuickActionsBar";
import { useFeature } from "@ledgerhq/live-common/featureFlags/index";
import AccountsListView from "LLM/features/Accounts/components/AccountsListView";
import AssetsListView from "LLM/features/Assets/components/AssetsListView";
import AddAccountButton from "LLM/features/Accounts/components/AddAccountButton";
import useListsAnimation from "./useListsAnimation";

type Props = {
hideEmptyTokenAccount: boolean;
openAddModal: () => void;
};

export const TAB_OPTIONS = {
Assets: "Assets",
Accounts: "Accounts",
} as const;

const maxAssetsToDisplay = 5;

const PortfolioAssets = ({ hideEmptyTokenAccount, openAddModal }: Props) => {
Expand Down Expand Up @@ -49,9 +58,31 @@ const PortfolioAssets = ({ hideEmptyTokenAccount, openAddModal }: Props) => {
[distribution, blacklistedTokenIdsSet],
);

const goToAssets = useCallback(
const { selectedTab, handleToggle, handleLayout, assetsAnimatedStyle, accountsAnimatedStyle } =
useListsAnimation(TAB_OPTIONS.Assets);

const showAssets = selectedTab === TAB_OPTIONS.Assets;
const showAccounts = selectedTab === TAB_OPTIONS.Accounts;

const onPressButton = useCallback(
(uiEvent: GestureResponderEvent) => {
startNavigationTTITimer({ source: ScreenName.Portfolio, uiEvent });
track("button_clicked", {
button: showAssets ? "See all assets" : "See all accounts",
page: "Wallet",
});
if (!showAssets && isAccountListUIEnabled) {
navigation.navigate(NavigatorName.Accounts, {
screen: ScreenName.AccountsList,
params: {
sourceScreenName: ScreenName.Portfolio,
showHeader: true,
canAddAccount: true,
isSyncEnabled: true,
},
});
return;
}
if (isAccountListUIEnabled) {
navigation.navigate(NavigatorName.Assets, {
screen: ScreenName.AssetsList,
Expand All @@ -67,20 +98,49 @@ const PortfolioAssets = ({ hideEmptyTokenAccount, openAddModal }: Props) => {
});
}
},
[startNavigationTTITimer, isAccountListUIEnabled, navigation],
[startNavigationTTITimer, showAssets, isAccountListUIEnabled, navigation],
);

const showAddAccountButton =
isAccountListUIEnabled && showAccounts && distribution.list.length >= maxAssetsToDisplay;

return (
<>
<TrackScreen
category="Wallet"
accountsLength={distribution.list && distribution.list.length}
discreet={discreetMode}
/>
<Box mb={24} mt={18}>
<Box my={24}>
<PortfolioQuickActionsBar />
</Box>
<Assets assets={assetsToDisplay} />
{isAccountListUIEnabled ? (
<>
<Box height={40} mb={16}>
<TabSelector
labels={[t("assets.title"), t("accounts.title")]}
onToggle={handleToggle}
/>
</Box>
<Flex flexDirection="row" overflow="hidden" onLayout={handleLayout} width="200%">
<Animated.View style={[{ flex: 1 }, assetsAnimatedStyle]}>
<AssetsListView
sourceScreenName={ScreenName.Portfolio}
limitNumberOfAssets={maxAssetsToDisplay}
/>
</Animated.View>
<Animated.View style={[{ flex: 1 }, accountsAnimatedStyle]}>
<AccountsListView
sourceScreenName={ScreenName.Portfolio}
limitNumberOfAccounts={maxAssetsToDisplay}
/>
</Animated.View>
</Flex>
</>
) : (
<Assets assets={assetsToDisplay} />
)}
{showAddAccountButton ? <AddAccountButton sourceScreenName="Wallet" /> : null}
{distribution.list.length < maxAssetsToDisplay ? (
<Button
type="shade"
Expand All @@ -94,8 +154,8 @@ const PortfolioAssets = ({ hideEmptyTokenAccount, openAddModal }: Props) => {
{t("account.emptyState.addAccountCta")}
</Button>
) : (
<Button type="shade" size="large" outline mt={6} onPress={goToAssets}>
{t("portfolio.seelAllAssets")}
<Button type="shade" size="large" outline onPress={onPressButton}>
{showAssets ? t("portfolio.seeAllAssets") : t("portfolio.seeAllAccounts")}
</Button>
)}
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ function ReadOnlyPortfolio({ navigation }: NavigationProps) {
<Box background={colors.background.main} px={6} mt={6} key="Assets">
<Assets assets={assetsToDisplay} />
<Button type="shade" size="large" outline mt={6} onPress={goToAssets}>
{t("portfolio.seelAllAssets")}
{t("portfolio.seeAllAssets")}
</Button>
</Box>,
...(!hasOrderedNano
Expand Down
Loading

0 comments on commit 3c3c2c9

Please sign in to comment.