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 12, 2024
1 parent e764d7d commit 9ed5e36
Show file tree
Hide file tree
Showing 26 changed files with 943 additions and 415 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>
>;
};
10 changes: 9 additions & 1 deletion apps/ledger-live-mobile/src/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -7118,5 +7118,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 @@ -43,6 +43,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
@@ -1,28 +1,23 @@
import React, { useCallback, useEffect, useMemo } from "react";
import { Trans, useTranslation } from "react-i18next";
import React, { useCallback } from "react";
import { Trans } from "react-i18next";
import { FlatList } from "react-native";
import debounce from "lodash/debounce";
import { useSelector } from "react-redux";

import type {
CryptoCurrency,
CryptoOrTokenCurrency,
TokenCurrency,
} from "@ledgerhq/types-cryptoassets";
import { findCryptoCurrencyByKeyword } from "@ledgerhq/live-common/currencies/index";
import { getEnv } from "@ledgerhq/live-env";
import { useGroupedCurrenciesByProvider } from "@ledgerhq/live-common/deposit/index";

import SafeAreaView from "~/components/SafeAreaView";
import { Flex, InfiniteLoader, Text } from "@ledgerhq/native-ui";
import { NavigatorName, ScreenName } from "~/const";
import { track, TrackScreen } from "~/analytics";
import { ScreenName } from "~/const";
import { TrackScreen } from "~/analytics";
import FilteredSearchBar from "~/components/FilteredSearchBar";
import BigCurrencyRow from "~/components/BigCurrencyRow";
import { flattenAccountsSelector } from "~/reducers/accounts";
import { StackNavigatorProps } from "~/components/RootNavigator/types/helpers";
import { findAccountByCurrency } from "~/logic/deposit";
import { AssetSelectionNavigatorParamsList } from "../../types";
import useSelectCryptoViewModel from "./useSelectCryptoViewModel";

const SEARCH_KEYS = getEnv("CRYPTO_ASSET_SEARCH_KEYS");

Expand All @@ -37,82 +32,11 @@ const renderEmptyList = () => (
);

export default function SelectCrypto({
navigation,
route,
}: StackNavigatorProps<AssetSelectionNavigatorParamsList, ScreenName.AddAccountsSelectCrypto>) {
const paramsCurrency = route?.params?.currency;
const filterCurrencyIds = route?.params?.filterCurrencyIds;
const filterCurrencyIdsSet = useMemo(
() => (filterCurrencyIds ? new Set(filterCurrencyIds) : null),
[filterCurrencyIds],
);

const { t } = useTranslation();
const accounts = useSelector(flattenAccountsSelector);

const { currenciesByProvider, sortedCryptoCurrencies } = useGroupedCurrenciesByProvider();

const onPressItem = useCallback(
(curr: CryptoCurrency | TokenCurrency) => {
track("asset_clicked", {
asset: curr.name,
page: "Choose a crypto to secure",
});

const provider = currenciesByProvider.find(elem =>
elem.currenciesByNetwork.some(
currencyByNetwork => (currencyByNetwork as CryptoCurrency | TokenCurrency).id === curr.id,
),
);

// If the selected currency exists on multiple networks we redirect to the SelectNetwork screen
if (provider && provider?.currenciesByNetwork.length > 1) {
navigation.navigate(ScreenName.SelectNetwork, {
provider,
filterCurrencyIds,
});
return;
}

const isToken = curr.type === "TokenCurrency";
const currency = isToken ? curr.parentCurrency : curr;
const currencyAccounts = findAccountByCurrency(accounts, currency);

if (currencyAccounts.length > 0) {
// If we found one or more accounts of the currency then we select account
navigation.navigate(NavigatorName.AddAccounts, {
screen: ScreenName.SelectAccounts,
params: {
currency,
},
});
} else {
// If we didn't find any account of the parent currency then we add one
navigation.navigate(NavigatorName.DeviceSelection, {
screen: ScreenName.SelectDevice,
params: {
currency,
createTokenAccount: isToken || undefined,
},
});
}
},
[currenciesByProvider, accounts, navigation, filterCurrencyIds],
);

useEffect(() => {
if (paramsCurrency) {
const selectedCurrency = findCryptoCurrencyByKeyword(paramsCurrency.toUpperCase());

if (selectedCurrency) {
onPressItem(selectedCurrency);
}
}
}, [onPressItem, paramsCurrency]);

const debounceTrackOnSearchChange = debounce((newQuery: string) => {
track("asset_searched", { page: "Choose a crypto to secure", asset: newQuery });
}, 1500);
const { filterCurrencyIds, currency: paramsCurrency, context } = route?.params || {};
const { titleText, list, onPressItem, debounceTrackOnSearchChange, providersLoadingStatus } =
useSelectCryptoViewModel({ context, filterCurrencyIds, paramsCurrency });

const renderList = useCallback(
(items: CryptoOrTokenCurrency[]) => (
Expand All @@ -129,35 +53,41 @@ export default function SelectCrypto({
[onPressItem],
);

const list = useMemo(
() =>
filterCurrencyIdsSet
? sortedCryptoCurrencies.filter(crypto => filterCurrencyIdsSet.has(crypto.id))
: sortedCryptoCurrencies,
[filterCurrencyIdsSet, sortedCryptoCurrencies],
);
const renderListView = useCallback(() => {
switch (providersLoadingStatus) {
case "success":
return list.length > 0 ? (
<Flex flex={1} ml={6} mr={6} mt={3}>
<FilteredSearchBar
keys={SEARCH_KEYS}
list={list}
renderList={renderList}
renderEmptySearch={renderEmptyList}
onSearchChange={debounceTrackOnSearchChange}
/>
</Flex>
) : (
renderEmptyList()
);
case "error":
// TODO: in an improvement feature, when the network fetch status is on error, implement a clean error message with a retry CTA
return renderEmptyList();
default:
return (
<Flex flex={1} mt={6}>
<InfiniteLoader />
</Flex>
);
}
}, [providersLoadingStatus, list, renderList, debounceTrackOnSearchChange]);

return (
<SafeAreaView edges={["left", "right"]} isFlex>
<TrackScreen category="Deposit" name="Choose a crypto to secure" />
<Text variant="h4" fontWeight="semiBold" mx={6} testID="receive-header-step1-title">
{t("transfer.receive.selectCrypto.title")}
{titleText}
</Text>
{list.length > 0 ? (
<Flex flex={1} ml={6} mr={6} mt={3}>
<FilteredSearchBar
keys={SEARCH_KEYS}
list={list}
renderList={renderList}
renderEmptySearch={renderEmptyList}
onSearchChange={debounceTrackOnSearchChange}
/>
</Flex>
) : (
<Flex flex={1} mt={6}>
<InfiniteLoader />
</Flex>
)}
{renderListView()}
</SafeAreaView>
);
}
Loading

0 comments on commit 9ed5e36

Please sign in to comment.