diff --git a/components/BlurredBalanceView.tsx b/components/BlurredBalanceView.tsx index 23e369c29d..82cd75b45a 100644 --- a/components/BlurredBalanceView.tsx +++ b/components/BlurredBalanceView.tsx @@ -1,18 +1,27 @@ import React from 'react'; -import { ImageBackground, StyleSheet, View } from 'react-native'; +import { StyleSheet, View } from 'react-native'; import { Icon } from '@rneui/themed'; 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 }, + container: { + flexDirection: 'row', + alignItems: 'center', + borderRadius: 9, + }, + background: { + backgroundColor: 'rgba(255, 255, 255, 0.5)', + height: 30, + width: 110, + marginRight: 8, + borderRadius: 9, + }, }); diff --git a/components/HeaderMenuButton.tsx b/components/HeaderMenuButton.tsx new file mode 100644 index 0000000000..ac4dc38b99 --- /dev/null +++ b/components/HeaderMenuButton.tsx @@ -0,0 +1,31 @@ +import React from 'react'; +import ToolTipMenu from './TooltipMenu'; +import { useTheme } from './themes'; +import { Icon } from '@rneui/themed'; +import { Platform } from 'react-native'; +import { Action } from './types'; + +interface HeaderMenuButtonProps { + onPressMenuItem: (id: string) => void; + actions: Action[]; + disabled?: boolean; +} + +const HeaderMenuButton: React.FC = ({ onPressMenuItem, actions, disabled }) => { + const { colors } = useTheme(); + const styleProps = Platform.OS === 'android' ? { iconStyle: { transform: [{ rotate: '90deg' }] } } : {}; + return ( + + + + ); +}; + +export default HeaderMenuButton; diff --git a/components/TransactionsNavigationHeader.tsx b/components/TransactionsNavigationHeader.tsx index 98b86f98d9..89417af70f 100644 --- a/components/TransactionsNavigationHeader.tsx +++ b/components/TransactionsNavigationHeader.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import Clipboard from '@react-native-clipboard/clipboard'; -import { I18nManager, Image, LayoutAnimation, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; +import { I18nManager, Image, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; import LinearGradient from 'react-native-linear-gradient'; import { LightningCustodianWallet, MultisigHDWallet } from '../class'; import WalletGradient from '../class/wallet-gradient'; @@ -11,6 +11,7 @@ import { FiatUnit } from '../models/fiatUnit'; import { BlurredBalanceView } from './BlurredBalanceView'; import { useSettings } from '../hooks/context/useSettings'; import ToolTipMenu from './TooltipMenu'; +import useAnimateOnChange from '../hooks/useAnimateOnChange'; interface TransactionsNavigationHeaderProps { wallet: TWallet; @@ -36,16 +37,14 @@ const TransactionsNavigationHeader: React.FC wallet .allowOnchainAddress() .then((value: boolean) => setAllowOnchainAddress(value)) - .catch((e: Error) => { - console.log('This LNDhub wallet does not have an onchain address API.'); + .catch(() => { + console.error('This LNDhub wallet does not have an onchain address API.'); setAllowOnchainAddress(false); }); } }, [wallet]); useEffect(() => { - LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut); - setWallet(initialWallet); }, [initialWallet]); @@ -61,7 +60,6 @@ const TransactionsNavigationHeader: React.FC }, [unit, wallet]); const handleBalanceVisibility = useCallback(() => { - LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut); onWalletBalanceVisibilityChange?.(!wallet.hideBalance); }, [onWalletBalanceVisibilityChange, wallet.hideBalance]); @@ -76,7 +74,6 @@ const TransactionsNavigationHeader: React.FC newWalletPreferredUnit = BitcoinUnit.BTC; } - LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut); onWalletUnitChange(newWalletPreferredUnit); }; @@ -116,7 +113,6 @@ const TransactionsNavigationHeader: React.FC }, []); const balance = useMemo(() => { - LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut); const hideBalance = wallet.hideBalance; const balanceFormatted = unit === BitcoinUnit.LOCAL_CURRENCY @@ -165,6 +161,11 @@ const TransactionsNavigationHeader: React.FC } }, [wallet.type]); + useAnimateOnChange(balance); + useAnimateOnChange(wallet.hideBalance); + useAnimateOnChange(unit); + useAnimateOnChange(wallet.getID?.()); + return ( ) : ( (value: T) => { + const prevValue = useRef(undefined); + useEffect(() => { + if (prevValue.current !== undefined && prevValue.current !== value) { + LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut); + } + prevValue.current = value; + }, [value]); +}; +export default useAnimateOnChange; diff --git a/package-lock.json b/package-lock.json index cac898f9f5..1c78a28ff0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -89,7 +89,7 @@ "react-native-quick-actions": "0.3.13", "react-native-randombytes": "3.6.1", "react-native-rate": "1.2.12", - "react-native-reanimated": "3.15.3", + "react-native-reanimated": "3.15.4", "react-native-safe-area-context": "4.11.0", "react-native-screens": "3.34.0", "react-native-secure-key-store": "github:BlueWallet/react-native-secure-key-store#2076b4849e88aa0a78e08bfbb4ce3923e0925cbc", @@ -20882,9 +20882,9 @@ } }, "node_modules/react-native-reanimated": { - "version": "3.15.3", - "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.15.3.tgz", - "integrity": "sha512-5QBk/7PZvZ98Adxm4MRyglwzsRzReTQIe4Hd2wbBBAZ68IC4OYKvsc8cPEjgx3/1mG8HgHFYhbcDe5U2RjeFqw==", + "version": "3.15.4", + "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.15.4.tgz", + "integrity": "sha512-jcpHE+MnsvSbClhHgAFoken7SnaHrUJ5gVA8BUw8S1j6rkrw2VzRpht6cxn14NlqYx5ytjfG9IXJDOzq8tFvfw==", "license": "MIT", "dependencies": { "@babel/plugin-transform-arrow-functions": "^7.0.0-0", diff --git a/package.json b/package.json index 50f6883ef2..3bf239ca15 100644 --- a/package.json +++ b/package.json @@ -153,7 +153,7 @@ "react-native-quick-actions": "0.3.13", "react-native-randombytes": "3.6.1", "react-native-rate": "1.2.12", - "react-native-reanimated": "3.15.3", + "react-native-reanimated": "3.15.4", "react-native-safe-area-context": "4.11.0", "react-native-screens": "3.34.0", "react-native-secure-key-store": "github:BlueWallet/react-native-secure-key-store#2076b4849e88aa0a78e08bfbb4ce3923e0925cbc", diff --git a/screen/receive/details.js b/screen/receive/details.js index 5b0a48c781..0c78a1f5d1 100644 --- a/screen/receive/details.js +++ b/screen/receive/details.js @@ -35,9 +35,8 @@ import { SuccessView } from '../send/success'; import { useStorage } from '../../hooks/context/useStorage'; import { HandOffActivityType } from '../../components/types'; import SegmentedControl from '../../components/SegmentControl'; -import ToolTipMenu from '../../components/TooltipMenu'; -import { Icon } from '@rneui/themed'; import { CommonToolTipActions } from '../../typings/CommonToolTipActions'; +import HeaderMenuButton from '../../components/HeaderMenuButton'; const segmentControlValues = [loc.wallets.details_address, loc.bip47.payment_code]; @@ -157,7 +156,7 @@ const ReceiveDetails = () => { const toolTipActions = useMemo(() => { const action = CommonToolTipActions.PaymentCode; - action.menuState = wallet.isBIP47Enabled(); + action.menuState = wallet?.isBIP47Enabled(); return [action]; }, [wallet]); @@ -167,12 +166,9 @@ const ReceiveDetails = () => { }, [onEnablePaymentsCodeSwitchValue]); const HeaderRight = useMemo( - () => ( - - - - ), - [colors.foregroundColor, onPressMenuItem, toolTipActions], + () => , + + [onPressMenuItem, toolTipActions], ); const handleClose = useCallback(() => { @@ -195,11 +191,11 @@ const ReceiveDetails = () => { ); useEffect(() => { - wallet.allowBIP47() && - !wallet.isBIP47Enabled() && + wallet?.allowBIP47() && + wallet?.isBIP47Enabled() && setOptions({ - headerLeft: () => (wallet.isBIP47Enabled() ? null : HeaderLeft), - headerRight: () => (wallet.isBIP47Enabled() ? HeaderLeft : HeaderRight), + headerLeft: () => (wallet?.isBIP47Enabled() ? null : HeaderLeft), + headerRight: () => (wallet?.isBIP47Enabled() ? HeaderLeft : HeaderRight), }); }, [HeaderLeft, HeaderRight, colors.foregroundColor, setOptions, wallet]); @@ -480,7 +476,7 @@ const ReceiveDetails = () => { contentContainerStyle={[styles.root, stylesHook.root]} keyboardShouldPersistTaps="always" > - {wallet?.allowBIP47() && wallet.isBIP47Enabled() && ( + {wallet?.allowBIP47() && wallet?.isBIP47Enabled() && ( { const setHeaderRightOptions = () => { navigation.setOptions({ // eslint-disable-next-line react/no-unstable-nested-components - headerRight: () => ( - - - - ), + headerRight: () => , }); }; @@ -1499,11 +1487,6 @@ const styles = StyleSheet.create({ alignItems: 'center', paddingHorizontal: 10, }, - advancedOptions: { - minWidth: 40, - height: 40, - justifyContent: 'center', - }, frozenContainer: { flexDirection: 'row', justifyContent: 'center', diff --git a/screen/send/psbtMultisigQRCode.js b/screen/send/psbtMultisigQRCode.js index 0bff5a4a72..c06143f8e4 100644 --- a/screen/send/psbtMultisigQRCode.js +++ b/screen/send/psbtMultisigQRCode.js @@ -60,7 +60,7 @@ const PsbtMultisigQRCode = () => { }; const openScanner = async () => { - const scanned = await scanQrHelper(name, true, undefined); + const scanned = await scanQrHelper(name, true); onBarScanned({ data: scanned }); }; diff --git a/screen/wallets/Add.tsx b/screen/wallets/Add.tsx index 1e6b6355c2..5a9021a8ee 100644 --- a/screen/wallets/Add.tsx +++ b/screen/wallets/Add.tsx @@ -25,11 +25,10 @@ import WalletButton from '../../components/WalletButton'; import loc from '../../loc'; import { Chain } from '../../models/bitcoinUnits'; import { useStorage } from '../../hooks/context/useStorage'; -import ToolTipMenu from '../../components/TooltipMenu'; -import { Icon } from '@rneui/themed'; import { CommonToolTipActions } from '../../typings/CommonToolTipActions'; import { Action } from '../../components/types'; import { getLNDHub } from '../../helpers/lndHub'; +import HeaderMenuButton from '../../components/HeaderMenuButton'; enum ButtonSelected { // @ts-ignore: Return later to update @@ -228,9 +227,7 @@ const WalletsAdd: React.FC = () => { const HeaderRight = useMemo( () => ( - { LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut); if (id === HDSegwitBech32Wallet.type) { @@ -246,11 +243,9 @@ const WalletsAdd: React.FC = () => { } }} actions={toolTipActions} - > - - + /> ), - [colors.foregroundColor, handleOnLightningButtonPressed, navigateToEntropy, toolTipActions], + [handleOnLightningButtonPressed, navigateToEntropy, toolTipActions], ); useEffect(() => { diff --git a/screen/wallets/import.js b/screen/wallets/import.js index 6455620be0..efbcdd1be6 100644 --- a/screen/wallets/import.js +++ b/screen/wallets/import.js @@ -11,11 +11,10 @@ import { DoneAndDismissKeyboardInputAccessory, DoneAndDismissKeyboardInputAccessoryViewID, } from '../../components/DoneAndDismissKeyboardInputAccessory'; -import { Icon } from '@rneui/themed'; import { CommonToolTipActions } from '../../typings/CommonToolTipActions'; import { useKeyboard } from '../../hooks/useKeyboard'; -import ToolTipMenu from '../../components/TooltipMenu'; import { useExtendedNavigation } from '../../hooks/useExtendedNavigation'; +import HeaderMenuButton from '../../components/HeaderMenuButton'; const WalletsImport = () => { const navigation = useExtendedNavigation(); @@ -140,18 +139,8 @@ const WalletsImport = () => { }, [askPassphrase, searchAccounts]); const HeaderRight = useMemo( - () => ( - - - - ), - [toolTipOnPressMenuItem, toolTipActions, colors.foregroundColor], + () => , + [toolTipOnPressMenuItem, toolTipActions], ); // Adding the ToolTipMenu to the header diff --git a/tests/e2e/bluewallet.spec.js b/tests/e2e/bluewallet.spec.js index a62828594e..bacf3ff3f4 100644 --- a/tests/e2e/bluewallet.spec.js +++ b/tests/e2e/bluewallet.spec.js @@ -677,9 +677,9 @@ describe('BlueWallet UI Tests - no wallets', () => { await element(by.id('MnemonicInput')).replaceText( 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about', ); - await element(by.id('HeaderRightButton')).tap(); + await element(by.id('HeaderMenuButton')).tap(); await element(by.text('Passphrase')).tap(); - await element(by.id('HeaderRightButton')).tap(); + await element(by.id('HeaderMenuButton')).tap(); await element(by.text('Search accounts')).tap(); await element(by.id('DoImport')).tap(); await sleep(1000); diff --git a/tests/e2e/bluewallet2.spec.js b/tests/e2e/bluewallet2.spec.js index c75a53107e..3320bc4ed4 100644 --- a/tests/e2e/bluewallet2.spec.js +++ b/tests/e2e/bluewallet2.spec.js @@ -202,23 +202,23 @@ describe('BlueWallet UI Tests - import BIP84 wallet', () => { await element(by.text('OK')).tap(); // lest add another two outputs - await element(by.id('advancedOptionsMenuButton')).tap(); + await element(by.id('HeaderMenuButton')).tap(); await element(by.text('Add Recipient')).tap(); await yo('Transaction1'); // adding a recipient autoscrolls it to the last one await element(by.id('AddressInput').withAncestor(by.id('Transaction1'))).replaceText('grs1q063ctu6jhe5k4v8ka99qac8rcm2tzjjnp8h967'); await element(by.id('BitcoinAmountInput').withAncestor(by.id('Transaction1'))).replaceText('0.0002\n'); - await element(by.id('advancedOptionsMenuButton')).tap(); + await element(by.id('HeaderMenuButton')).tap(); await element(by.text('Add Recipient')).tap(); await yo('Transaction2'); // adding a recipient autoscrolls it to the last one // remove last output, check if second output is shown - await element(by.id('advancedOptionsMenuButton')).tap(); + await element(by.id('HeaderMenuButton')).tap(); await element(by.text('Remove Recipient')).tap(); await yo('Transaction1'); // adding it again - await element(by.id('advancedOptionsMenuButton')).tap(); + await element(by.id('HeaderMenuButton')).tap(); await element(by.text('Add Recipient')).tap(); await yo('Transaction2'); // adding a recipient autoscrolls it to the last one await element(by.id('AddressInput').withAncestor(by.id('Transaction2'))).replaceText('grs1qh6tf004ty7z7un2v5ntu4mkf630545gv2pf49l'); @@ -227,7 +227,7 @@ describe('BlueWallet UI Tests - import BIP84 wallet', () => { // remove second output await element(by.id('Transaction2')).swipe('right', 'fast', NaN, 0.2); await sleep(5000); - await element(by.id('advancedOptionsMenuButton')).tap(); + await element(by.id('HeaderMenuButton')).tap(); await element(by.text('Remove Recipient')).tap(); // creating and verifying. tx should have 3 outputs @@ -275,7 +275,7 @@ describe('BlueWallet UI Tests - import BIP84 wallet', () => { // first send MAX output await element(by.id('AddressInput')).replaceText('grs1qnapskphjnwzw2w3dk4anpxntunc77v6q7dpwsl'); await element(by.id('BitcoinAmountInput')).typeText('0.0001\n'); - await element(by.id('advancedOptionsMenuButton')).tap(); + await element(by.id('HeaderMenuButton')).tap(); await element(by.text('Use Full Balance')).tap(); await element(by.text('OK')).tap(); @@ -294,7 +294,7 @@ describe('BlueWallet UI Tests - import BIP84 wallet', () => { // add second output with amount await device.pressBack(); await device.pressBack(); - await element(by.id('advancedOptionsMenuButton')).tap(); + await element(by.id('HeaderMenuButton')).tap(); await element(by.text('Add Recipient')).tap(); await yo('Transaction1'); await element(by.id('AddressInput').withAncestor(by.id('Transaction1'))).replaceText('grs1q063ctu6jhe5k4v8ka99qac8rcm2tzjjnp8h967'); @@ -334,7 +334,7 @@ describe('BlueWallet UI Tests - import BIP84 wallet', () => { await element(by.text('Imported HD SegWit (BIP84 Bech32 Native)')).tap(); await element(by.id('SendButton')).tap(); - await element(by.id('advancedOptionsMenuButton')).tap(); + await element(by.id('HeaderMenuButton')).tap(); await element(by.text('Sign a transaction')).tap(); // tapping 5 times invisible button is a backdoor: @@ -452,14 +452,14 @@ describe('BlueWallet UI Tests - import BIP84 wallet', () => { await device.pressBack(); await element(by.text('Imported HD SegWit (BIP84 Bech32 Native)')).tap(); await element(by.id('SendButton')).tap(); - await element(by.id('advancedOptionsMenuButton')).tap(); + await element(by.id('HeaderMenuButton')).tap(); await element(by.text('Insert Contact')).tap(); await element(by.id('ContactListItem0')).tap(); await element(by.id('BitcoinAmountInput')).typeText('0.0001\n'); - await element(by.id('advancedOptionsMenuButton')).tap(); + await element(by.id('HeaderMenuButton')).tap(); await element(by.text('Add Recipient')).tap(); - await element(by.id('advancedOptionsMenuButton')).tap(); + await element(by.id('HeaderMenuButton')).tap(); await element(by.text('Insert Contact')).tap(); await element(by.id('ContactListItem1')).tap(); await element(by.id('BitcoinAmountInput')).atIndex(1).typeText('0.0002\n'); @@ -601,7 +601,7 @@ describe('BlueWallet UI Tests - import BIP84 wallet', () => { await yo('WalletsList'); await element(by.text('Imported HD SegWit (BIP84 Bech32 Native)')).tap(); await element(by.id('SendButton')).tap(); - await element(by.id('advancedOptionsMenuButton')).tap(); + await element(by.id('HeaderMenuButton')).tap(); await element(by.text('Coin Control')).tap(); await waitFor(element(by.id('Loading'))) // wait for outputs to be loaded .not.toExist() @@ -620,7 +620,7 @@ describe('BlueWallet UI Tests - import BIP84 wallet', () => { await element(by.text('test2')).atIndex(0).tap(); await element(by.id('UseCoin')).tap(); await element(by.id('AddressInput')).replaceText('grs1q063ctu6jhe5k4v8ka99qac8rcm2tzjjnp8h967'); - await element(by.id('advancedOptionsMenuButton')).tap(); + await element(by.id('HeaderMenuButton')).tap(); await element(by.text('Use Full Balance')).tap(); await element(by.text('OK')).tap(); // setting fee rate: @@ -649,7 +649,7 @@ describe('BlueWallet UI Tests - import BIP84 wallet', () => { // create tx with unfrozen input await element(by.id('SendButton')).tap(); await element(by.id('AddressInput')).replaceText('grs1q063ctu6jhe5k4v8ka99qac8rcm2tzjjnp8h967'); - await element(by.id('advancedOptionsMenuButton')).tap(); + await element(by.id('HeaderMenuButton')).tap(); await element(by.text('Use Full Balance')).tap(); await element(by.text('OK')).tap(); // setting fee rate: diff --git a/tests/e2e/bluewallet3.spec.js b/tests/e2e/bluewallet3.spec.js index a080d54e5c..85a9068b8f 100644 --- a/tests/e2e/bluewallet3.spec.js +++ b/tests/e2e/bluewallet3.spec.js @@ -55,7 +55,7 @@ describe('BlueWallet UI Tests - import Watch-only wallet (zpub)', () => { await element(by.id('SendButton')).tap(); await element(by.text('OK')).tap(); - await element(by.id('advancedOptionsMenuButton')).tap(); + await element(by.id('HeaderMenuButton')).tap(); await element(by.text('Import Transaction (QR)')).tap(); // opens camera // produced by real Keystone device using MNEMONICS_KEYSTONE