Skip to content

Commit

Permalink
feat: migrate all entry screen, ctas to new add account (uncompleted …
Browse files Browse the repository at this point in the history
…v2) flow
  • Loading branch information
themooneer committed Dec 13, 2024
1 parent 1dd3a6d commit d582aaf
Show file tree
Hide file tree
Showing 29 changed files with 1,480 additions and 441 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export default function useAssetActions({ currency, accounts }: useAssetActionsP
const { data: currenciesAll } = useFetchCurrencyAll();

const ptxServiceCtaScreens = useFeature("ptxServiceCtaScreens");
const llmNetworkBasedAddAccountFlow = useFeature("llmNetworkBasedAddAccountFlow");

const { t } = useTranslation();
const stakeLabel = getStakeLabelLocaleBased();
Expand Down Expand Up @@ -226,11 +227,16 @@ export default function useAssetActions({ currency, accounts }: useAssetActionsP
label: t("addAccountsModal.ctaAdd"),
Icon: iconAddAccount,
navigationParams: [
NavigatorName.AddAccounts,
llmNetworkBasedAddAccountFlow?.enabled
? NavigatorName.AssetSelection
: NavigatorName.AddAccounts,
{
screen: ScreenName.AddAccountsSelectCrypto,
params: {
filterCurrencyIds: currency ? [currency.id] : undefined,
...(llmNetworkBasedAddAccountFlow?.enabled && {
context: "addAccounts",
}),
},
},
] as const,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ import type { Transaction as CasperTransaction } from "@ledgerhq/live-common/fam
import type { Transaction as TonTransaction } from "@ledgerhq/live-common/families/ton/types";
import BigNumber from "bignumber.js";
import { Account, Operation } from "@ledgerhq/types-live";
import { ScreenName } from "~/const";
import { NavigatorName, ScreenName } from "~/const";
import { NavigatorScreenParams } from "@react-navigation/core";
import { AssetSelectionNavigatorParamsList } from "~/newArch/features/AssetSelection/types";

type Target = "from" | "to";

Expand Down Expand Up @@ -329,4 +331,7 @@ export type SwapNavigatorParamList = {
| ScreenName.SendSelectDevice
| ScreenName.SwapForm;
};
[NavigatorName.AssetSelection]?: Partial<
NavigatorScreenParams<AssetSelectionNavigatorParamsList>
>;
};
8 changes: 8 additions & 0 deletions apps/ledger-live-mobile/src/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -7122,5 +7122,13 @@
}
}
}
},
"assetSelection": {
"selectCrypto": {
"title": "Select Asset"
},
"selectNetwork": {
"title": "Select the blockchain network the asset belongs to"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export default function Navigator() {
options={{
headerTitle: "",
}}
initialParams={route.params}
/>

{/* Scan accounts from device */}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,15 @@ const useSelectAddAccountMethodViewModel = ({
const navigationParams = useMemo(() => {
return hasCurrency
? currency.type === "TokenCurrency"
? { token: currency }
: { currency }
: {};
}, [hasCurrency, currency]);
? {
token: currency,
...(llmNetworkBasedAddAccountFlow?.enabled && { context: "addAccounts" }),
}
: { currency, ...(llmNetworkBasedAddAccountFlow?.enabled && { context: "addAccounts" }) }
: llmNetworkBasedAddAccountFlow?.enabled
? { context: "addAccounts" }
: {};
}, [hasCurrency, currency, llmNetworkBasedAddAccountFlow?.enabled]);

const trackButtonClick = useCallback((button: string) => {
track("button_clicked", {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@ import { CryptoCurrency, TokenCurrency } from "@ledgerhq/types-cryptoassets";
import { ScreenName } from "~/const";
import { Device } from "@ledgerhq/types-devices";

type CommonParams = {
context?: "addAccounts" | "receiveFunds";
onSuccess?: () => void;
};
export type NetworkBasedAddAccountNavigator = {
[ScreenName.SelectAccounts]: {
[ScreenName.SelectAccounts]: CommonParams & {
currency: CryptoCurrency | TokenCurrency;
createTokenAccount?: boolean;
};
[ScreenName.ScanDeviceAccounts]: {
[ScreenName.ScanDeviceAccounts]: CommonParams & {
currency: CryptoCurrency | TokenCurrency;
device: Device;
onSuccess?: (_?: unknown) => void;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Platform } from "react-native";
import { createStackNavigator } from "@react-navigation/stack";
import { useTheme } from "styled-components/native";
import { useRoute } from "@react-navigation/native";
import { ScreenName } from "~/const";
import { NavigatorName, ScreenName } from "~/const";
import { getStackNavigatorConfig } from "~/navigation/navigatorConfig";
import { track } from "~/analytics";
import { Flex } from "@ledgerhq/native-ui";
Expand All @@ -16,13 +16,23 @@ import SelectNetwork from "LLM/features/AssetSelection/screens/SelectNetwork";
import { NavigationHeaderCloseButtonAdvanced } from "~/components/NavigationHeaderCloseButton";
import { NavigationHeaderBackButton } from "~/components/NavigationHeaderBackButton";
import { AssetSelectionNavigatorParamsList } from "./types";
import { BaseComposite, StackNavigatorProps } from "~/components/RootNavigator/types/helpers";
import TokenCurrencyDisclaimer from "./screens/TokenCurrencyDisclamer";
import { useTranslation } from "react-i18next";

type NavigationProps = BaseComposite<
StackNavigatorProps<AssetSelectionNavigatorParamsList, NavigatorName.AssetSelection>
>;

export default function Navigator() {
const { colors } = useTheme();
const route = useRoute();
const route = useRoute<NavigationProps["route"]>();

const { t } = useTranslation();
const hasClosedNetworkBanner = useSelector(hasClosedNetworkBannerSelector);

const { token } = route.params || {};

const onClose = useCallback(() => {
track("button_clicked", {
button: "Close",
Expand All @@ -44,15 +54,19 @@ export default function Navigator() {
...stackNavigationConfig,
gestureEnabled: Platform.OS === "ios",
}}
initialRouteName={
token ? ScreenName.AddAccountsTokenCurrencyDisclaimer : ScreenName.AddAccountsSelectCrypto
}
>
<Stack.Screen
name={ScreenName.AddAccountsSelectCrypto}
component={SelectCrypto}
options={{
headerLeft: () => <NavigationHeaderBackButton />,
headerTitle: "",
title: "",
headerRight: () => <NavigationHeaderCloseButtonAdvanced onClose={onClose} />,
}}
initialParams={route.params}
/>

<Stack.Screen
Expand All @@ -70,6 +84,21 @@ export default function Navigator() {
</Flex>
),
}}
initialParams={route.params}
/>
<Stack.Screen
name={ScreenName.AddAccountsTokenCurrencyDisclaimer}
component={TokenCurrencyDisclaimer}
initialParams={
token
? {
token,
}
: undefined
}
options={{
title: t("addAccounts.tokens.title"),
}}
/>
</Stack.Navigator>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import React from "react";
import { render, screen } from "@tests/test-renderer";
import AssetSelectionNavigator from "../Navigator";
import { useRoute, useNavigation } from "@react-navigation/native";
import { useGroupedCurrenciesByProvider } from "@ledgerhq/live-common/deposit/index";
import {
mockGroupedCurrenciesBySingleProviderData,
mockGroupedCurrenciesWithMultipleProviderData,
} from "./mockData";

const MockUseRoute = useRoute as jest.Mock;
const MockUseGroupedCurrenciesByProvider = useGroupedCurrenciesByProvider as jest.Mock;
const mockNavigate = jest.fn();

(useNavigation as jest.Mock).mockReturnValue({
navigate: mockNavigate,
});

jest.mock("../components/NetworkBanner", () => {
return {
__esModule: true,
default: () => <div data-testid="network-banner">Mock Network Banner</div>,
};
});


jest.mock("@ledgerhq/live-common/deposit/index", () => ({
useGroupedCurrenciesByProvider: jest.fn(),
}));

jest.mock("@react-navigation/native", () => ({
...jest.requireActual("@react-navigation/native"),
useRoute: jest.fn(),
useNavigation: jest.fn(),
}));

jest.useFakeTimers();

describe("Asset Selection test suite", () => {
it("should render crypto selection screen when no currency is defined in the navigation route showing a loader", () => {
MockUseRoute.mockReturnValue({
params: {
context: "addAccounts",
},
});

MockUseGroupedCurrenciesByProvider.mockReturnValue({
result: { currenciesByProvider: [], sortedCryptoCurrencies: [] },
loadingStatus: "pending",
});

render(<AssetSelectionNavigator />);

const screenTitle = screen.getByText(/select asset/i);
const selectCryptoViewArea = screen.getByTestId("select-crypto-view-area");
const loader = screen.getByTestId("loader");
expect(screenTitle).toBeVisible();
expect(screenTitle).toHaveProp("testID", "select-crypto-header-step1-title");
expect(selectCryptoViewArea).toBeVisible();
expect(loader).toBeVisible();
});

it("should render crypto selection screen with empty list when useGroupedCurrenciesByProvider finish loading with empty result", () => {
MockUseRoute.mockReturnValue({
params: {
context: "addAccounts",
},
});

MockUseGroupedCurrenciesByProvider.mockReturnValue({
result: { currenciesByProvider: [], sortedCryptoCurrencies: [] },
loadingStatus: "success",
});

render(<AssetSelectionNavigator />);

const emptyList = screen.getByText(/no crypto assets found/i);
expect(emptyList).toBeVisible();
});
it("should display a list of cryptocurrencies when useGroupedCurrenciesByProvider successfully loads data", () => {
MockUseRoute.mockReturnValue({
params: {
context: "addAccounts",
},
});

MockUseGroupedCurrenciesByProvider.mockReturnValue({
result: mockGroupedCurrenciesBySingleProviderData,
loadingStatus: "success",
});

render(<AssetSelectionNavigator />);

const selectCryptoViewArea = screen.getByTestId("select-crypto-view-area");
const cryptoCurrencyRow = screen.getByText(/bitcoin/i);
expect(selectCryptoViewArea).toBeVisible();
expect(cryptoCurrencyRow).toBeVisible();
});
it("should navigate to network selection when currency has more than one network provider", () => {
MockUseRoute.mockReturnValue({
params: {
context: "addAccounts",
currency: "ethereum",
},
});

MockUseGroupedCurrenciesByProvider.mockReturnValue({
result: mockGroupedCurrenciesWithMultipleProviderData,
loadingStatus: "success",
});

render(<AssetSelectionNavigator />);

const selectNetwork = screen.getByTestId("select-network-view-area");
const title = screen.getByText(/Select the blockchain network the asset belongs to/i);
const arbitrum = screen.getByText(/arbitrum/i);
const blast = screen.getByText(/blast/i);
const boba = screen.getByText(/boba/i);
expect(selectNetwork).toBeVisible();
expect(title).toBeVisible();
[arbitrum, blast, boba].forEach(network => {
expect(network).toBeVisible();
});
});
});
Loading

0 comments on commit d582aaf

Please sign in to comment.