From 555125f85eef227e25aa4f33a39191f4d74cef2f Mon Sep 17 00:00:00 2001 From: marcosrdz Date: Fri, 26 Feb 2021 19:23:59 -0500 Subject: [PATCH] REF: Use actionsheet anchor property button ipad options --- BlueComponents.js | 17 +++++--- blue_modules/fs.js | 4 +- components/FloatButtons.js | 14 +++---- components/SquareButton.js | 7 ++-- components/WalletsCarousel.js | 2 +- screen/send/isItMyAddress.js | 9 +++-- screen/send/psbtMultisigQRCode.js | 8 ++-- screen/send/psbtWithHardwareWallet.js | 5 ++- screen/wallets/addMultisigStep2.js | 6 ++- screen/wallets/details.js | 4 +- screen/wallets/list.js | 43 ++++++++++++--------- screen/wallets/transactions.js | 43 ++++++++++++--------- screen/wallets/viewEditMultisigCosigners.js | 6 ++- 13 files changed, 98 insertions(+), 70 deletions(-) diff --git a/BlueComponents.js b/BlueComponents.js index 722feae8f..3ebab4050 100644 --- a/BlueComponents.js +++ b/BlueComponents.js @@ -1,5 +1,5 @@ /* eslint react/prop-types: "off", react-native/no-inline-styles: "off" */ -import React, { Component, useState, useMemo, useCallback, useContext, useRef } from 'react'; +import React, { Component, useState, useMemo, useCallback, useContext, useRef, forwardRef } from 'react'; import PropTypes from 'prop-types'; import { Icon, Input, Text, Header, ListItem, Avatar } from 'react-native-elements'; import { @@ -7,6 +7,7 @@ import { Alert, Animated, Dimensions, + findNodeHandle, Image, InputAccessoryView, Keyboard, @@ -93,7 +94,7 @@ export const BlueButton = props => { ); }; -export const SecondButton = props => { +export const SecondButton = forwardRef((props, ref) => { const { colors } = useTheme(); let backgroundColor = props.backgroundColor ? props.backgroundColor : colors.buttonBlueBackgroundColor; let fontColor = colors.buttonTextColor; @@ -117,6 +118,7 @@ export const SecondButton = props => { alignItems: 'center', }} {...props} + ref={ref} > {props.icon && } @@ -124,7 +126,7 @@ export const SecondButton = props => { ); -}; +}); export const BitcoinButton = props => { const { colors } = useTheme(); @@ -453,7 +455,7 @@ export class BlueWalletNavigationHeader extends Component { } } -export const BlueButtonLink = props => { +export const BlueButtonLink = forwardRef((props, ref) => { const { colors } = useTheme(); return ( { justifyContent: 'center', }} {...props} + ref={ref} > {props.title} ); -}; +}); export const BlueAlertWalletExportReminder = ({ onSuccess = () => {}, onFailure }) => { Alert.alert( @@ -1562,6 +1565,7 @@ export const BlueAddressInput = ({ launchedBy, }) => { const { colors } = useTheme(); + const scanButtonRef = useRef(); return ( { Keyboard.dismiss(); if (isDesktop) { - fs.showActionSheet().then(onBarScanned); + fs.showActionSheet({ anchor: findNodeHandle(scanButtonRef.current) }).then(onBarScanned); } else { NavigationService.navigate('ScanQRCodeRoot', { screen: 'ScanQRCode', diff --git a/blue_modules/fs.js b/blue_modules/fs.js index c50cebaf9..7f9c97888 100644 --- a/blue_modules/fs.js +++ b/blue_modules/fs.js @@ -193,7 +193,7 @@ const showFilePickerAndReadFile = async function () { }; // Intended for macOS Catalina. Not for long press shortcut -const showActionSheet = async () => { +const showActionSheet = async props => { const isClipboardEmpty = (await Clipboard.getString()).replace(' ', '').length === 0; let copyFromClipboardIndex; const options = [loc._.cancel, loc.wallets.take_photo, loc.wallets.list_long_choose]; @@ -206,7 +206,7 @@ const showActionSheet = async () => { const importFileButtonIndex = options.length - 1; return new Promise(resolve => - ActionSheet.showActionSheetWithOptions({ options, cancelButtonIndex: 0 }, async buttonIndex => { + ActionSheet.showActionSheetWithOptions({ options, cancelButtonIndex: 0, anchor: props.anchor }, async buttonIndex => { if (buttonIndex === 1) { takePhotoWithImagePickerAndReadPhoto().then(resolve); } else if (buttonIndex === 2) { diff --git a/components/FloatButtons.js b/components/FloatButtons.js index a87e556b0..08810b1d8 100644 --- a/components/FloatButtons.js +++ b/components/FloatButtons.js @@ -1,4 +1,4 @@ -import React, { useState, useRef } from 'react'; +import React, { useState, useRef, forwardRef } from 'react'; import PropTypes from 'prop-types'; import { View, Text, TouchableOpacity, StyleSheet, Dimensions, PixelRatio } from 'react-native'; import { useTheme } from '@react-navigation/native'; @@ -25,7 +25,7 @@ const cStyles = StyleSheet.create({ }, }); -export const FContainer = ({ children }) => { +export const FContainer = forwardRef((props, ref) => { const [newWidth, setNewWidth] = useState(); const layoutCalculated = useRef(false); @@ -34,7 +34,7 @@ export const FContainer = ({ children }) => { const maxWidth = Dimensions.get('window').width - BORDER_RADIUS - 20; const { width } = event.nativeEvent.layout; const withPaddings = Math.ceil(width + PADDINGS * 2); - const len = React.Children.toArray(children).filter(Boolean).length; + const len = React.Children.toArray(props.children).filter(Boolean).length; let newWidth = withPaddings * len > maxWidth ? Math.floor(maxWidth / len) : withPaddings; if (len === 1 && newWidth < 90) newWidth = 90; // to add Paddings for lonely small button, like Scan on main screen setNewWidth(newWidth); @@ -42,9 +42,9 @@ export const FContainer = ({ children }) => { }; return ( - + {newWidth - ? React.Children.toArray(children) + ? React.Children.toArray(props.children) .filter(Boolean) .map((c, index, array) => React.cloneElement(c, { @@ -54,10 +54,10 @@ export const FContainer = ({ children }) => { last: index === array.length - 1, }), ) - : children} + : props.children} ); -}; +}); FContainer.propTypes = { children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.element), PropTypes.element]), diff --git a/components/SquareButton.js b/components/SquareButton.js index e7f43a31d..3003be53b 100644 --- a/components/SquareButton.js +++ b/components/SquareButton.js @@ -1,10 +1,10 @@ /* eslint react/prop-types: "off", react-native/no-inline-styles: "off" */ -import React from 'react'; +import React, { forwardRef } from 'react'; import { TouchableOpacity, View, Text } from 'react-native'; import { Icon } from 'react-native-elements'; import { useTheme } from '@react-navigation/native'; -export const SquareButton = props => { +export const SquareButton = forwardRef((props, ref) => { const { colors } = useTheme(); let backgroundColor = props.backgroundColor ? props.backgroundColor : colors.buttonBlueBackgroundColor; let fontColor = colors.buttonTextColor; @@ -28,6 +28,7 @@ export const SquareButton = props => { alignItems: 'center', }} {...props} + ref={ref} > {props.icon && } @@ -35,4 +36,4 @@ export const SquareButton = props => { ); -}; +}); diff --git a/components/WalletsCarousel.js b/components/WalletsCarousel.js index a58264695..81c5c29e7 100644 --- a/components/WalletsCarousel.js +++ b/components/WalletsCarousel.js @@ -346,7 +346,7 @@ const WalletsCarousel = forwardRef((props, ref) => { WalletsCarousel.propTypes = { vertical: PropTypes.bool, - selectedWallet: PropTypes.object, + selectedWallet: PropTypes.string, onPress: PropTypes.func.isRequired, handleLongPress: PropTypes.func.isRequired, }; diff --git a/screen/send/isItMyAddress.js b/screen/send/isItMyAddress.js index df834523b..69b1d8a38 100644 --- a/screen/send/isItMyAddress.js +++ b/screen/send/isItMyAddress.js @@ -1,6 +1,6 @@ -import React, { useState, useContext } from 'react'; +import React, { useState, useContext, useRef } from 'react'; import { useNavigation, useRoute, useTheme } from '@react-navigation/native'; -import { StyleSheet, View, KeyboardAvoidingView, Platform, TextInput, Keyboard } from 'react-native'; +import { StyleSheet, View, KeyboardAvoidingView, Platform, TextInput, Keyboard, findNodeHandle } from 'react-native'; import loc from '../../loc'; import { BlueButton, BlueButtonLink, BlueCard, BlueSpacing10, BlueSpacing20, BlueText, SafeBlueArea } from '../../BlueComponents'; @@ -15,6 +15,7 @@ const IsItMyAddress = () => { const { navigate } = useNavigation(); const { name } = useRoute(); const { colors } = useTheme(); + const scanButtonRef = useRef(); const [address, setAddress] = useState(''); const [result, setResult] = useState(''); @@ -58,7 +59,7 @@ const IsItMyAddress = () => { const importScan = () => { if (isMacCatalina) { - fs.showActionSheet().then(onBarScanned); + fs.showActionSheet({ anchor: findNodeHandle(scanButtonRef.current) }).then(onBarScanned); } else { navigate('ScanQRCodeRoot', { screen: 'ScanQRCode', @@ -102,7 +103,7 @@ const IsItMyAddress = () => { - + diff --git a/screen/send/psbtMultisigQRCode.js b/screen/send/psbtMultisigQRCode.js index 83176116d..52dd238b8 100644 --- a/screen/send/psbtMultisigQRCode.js +++ b/screen/send/psbtMultisigQRCode.js @@ -1,6 +1,6 @@ /* global alert */ -import React, { useState } from 'react'; -import { ActivityIndicator, ScrollView, StyleSheet, View } from 'react-native'; +import React, { useRef, useState } from 'react'; +import { ActivityIndicator, findNodeHandle, ScrollView, StyleSheet, View } from 'react-native'; import { getSystemName } from 'react-native-device-info'; import { useNavigation, useRoute, useTheme } from '@react-navigation/native'; @@ -17,6 +17,7 @@ const isDesktop = getSystemName() === 'Mac OS X'; const PsbtMultisigQRCode = () => { const { navigate } = useNavigation(); const { colors } = useTheme(); + const openScannerButton = useRef(); const { psbtBase64, isShowOpenScanner } = useRoute().params; const [isLoading, setIsLoading] = useState(false); @@ -50,7 +51,7 @@ const PsbtMultisigQRCode = () => { const openScanner = () => { if (isDesktop) { - fs.showActionSheet().then(data => onBarScanned({ data })); + fs.showActionSheet({ anchor: findNodeHandle(openScannerButton.current) }).then(data => onBarScanned({ data })); } else { navigate('ScanQRCodeRoot', { screen: 'ScanQRCode', @@ -79,6 +80,7 @@ const PsbtMultisigQRCode = () => { testID="CosignedScanOrImportFile" style={[styles.exportButton, stylesHook.exportButton]} onPress={openScanner} + ref={openScannerButton} title={loc.multisig.scan_or_import_file} /> diff --git a/screen/send/psbtWithHardwareWallet.js b/screen/send/psbtWithHardwareWallet.js index 737796920..7b6bb46b6 100644 --- a/screen/send/psbtWithHardwareWallet.js +++ b/screen/send/psbtWithHardwareWallet.js @@ -12,6 +12,7 @@ import { Text, StyleSheet, Alert, + findNodeHandle, } from 'react-native'; import Clipboard from '@react-native-community/clipboard'; import Share from 'react-native-share'; @@ -50,6 +51,7 @@ const PsbtWithHardwareWallet = () => { const { colors } = useTheme(); const [isLoading, setIsLoading] = useState(false); const [txHex, setTxHex] = useState(route.params.txhex); + const openScannerButton = useRef(); const stylesHook = StyleSheet.create({ root: { @@ -250,7 +252,7 @@ const PsbtWithHardwareWallet = () => { const openScanner = () => { if (isMacCatalina) { - fs.showActionSheet().then(data => onBarScanned({ data })); + fs.showActionSheet({ anchor: findNodeHandle(openScannerButton.current) }).then(data => onBarScanned({ data })); } else { navigation.navigate('ScanQRCodeRoot', { screen: 'ScanQRCode', @@ -289,6 +291,7 @@ const PsbtWithHardwareWallet = () => { color: colors.buttonTextColor, }} onPress={openScanner} + ref={openScannerButton} title={loc.send.psbt_tx_scan} /> diff --git a/screen/wallets/addMultisigStep2.js b/screen/wallets/addMultisigStep2.js index bb26e86b6..b05c72fcd 100644 --- a/screen/wallets/addMultisigStep2.js +++ b/screen/wallets/addMultisigStep2.js @@ -13,6 +13,7 @@ import { TouchableOpacity, View, Alert, + findNodeHandle, } from 'react-native'; import { Icon } from 'react-native-elements'; import { useNavigation, useRoute, useTheme } from '@react-navigation/native'; @@ -66,6 +67,7 @@ const WalletsAddMultisigStep2 = () => { const [vaultKeyData, setVaultKeyData] = useState({ keyIndex: 1, xpub: '', seed: '', isLoading: false }); // string rendered in modal const [importText, setImportText] = useState(''); const tooltip = useRef(); + const openScannerButton = useRef(); const data = useRef(new Array(n)); const hasUnsavedChanges = Boolean(cosigners.length > 0 && cosigners.length !== n); @@ -428,7 +430,7 @@ const WalletsAddMultisigStep2 = () => { const scanOrOpenFile = () => { if (isDesktop) { - fs.showActionSheet().then(onBarScanned); + fs.showActionSheet({ anchor: findNodeHandle(openScannerButton.current) }).then(onBarScanned); } else { setIsProvideMnemonicsModalVisible(false); navigation.navigate('ScanQRCodeRoot', { @@ -614,7 +616,7 @@ const WalletsAddMultisigStep2 = () => { ) : ( )} - + diff --git a/screen/wallets/details.js b/screen/wallets/details.js index 43f111662..c301e3092 100644 --- a/screen/wallets/details.js +++ b/screen/wallets/details.js @@ -164,7 +164,9 @@ const WalletDetails = () => { }, []); useEffect(() => { - setSelectedWallet(walletID); + if (wallets.some(wallet => wallet.getID() === walletID)) { + setSelectedWallet(walletID); + } // eslint-disable-next-line react-hooks/exhaustive-deps }, [walletID]); diff --git a/screen/wallets/list.js b/screen/wallets/list.js index 560341bc5..63be55554 100644 --- a/screen/wallets/list.js +++ b/screen/wallets/list.js @@ -12,6 +12,7 @@ import { Dimensions, useWindowDimensions, SafeAreaView, + findNodeHandle, } from 'react-native'; import { BlueHeaderDefaultMain, BlueTransactionListItem } from '../../BlueComponents'; import WalletsCarousel from '../../components/WalletsCarousel'; @@ -50,6 +51,7 @@ const WalletsList = () => { const [carouselData, setCarouselData] = useState([]); const dataSource = getTransactions(null, 10); const walletsCount = useRef(wallets.length); + const walletActionButtonsRef = useRef(); const stylesHook = StyleSheet.create({ walletsListWrapper: { @@ -318,7 +320,7 @@ const WalletsList = () => { const renderScanButton = () => { if (carouselData.length > 0 && !carouselData.some(wallet => wallet.type === PlaceholderWallet.type)) { return ( - + { const onScanButtonPressed = () => { if (isMacCatalina) { - fs.showActionSheet().then(onBarScanned); + fs.showActionSheet({ anchor: walletActionButtonsRef.current }).then(onBarScanned); } else { navigate('ScanQRCodeRoot', { screen: 'ScanQRCode', @@ -366,28 +368,31 @@ const WalletsList = () => { const isClipboardEmpty = (await Clipboard.getString()).replace(' ', '').length === 0; if (Platform.OS === 'ios') { if (isMacCatalina) { - fs.showActionSheet().then(onBarScanned); + fs.showActionSheet({ anchor: findNodeHandle(walletActionButtonsRef.current) }).then(onBarScanned); } else { const options = [loc._.cancel, loc.wallets.list_long_choose, loc.wallets.list_long_scan]; if (!isClipboardEmpty) { options.push(loc.wallets.list_long_clipboard); } - ActionSheet.showActionSheetWithOptions({ options, cancelButtonIndex: 0 }, buttonIndex => { - if (buttonIndex === 1) { - fs.showImagePickerAndReadImage().then(onBarScanned); - } else if (buttonIndex === 2) { - navigate('ScanQRCodeRoot', { - screen: 'ScanQRCode', - params: { - launchedBy: routeName, - onBarScanned, - showFileImportButton: false, - }, - }); - } else if (buttonIndex === 3) { - copyFromClipboard(); - } - }); + ActionSheet.showActionSheetWithOptions( + { options, cancelButtonIndex: 0, anchor: findNodeHandle(walletActionButtonsRef.current) }, + buttonIndex => { + if (buttonIndex === 1) { + fs.showImagePickerAndReadImage().then(onBarScanned); + } else if (buttonIndex === 2) { + navigate('ScanQRCodeRoot', { + screen: 'ScanQRCode', + params: { + launchedBy: routeName, + onBarScanned, + showFileImportButton: false, + }, + }); + } else if (buttonIndex === 3) { + copyFromClipboard(); + } + }, + ); } } else if (Platform.OS === 'android') { const buttons = [ diff --git a/screen/wallets/transactions.js b/screen/wallets/transactions.js index 2e003346b..f71ec56aa 100644 --- a/screen/wallets/transactions.js +++ b/screen/wallets/transactions.js @@ -1,5 +1,5 @@ /* global alert */ -import React, { useEffect, useState, useCallback, useContext } from 'react'; +import React, { useEffect, useState, useCallback, useContext, useRef } from 'react'; import { ActivityIndicator, Alert, @@ -15,6 +15,7 @@ import { StatusBar, StyleSheet, Text, + findNodeHandle, TouchableOpacity, View, } from 'react-native'; @@ -58,6 +59,7 @@ const WalletTransactions = () => { const [pageSize, setPageSize] = useState(20); const { setParams, setOptions, navigate } = useNavigation(); const { colors } = useTheme(); + const walletActionButtonsRef = useRef(); const stylesHook = StyleSheet.create({ advancedTransactionOptionsModalContent: { @@ -513,7 +515,7 @@ const WalletTransactions = () => { const sendButtonLongPress = async () => { if (isMacCatalina) { - fs.showActionSheet().then(onBarCodeRead); + fs.showActionSheet({ anchor: walletActionButtonsRef.current }).then(onBarCodeRead); } else { const isClipboardEmpty = (await Clipboard.getString()).replace(' ', '').length === 0; if (Platform.OS === 'ios') { @@ -521,22 +523,25 @@ const WalletTransactions = () => { if (!isClipboardEmpty) { options.push(loc.wallets.list_long_clipboard); } - ActionSheet.showActionSheetWithOptions({ options, cancelButtonIndex: 0 }, buttonIndex => { - if (buttonIndex === 1) { - choosePhoto(); - } else if (buttonIndex === 2) { - navigate('ScanQRCodeRoot', { - screen: 'ScanQRCode', - params: { - launchedBy: name, - onBarScanned: onBarCodeRead, - showFileImportButton: false, - }, - }); - } else if (buttonIndex === 3) { - copyFromClipboard(); - } - }); + ActionSheet.showActionSheetWithOptions( + { options, cancelButtonIndex: 0, anchor: findNodeHandle(walletActionButtonsRef.current) }, + buttonIndex => { + if (buttonIndex === 1) { + choosePhoto(); + } else if (buttonIndex === 2) { + navigate('ScanQRCodeRoot', { + screen: 'ScanQRCode', + params: { + launchedBy: name, + onBarScanned: onBarCodeRead, + showFileImportButton: false, + }, + }); + } else if (buttonIndex === 3) { + copyFromClipboard(); + } + }, + ); } else if (Platform.OS === 'android') { const buttons = [ { @@ -673,7 +678,7 @@ const WalletTransactions = () => { {renderManageFundsModal()} - + {wallet.allowReceive() && ( { const { wallets, setWalletsWithNewOrder, setIsDrawerListBlurred } = useContext(BlueStorageContext); const { navigate, dispatch, goBack, addListener } = useNavigation(); const route = useRoute(); + const openScannerButtonRef = useRef(); const { walletId } = route.params; const w = useRef(wallets.find(wallet => wallet.getID() === walletId)); const tempWallet = useRef(new MultisigHDWallet()); @@ -473,7 +475,7 @@ const ViewEditMultisigCosigners = () => { const scanOrOpenFile = () => { if (isMacCatalina) { - fs.showActionSheet().then(result => { + fs.showActionSheet({ anchor: findNodeHandle(openScannerButtonRef.current) }).then(result => { // Triggers FlatList re-render setImportText(result); // @@ -525,7 +527,7 @@ const ViewEditMultisigCosigners = () => { onPress={handleUseMnemonicPhrase} /> )} - +