From 0df5aacc55a1998ff1c9b5c1a004a5b43b7b6845 Mon Sep 17 00:00:00 2001 From: MuckT Date: Thu, 12 Oct 2023 17:40:33 -0700 Subject: [PATCH 01/13] feat: more actions token bottom sheet --- locales/base/translation.json | 7 ++- src/tokens/MoreActionsBottomSheet.tsx | 75 +++++++++++++++++++++++++++ src/tokens/TokenDetails.tsx | 62 +++++++++++++++------- 3 files changed, 123 insertions(+), 21 deletions(-) create mode 100644 src/tokens/MoreActionsBottomSheet.tsx diff --git a/locales/base/translation.json b/locales/base/translation.json index d4a252eca64..74d4ea863d9 100644 --- a/locales/base/translation.json +++ b/locales/base/translation.json @@ -1845,10 +1845,15 @@ "priceDeltaSuffix": "Today", "actions": { "send": "Send", + "sendDetails": "Use any Celo or Ethereum address, domain, phone number, etc.", "swap": "Swap", + "swapDetails": "Swap your tokens without leaving your wallet", "add": "Add", + "addDetails": "Buy tokens using one of our trusted providers", "withdraw": "Withdraw", + "withdrawDetails": "Transfer tokens to a bank account, mobile money, gift card etc.", "more": "More" - } + }, + "moreActions": "More Actions" } } diff --git a/src/tokens/MoreActionsBottomSheet.tsx b/src/tokens/MoreActionsBottomSheet.tsx new file mode 100644 index 00000000000..e7093b30dd4 --- /dev/null +++ b/src/tokens/MoreActionsBottomSheet.tsx @@ -0,0 +1,75 @@ +import React, { RefObject } from 'react' +import { useTranslation } from 'react-i18next' +import { StyleSheet, Text, View } from 'react-native' +import BottomSheet, { BottomSheetRefType } from 'src/components/BottomSheet' +import Touchable from 'src/components/Touchable' +import Colors from 'src/styles/colors' +import { typeScale } from 'src/styles/fonts' +import { Spacing } from 'src/styles/styles' +import { useActions } from 'src/tokens/TokenDetails' +import { TokenBalance } from 'src/tokens/slice' + +export default function MoreActionsBottomSheet({ + forwardedRef, + token, +}: { + forwardedRef: RefObject + token: TokenBalance +}) { + const { t } = useTranslation() + const actions = useActions(token) + return ( + + + {actions.map((action) => { + return ( + + + + + + {action.text} + {action.details} + + + + + ) + })} + + + ) +} + +const styles = StyleSheet.create({ + actionsContainer: { + gap: Spacing.Regular16, + }, + actionContainer: { + backgroundColor: Colors.gray1, + borderRadius: 20, + }, + touchable: { + margin: Spacing.Regular16, + }, + touchableView: { + flexDirection: 'row', + gap: Spacing.Regular16, + alignItems: 'center', + }, + actionTitle: { + ...typeScale.labelMedium, + }, + actionDetails: { + ...typeScale.bodySmall, + }, +}) diff --git a/src/tokens/TokenDetails.tsx b/src/tokens/TokenDetails.tsx index 6b687d2c163..67d510b0d33 100644 --- a/src/tokens/TokenDetails.tsx +++ b/src/tokens/TokenDetails.tsx @@ -1,5 +1,5 @@ import { NativeStackScreenProps } from '@react-navigation/native-stack' -import React from 'react' +import React, { useRef } from 'react' import { useTranslation } from 'react-i18next' import { ScrollView, StyleSheet, Text, View } from 'react-native' import { SafeAreaView } from 'react-native-safe-area-context' @@ -7,6 +7,7 @@ import { useSelector } from 'react-redux' import { AssetsEvents } from 'src/analytics/Events' import { TokenProperties } from 'src/analytics/Properties' import ValoraAnalytics from 'src/analytics/ValoraAnalytics' +import { BottomSheetRefType } from 'src/components/BottomSheet' import Button, { BtnSizes } from 'src/components/Button' import PercentageIndicator from 'src/components/PercentageIndicator' import TokenDisplay from 'src/components/TokenDisplay' @@ -31,6 +32,7 @@ import { StackParamList } from 'src/navigator/types' import Colors from 'src/styles/colors' import { typeScale } from 'src/styles/fonts' import { Spacing } from 'src/styles/styles' +import MoreActionsBottomSheet from 'src/tokens/MoreActionsBottomSheet' import { TokenBalanceItem } from 'src/tokens/TokenBalanceItem' import { useCashInTokens, @@ -52,6 +54,7 @@ export default function TokenDetailsScreen({ route }: Props) { const { tokenId } = route.params const { t } = useTranslation() const token = useTokenInfo(tokenId) + const moreActionsBottomSheetRef = useRef(null) if (!token) { throw new Error(`token with id ${tokenId} not found`) @@ -86,7 +89,7 @@ export default function TokenDetailsScreen({ route }: Props) { testID="TokenDetails/Chart" /> )} - + {t('tokenDetails.yourBalance')} {token.infoUrl && ( @@ -97,6 +100,7 @@ export default function TokenDetailsScreen({ route }: Props) { /> )} + ) } @@ -136,7 +140,21 @@ function PriceInfo({ token }: { token: TokenBalance }) { ) } -function Actions({ token }: { token: TokenBalance }) { +export const onPressCicoAction = (token: TokenBalance, flow: CICOFlow) => { + const tokenSymbol = token.symbol + // this should always be true given that we only show Add / Withdraw if a + // token is CiCoCurrency, but adding it here to ensure type safety + if (isCicoToken(tokenSymbol)) { + navigate(Screens.FiatExchangeAmount, { + currency: tokenSymbol, + tokenId: token.tokenId, + flow, + network: Network.Celo, + }) + } +} + +export const useActions = (token: TokenBalance) => { const { t } = useTranslation() const sendableTokens = useSendableTokens() const swappableTokens = useSwappableTokens() @@ -145,24 +163,11 @@ function Actions({ token }: { token: TokenBalance }) { const isSwapEnabled = useSelector(isAppSwapsEnabledSelector) const showWithdraw = !!cashOutTokens.find((tokenInfo) => tokenInfo.tokenId === token.tokenId) - const onPressCicoAction = (flow: CICOFlow) => { - const tokenSymbol = token.symbol - // this should always be true given that we only show Add / Withdraw if a - // token is CiCoCurrency, but adding it here to ensure type safety - if (isCicoToken(tokenSymbol)) { - navigate(Screens.FiatExchangeAmount, { - currency: tokenSymbol, - tokenId: token.tokenId, - flow, - network: Network.Celo, - }) - } - } - - const actions = [ + return [ { name: TokenDetailsActionName.Send, text: t('tokenDetails.actions.send'), + details: t('tokenDetails.actions.sendDetails'), iconComponent: QuickActionsSend, onPress: () => { navigate(Screens.Send, { defaultTokenIdOverride: token.tokenId }) @@ -172,6 +177,7 @@ function Actions({ token }: { token: TokenBalance }) { { name: TokenDetailsActionName.Swap, text: t('tokenDetails.actions.swap'), + details: t('tokenDetails.actions.swapDetails'), iconComponent: QuickActionsSwap, onPress: () => { navigate(Screens.SwapScreenWithBack, { fromTokenId: token.tokenId }) @@ -184,22 +190,37 @@ function Actions({ token }: { token: TokenBalance }) { { name: TokenDetailsActionName.Add, text: t('tokenDetails.actions.add'), + details: t('tokenDetails.actions.addDetails'), iconComponent: QuickActionsAdd, onPress: () => { - onPressCicoAction(CICOFlow.CashIn) + onPressCicoAction(token, CICOFlow.CashIn) }, visible: !!cashInTokens.find((tokenInfo) => tokenInfo.tokenId === token.tokenId), }, { name: TokenDetailsActionName.Withdraw, text: t('tokenDetails.actions.withdraw'), + details: t('tokenDetails.actions.withdrawDetails'), iconComponent: QuickActionsWithdraw, onPress: () => { - onPressCicoAction(CICOFlow.CashOut) + onPressCicoAction(token, CICOFlow.CashOut) }, visible: showWithdraw, }, ].filter((action) => action.visible) +} + +function Actions({ + token, + moreActionsBottomSheetRef, +}: { + token: TokenBalance + moreActionsBottomSheetRef: React.RefObject +}) { + const { t } = useTranslation() + const actions = useActions(token) + const cashOutTokens = useCashOutTokens() + const showWithdraw = !!cashOutTokens.find((tokenInfo) => tokenInfo.tokenId === token.tokenId) const moreAction = { name: TokenDetailsActionName.More, @@ -207,6 +228,7 @@ function Actions({ token }: { token: TokenBalance }) { iconComponent: QuickActionsMore, onPress: () => { // TODO(ACT-917): open bottom sheet + moreActionsBottomSheetRef.current?.snapToIndex(0) }, } From d60da9fc6b681d2bece6008451c75e5a0a6d2551 Mon Sep 17 00:00:00 2001 From: MuckT Date: Fri, 13 Oct 2023 11:40:43 -0700 Subject: [PATCH 02/13] fix: make android ripple extend to edge of touchable --- src/tokens/MoreActionsBottomSheet.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tokens/MoreActionsBottomSheet.tsx b/src/tokens/MoreActionsBottomSheet.tsx index e7093b30dd4..c00968f8517 100644 --- a/src/tokens/MoreActionsBottomSheet.tsx +++ b/src/tokens/MoreActionsBottomSheet.tsx @@ -59,7 +59,7 @@ const styles = StyleSheet.create({ borderRadius: 20, }, touchable: { - margin: Spacing.Regular16, + padding: Spacing.Regular16, }, touchableView: { flexDirection: 'row', From 38281f56c94ca9e849b5cb25f516ee26f761e72d Mon Sep 17 00:00:00 2001 From: MuckT Date: Fri, 13 Oct 2023 18:12:54 -0700 Subject: [PATCH 03/13] refactor: use bottom sheet without ref --- locales/base/translation.json | 12 ++-- src/analytics/Events.tsx | 1 + src/analytics/Properties.tsx | 3 + src/analytics/docs.ts | 1 + src/navigator/Navigator.tsx | 5 ++ src/navigator/Screens.tsx | 1 + src/navigator/types.tsx | 3 + src/tokens/MoreActionsBottomSheet.tsx | 75 --------------------- src/tokens/TokenDetails.tsx | 33 ++++------ src/tokens/TokenDetailsMoreActions.tsx | 90 ++++++++++++++++++++++++++ src/tokens/types.ts | 11 ++++ 11 files changed, 134 insertions(+), 101 deletions(-) delete mode 100644 src/tokens/MoreActionsBottomSheet.tsx create mode 100644 src/tokens/TokenDetailsMoreActions.tsx diff --git a/locales/base/translation.json b/locales/base/translation.json index 74d4ea863d9..d41609eb03d 100644 --- a/locales/base/translation.json +++ b/locales/base/translation.json @@ -1845,15 +1845,17 @@ "priceDeltaSuffix": "Today", "actions": { "send": "Send", - "sendDetails": "Use any Celo or Ethereum address, domain, phone number, etc.", "swap": "Swap", - "swapDetails": "Swap your tokens without leaving your wallet", "add": "Add", - "addDetails": "Buy tokens using one of our trusted providers", "withdraw": "Withdraw", - "withdrawDetails": "Transfer tokens to a bank account, mobile money, gift card etc.", "more": "More" }, - "moreActions": "More Actions" + "moreActions": "More Actions", + "actionDescriptions": { + "send": "Use any Celo address, domain, phone number, etc.", + "swap": "Swap your tokens without leaving your wallet", + "add": "Buy tokens using one of our trusted providers", + "withdraw": "Transfer tokens to a bank account, mobile money, gift card etc." + } } } diff --git a/src/analytics/Events.tsx b/src/analytics/Events.tsx index 4a7b9e0627c..5143c4a5df3 100644 --- a/src/analytics/Events.tsx +++ b/src/analytics/Events.tsx @@ -617,6 +617,7 @@ export enum AssetsEvents { tap_claim_rewards = 'tap_claim_rewards', tap_token_details_action = 'tap_token_details_action', tap_token_details_learn_more = 'tap_token_details_learn_more', + tap_token_details_bottom_sheet_action = 'tap_token_details_bottom_sheet_action', } export enum NftEvents { diff --git a/src/analytics/Properties.tsx b/src/analytics/Properties.tsx index fc0591e29bd..270a38f8b1e 100644 --- a/src/analytics/Properties.tsx +++ b/src/analytics/Properties.tsx @@ -1369,6 +1369,9 @@ interface AssetsEventsProperties { action: TokenDetailsActionName } & TokenProperties [AssetsEvents.tap_token_details_learn_more]: TokenProperties + [AssetsEvents.tap_token_details_bottom_sheet_action]: { + action: TokenDetailsActionName + } & TokenProperties } interface NftsEventsProperties { diff --git a/src/analytics/docs.ts b/src/analytics/docs.ts index 7ba46fac85b..805ec921c16 100644 --- a/src/analytics/docs.ts +++ b/src/analytics/docs.ts @@ -529,6 +529,7 @@ export const eventDocs: Record = { [AssetsEvents.tap_asset]: `When a user taps on an asset`, [AssetsEvents.tap_claim_rewards]: `When a user taps on the "Claim Rewards" button`, [AssetsEvents.tap_token_details_action]: `When a user taps one of the actions on the token details screen`, + [AssetsEvents.tap_token_details_bottom_sheet_action]: `When a user taps one of the actions on the more actions bottom sheet`, [AssetsEvents.tap_token_details_learn_more]: `When a user taps the learn more link on the token details screen`, [NftEvents.nft_error_screen_open]: `When the high level error screen is mounted`, [NftEvents.nft_media_load]: `When attempting to load NFT media`, diff --git a/src/navigator/Navigator.tsx b/src/navigator/Navigator.tsx index 41974e5f6e9..a77eafbf019 100644 --- a/src/navigator/Navigator.tsx +++ b/src/navigator/Navigator.tsx @@ -113,6 +113,7 @@ import SwapScreen from 'src/swap/SwapScreen' import AssetsScreen from 'src/tokens/Assets' import TokenBalancesScreen from 'src/tokens/TokenBalances' import TokenDetailsScreen from 'src/tokens/TokenDetails' +import TokenDetailsMoreActions from 'src/tokens/TokenDetailsMoreActions' import TransactionDetailsScreen from 'src/transactions/feed/TransactionDetailsScreen' import Logger from 'src/utils/Logger' import { ExtractProps } from 'src/utils/typescript' @@ -676,6 +677,10 @@ function nativeBottomSheets(BottomSheet: typeof RootStack) { name={Screens.DappShortcutTransactionRequest} component={DappShortcutTransactionRequest} /> + ) } diff --git a/src/navigator/Screens.tsx b/src/navigator/Screens.tsx index 79aff12d19e..b8b2a01d9b2 100644 --- a/src/navigator/Screens.tsx +++ b/src/navigator/Screens.tsx @@ -86,6 +86,7 @@ export enum Screens { SwapReviewScreen = 'SwapReviewScreen', TokenBalances = 'TokenBalances', TokenDetails = 'TokenDetails', + TokenDetailsMoreActions = 'TokenDetailsMoreActions', TransactionDetailsScreen = 'TransactionDetailsScreen', UpgradeScreen = 'UpgradeScreen', ValidateRecipientAccount = 'ValidateRecipientAccount', diff --git a/src/navigator/types.tsx b/src/navigator/types.tsx index 64f1b9d2f71..96836f84f6c 100644 --- a/src/navigator/types.tsx +++ b/src/navigator/types.tsx @@ -267,6 +267,9 @@ export type StackParamList = { [Screens.SwapReviewScreen]: undefined [Screens.SwapScreenWithBack]: { fromTokenId: string } | undefined [Screens.TokenDetails]: { tokenId: string } + [Screens.TokenDetailsMoreActions]: { + tokenId: string + } [Screens.TransactionDetailsScreen]: { transaction: TokenTransaction } diff --git a/src/tokens/MoreActionsBottomSheet.tsx b/src/tokens/MoreActionsBottomSheet.tsx deleted file mode 100644 index c00968f8517..00000000000 --- a/src/tokens/MoreActionsBottomSheet.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import React, { RefObject } from 'react' -import { useTranslation } from 'react-i18next' -import { StyleSheet, Text, View } from 'react-native' -import BottomSheet, { BottomSheetRefType } from 'src/components/BottomSheet' -import Touchable from 'src/components/Touchable' -import Colors from 'src/styles/colors' -import { typeScale } from 'src/styles/fonts' -import { Spacing } from 'src/styles/styles' -import { useActions } from 'src/tokens/TokenDetails' -import { TokenBalance } from 'src/tokens/slice' - -export default function MoreActionsBottomSheet({ - forwardedRef, - token, -}: { - forwardedRef: RefObject - token: TokenBalance -}) { - const { t } = useTranslation() - const actions = useActions(token) - return ( - - - {actions.map((action) => { - return ( - - - - - - {action.text} - {action.details} - - - - - ) - })} - - - ) -} - -const styles = StyleSheet.create({ - actionsContainer: { - gap: Spacing.Regular16, - }, - actionContainer: { - backgroundColor: Colors.gray1, - borderRadius: 20, - }, - touchable: { - padding: Spacing.Regular16, - }, - touchableView: { - flexDirection: 'row', - gap: Spacing.Regular16, - alignItems: 'center', - }, - actionTitle: { - ...typeScale.labelMedium, - }, - actionDetails: { - ...typeScale.bodySmall, - }, -}) diff --git a/src/tokens/TokenDetails.tsx b/src/tokens/TokenDetails.tsx index 67d510b0d33..58ea0c045f8 100644 --- a/src/tokens/TokenDetails.tsx +++ b/src/tokens/TokenDetails.tsx @@ -32,7 +32,6 @@ import { StackParamList } from 'src/navigator/types' import Colors from 'src/styles/colors' import { typeScale } from 'src/styles/fonts' import { Spacing } from 'src/styles/styles' -import MoreActionsBottomSheet from 'src/tokens/MoreActionsBottomSheet' import { TokenBalanceItem } from 'src/tokens/TokenBalanceItem' import { useCashInTokens, @@ -100,7 +99,6 @@ export default function TokenDetailsScreen({ route }: Props) { /> )} - ) } @@ -166,8 +164,8 @@ export const useActions = (token: TokenBalance) => { return [ { name: TokenDetailsActionName.Send, - text: t('tokenDetails.actions.send'), - details: t('tokenDetails.actions.sendDetails'), + title: t('tokenDetails.actions.send'), + details: t('tokenDetails.actionDescriptions.send'), iconComponent: QuickActionsSend, onPress: () => { navigate(Screens.Send, { defaultTokenIdOverride: token.tokenId }) @@ -176,8 +174,8 @@ export const useActions = (token: TokenBalance) => { }, { name: TokenDetailsActionName.Swap, - text: t('tokenDetails.actions.swap'), - details: t('tokenDetails.actions.swapDetails'), + title: t('tokenDetails.actions.swap'), + details: t('tokenDetails.actionDescriptions.swap'), iconComponent: QuickActionsSwap, onPress: () => { navigate(Screens.SwapScreenWithBack, { fromTokenId: token.tokenId }) @@ -189,8 +187,8 @@ export const useActions = (token: TokenBalance) => { }, { name: TokenDetailsActionName.Add, - text: t('tokenDetails.actions.add'), - details: t('tokenDetails.actions.addDetails'), + title: t('tokenDetails.actions.add'), + details: t('tokenDetails.actionDescriptions.add'), iconComponent: QuickActionsAdd, onPress: () => { onPressCicoAction(token, CICOFlow.CashIn) @@ -199,8 +197,8 @@ export const useActions = (token: TokenBalance) => { }, { name: TokenDetailsActionName.Withdraw, - text: t('tokenDetails.actions.withdraw'), - details: t('tokenDetails.actions.withdrawDetails'), + title: t('tokenDetails.actions.withdraw'), + details: t('tokenDetails.actionDescriptions.withdraw'), iconComponent: QuickActionsWithdraw, onPress: () => { onPressCicoAction(token, CICOFlow.CashOut) @@ -210,13 +208,7 @@ export const useActions = (token: TokenBalance) => { ].filter((action) => action.visible) } -function Actions({ - token, - moreActionsBottomSheetRef, -}: { - token: TokenBalance - moreActionsBottomSheetRef: React.RefObject -}) { +function Actions({ token }: { token: TokenBalance }) { const { t } = useTranslation() const actions = useActions(token) const cashOutTokens = useCashOutTokens() @@ -224,11 +216,10 @@ function Actions({ const moreAction = { name: TokenDetailsActionName.More, - text: t('tokenDetails.actions.more'), + title: t('tokenDetails.actions.more'), iconComponent: QuickActionsMore, onPress: () => { - // TODO(ACT-917): open bottom sheet - moreActionsBottomSheetRef.current?.snapToIndex(0) + navigate(Screens.TokenDetailsMoreActions, { tokenId: token.tokenId }) }, } @@ -252,7 +243,7 @@ function Actions({ {actionButtons.map((action) => (