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}
/>
)}
-
+