diff --git a/BlueComponents.js b/BlueComponents.js index bf6740650..465f3bf58 100644 --- a/BlueComponents.js +++ b/BlueComponents.js @@ -15,7 +15,6 @@ import { TouchableOpacity, View, I18nManager, - ImageBackground, } from 'react-native'; import Clipboard from '@react-native-clipboard/clipboard'; import NetworkTransactionFees, { NetworkTransactionFee, NetworkTransactionFeeType } from './models/networkTransactionFees'; @@ -191,18 +190,6 @@ export const BlueButtonLink = forwardRef((props, ref) => { ); }); -export const BluePrivateBalance = () => { - return ( - - - - - ); -}; - export const BlueCard = props => { return ; }; diff --git a/components/BlurredBalanceView.tsx b/components/BlurredBalanceView.tsx new file mode 100644 index 000000000..41450e395 --- /dev/null +++ b/components/BlurredBalanceView.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import { ImageBackground, StyleSheet, View } from 'react-native'; +import { Icon } from 'react-native-elements'; + +export const BlurredBalanceView = () => { + return ( + + {/* @ts-ignore: We just want the blur effect. No source prop needed */} + + + + ); +}; + +const styles = StyleSheet.create({ + container: { flexDirection: 'row', alignItems: 'center', borderRadius: 9 }, + background: { backgroundColor: '#FFFFFF', opacity: 0.5, height: 30, width: 110, marginRight: 8, borderRadius: 9 }, +}); diff --git a/components/TooltipMenu.ios.js b/components/TooltipMenu.ios.js index 01de35633..439ef6fda 100644 --- a/components/TooltipMenu.ios.js +++ b/components/TooltipMenu.ios.js @@ -41,6 +41,7 @@ const ToolTipMenu = (props, ref) => { const isMenuPrimaryAction = props.isMenuPrimaryAction ? props.isMenuPrimaryAction : false; const renderPreview = props.renderPreview ?? undefined; const disabled = props.disabled ?? false; + const onPress = props.onPress ?? undefined; const buttonStyle = props.buttonStyle; return isButton ? ( @@ -55,14 +56,11 @@ const ToolTipMenu = (props, ref) => { menuTitle, menuItems, }} + style={buttonStyle} + onPress={onPress} + accessibilityRole="button" > - {props.onPress ? ( - - {props.children} - - ) : ( - props.children - )} + {props.children} ) : props.onPress ? ( diff --git a/components/TransactionsNavigationHeader.tsx b/components/TransactionsNavigationHeader.tsx index 5fd61c944..32a693b4a 100644 --- a/components/TransactionsNavigationHeader.tsx +++ b/components/TransactionsNavigationHeader.tsx @@ -1,17 +1,16 @@ import React, { useState, useEffect, useRef, useContext, useCallback, useMemo } from 'react'; -import { Image, Text, TouchableOpacity, View, I18nManager, StyleSheet } from 'react-native'; +import { Image, Text, TouchableOpacity, View, I18nManager, StyleSheet, LayoutAnimation } from 'react-native'; import Clipboard from '@react-native-clipboard/clipboard'; import LinearGradient from 'react-native-linear-gradient'; import { HDSegwitBech32Wallet, LightningCustodianWallet, LightningLdkWallet, MultisigHDWallet } from '../class'; import { BitcoinUnit } from '../models/bitcoinUnits'; import WalletGradient from '../class/wallet-gradient'; -import Biometric from '../class/biometrics'; -import loc, { formatBalance } from '../loc'; +import loc, { formatBalance, formatBalanceWithoutSuffix } from '../loc'; import { BlueStorageContext } from '../blue_modules/storage-context'; import ToolTipMenu from './TooltipMenu'; -import { BluePrivateBalance } from '../BlueComponents'; import { FiatUnit } from '../models/fiatUnit'; import { TWallet } from '../class/wallets/types'; +import { BlurredBalanceView } from './BlurredBalanceView'; interface TransactionsNavigationHeaderProps { wallet: TWallet; @@ -20,7 +19,8 @@ interface TransactionsNavigationHeaderProps { navigate: (route: string, params?: any) => void; goBack: () => void; }; - onManageFundsPressed?: (id: string) => void; // Add a type definition for this prop + onManageFundsPressed?: (id: string) => void; + onWalletBalanceVisibilityChange?: (isShouldBeVisible: boolean) => void; actionKeys: { CopyToClipboard: 'copyToClipboard'; WalletBalanceVisibility: 'walletBalanceVisibility'; @@ -38,11 +38,13 @@ const TransactionsNavigationHeader: React.FC navigation, // @ts-ignore: Ugh onManageFundsPressed, + // @ts-ignore: Ugh + onWalletBalanceVisibilityChange, }) => { const [wallet, setWallet] = useState(initialWallet); const [allowOnchainAddress, setAllowOnchainAddress] = useState(false); - const { preferredFiatCurrency, saveToDisk } = useContext(BlueStorageContext); + const { preferredFiatCurrency } = useContext(BlueStorageContext); const menuRef = useRef(null); const verifyIfWalletAllowsOnchainAddress = useCallback(() => { @@ -72,23 +74,8 @@ const TransactionsNavigationHeader: React.FC } }; - const updateWalletVisibility = (w: TWallet, newHideBalance: boolean) => { - w.hideBalance = newHideBalance; - return w; - }; - - const handleBalanceVisibility = async () => { - const isBiometricsEnabled = await Biometric.isBiometricUseCapableAndEnabled(); - - if (isBiometricsEnabled && wallet.hideBalance) { - if (!(await Biometric.unlockWithBiometrics())) { - return navigation.goBack(); - } - } - - const updatedWallet = updateWalletVisibility(wallet, !wallet.hideBalance); - setWallet(updatedWallet); - saveToDisk(); + const handleBalanceVisibility = () => { + onWalletBalanceVisibilityChange?.(!wallet.hideBalance); }; const updateWalletWithNewUnit = (w: TWallet, newPreferredUnit: BitcoinUnit) => { @@ -109,6 +96,8 @@ const TransactionsNavigationHeader: React.FC newWalletPreferredUnit = BitcoinUnit.BTC; } + LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut); + const updatedWallet = updateWalletWithNewUnit(wallet, newWalletPreferredUnit); setWallet(updatedWallet); onWalletUnitChange?.(updatedWallet); @@ -136,8 +125,11 @@ const TransactionsNavigationHeader: React.FC const balance = useMemo(() => { const hideBalance = wallet.hideBalance; const balanceUnit = wallet.getPreferredBalanceUnit(); - const balanceFormatted = formatBalance(wallet.getBalance(), balanceUnit, true); - return !hideBalance && balanceFormatted?.toString(); + const balanceFormatted = + balanceUnit === BitcoinUnit.LOCAL_CURRENCY + ? formatBalance(wallet.getBalance(), balanceUnit, true) + : formatBalanceWithoutSuffix(wallet.getBalance(), balanceUnit, true); + return !hideBalance && balanceFormatted; // eslint-disable-next-line react-hooks/exhaustive-deps }, [wallet.hideBalance, wallet.getPreferredBalanceUnit()]); @@ -163,68 +155,82 @@ const TransactionsNavigationHeader: React.FC style={styles.chainIcon} /> - + {wallet.getLabel()} - + - - {wallet.hideBalance ? ( - - ) : ( - - {balance} - - )} - - + ] + } + > + + {wallet.hideBalance ? ( + + ) : ( + + + {balance} + + + )} + + + + + {wallet.getPreferredBalanceUnit() === BitcoinUnit.LOCAL_CURRENCY + ? preferredFiatCurrency?.endPointKey ?? FiatUnit.USD + : wallet.getPreferredBalanceUnit()} + + + {wallet.type === LightningCustodianWallet.type && allowOnchainAddress && ( {item.hideBalance ? ( - + <> + + + ) : ( { {tx.fee && ( <> {loc.send.create_fee} - {tx.fee + ' sats'} + {tx.fee + ` ${BitcoinUnit.SATS}`} )} diff --git a/screen/wallets/selectWallet.js b/screen/wallets/selectWallet.js index 18f09c58a..1e80b3a52 100644 --- a/screen/wallets/selectWallet.js +++ b/screen/wallets/selectWallet.js @@ -3,7 +3,7 @@ import { View, ActivityIndicator, Image, Text, TouchableOpacity, I18nManager, Fl import LinearGradient from 'react-native-linear-gradient'; import { useRoute, useNavigation, useNavigationState } from '@react-navigation/native'; -import { BlueText, BlueSpacing20, BluePrivateBalance } from '../../BlueComponents'; +import { BlueText, BlueSpacing20 } from '../../BlueComponents'; import navigationStyle from '../../components/navigationStyle'; import WalletGradient from '../../class/wallet-gradient'; import loc, { formatBalance, transactionTimeToReadable } from '../../loc'; @@ -13,6 +13,7 @@ import { useTheme } from '../../components/themes'; import triggerHapticFeedback, { HapticFeedbackTypes } from '../../blue_modules/hapticFeedback'; import SafeArea from '../../components/SafeArea'; import { Chain } from '../../models/bitcoinUnits'; +import { BlurredBalanceView } from '../../components/BlurredBalanceView'; const SelectWallet = () => { const { chainType, onWalletSelect, availableWallets, noWalletExplanationText, onChainRequireSend = false } = useRoute().params; @@ -167,7 +168,7 @@ const SelectWallet = () => { {item.getLabel()} {item.hideBalance ? ( - + ) : ( {formatBalance(Number(item.getBalance()), item.getPreferredBalanceUnit(), true)} diff --git a/screen/wallets/transactions.js b/screen/wallets/transactions.js index cb5a94b1a..fc50b110b 100644 --- a/screen/wallets/transactions.js +++ b/screen/wallets/transactions.js @@ -15,6 +15,7 @@ import { TouchableOpacity, View, findNodeHandle, + LayoutAnimation, } from 'react-native'; import { Icon } from 'react-native-elements'; @@ -38,6 +39,7 @@ import { useExtendedNavigation } from '../../hooks/useExtendedNavigation'; import loc from '../../loc'; import { Chain } from '../../models/bitcoinUnits'; import ActionSheet from '../ActionSheet'; +import Biometric from '../../class/biometrics'; const BlueElectrum = require('../../blue_modules/BlueElectrum'); @@ -511,6 +513,20 @@ const WalletTransactions = ({ navigation }) => { saveToDisk(); }) } + onWalletBalanceVisibilityChange={async isShouldBeVisible => { + const isBiometricsEnabled = await Biometric.isBiometricUseCapableAndEnabled(); + + if (wallet.hideBalance && isBiometricsEnabled) { + const unlocked = await Biometric.unlockWithBiometrics(); + if (!unlocked) { + throw new Error('Biometrics failed'); + } + } + + wallet.hideBalance = isShouldBeVisible; + LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut); + await saveToDisk(); + }} onManageFundsPressed={id => { if (wallet.type === MultisigHDWallet.type) { navigateToViewEditCosigners(); diff --git a/tests/e2e/bluewallet2.spec.js b/tests/e2e/bluewallet2.spec.js index 2a710887b..5cc4018c6 100644 --- a/tests/e2e/bluewallet2.spec.js +++ b/tests/e2e/bluewallet2.spec.js @@ -18,7 +18,7 @@ beforeAll(async () => { await device.launchApp({ delete: true }); console.log('before all - importing bip48...'); - await helperImportWallet(process.env.HD_MNEMONIC_BIP84, 'HDsegwitBech32', 'Imported HD SegWit (BIP84 Bech32 Native)', '0.00105526 BTC'); + await helperImportWallet(process.env.HD_MNEMONIC_BIP84, 'HDsegwitBech32', 'Imported HD SegWit (BIP84 Bech32 Native)', '0.00105526'); console.log('...imported!'); await device.pressBack(); await sleep(15000); diff --git a/tests/e2e/bluewallet3.spec.js b/tests/e2e/bluewallet3.spec.js index 13dfd15fe..c3044296b 100644 --- a/tests/e2e/bluewallet3.spec.js +++ b/tests/e2e/bluewallet3.spec.js @@ -26,7 +26,7 @@ describe('BlueWallet UI Tests - import Watch-only wallet (zpub)', () => { 'zpub6s2EvLxwvDpaHNVP5vfordTyi8cH1fR8usmEjz7RsSQjfTTGU2qA5VEcEyYYBxpZAyBarJoTraB4VRJKVz97Au9jRNYfLAeeHC5UnRZbz8Y', 'watchOnly', 'Imported Watch-only', - '0.0001 BTC', + '0.0001', ); await sleep(15000);