mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-02-22 15:04:50 +01:00
REF: Replace abandoned package
This commit is contained in:
parent
c2287f2df6
commit
2d663ac8e2
13 changed files with 486 additions and 532 deletions
14
App.tsx
14
App.tsx
|
@ -10,13 +10,25 @@ import { BlueDarkTheme, BlueDefaultTheme } from './components/themes';
|
|||
import MasterView from './navigation/MasterView';
|
||||
import { navigationRef } from './NavigationService';
|
||||
import { StorageProvider } from './components/Context/StorageProvider';
|
||||
import { TrueSheet } from '@lodev09/react-native-true-sheet';
|
||||
|
||||
const App = () => {
|
||||
const colorScheme = useColorScheme();
|
||||
|
||||
const onReady = () => {
|
||||
// @ts-ignore: fix later
|
||||
navigationRef.current?.addListener('beforeRemove', async (e: any) => {
|
||||
if (e.data.action.type === 'NAVIGATE') {
|
||||
e.preventDefault();
|
||||
await TrueSheet.dismiss('BottomModal');
|
||||
navigationRef.current?.dispatch(e.data.action);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<LargeScreenProvider>
|
||||
<NavigationContainer ref={navigationRef} theme={colorScheme === 'dark' ? BlueDarkTheme : BlueDefaultTheme}>
|
||||
<NavigationContainer ref={navigationRef} theme={colorScheme === 'dark' ? BlueDarkTheme : BlueDefaultTheme} onReady={onReady}>
|
||||
<SafeAreaProvider>
|
||||
<StorageProvider>
|
||||
<SettingsProvider>
|
||||
|
|
|
@ -5,8 +5,8 @@ buildscript {
|
|||
minSdkVersion = 24
|
||||
supportLibVersion = "28.0.0"
|
||||
buildToolsVersion = "34.0.0"
|
||||
compileSdkVersion = 33
|
||||
targetSdkVersion = 33
|
||||
compileSdkVersion = 34
|
||||
targetSdkVersion = 34
|
||||
googlePlayServicesVersion = "16.+"
|
||||
googlePlayServicesIidVersion = "16.0.1"
|
||||
firebaseVersion = "17.3.4"
|
||||
|
@ -63,7 +63,7 @@ subprojects {
|
|||
if (project.hasProperty("android")) {
|
||||
android {
|
||||
buildToolsVersion "34.0.0"
|
||||
compileSdkVersion 33
|
||||
compileSdkVersion 34
|
||||
defaultConfig {
|
||||
minSdkVersion 24
|
||||
}
|
||||
|
|
|
@ -1,83 +1,56 @@
|
|||
import React from 'react';
|
||||
import { Platform, StyleSheet, useWindowDimensions, View } from 'react-native';
|
||||
import Modal from 'react-native-modal';
|
||||
import React, { forwardRef, useImperativeHandle, useRef } from 'react';
|
||||
import { TrueSheet, TrueSheetProps } from '@lodev09/react-native-true-sheet';
|
||||
|
||||
import { BlueSpacing10 } from '../BlueComponents';
|
||||
import loc from '../loc';
|
||||
import Button from './Button';
|
||||
import { useTheme } from './themes';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
justifyContent: 'flex-end',
|
||||
margin: 0,
|
||||
},
|
||||
hasDoneButton: {
|
||||
padding: 16,
|
||||
paddingBottom: 24,
|
||||
},
|
||||
});
|
||||
|
||||
interface BottomModalProps {
|
||||
interface BottomModalProps extends TrueSheetProps {
|
||||
children?: React.ReactNode;
|
||||
onBackButtonPress?: () => void;
|
||||
onBackdropPress?: () => void;
|
||||
onClose: () => void;
|
||||
windowHeight?: number;
|
||||
windowWidth?: number;
|
||||
doneButton?: boolean;
|
||||
avoidKeyboard?: boolean;
|
||||
allowBackdropPress?: boolean;
|
||||
isVisible: boolean;
|
||||
coverScreen?: boolean;
|
||||
onClose?: () => void;
|
||||
name?: string;
|
||||
isGrabberVisible?: boolean;
|
||||
}
|
||||
|
||||
const BottomModal: React.FC<BottomModalProps> = ({
|
||||
onBackButtonPress,
|
||||
onBackdropPress,
|
||||
onClose,
|
||||
windowHeight,
|
||||
windowWidth,
|
||||
doneButton,
|
||||
isVisible,
|
||||
avoidKeyboard = false,
|
||||
allowBackdropPress = true,
|
||||
coverScreen = true,
|
||||
...props
|
||||
}) => {
|
||||
const { height: valueWindowHeight, width: valueWindowWidth } = useWindowDimensions();
|
||||
const handleBackButtonPress = onBackButtonPress ?? onClose;
|
||||
const handleBackdropPress = allowBackdropPress ? onBackdropPress ?? onClose : undefined;
|
||||
const { colors } = useTheme();
|
||||
const stylesHook = StyleSheet.create({
|
||||
hasDoneButton: {
|
||||
backgroundColor: colors.elevated,
|
||||
},
|
||||
});
|
||||
export interface BottomModalHandle {
|
||||
present: () => Promise<void>;
|
||||
dismiss: () => Promise<void>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
style={styles.root}
|
||||
deviceHeight={windowHeight ?? valueWindowHeight}
|
||||
deviceWidth={windowWidth ?? valueWindowWidth}
|
||||
onBackButtonPress={handleBackButtonPress}
|
||||
onBackdropPress={handleBackdropPress}
|
||||
isVisible={isVisible}
|
||||
coverScreen={coverScreen}
|
||||
{...props}
|
||||
accessibilityViewIsModal
|
||||
avoidKeyboard={avoidKeyboard}
|
||||
useNativeDriverForBackdrop={Platform.OS === 'android'}
|
||||
>
|
||||
{props.children}
|
||||
{doneButton && (
|
||||
<View style={[styles.hasDoneButton, stylesHook.hasDoneButton]}>
|
||||
<Button title={loc.send.input_done} onPress={onClose} testID="ModalDoneButton" />
|
||||
<BlueSpacing10 />
|
||||
</View>
|
||||
)}
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
const BottomModal = forwardRef<BottomModalHandle, BottomModalProps>(
|
||||
({ name, onClose, onPresent, onSizeChange, isGrabberVisible = true, children, ...props }, ref) => {
|
||||
const trueSheetRef = useRef<TrueSheet>(null);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
present: async () => {
|
||||
if (trueSheetRef.current?.present) {
|
||||
await trueSheetRef.current.present();
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
},
|
||||
dismiss: async () => {
|
||||
if (trueSheetRef.current?.dismiss) {
|
||||
await trueSheetRef.current.dismiss();
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
return (
|
||||
<TrueSheet
|
||||
name={name ?? 'BottomModal'}
|
||||
ref={trueSheetRef}
|
||||
cornerRadius={24}
|
||||
sizes={['auto']}
|
||||
blurTint="regular"
|
||||
onDismiss={onClose}
|
||||
onPresent={onPresent}
|
||||
onSizeChange={onSizeChange}
|
||||
grabber={isGrabberVisible}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</TrueSheet>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
export default BottomModal;
|
||||
|
|
|
@ -517,6 +517,9 @@ PODS:
|
|||
- RNWatch (1.1.0):
|
||||
- React
|
||||
- SocketRocket (0.6.1)
|
||||
- TrueSheet (0.12.2):
|
||||
- RCT-Folly (= 2021.07.22.00)
|
||||
- React-Core
|
||||
- Yoga (1.14.0)
|
||||
|
||||
DEPENDENCIES:
|
||||
|
@ -600,6 +603,7 @@ DEPENDENCIES:
|
|||
- RNSVG (from `../node_modules/react-native-svg`)
|
||||
- RNVectorIcons (from `../node_modules/react-native-vector-icons`)
|
||||
- RNWatch (from `../node_modules/react-native-watch-connectivity`)
|
||||
- "TrueSheet (from `../node_modules/@lodev09/react-native-true-sheet`)"
|
||||
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
|
||||
|
||||
SPEC REPOS:
|
||||
|
@ -767,6 +771,8 @@ EXTERNAL SOURCES:
|
|||
:path: "../node_modules/react-native-vector-icons"
|
||||
RNWatch:
|
||||
:path: "../node_modules/react-native-watch-connectivity"
|
||||
TrueSheet:
|
||||
:path: "../node_modules/@lodev09/react-native-true-sheet"
|
||||
Yoga:
|
||||
:path: "../node_modules/react-native/ReactCommon/yoga"
|
||||
|
||||
|
@ -854,6 +860,7 @@ SPEC CHECKSUMS:
|
|||
RNVectorIcons: 32462e7c7e58fe457474fc79c4d7de3f0ef08d70
|
||||
RNWatch: fd30ca40a5b5ef58dcbc195638e68219bc455236
|
||||
SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17
|
||||
TrueSheet: 8235baf14119d4f9d8a904ed1de26111f73b733c
|
||||
Yoga: c32e0be1a17f8f1f0e633a3122f7666441f52c82
|
||||
|
||||
PODFILE CHECKSUM: f19eea438501edfe85fb2fa51d40ba1b57540758
|
||||
|
|
57
package-lock.json
generated
57
package-lock.json
generated
|
@ -14,6 +14,7 @@
|
|||
"@bugsnag/react-native": "7.24.0",
|
||||
"@bugsnag/source-maps": "2.3.3",
|
||||
"@keystonehq/bc-ur-registry": "0.6.4",
|
||||
"@lodev09/react-native-true-sheet": "github:BlueWallet/react-native-true-sheet#329026d",
|
||||
"@ngraveio/bc-ur": "1.1.12",
|
||||
"@noble/secp256k1": "1.6.3",
|
||||
"@react-native-async-storage/async-storage": "1.23.1",
|
||||
|
@ -80,7 +81,6 @@
|
|||
"react-native-keychain": "8.2.0",
|
||||
"react-native-linear-gradient": "2.8.3",
|
||||
"react-native-localize": "3.2.0",
|
||||
"react-native-modal": "13.0.1",
|
||||
"react-native-obscure": "https://github.com/BlueWallet/react-native-obscure.git#f4b83b4a261e39b1f5ed4a45ac5bcabc8a59eadb",
|
||||
"react-native-permissions": "4.1.5",
|
||||
"react-native-privacy-snapshot": "https://github.com/BlueWallet/react-native-privacy-snapshot#529e4627d93f67752a27e82a040ff7b64dca0783",
|
||||
|
@ -4234,6 +4234,19 @@
|
|||
"tslib": "^2.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@lodev09/react-native-true-sheet": {
|
||||
"version": "0.12.2",
|
||||
"resolved": "git+ssh://git@github.com/BlueWallet/react-native-true-sheet.git#329026d62e8c077aa1ae55e09d6d57dcc5fd6efd",
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
"example",
|
||||
"docs"
|
||||
],
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@ngraveio/bc-ur": {
|
||||
"version": "1.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@ngraveio/bc-ur/-/bc-ur-1.1.12.tgz",
|
||||
|
@ -19558,14 +19571,6 @@
|
|||
"react": "18.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-animatable": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/react-native-animatable/-/react-native-animatable-1.3.3.tgz",
|
||||
"integrity": "sha512-2ckIxZQAsvWn25Ho+DK3d1mXIgj7tITkrS4pYDvx96WyOttSvzzFeQnM2od0+FUMzILbdHDsDEqZvnz1DYNQ1w==",
|
||||
"dependencies": {
|
||||
"prop-types": "^15.7.2"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-biometrics": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-biometrics/-/react-native-biometrics-3.0.1.tgz",
|
||||
|
@ -19786,19 +19791,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-modal": {
|
||||
"version": "13.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-modal/-/react-native-modal-13.0.1.tgz",
|
||||
"integrity": "sha512-UB+mjmUtf+miaG/sDhOikRfBOv0gJdBU2ZE1HtFWp6UixW9jCk/bhGdHUgmZljbPpp0RaO/6YiMmQSSK3kkMaw==",
|
||||
"dependencies": {
|
||||
"prop-types": "^15.6.2",
|
||||
"react-native-animatable": "1.3.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
"react-native": ">=0.65.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-obscure": {
|
||||
"name": "@talaikis/react-native-obscure",
|
||||
"version": "0.0.3",
|
||||
|
@ -25761,6 +25753,10 @@
|
|||
"tslib": "^2.3.0"
|
||||
}
|
||||
},
|
||||
"@lodev09/react-native-true-sheet": {
|
||||
"version": "git+ssh://git@github.com/BlueWallet/react-native-true-sheet.git#329026d62e8c077aa1ae55e09d6d57dcc5fd6efd",
|
||||
"from": "@lodev09/react-native-true-sheet@github:BlueWallet/react-native-true-sheet#329026d"
|
||||
},
|
||||
"@ngraveio/bc-ur": {
|
||||
"version": "1.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@ngraveio/bc-ur/-/bc-ur-1.1.12.tgz",
|
||||
|
@ -37350,14 +37346,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"react-native-animatable": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/react-native-animatable/-/react-native-animatable-1.3.3.tgz",
|
||||
"integrity": "sha512-2ckIxZQAsvWn25Ho+DK3d1mXIgj7tITkrS4pYDvx96WyOttSvzzFeQnM2od0+FUMzILbdHDsDEqZvnz1DYNQ1w==",
|
||||
"requires": {
|
||||
"prop-types": "^15.7.2"
|
||||
}
|
||||
},
|
||||
"react-native-biometrics": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-biometrics/-/react-native-biometrics-3.0.1.tgz",
|
||||
|
@ -37489,15 +37477,6 @@
|
|||
"resolved": "https://registry.npmjs.org/react-native-localize/-/react-native-localize-3.2.0.tgz",
|
||||
"integrity": "sha512-Wi486WdDoBcDKUNxzr2/8f64ZwkWExlIvn0lcfFhsRNP2CVXGkjLyXhr3h6jf3Utkv5EmoFKiNqcmZn9kjYDxQ=="
|
||||
},
|
||||
"react-native-modal": {
|
||||
"version": "13.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-modal/-/react-native-modal-13.0.1.tgz",
|
||||
"integrity": "sha512-UB+mjmUtf+miaG/sDhOikRfBOv0gJdBU2ZE1HtFWp6UixW9jCk/bhGdHUgmZljbPpp0RaO/6YiMmQSSK3kkMaw==",
|
||||
"requires": {
|
||||
"prop-types": "^15.6.2",
|
||||
"react-native-animatable": "1.3.3"
|
||||
}
|
||||
},
|
||||
"react-native-obscure": {
|
||||
"version": "git+ssh://git@github.com/BlueWallet/react-native-obscure.git#f4b83b4a261e39b1f5ed4a45ac5bcabc8a59eadb",
|
||||
"integrity": "sha512-bmzbnlXII8hW7steqwouzQW9cJ+mdA8HJ8pWkb0KD60jC5dYgJ0e66wgUiSTDNFPrvlEKriE4eEanoJFj7Q9pg==",
|
||||
|
|
|
@ -98,6 +98,7 @@
|
|||
"@bugsnag/react-native": "7.24.0",
|
||||
"@bugsnag/source-maps": "2.3.3",
|
||||
"@keystonehq/bc-ur-registry": "0.6.4",
|
||||
"@lodev09/react-native-true-sheet": "github:BlueWallet/react-native-true-sheet#329026d",
|
||||
"@ngraveio/bc-ur": "1.1.12",
|
||||
"@noble/secp256k1": "1.6.3",
|
||||
"@react-native-async-storage/async-storage": "1.23.1",
|
||||
|
@ -164,7 +165,6 @@
|
|||
"react-native-keychain": "8.2.0",
|
||||
"react-native-linear-gradient": "2.8.3",
|
||||
"react-native-localize": "3.2.0",
|
||||
"react-native-modal": "13.0.1",
|
||||
"react-native-obscure": "https://github.com/BlueWallet/react-native-obscure.git#f4b83b4a261e39b1f5ed4a45ac5bcabc8a59eadb",
|
||||
"react-native-permissions": "4.1.5",
|
||||
"react-native-privacy-snapshot": "https://github.com/BlueWallet/react-native-privacy-snapshot#529e4627d93f67752a27e82a040ff7b64dca0783",
|
||||
|
|
|
@ -6,7 +6,7 @@ import { BlueLoading, BlueSpacing10, BlueSpacing20, BlueTextCentered } from '../
|
|||
import { LightningLdkWallet } from '../../class';
|
||||
import { TWallet } from '../../class/wallets/types';
|
||||
import presentAlert from '../../components/Alert';
|
||||
import BottomModal from '../../components/BottomModal';
|
||||
import BottomModal, { BottomModalHandle } from '../../components/BottomModal';
|
||||
import Button from '../../components/Button';
|
||||
import LNNodeBar from '../../components/LNNodeBar';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
|
@ -46,6 +46,7 @@ const LdkInfo = () => {
|
|||
const [pendingChannels, setPendingChannels] = useState<any[]>([]);
|
||||
const [wBalance, setWalletBalance] = useState<{ confirmedBalance?: number }>({});
|
||||
const [maturingBalance, setMaturingBalance] = useState(0);
|
||||
const bottomModalRef = useRef<BottomModalHandle>(null);
|
||||
const [maturingEta, setMaturingEta] = useState('');
|
||||
const centerContent = channels.length === 0 && pendingChannels.length === 0 && inactiveChannels.length === 0;
|
||||
const allChannelsAmount = useRef(0);
|
||||
|
@ -210,6 +211,7 @@ const LdkInfo = () => {
|
|||
const closeModal = () => {
|
||||
Keyboard.dismiss();
|
||||
setSelectedChannelIndex(undefined);
|
||||
bottomModalRef.current?.dismiss();
|
||||
};
|
||||
|
||||
const handleOnConnectPeerTapped = async (channelData: any) => {
|
||||
|
@ -222,7 +224,7 @@ const LdkInfo = () => {
|
|||
const status = selectedChannelIndex?.status;
|
||||
const channelData = selectedChannelIndex?.channel.item;
|
||||
return (
|
||||
<BottomModal isVisible={selectedChannelIndex !== undefined} onClose={closeModal} avoidKeyboard>
|
||||
<BottomModal ref={bottomModalRef}>
|
||||
<View style={[styles.modalContent, stylesHook.modalContent]}>
|
||||
<Text style={stylesHook.detailsText}>{loc.lnd.node_alias}</Text>
|
||||
<BlueSpacing10 />
|
||||
|
|
|
@ -1,17 +1,6 @@
|
|||
import { useFocusEffect, useRoute } from '@react-navigation/native';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
BackHandler,
|
||||
InteractionManager,
|
||||
Keyboard,
|
||||
KeyboardAvoidingView,
|
||||
Platform,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TextInput,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import { BackHandler, InteractionManager, Keyboard, ScrollView, StyleSheet, Text, TextInput, View } from 'react-native';
|
||||
import Share from 'react-native-share';
|
||||
|
||||
import * as BlueElectrum from '../../blue_modules/BlueElectrum';
|
||||
|
@ -47,12 +36,12 @@ const ReceiveDetails = () => {
|
|||
const [customUnit, setCustomUnit] = useState(BitcoinUnit.BTC);
|
||||
const [bip21encoded, setBip21encoded] = useState();
|
||||
const [isCustom, setIsCustom] = useState(false);
|
||||
const [isCustomModalVisible, setIsCustomModalVisible] = useState(false);
|
||||
const [showPendingBalance, setShowPendingBalance] = useState(false);
|
||||
const [showConfirmedBalance, setShowConfirmedBalance] = useState(false);
|
||||
const [showAddress, setShowAddress] = useState(false);
|
||||
const [currentTab, setCurrentTab] = useState(segmentControlValues[0]);
|
||||
const { goBack, setParams } = useExtendedNavigation();
|
||||
const bottomModalRef = useRef(null);
|
||||
const { colors } = useTheme();
|
||||
const [intervalMs, setIntervalMs] = useState(5000);
|
||||
const [eta, setEta] = useState('');
|
||||
|
@ -62,11 +51,6 @@ const ReceiveDetails = () => {
|
|||
const fetchAddressInterval = useRef();
|
||||
const receiveAddressButton = useRef();
|
||||
const stylesHook = StyleSheet.create({
|
||||
modalContent: {
|
||||
backgroundColor: colors.modal,
|
||||
borderTopColor: colors.foregroundColor,
|
||||
borderWidth: colors.borderWidth,
|
||||
},
|
||||
customAmount: {
|
||||
borderColor: colors.formBorder,
|
||||
borderBottomColor: colors.formBorder,
|
||||
|
@ -324,16 +308,16 @@ const ReceiveDetails = () => {
|
|||
|
||||
const dismissCustomAmountModal = () => {
|
||||
Keyboard.dismiss();
|
||||
setIsCustomModalVisible(false);
|
||||
bottomModalRef.current.dismiss();
|
||||
};
|
||||
|
||||
const showCustomAmountModal = () => {
|
||||
setIsCustomModalVisible(true);
|
||||
bottomModalRef.current.present();
|
||||
};
|
||||
|
||||
const createCustomAmountAddress = () => {
|
||||
setIsCustom(true);
|
||||
setIsCustomModalVisible(false);
|
||||
bottomModalRef.current.dismiss();
|
||||
let amount = customAmount;
|
||||
switch (customUnit) {
|
||||
case BitcoinUnit.BTC:
|
||||
|
@ -357,34 +341,35 @@ const ReceiveDetails = () => {
|
|||
|
||||
const renderCustomAmountModal = () => {
|
||||
return (
|
||||
<BottomModal isVisible={isCustomModalVisible} onClose={dismissCustomAmountModal}>
|
||||
<KeyboardAvoidingView enabled={!Platform.isPad} behavior={Platform.OS === 'ios' ? 'position' : null}>
|
||||
<View style={[styles.modalContent, stylesHook.modalContent]}>
|
||||
<AmountInput unit={customUnit} amount={customAmount || ''} onChangeText={setCustomAmount} onAmountUnitChange={setCustomUnit} />
|
||||
<View style={[styles.customAmount, stylesHook.customAmount]}>
|
||||
<TextInput
|
||||
onChangeText={setCustomLabel}
|
||||
placeholderTextColor="#81868e"
|
||||
placeholder={loc.receive.details_label}
|
||||
value={customLabel || ''}
|
||||
numberOfLines={1}
|
||||
style={[styles.customAmountText, stylesHook.customAmountText]}
|
||||
testID="CustomAmountDescription"
|
||||
/>
|
||||
</View>
|
||||
<BlueSpacing20 />
|
||||
<View>
|
||||
<Button
|
||||
testID="CustomAmountSaveButton"
|
||||
style={[styles.modalButton, stylesHook.modalButton]}
|
||||
title={loc.receive.details_create}
|
||||
onPress={createCustomAmountAddress}
|
||||
/>
|
||||
<BlueSpacing20 />
|
||||
</View>
|
||||
<BlueSpacing20 />
|
||||
</View>
|
||||
</KeyboardAvoidingView>
|
||||
<BottomModal
|
||||
ref={bottomModalRef}
|
||||
onClose={dismissCustomAmountModal}
|
||||
backgroundColor={colors.modal}
|
||||
contentContainerStyle={styles.modalContent}
|
||||
>
|
||||
<AmountInput unit={customUnit} amount={customAmount || ''} onChangeText={setCustomAmount} onAmountUnitChange={setCustomUnit} />
|
||||
<View style={[styles.customAmount, stylesHook.customAmount]}>
|
||||
<TextInput
|
||||
onChangeText={setCustomLabel}
|
||||
placeholderTextColor="#81868e"
|
||||
placeholder={loc.receive.details_label}
|
||||
value={customLabel || ''}
|
||||
numberOfLines={1}
|
||||
style={[styles.customAmountText, stylesHook.customAmountText]}
|
||||
testID="CustomAmountDescription"
|
||||
/>
|
||||
</View>
|
||||
<BlueSpacing20 />
|
||||
<View>
|
||||
<Button
|
||||
testID="CustomAmountSaveButton"
|
||||
style={[styles.modalButton, stylesHook.modalButton]}
|
||||
title={loc.receive.details_create}
|
||||
onPress={createCustomAmountAddress}
|
||||
/>
|
||||
<BlueSpacing20 />
|
||||
</View>
|
||||
<BlueSpacing20 />
|
||||
</BottomModal>
|
||||
);
|
||||
};
|
||||
|
@ -480,10 +465,6 @@ const styles = StyleSheet.create({
|
|||
padding: 22,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
borderTopLeftRadius: 16,
|
||||
borderTopRightRadius: 16,
|
||||
minHeight: 350,
|
||||
height: 350,
|
||||
},
|
||||
customAmount: {
|
||||
flexDirection: 'row',
|
||||
|
|
|
@ -36,7 +36,7 @@ import { AbstractHDElectrumWallet } from '../../class/wallets/abstract-hd-electr
|
|||
import AddressInput from '../../components/AddressInput';
|
||||
import presentAlert from '../../components/Alert';
|
||||
import AmountInput from '../../components/AmountInput';
|
||||
import BottomModal from '../../components/BottomModal';
|
||||
import BottomModal, { BottomModalHandle } from '../../components/BottomModal';
|
||||
import Button from '../../components/Button';
|
||||
import CoinsSelected from '../../components/CoinsSelected';
|
||||
import InputAccessoryAllFunds from '../../components/InputAccessoryAllFunds';
|
||||
|
@ -91,10 +91,10 @@ const SendDetails = () => {
|
|||
const [width, setWidth] = useState(Dimensions.get('window').width);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [wallet, setWallet] = useState<TWallet | null>(null);
|
||||
const feeModalRef = useRef<BottomModalHandle>(null);
|
||||
const optionsModalRef = useRef<BottomModalHandle>(null);
|
||||
const [walletSelectionOrCoinsSelectedHidden, setWalletSelectionOrCoinsSelectedHidden] = useState(false);
|
||||
const [isAmountToolbarVisibleForAndroid, setIsAmountToolbarVisibleForAndroid] = useState(false);
|
||||
const [isFeeSelectionModalVisible, setIsFeeSelectionModalVisible] = useState(false);
|
||||
const [optionsVisible, setOptionsVisible] = useState(false);
|
||||
const [isTransactionReplaceable, setIsTransactionReplaceable] = useState<boolean>(false);
|
||||
const [addresses, setAddresses] = useState<IPaymentDestinations[]>([]);
|
||||
const [units, setUnits] = useState<BitcoinUnit[]>([]);
|
||||
|
@ -609,11 +609,16 @@ const SendDetails = () => {
|
|||
|
||||
if (tx && routeParams.launchedBy && psbt) {
|
||||
console.warn('navigating back to ', routeParams.launchedBy);
|
||||
feeModalRef.current?.dismiss();
|
||||
optionsModalRef.current?.dismiss();
|
||||
// @ts-ignore idk how to fix FIXME?
|
||||
|
||||
navigation.navigate(routeParams.launchedBy, { psbt });
|
||||
}
|
||||
|
||||
if (wallet?.type === WatchOnlyWallet.type) {
|
||||
feeModalRef.current?.dismiss();
|
||||
optionsModalRef.current?.dismiss();
|
||||
// watch-only wallets with enabled HW wallet support have different flow. we have to show PSBT to user as QR code
|
||||
// so he can scan it and sign it. then we have to scan it back from user (via camera and QR code), and ask
|
||||
// user whether he wants to broadcast it
|
||||
|
@ -628,6 +633,8 @@ const SendDetails = () => {
|
|||
}
|
||||
|
||||
if (wallet?.type === MultisigHDWallet.type) {
|
||||
feeModalRef.current?.dismiss();
|
||||
optionsModalRef.current?.dismiss();
|
||||
navigation.navigate('PsbtMultisig', {
|
||||
memo: transactionMemo,
|
||||
psbtBase64: psbt.toBase64(),
|
||||
|
@ -652,6 +659,8 @@ const SendDetails = () => {
|
|||
// (ez can be the case for single-address wallet when doing self-payment for consolidation)
|
||||
recipients = outputs;
|
||||
}
|
||||
feeModalRef.current?.dismiss();
|
||||
optionsModalRef.current?.dismiss();
|
||||
|
||||
navigation.navigate('Confirm', {
|
||||
fee: new BigNumber(fee).dividedBy(100000000).toNumber(),
|
||||
|
@ -685,8 +694,9 @@ const SendDetails = () => {
|
|||
return presentAlert({ title: loc.errors.error, message: 'Importing transaction in non-watchonly wallet (this should never happen)' });
|
||||
}
|
||||
|
||||
setOptionsVisible(false);
|
||||
requestCameraAuthorization().then(() => {
|
||||
feeModalRef.current?.dismiss();
|
||||
optionsModalRef.current?.dismiss();
|
||||
navigation.navigate('ScanQRCodeRoot', {
|
||||
screen: 'ScanQRCode',
|
||||
params: {
|
||||
|
@ -707,18 +717,20 @@ const SendDetails = () => {
|
|||
// this looks like NOT base64, so maybe its transaction's hex
|
||||
// we dont support it in this flow
|
||||
} else {
|
||||
feeModalRef.current?.dismiss();
|
||||
optionsModalRef.current?.dismiss();
|
||||
// psbt base64?
|
||||
|
||||
// we construct PSBT object and pass to next screen
|
||||
// so user can do smth with it:
|
||||
const psbt = bitcoin.Psbt.fromBase64(ret.data);
|
||||
|
||||
navigation.navigate('PsbtWithHardwareWallet', {
|
||||
memo: transactionMemo,
|
||||
fromWallet: wallet,
|
||||
psbt,
|
||||
});
|
||||
setIsLoading(false);
|
||||
setOptionsVisible(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -731,6 +743,8 @@ const SendDetails = () => {
|
|||
* @returns {Promise<void>}
|
||||
*/
|
||||
const importTransaction = async () => {
|
||||
feeModalRef.current?.dismiss();
|
||||
optionsModalRef.current?.dismiss();
|
||||
if (wallet?.type !== WatchOnlyWallet.type) {
|
||||
return presentAlert({ title: loc.errors.error, message: 'Importing transaction in non-watchonly wallet (this should never happen)' });
|
||||
}
|
||||
|
@ -751,7 +765,7 @@ const SendDetails = () => {
|
|||
const txhex = psbt.extractTransaction().toHex();
|
||||
navigation.navigate('PsbtWithHardwareWallet', { memo: transactionMemo, fromWallet: wallet, txhex });
|
||||
setIsLoading(false);
|
||||
setOptionsVisible(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -762,7 +776,7 @@ const SendDetails = () => {
|
|||
const psbt = bitcoin.Psbt.fromBase64(file);
|
||||
navigation.navigate('PsbtWithHardwareWallet', { memo: transactionMemo, fromWallet: wallet, psbt });
|
||||
setIsLoading(false);
|
||||
setOptionsVisible(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -771,7 +785,7 @@ const SendDetails = () => {
|
|||
const file = (await RNFS.readFile(res.uri, 'ascii')).replace('\n', '').replace('\r', '');
|
||||
navigation.navigate('PsbtWithHardwareWallet', { memo: transactionMemo, fromWallet: wallet, txhex: file });
|
||||
setIsLoading(false);
|
||||
setOptionsVisible(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -805,6 +819,8 @@ const SendDetails = () => {
|
|||
};
|
||||
|
||||
const _importTransactionMultisig = async (base64arg: string | false) => {
|
||||
feeModalRef.current?.dismiss();
|
||||
optionsModalRef.current?.dismiss();
|
||||
try {
|
||||
const base64 = base64arg || (await fs.openSignedTransaction());
|
||||
if (!base64) return;
|
||||
|
@ -830,7 +846,6 @@ const SendDetails = () => {
|
|||
presentAlert({ title: loc.send.problem_with_psbt, message: error.message });
|
||||
}
|
||||
setIsLoading(false);
|
||||
setOptionsVisible(false);
|
||||
};
|
||||
|
||||
const importTransactionMultisig = () => {
|
||||
|
@ -852,7 +867,8 @@ const SendDetails = () => {
|
|||
};
|
||||
|
||||
const importTransactionMultisigScanQr = () => {
|
||||
setOptionsVisible(false);
|
||||
feeModalRef.current?.dismiss();
|
||||
optionsModalRef.current?.dismiss();
|
||||
requestCameraAuthorization().then(() => {
|
||||
navigation.navigate('ScanQRCodeRoot', {
|
||||
screen: 'ScanQRCode',
|
||||
|
@ -867,7 +883,7 @@ const SendDetails = () => {
|
|||
const handleAddRecipient = async () => {
|
||||
console.log('handleAddRecipient');
|
||||
setAddresses(addrs => [...addrs, { address: '', key: String(Math.random()) } as IPaymentDestinations]);
|
||||
setOptionsVisible(false);
|
||||
|
||||
await sleep(200); // wait for animation
|
||||
scrollView.current?.scrollToEnd();
|
||||
if (addresses.length === 0) return;
|
||||
|
@ -881,7 +897,7 @@ const SendDetails = () => {
|
|||
addrs.splice(scrollIndex.current, 1);
|
||||
return [...addrs];
|
||||
});
|
||||
setOptionsVisible(false);
|
||||
|
||||
if (addresses.length === 0) return;
|
||||
await sleep(200); // wait for animation
|
||||
scrollView.current?.flashScrollIndicators();
|
||||
|
@ -890,7 +906,8 @@ const SendDetails = () => {
|
|||
|
||||
const handleCoinControl = () => {
|
||||
if (!wallet) return;
|
||||
setOptionsVisible(false);
|
||||
feeModalRef.current?.dismiss();
|
||||
optionsModalRef.current?.dismiss();
|
||||
navigation.navigate('CoinControl', {
|
||||
walletID: wallet?.getID(),
|
||||
onUTXOChoose: (u: CreateTransactionUtxo[]) => setUtxo(u),
|
||||
|
@ -899,13 +916,16 @@ const SendDetails = () => {
|
|||
|
||||
const handleInsertContact = () => {
|
||||
if (!wallet) return;
|
||||
setOptionsVisible(false);
|
||||
feeModalRef.current?.dismiss();
|
||||
optionsModalRef.current?.dismiss();
|
||||
navigation.navigate('PaymentCodeList', { walletID: wallet.getID() });
|
||||
};
|
||||
|
||||
const handlePsbtSign = async () => {
|
||||
setIsLoading(true);
|
||||
setOptionsVisible(false);
|
||||
feeModalRef.current?.dismiss();
|
||||
optionsModalRef.current?.dismiss();
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 100)); // sleep for animations
|
||||
const scannedData = await scanQrHelper(name);
|
||||
if (!scannedData) return setIsLoading(false);
|
||||
|
@ -947,7 +967,6 @@ const SendDetails = () => {
|
|||
|
||||
const hideOptions = () => {
|
||||
Keyboard.dismiss();
|
||||
setOptionsVisible(false);
|
||||
};
|
||||
|
||||
// Header Right Button
|
||||
|
@ -1071,7 +1090,8 @@ const SendDetails = () => {
|
|||
style={styles.advancedOptions}
|
||||
onPress={() => {
|
||||
Keyboard.dismiss();
|
||||
setOptionsVisible(true);
|
||||
feeModalRef.current?.dismiss();
|
||||
optionsModalRef.current?.present();
|
||||
}}
|
||||
testID="advancedOptionsMenuButton"
|
||||
>
|
||||
|
@ -1127,7 +1147,6 @@ const SendDetails = () => {
|
|||
return [...u];
|
||||
});
|
||||
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
|
||||
setOptionsVisible(false);
|
||||
},
|
||||
style: 'default',
|
||||
},
|
||||
|
@ -1237,69 +1256,67 @@ const SendDetails = () => {
|
|||
];
|
||||
|
||||
return (
|
||||
<BottomModal isVisible={isFeeSelectionModalVisible} onClose={() => setIsFeeSelectionModalVisible(false)}>
|
||||
<KeyboardAvoidingView enabled={!isTablet} behavior={Platform.OS === 'ios' ? 'position' : undefined}>
|
||||
<View style={[styles.modalContent, stylesHook.modalContent]}>
|
||||
{options.map(({ label, time, fee, rate, active, disabled }, index) => (
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
key={label}
|
||||
disabled={disabled}
|
||||
onPress={() => {
|
||||
setFeePrecalc(fp => ({ ...fp, current: fee }));
|
||||
setIsFeeSelectionModalVisible(false);
|
||||
setCustomFee(rate.toString());
|
||||
}}
|
||||
style={[styles.feeModalItem, active && styles.feeModalItemActive, active && !disabled && stylesHook.feeModalItemActive]}
|
||||
>
|
||||
<View style={styles.feeModalRow}>
|
||||
<Text style={[styles.feeModalLabel, disabled ? stylesHook.feeModalItemTextDisabled : stylesHook.feeModalLabel]}>
|
||||
{label}
|
||||
</Text>
|
||||
<View style={[styles.feeModalTime, disabled ? stylesHook.feeModalItemDisabled : stylesHook.feeModalTime]}>
|
||||
<Text style={stylesHook.feeModalTimeText}>~{time}</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.feeModalRow}>
|
||||
<Text style={disabled ? stylesHook.feeModalItemTextDisabled : stylesHook.feeModalValue}>{fee && formatFee(fee)}</Text>
|
||||
<Text style={disabled ? stylesHook.feeModalItemTextDisabled : stylesHook.feeModalValue}>
|
||||
{rate} {loc.units.sat_vbyte}
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
<BottomModal ref={feeModalRef}>
|
||||
<View style={[styles.modalContent, stylesHook.modalContent]}>
|
||||
{options.map(({ label, time, fee, rate, active, disabled }, index) => (
|
||||
<TouchableOpacity
|
||||
testID="feeCustom"
|
||||
accessibilityRole="button"
|
||||
style={styles.feeModalCustom}
|
||||
onPress={async () => {
|
||||
let error = loc.send.fee_satvbyte;
|
||||
while (true) {
|
||||
let fee: number | string;
|
||||
key={label}
|
||||
disabled={disabled}
|
||||
onPress={() => {
|
||||
setFeePrecalc(fp => ({ ...fp, current: fee }));
|
||||
feeModalRef.current?.dismiss();
|
||||
setCustomFee(rate.toString());
|
||||
}}
|
||||
style={[styles.feeModalItem, active && styles.feeModalItemActive, active && !disabled && stylesHook.feeModalItemActive]}
|
||||
>
|
||||
<View style={styles.feeModalRow}>
|
||||
<Text style={[styles.feeModalLabel, disabled ? stylesHook.feeModalItemTextDisabled : stylesHook.feeModalLabel]}>
|
||||
{label}
|
||||
</Text>
|
||||
<View style={[styles.feeModalTime, disabled ? stylesHook.feeModalItemDisabled : stylesHook.feeModalTime]}>
|
||||
<Text style={stylesHook.feeModalTimeText}>~{time}</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.feeModalRow}>
|
||||
<Text style={disabled ? stylesHook.feeModalItemTextDisabled : stylesHook.feeModalValue}>{fee && formatFee(fee)}</Text>
|
||||
<Text style={disabled ? stylesHook.feeModalItemTextDisabled : stylesHook.feeModalValue}>
|
||||
{rate} {loc.units.sat_vbyte}
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
<TouchableOpacity
|
||||
testID="feeCustom"
|
||||
accessibilityRole="button"
|
||||
style={styles.feeModalCustom}
|
||||
onPress={async () => {
|
||||
let error = loc.send.fee_satvbyte;
|
||||
while (true) {
|
||||
let fee: number | string;
|
||||
|
||||
try {
|
||||
fee = await prompt(loc.send.create_fee, error, true, 'numeric');
|
||||
} catch (_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!/^\d+$/.test(fee)) {
|
||||
error = loc.send.details_fee_field_is_not_valid;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Number(fee) < 1) fee = '1';
|
||||
fee = Number(fee).toString(); // this will remove leading zeros if any
|
||||
setCustomFee(fee);
|
||||
setIsFeeSelectionModalVisible(false);
|
||||
try {
|
||||
fee = await prompt(loc.send.create_fee, error, true, 'numeric');
|
||||
} catch (_) {
|
||||
return;
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Text style={[styles.feeModalCustomText, stylesHook.feeModalCustomText]}>{loc.send.fee_custom}</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</KeyboardAvoidingView>
|
||||
|
||||
if (!/^\d+$/.test(fee)) {
|
||||
error = loc.send.details_fee_field_is_not_valid;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Number(fee) < 1) fee = '1';
|
||||
fee = Number(fee).toString(); // this will remove leading zeros if any
|
||||
setCustomFee(fee);
|
||||
feeModalRef.current?.dismiss();
|
||||
return;
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Text style={[styles.feeModalCustomText, stylesHook.feeModalCustomText]}>{loc.send.fee_custom}</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</BottomModal>
|
||||
);
|
||||
};
|
||||
|
@ -1308,56 +1325,57 @@ const SendDetails = () => {
|
|||
const isSendMaxUsed = addresses.some(element => element.amount === BitcoinUnit.MAX);
|
||||
|
||||
return (
|
||||
<BottomModal isVisible={optionsVisible} onClose={hideOptions}>
|
||||
<KeyboardAvoidingView enabled={!isTablet} behavior={undefined}>
|
||||
<View style={[styles.optionsContent, stylesHook.optionsContent]}>
|
||||
{wallet?.allowBIP47() && wallet.isBIP47Enabled() && (
|
||||
<ListItem testID="InsertContactButton" title={loc.send.details_insert_contact} onPress={handleInsertContact} />
|
||||
)}
|
||||
{isEditable && (
|
||||
<ListItem
|
||||
testID="sendMaxButton"
|
||||
disabled={balance === 0 || isSendMaxUsed}
|
||||
title={loc.send.details_adv_full}
|
||||
onPress={onUseAllPressed}
|
||||
/>
|
||||
)}
|
||||
{wallet?.type === HDSegwitBech32Wallet.type && isEditable && (
|
||||
<ListItem
|
||||
title={loc.send.details_adv_fee_bump}
|
||||
Component={TouchableWithoutFeedback}
|
||||
switch={{ value: isTransactionReplaceable, onValueChange: onReplaceableFeeSwitchValueChanged }}
|
||||
/>
|
||||
)}
|
||||
{wallet?.type === WatchOnlyWallet.type && wallet.isHd() && (
|
||||
<ListItem title={loc.send.details_adv_import} onPress={importTransaction} />
|
||||
)}
|
||||
{wallet?.type === WatchOnlyWallet.type && wallet.isHd() && (
|
||||
<ListItem testID="ImportQrTransactionButton" title={loc.send.details_adv_import_qr} onPress={importQrTransaction} />
|
||||
)}
|
||||
{wallet?.type === MultisigHDWallet.type && isEditable && (
|
||||
<ListItem title={loc.send.details_adv_import} onPress={importTransactionMultisig} />
|
||||
)}
|
||||
{wallet?.type === MultisigHDWallet.type && wallet.howManySignaturesCanWeMake() > 0 && isEditable && (
|
||||
<ListItem title={loc.multisig.co_sign_transaction} onPress={importTransactionMultisigScanQr} />
|
||||
)}
|
||||
{isEditable && (
|
||||
<>
|
||||
<ListItem testID="AddRecipient" title={loc.send.details_add_rec_add} onPress={handleAddRecipient} />
|
||||
<ListItem
|
||||
testID="RemoveRecipient"
|
||||
title={loc.send.details_add_rec_rem}
|
||||
disabled={addresses.length < 2}
|
||||
onPress={handleRemoveRecipient}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<ListItem testID="CoinControl" title={loc.cc.header} onPress={handleCoinControl} />
|
||||
{(wallet as MultisigHDWallet)?.allowCosignPsbt() && isEditable && (
|
||||
<ListItem testID="PsbtSign" title={loc.send.psbt_sign} onPress={handlePsbtSign} />
|
||||
)}
|
||||
</View>
|
||||
</KeyboardAvoidingView>
|
||||
<BottomModal
|
||||
ref={optionsModalRef}
|
||||
onClose={hideOptions}
|
||||
backgroundColor={colors.modal}
|
||||
contentContainerStyle={[styles.optionsContent, stylesHook.optionsContent]}
|
||||
>
|
||||
{wallet?.allowBIP47() && wallet.isBIP47Enabled() && (
|
||||
<ListItem testID="InsertContactButton" title={loc.send.details_insert_contact} onPress={handleInsertContact} />
|
||||
)}
|
||||
{isEditable && (
|
||||
<ListItem
|
||||
testID="sendMaxButton"
|
||||
disabled={balance === 0 || isSendMaxUsed}
|
||||
title={loc.send.details_adv_full}
|
||||
onPress={onUseAllPressed}
|
||||
/>
|
||||
)}
|
||||
{wallet?.type === HDSegwitBech32Wallet.type && isEditable && (
|
||||
<ListItem
|
||||
title={loc.send.details_adv_fee_bump}
|
||||
Component={TouchableWithoutFeedback}
|
||||
switch={{ value: isTransactionReplaceable, onValueChange: onReplaceableFeeSwitchValueChanged }}
|
||||
/>
|
||||
)}
|
||||
{wallet?.type === WatchOnlyWallet.type && wallet.isHd() && (
|
||||
<ListItem title={loc.send.details_adv_import} onPress={importTransaction} />
|
||||
)}
|
||||
{wallet?.type === WatchOnlyWallet.type && wallet.isHd() && (
|
||||
<ListItem testID="ImportQrTransactionButton" title={loc.send.details_adv_import_qr} onPress={importQrTransaction} />
|
||||
)}
|
||||
{wallet?.type === MultisigHDWallet.type && isEditable && (
|
||||
<ListItem title={loc.send.details_adv_import} onPress={importTransactionMultisig} />
|
||||
)}
|
||||
{wallet?.type === MultisigHDWallet.type && wallet.howManySignaturesCanWeMake() > 0 && isEditable && (
|
||||
<ListItem title={loc.multisig.co_sign_transaction} onPress={importTransactionMultisigScanQr} />
|
||||
)}
|
||||
{isEditable && (
|
||||
<>
|
||||
<ListItem testID="AddRecipient" title={loc.send.details_add_rec_add} onPress={handleAddRecipient} />
|
||||
<ListItem
|
||||
testID="RemoveRecipient"
|
||||
title={loc.send.details_add_rec_rem}
|
||||
disabled={addresses.length < 2}
|
||||
onPress={handleRemoveRecipient}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<ListItem testID="CoinControl" title={loc.cc.header} onPress={handleCoinControl} />
|
||||
{(wallet as MultisigHDWallet)?.allowCosignPsbt() && isEditable && (
|
||||
<ListItem testID="PsbtSign" title={loc.send.psbt_sign} onPress={handlePsbtSign} />
|
||||
)}
|
||||
</BottomModal>
|
||||
);
|
||||
};
|
||||
|
@ -1400,7 +1418,11 @@ const SendDetails = () => {
|
|||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
style={styles.selectTouch}
|
||||
onPress={() => navigation.navigate('SelectWallet', { chainType: Chain.ONCHAIN })}
|
||||
onPress={() => {
|
||||
feeModalRef.current?.dismiss();
|
||||
optionsModalRef.current?.dismiss();
|
||||
navigation.navigate('SelectWallet', { chainType: Chain.ONCHAIN });
|
||||
}}
|
||||
>
|
||||
<Text style={styles.selectText}>{loc.wallets.select_wallet.toLowerCase()}</Text>
|
||||
<Icon name={I18nManager.isRTL ? 'angle-left' : 'angle-right'} size={18} type="font-awesome" color="#9aa0aa" />
|
||||
|
@ -1410,7 +1432,11 @@ const SendDetails = () => {
|
|||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
style={styles.selectTouch}
|
||||
onPress={() => navigation.navigate('SelectWallet', { chainType: Chain.ONCHAIN })}
|
||||
onPress={() => {
|
||||
feeModalRef.current?.dismiss();
|
||||
optionsModalRef.current?.dismiss();
|
||||
navigation.navigate('SelectWallet', { chainType: Chain.ONCHAIN });
|
||||
}}
|
||||
disabled={!isEditable || isLoading}
|
||||
>
|
||||
<Text style={[styles.selectLabel, stylesHook.selectLabel]}>{wallet?.getLabel()}</Text>
|
||||
|
@ -1558,7 +1584,7 @@ const SendDetails = () => {
|
|||
<TouchableOpacity
|
||||
testID="chooseFee"
|
||||
accessibilityRole="button"
|
||||
onPress={() => setIsFeeSelectionModalVisible(true)}
|
||||
onPress={() => feeModalRef.current?.present()}
|
||||
disabled={isLoading}
|
||||
style={styles.fee}
|
||||
>
|
||||
|
@ -1641,15 +1667,9 @@ const styles = StyleSheet.create({
|
|||
},
|
||||
modalContent: {
|
||||
padding: 22,
|
||||
borderTopLeftRadius: 16,
|
||||
borderTopRightRadius: 16,
|
||||
minHeight: 200,
|
||||
},
|
||||
optionsContent: {
|
||||
padding: 22,
|
||||
borderTopLeftRadius: 16,
|
||||
borderTopRightRadius: 16,
|
||||
minHeight: 130,
|
||||
},
|
||||
feeModalItem: {
|
||||
paddingHorizontal: 16,
|
||||
|
|
|
@ -256,6 +256,7 @@ const CoinControl = () => {
|
|||
const { colors } = useTheme();
|
||||
const navigation = useNavigation();
|
||||
const { width } = useWindowDimensions();
|
||||
const bottomModalRef = useRef(null);
|
||||
const { walletID, onUTXOChoose } = useRoute().params;
|
||||
const { wallets, saveToDisk, sleep } = useStorage();
|
||||
const wallet = wallets.find(w => w.getID() === walletID);
|
||||
|
@ -386,6 +387,14 @@ const CoinControl = () => {
|
|||
return <OutputModalContent output={output} wallet={wallet} onUseCoin={handleUseCoin} frozen={oFrozen} setFrozen={setOFrozen} />;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (output) {
|
||||
bottomModalRef.current?.present();
|
||||
} else {
|
||||
bottomModalRef.current?.dismiss();
|
||||
}
|
||||
}, [output]);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<SafeArea style={[styles.center, { backgroundColor: colors.elevated }]}>
|
||||
|
@ -403,7 +412,7 @@ const CoinControl = () => {
|
|||
)}
|
||||
|
||||
<BottomModal
|
||||
isVisible={Boolean(output)}
|
||||
ref={bottomModalRef}
|
||||
onClose={() => {
|
||||
Keyboard.dismiss();
|
||||
setOutput(false);
|
||||
|
|
|
@ -31,7 +31,7 @@ import {
|
|||
} from '../../BlueComponents';
|
||||
import { HDSegwitBech32Wallet, MultisigCosigner, MultisigHDWallet } from '../../class';
|
||||
import presentAlert from '../../components/Alert';
|
||||
import BottomModal from '../../components/BottomModal';
|
||||
import BottomModal, { BottomModalHandle } from '../../components/BottomModal';
|
||||
import Button from '../../components/Button';
|
||||
import MultipleStepsListItem, {
|
||||
MultipleStepsListItemButtohType,
|
||||
|
@ -68,9 +68,9 @@ const ViewEditMultisigCosigners: React.FC = () => {
|
|||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [isSaveButtonDisabled, setIsSaveButtonDisabled] = useState(true);
|
||||
const [currentlyEditingCosignerNum, setCurrentlyEditingCosignerNum] = useState<number | false>(false);
|
||||
const [isProvideMnemonicsModalVisible, setIsProvideMnemonicsModalVisible] = useState(false);
|
||||
const [isMnemonicsModalVisible, setIsMnemonicsModalVisible] = useState(false);
|
||||
const [isShareModalVisible, setIsShareModalVisible] = useState(false);
|
||||
const shareModalRef = useRef<BottomModalHandle>(null);
|
||||
const provideMnemonicsModalRef = useRef<BottomModalHandle>(null);
|
||||
const mnemonicsModalRef = useRef<BottomModalHandle>(null);
|
||||
const [importText, setImportText] = useState('');
|
||||
const [exportString, setExportString] = useState('{}'); // used in exportCosigner()
|
||||
const [exportStringURv2, setExportStringURv2] = useState(''); // used in QR
|
||||
|
@ -164,7 +164,7 @@ const ViewEditMultisigCosigners: React.FC = () => {
|
|||
);
|
||||
|
||||
const saveFileButtonAfterOnPress = () => {
|
||||
setIsShareModalVisible(false);
|
||||
shareModalRef.current?.dismiss();
|
||||
};
|
||||
|
||||
const onSave = async () => {
|
||||
|
@ -226,12 +226,12 @@ const ViewEditMultisigCosigners: React.FC = () => {
|
|||
|
||||
const hideMnemonicsModal = () => {
|
||||
Keyboard.dismiss();
|
||||
setIsMnemonicsModalVisible(false);
|
||||
mnemonicsModalRef.current?.dismiss();
|
||||
};
|
||||
|
||||
const renderMnemonicsModal = () => {
|
||||
return (
|
||||
<BottomModal isVisible={isMnemonicsModalVisible} onClose={hideMnemonicsModal} coverScreen={false}>
|
||||
<BottomModal ref={mnemonicsModalRef} onClose={hideMnemonicsModal}>
|
||||
<View style={[styles.newKeyModalContent, stylesHook.modalContent]}>
|
||||
<View style={styles.itemKeyUnprovidedWrapper}>
|
||||
<View style={[styles.vaultKeyCircleSuccess, stylesHook.vaultKeyCircleSuccess]}>
|
||||
|
@ -274,14 +274,14 @@ const ViewEditMultisigCosigners: React.FC = () => {
|
|||
<Button
|
||||
title={loc.multisig.share}
|
||||
onPress={() => {
|
||||
setIsMnemonicsModalVisible(false);
|
||||
mnemonicsModalRef.current?.dismiss();
|
||||
setTimeout(() => {
|
||||
setIsShareModalVisible(true);
|
||||
shareModalRef.current?.present();
|
||||
}, 1000);
|
||||
}}
|
||||
/>
|
||||
<BlueSpacing20 />
|
||||
<Button title={loc.send.success_done} onPress={() => setIsMnemonicsModalVisible(false)} />
|
||||
<Button title={loc.send.success_done} onPress={() => mnemonicsModalRef.current?.dismiss()} />
|
||||
</View>
|
||||
</BottomModal>
|
||||
);
|
||||
|
@ -346,7 +346,7 @@ const ViewEditMultisigCosigners: React.FC = () => {
|
|||
setExportString(MultisigCosigner.exportToJson(fp, xpub, path));
|
||||
setExportStringURv2(encodeUR(MultisigCosigner.exportToJson(fp, xpub, path))[0]);
|
||||
setExportFilename('bw-cosigner-' + fp + '.json');
|
||||
setIsMnemonicsModalVisible(true);
|
||||
mnemonicsModalRef.current?.present();
|
||||
},
|
||||
}}
|
||||
dashes={MultipleStepsListItemDashType.topAndBottom}
|
||||
|
@ -360,7 +360,7 @@ const ViewEditMultisigCosigners: React.FC = () => {
|
|||
disabled: vaultKeyData.isLoading,
|
||||
onPress: () => {
|
||||
setCurrentlyEditingCosignerNum(el.index + 1);
|
||||
setIsProvideMnemonicsModalVisible(true);
|
||||
provideMnemonicsModalRef.current?.present();
|
||||
},
|
||||
}}
|
||||
dashes={el.index === length - 1 ? MultipleStepsListItemDashType.top : MultipleStepsListItemDashType.topAndBottom}
|
||||
|
@ -389,7 +389,7 @@ const ViewEditMultisigCosigners: React.FC = () => {
|
|||
passphrase: passphrase ?? '',
|
||||
isLoading: false,
|
||||
});
|
||||
setIsMnemonicsModalVisible(true);
|
||||
mnemonicsModalRef.current?.present();
|
||||
const fp = wallet.getFingerprint(keyIndex);
|
||||
const path = wallet.getCustomDerivationPathForCosigner(keyIndex);
|
||||
if (!path) {
|
||||
|
@ -485,7 +485,7 @@ const ViewEditMultisigCosigners: React.FC = () => {
|
|||
|
||||
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
|
||||
setWallet(wallet);
|
||||
setIsProvideMnemonicsModalVisible(false);
|
||||
provideMnemonicsModalRef.current?.dismiss();
|
||||
setIsSaveButtonDisabled(false);
|
||||
setImportText('');
|
||||
setAskPassphrase(false);
|
||||
|
@ -509,75 +509,71 @@ const ViewEditMultisigCosigners: React.FC = () => {
|
|||
};
|
||||
|
||||
const scanOrOpenFile = async () => {
|
||||
setIsProvideMnemonicsModalVisible(false);
|
||||
provideMnemonicsModalRef.current?.dismiss();
|
||||
const scanned = await scanQrHelper(route.name, true);
|
||||
setImportText(String(scanned));
|
||||
setIsProvideMnemonicsModalVisible(true);
|
||||
provideMnemonicsModalRef.current?.present();
|
||||
};
|
||||
|
||||
const hideProvideMnemonicsModal = () => {
|
||||
Keyboard.dismiss();
|
||||
setIsProvideMnemonicsModalVisible(false);
|
||||
provideMnemonicsModalRef.current?.dismiss();
|
||||
setImportText('');
|
||||
setAskPassphrase(false);
|
||||
};
|
||||
|
||||
const hideShareModal = () => {
|
||||
setIsShareModalVisible(false);
|
||||
shareModalRef.current?.dismiss();
|
||||
};
|
||||
|
||||
const renderProvideMnemonicsModal = () => {
|
||||
return (
|
||||
<BottomModal avoidKeyboard isVisible={isProvideMnemonicsModalVisible} onClose={hideProvideMnemonicsModal} coverScreen={false}>
|
||||
<KeyboardAvoidingView enabled={!isTablet} behavior={Platform.OS === 'ios' ? 'position' : 'padding'} keyboardVerticalOffset={120}>
|
||||
<View style={[styles.modalContent, stylesHook.modalContent]}>
|
||||
<BlueTextCentered>{loc.multisig.type_your_mnemonics}</BlueTextCentered>
|
||||
<BlueSpacing20 />
|
||||
<BlueFormMultiInput value={importText} onChangeText={setImportText} />
|
||||
{isAdvancedModeEnabled && (
|
||||
<>
|
||||
<BlueSpacing10 />
|
||||
<View style={styles.row}>
|
||||
<BlueText>{loc.wallets.import_passphrase}</BlueText>
|
||||
<Switch testID="AskPassphrase" value={askPassphrase} onValueChange={setAskPassphrase} />
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
<BlueSpacing20 />
|
||||
{isLoading ? (
|
||||
<ActivityIndicator />
|
||||
) : (
|
||||
<Button disabled={importText.trim().length === 0} title={loc.wallets.import_do_import} onPress={handleUseMnemonicPhrase} />
|
||||
)}
|
||||
<BlueButtonLink ref={openScannerButtonRef} disabled={isLoading} onPress={scanOrOpenFile} title={loc.wallets.import_scan_qr} />
|
||||
</View>
|
||||
</KeyboardAvoidingView>
|
||||
<BottomModal onClose={hideProvideMnemonicsModal} ref={provideMnemonicsModalRef}>
|
||||
<View style={[styles.modalContent, stylesHook.modalContent]}>
|
||||
<BlueTextCentered>{loc.multisig.type_your_mnemonics}</BlueTextCentered>
|
||||
<BlueSpacing20 />
|
||||
<BlueFormMultiInput value={importText} onChangeText={setImportText} />
|
||||
{isAdvancedModeEnabled && (
|
||||
<>
|
||||
<BlueSpacing10 />
|
||||
<View style={styles.row}>
|
||||
<BlueText>{loc.wallets.import_passphrase}</BlueText>
|
||||
<Switch testID="AskPassphrase" value={askPassphrase} onValueChange={setAskPassphrase} />
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
<BlueSpacing20 />
|
||||
{isLoading ? (
|
||||
<ActivityIndicator />
|
||||
) : (
|
||||
<Button disabled={importText.trim().length === 0} title={loc.wallets.import_do_import} onPress={handleUseMnemonicPhrase} />
|
||||
)}
|
||||
<BlueButtonLink ref={openScannerButtonRef} disabled={isLoading} onPress={scanOrOpenFile} title={loc.wallets.import_scan_qr} />
|
||||
</View>
|
||||
</BottomModal>
|
||||
);
|
||||
};
|
||||
|
||||
const renderShareModal = () => {
|
||||
return (
|
||||
<BottomModal isVisible={isShareModalVisible} onClose={hideShareModal} doneButton coverScreen={false}>
|
||||
<KeyboardAvoidingView enabled={!isTablet} behavior={Platform.OS === 'ios' ? 'position' : undefined}>
|
||||
<View style={[styles.modalContent, stylesHook.modalContent, styles.alignItemsCenter]}>
|
||||
<Text style={[styles.headerText, stylesHook.textDestination]}>
|
||||
{loc.multisig.this_is_cosigners_xpub} {Platform.OS === 'ios' ? loc.multisig.this_is_cosigners_xpub_airdrop : ''}
|
||||
</Text>
|
||||
<QRCodeComponent value={exportStringURv2} size={260} isLogoRendered={false} />
|
||||
<BlueSpacing20 />
|
||||
<View style={styles.squareButtonWrapper}>
|
||||
<SaveFileButton
|
||||
style={[styles.exportButton, stylesHook.exportButton]}
|
||||
fileContent={exportString}
|
||||
fileName={exportFilename}
|
||||
afterOnPress={saveFileButtonAfterOnPress}
|
||||
>
|
||||
<SquareButton title={loc.multisig.share} />
|
||||
</SaveFileButton>
|
||||
</View>
|
||||
<BottomModal ref={shareModalRef} onClose={hideShareModal}>
|
||||
<View style={[styles.modalContent, stylesHook.modalContent, styles.alignItemsCenter]}>
|
||||
<Text style={[styles.headerText, stylesHook.textDestination]}>
|
||||
{loc.multisig.this_is_cosigners_xpub} {Platform.OS === 'ios' ? loc.multisig.this_is_cosigners_xpub_airdrop : ''}
|
||||
</Text>
|
||||
<QRCodeComponent value={exportStringURv2} size={260} isLogoRendered={false} />
|
||||
<BlueSpacing20 />
|
||||
<View style={styles.squareButtonWrapper}>
|
||||
<SaveFileButton
|
||||
style={[styles.exportButton, stylesHook.exportButton]}
|
||||
fileContent={exportString}
|
||||
fileName={exportFilename}
|
||||
afterOnPress={saveFileButtonAfterOnPress}
|
||||
>
|
||||
<SquareButton title={loc.multisig.share} />
|
||||
</SaveFileButton>
|
||||
</View>
|
||||
</KeyboardAvoidingView>
|
||||
</View>
|
||||
</BottomModal>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { useNavigation, useRoute } from '@react-navigation/native';
|
||||
import LottieView from 'lottie-react-native';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { Keyboard, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||||
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||||
import { Icon } from '@rneui/themed';
|
||||
|
||||
import { BlueSpacing20 } from '../../BlueComponents';
|
||||
|
@ -18,10 +18,10 @@ const WalletsAddMultisig = () => {
|
|||
const { colors } = useTheme();
|
||||
const { navigate } = useNavigation();
|
||||
const loadingAnimation = useRef();
|
||||
const bottomModalRef = useRef();
|
||||
const { walletLabel } = useRoute().params;
|
||||
const [m, setM] = useState(2);
|
||||
const [n, setN] = useState(3);
|
||||
const [isModalVisible, setIsModalVisible] = useState(false);
|
||||
const [format, setFormat] = useState(MultisigHDWallet.FORMAT_P2WSH);
|
||||
const { isAdvancedModeEnabled } = useSettings();
|
||||
|
||||
|
@ -41,9 +41,11 @@ const WalletsAddMultisig = () => {
|
|||
color: colors.alternativeTextColor,
|
||||
},
|
||||
selectedItem: {
|
||||
paddingHorizontal: 8,
|
||||
backgroundColor: colors.elevated,
|
||||
},
|
||||
deSelectedItem: {
|
||||
paddingHorizontal: 8,
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
textHeader: {
|
||||
|
@ -99,88 +101,73 @@ const WalletsAddMultisig = () => {
|
|||
setN(n - 1);
|
||||
};
|
||||
|
||||
const closeModal = () => {
|
||||
Keyboard.dismiss();
|
||||
setIsModalVisible(false);
|
||||
};
|
||||
|
||||
const renderModal = () => {
|
||||
return (
|
||||
<BottomModal isVisible={isModalVisible} onClose={closeModal} doneButton propagateSwipe>
|
||||
<View style={[styles.modalContentShort, stylesHook.modalContentShort]}>
|
||||
<ScrollView>
|
||||
<Text style={[styles.textHeader, stylesHook.textHeader]}>{loc.multisig.quorum_header}</Text>
|
||||
<Text style={[styles.textSubtitle, stylesHook.textSubtitle]}>{loc.multisig.required_keys_out_of_total}</Text>
|
||||
<View style={styles.rowCenter}>
|
||||
<View style={styles.column}>
|
||||
<TouchableOpacity accessibilityRole="button" onPress={increaseM} disabled={n === m || m === 7} style={styles.chevron}>
|
||||
<Icon
|
||||
name="chevron-up"
|
||||
size={22}
|
||||
type="font-awesome-5"
|
||||
color={n === m || m === 7 ? colors.buttonDisabledTextColor : '#007AFF'}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
<Text style={[styles.textM, stylesHook.textHeader]}>{m}</Text>
|
||||
<TouchableOpacity accessibilityRole="button" onPress={decreaseM} disabled={m === 2} style={styles.chevron}>
|
||||
<Icon name="chevron-down" size={22} type="font-awesome-5" color={m === 2 ? colors.buttonDisabledTextColor : '#007AFF'} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<BottomModal ref={bottomModalRef} contentContainerStyle={styles.modalContentShort} backgroundColor={stylesHook.modalContentShort}>
|
||||
<Text style={[styles.textHeader, stylesHook.textHeader]}>{loc.multisig.quorum_header}</Text>
|
||||
<Text style={[styles.textSubtitle, stylesHook.textSubtitle]}>{loc.multisig.required_keys_out_of_total}</Text>
|
||||
<View style={styles.rowCenter}>
|
||||
<View style={styles.column}>
|
||||
<TouchableOpacity accessibilityRole="button" onPress={increaseM} disabled={n === m || m === 7} style={styles.chevron}>
|
||||
<Icon
|
||||
name="chevron-up"
|
||||
size={22}
|
||||
type="font-awesome-5"
|
||||
color={n === m || m === 7 ? colors.buttonDisabledTextColor : '#007AFF'}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
<Text style={[styles.textM, stylesHook.textHeader]}>{m}</Text>
|
||||
<TouchableOpacity accessibilityRole="button" onPress={decreaseM} disabled={m === 2} style={styles.chevron}>
|
||||
<Icon name="chevron-down" size={22} type="font-awesome-5" color={m === 2 ? colors.buttonDisabledTextColor : '#007AFF'} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
<View style={styles.columnOf}>
|
||||
<Text style={styles.textOf}>{loc.multisig.of}</Text>
|
||||
</View>
|
||||
<View style={styles.columnOf}>
|
||||
<Text style={styles.textOf}>{loc.multisig.of}</Text>
|
||||
</View>
|
||||
|
||||
<View style={styles.column}>
|
||||
<TouchableOpacity accessibilityRole="button" disabled={n === 7} onPress={increaseN} style={styles.chevron}>
|
||||
<Icon name="chevron-up" size={22} type="font-awesome-5" color={n === 7 ? colors.buttonDisabledTextColor : '#007AFF'} />
|
||||
</TouchableOpacity>
|
||||
<Text style={[styles.textM, stylesHook.textHeader]}>{n}</Text>
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
onPress={decreaseN}
|
||||
disabled={n === m}
|
||||
style={styles.chevron}
|
||||
testID="DecreaseN"
|
||||
>
|
||||
<Icon name="chevron-down" size={22} type="font-awesome-5" color={n === m ? colors.buttonDisabledTextColor : '#007AFF'} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<BlueSpacing20 />
|
||||
|
||||
<Text style={[styles.textHeader, stylesHook.textHeader]}>{loc.multisig.wallet_type}</Text>
|
||||
<BlueSpacing20 />
|
||||
<ListItem
|
||||
bottomDivider={false}
|
||||
onPress={setFormatP2wsh}
|
||||
title={`${loc.multisig.native_segwit_title} (${MultisigHDWallet.FORMAT_P2WSH})`}
|
||||
checkmark={isP2wsh()}
|
||||
containerStyle={[styles.borderRadius6, styles.item, isP2wsh() ? stylesHook.selectedItem : stylesHook.deSelectedItem]}
|
||||
/>
|
||||
<ListItem
|
||||
bottomDivider={false}
|
||||
onPress={setFormatP2shP2wsh}
|
||||
title={`${loc.multisig.wrapped_segwit_title} (${MultisigHDWallet.FORMAT_P2SH_P2WSH})`}
|
||||
checkmark={isP2shP2wsh()}
|
||||
containerStyle={[styles.borderRadius6, styles.item, isP2shP2wsh() ? stylesHook.selectedItem : stylesHook.deSelectedItem]}
|
||||
/>
|
||||
<ListItem
|
||||
bottomDivider={false}
|
||||
onPress={setFormatP2sh}
|
||||
title={`${loc.multisig.legacy_title} (${MultisigHDWallet.FORMAT_P2SH})`}
|
||||
checkmark={isP2sh()}
|
||||
containerStyle={[styles.borderRadius6, styles.item, isP2sh() ? stylesHook.selectedItem : stylesHook.deSelectedItem]}
|
||||
/>
|
||||
</ScrollView>
|
||||
<View style={styles.column}>
|
||||
<TouchableOpacity accessibilityRole="button" disabled={n === 7} onPress={increaseN} style={styles.chevron}>
|
||||
<Icon name="chevron-up" size={22} type="font-awesome-5" color={n === 7 ? colors.buttonDisabledTextColor : '#007AFF'} />
|
||||
</TouchableOpacity>
|
||||
<Text style={[styles.textM, stylesHook.textHeader]}>{n}</Text>
|
||||
<TouchableOpacity accessibilityRole="button" onPress={decreaseN} disabled={n === m} style={styles.chevron} testID="DecreaseN">
|
||||
<Icon name="chevron-down" size={22} type="font-awesome-5" color={n === m ? colors.buttonDisabledTextColor : '#007AFF'} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<BlueSpacing20 />
|
||||
|
||||
<Text style={[styles.textHeader, stylesHook.textHeader]}>{loc.multisig.wallet_type}</Text>
|
||||
<BlueSpacing20 />
|
||||
<ListItem
|
||||
bottomDivider={false}
|
||||
onPress={setFormatP2wsh}
|
||||
title={`${loc.multisig.native_segwit_title} (${MultisigHDWallet.FORMAT_P2WSH})`}
|
||||
checkmark={isP2wsh()}
|
||||
containerStyle={[styles.borderRadius6, styles.item, isP2wsh() ? stylesHook.selectedItem : stylesHook.deSelectedItem]}
|
||||
/>
|
||||
<ListItem
|
||||
bottomDivider={false}
|
||||
onPress={setFormatP2shP2wsh}
|
||||
title={`${loc.multisig.wrapped_segwit_title} (${MultisigHDWallet.FORMAT_P2SH_P2WSH})`}
|
||||
checkmark={isP2shP2wsh()}
|
||||
containerStyle={[styles.borderRadius6, styles.item, isP2shP2wsh() ? stylesHook.selectedItem : stylesHook.deSelectedItem]}
|
||||
/>
|
||||
<ListItem
|
||||
bottomDivider={false}
|
||||
onPress={setFormatP2sh}
|
||||
title={`${loc.multisig.legacy_title} (${MultisigHDWallet.FORMAT_P2SH})`}
|
||||
checkmark={isP2sh()}
|
||||
containerStyle={[styles.borderRadius6, styles.item, isP2sh() ? stylesHook.selectedItem : stylesHook.deSelectedItem]}
|
||||
/>
|
||||
</BottomModal>
|
||||
);
|
||||
};
|
||||
|
||||
const showAdvancedOptionsModal = () => {
|
||||
setIsModalVisible(true);
|
||||
bottomModalRef.current.present();
|
||||
};
|
||||
|
||||
const getCurrentlySelectedFormat = code => {
|
||||
|
@ -255,13 +242,7 @@ const styles = StyleSheet.create({
|
|||
flex: 0.8,
|
||||
},
|
||||
modalContentShort: {
|
||||
paddingHorizontal: 24,
|
||||
paddingTop: 24,
|
||||
justifyContent: 'center',
|
||||
borderTopLeftRadius: 16,
|
||||
borderTopRightRadius: 16,
|
||||
borderColor: 'rgba(0, 0, 0, 0.1)',
|
||||
minHeight: 350,
|
||||
padding: 24,
|
||||
},
|
||||
borderRadius6: {
|
||||
borderRadius: 6,
|
||||
|
|
|
@ -6,7 +6,6 @@ import {
|
|||
I18nManager,
|
||||
InteractionManager,
|
||||
Keyboard,
|
||||
KeyboardAvoidingView,
|
||||
LayoutAnimation,
|
||||
Platform,
|
||||
StyleSheet,
|
||||
|
@ -54,9 +53,9 @@ const WalletsAddMultisigStep2 = () => {
|
|||
|
||||
const [cosigners, setCosigners] = useState([]); // array of cosigners user provided. if format [cosigner, fp, path]
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isMnemonicsModalVisible, setIsMnemonicsModalVisible] = useState(false);
|
||||
const [isProvideMnemonicsModalVisible, setIsProvideMnemonicsModalVisible] = useState(false);
|
||||
const [isRenderCosignersXpubModalVisible, setIsRenderCosignersXpubModalVisible] = useState(false);
|
||||
const mnemonicsModalRef = useRef(null);
|
||||
const provideMnemonicsModalRef = useRef(null);
|
||||
const renderCosignersXpubModalRef = useRef(null);
|
||||
const [cosignerXpub, setCosignerXpub] = useState(''); // string used in exportCosigner()
|
||||
const [cosignerXpubURv2, setCosignerXpubURv2] = useState(''); // string displayed in renderCosignersXpubModal()
|
||||
const [cosignerXpubFilename, setCosignerXpubFilename] = useState('bw-cosigner.bwcosigner');
|
||||
|
@ -82,7 +81,7 @@ const WalletsAddMultisigStep2 = () => {
|
|||
(async function () {
|
||||
if (await confirm(loc.multisig.shared_key_detected, loc.multisig.shared_key_detected_question)) {
|
||||
setImportText(currentSharedCosigner);
|
||||
setIsProvideMnemonicsModalVisible(true);
|
||||
provideMnemonicsModalRef.current.present();
|
||||
setSharedCosigner('');
|
||||
}
|
||||
})();
|
||||
|
@ -184,8 +183,7 @@ const WalletsAddMultisigStep2 = () => {
|
|||
setCosigners(cosignersCopy);
|
||||
setVaultKeyData({ keyIndex: cosignersCopy.length, seed: w.getSecret(), xpub: w.getXpub(), isLoading: false });
|
||||
setIsLoading(true);
|
||||
setIsMnemonicsModalVisible(true);
|
||||
|
||||
mnemonicsModalRef.current.present();
|
||||
setTimeout(() => {
|
||||
// filling cache
|
||||
setXpubCacheForMnemonics(w.getSecret());
|
||||
|
@ -219,7 +217,7 @@ const WalletsAddMultisigStep2 = () => {
|
|||
setCosignerXpub(MultisigCosigner.exportToJson(cosigner[1], cosigner[0], cosigner[2]));
|
||||
setCosignerXpubURv2(encodeUR(MultisigCosigner.exportToJson(cosigner[1], cosigner[0], cosigner[2]))[0]);
|
||||
setCosignerXpubFilename('bw-cosigner-' + cosigner[1] + '.bwcosigner');
|
||||
setIsRenderCosignersXpubModalVisible(true);
|
||||
renderCosignersXpubModalRef.current.present();
|
||||
} else {
|
||||
const path = getPath();
|
||||
|
||||
|
@ -228,7 +226,7 @@ const WalletsAddMultisigStep2 = () => {
|
|||
setCosignerXpub(MultisigCosigner.exportToJson(fp, xpub, path));
|
||||
setCosignerXpubURv2(encodeUR(MultisigCosigner.exportToJson(fp, xpub, path))[0]);
|
||||
setCosignerXpubFilename('bw-cosigner-' + fp + '.bwcosigner');
|
||||
setIsRenderCosignersXpubModalVisible(true);
|
||||
renderCosignersXpubModalRef.current.present();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -255,12 +253,12 @@ const WalletsAddMultisigStep2 = () => {
|
|||
};
|
||||
|
||||
const iHaveMnemonics = () => {
|
||||
setIsProvideMnemonicsModalVisible(true);
|
||||
provideMnemonicsModalRef.current.present();
|
||||
};
|
||||
|
||||
const tryUsingXpub = async (xpub, fp, path) => {
|
||||
if (!MultisigHDWallet.isXpubForMultisig(xpub)) {
|
||||
setIsProvideMnemonicsModalVisible(false);
|
||||
provideMnemonicsModalRef.current.dismiss();
|
||||
setIsLoading(false);
|
||||
setImportText('');
|
||||
setAskPassphrase(false);
|
||||
|
@ -294,7 +292,7 @@ const WalletsAddMultisigStep2 = () => {
|
|||
}
|
||||
}
|
||||
|
||||
setIsProvideMnemonicsModalVisible(false);
|
||||
provideMnemonicsModalRef.current.dismiss();
|
||||
setIsLoading(false);
|
||||
setImportText('');
|
||||
setAskPassphrase(false);
|
||||
|
@ -350,7 +348,7 @@ const WalletsAddMultisigStep2 = () => {
|
|||
if (Platform.OS !== 'android') LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
|
||||
setCosigners(cosignersCopy);
|
||||
|
||||
setIsProvideMnemonicsModalVisible(false);
|
||||
provideMnemonicsModalRef.current.dismiss();
|
||||
setIsLoading(false);
|
||||
setImportText('');
|
||||
setAskPassphrase(false);
|
||||
|
@ -379,7 +377,7 @@ const WalletsAddMultisigStep2 = () => {
|
|||
if (ret.data.toUpperCase().startsWith('UR')) {
|
||||
presentAlert({ message: 'BC-UR not decoded. This should never happen' });
|
||||
} else if (isValidMnemonicSeed(ret.data)) {
|
||||
setIsProvideMnemonicsModalVisible(true);
|
||||
provideMnemonicsModalRef.current.present();
|
||||
setImportText(ret.data);
|
||||
} else {
|
||||
if (MultisigHDWallet.isXpubValid(ret.data) && !MultisigHDWallet.isXpubForMultisig(ret.data)) {
|
||||
|
@ -390,7 +388,7 @@ const WalletsAddMultisigStep2 = () => {
|
|||
}
|
||||
let cosigner = new MultisigCosigner(ret.data);
|
||||
if (!cosigner.isValid()) return presentAlert({ message: loc.multisig.invalid_cosigner });
|
||||
setIsProvideMnemonicsModalVisible(false);
|
||||
provideMnemonicsModalRef.current.dismiss();
|
||||
if (cosigner.howManyCosignersWeHave() > 1) {
|
||||
// lets look for the correct cosigner. thats probably gona be the one with specific corresponding path,
|
||||
// for example m/48'/0'/0'/2' if user chose to setup native segwit in BW
|
||||
|
@ -459,7 +457,6 @@ const WalletsAddMultisigStep2 = () => {
|
|||
};
|
||||
|
||||
const scanOrOpenFile = () => {
|
||||
setIsProvideMnemonicsModalVisible(false);
|
||||
InteractionManager.runAfterInteractions(async () => {
|
||||
const scanned = await scanQrHelper(name, true);
|
||||
onBarScanned({ data: scanned });
|
||||
|
@ -562,7 +559,7 @@ const WalletsAddMultisigStep2 = () => {
|
|||
|
||||
const renderMnemonicsModal = () => {
|
||||
return (
|
||||
<BottomModal isVisible={isMnemonicsModalVisible} onClose={Keyboard.dismiss}>
|
||||
<BottomModal onClose={Keyboard.dismiss} ref={mnemonicsModalRef} isGrabberVisible={false}>
|
||||
<View style={[styles.newKeyModalContent, stylesHook.modalContent]}>
|
||||
<View style={styles.itemKeyUnprovidedWrapper}>
|
||||
<View style={[styles.vaultKeyCircleSuccess, stylesHook.vaultKeyCircleSuccess]}>
|
||||
|
@ -581,7 +578,7 @@ const WalletsAddMultisigStep2 = () => {
|
|||
<BlueSpacing10 />
|
||||
<View style={styles.secretContainer}>{renderSecret(vaultKeyData.seed.split(' '))}</View>
|
||||
<BlueSpacing20 />
|
||||
{isLoading ? <ActivityIndicator /> : <Button title={loc.send.success_done} onPress={() => setIsMnemonicsModalVisible(false)} />}
|
||||
{isLoading ? <ActivityIndicator /> : <Button title={loc.send.success_done} onPress={() => mnemonicsModalRef.current.dismiss()} />}
|
||||
</View>
|
||||
</BottomModal>
|
||||
);
|
||||
|
@ -589,48 +586,47 @@ const WalletsAddMultisigStep2 = () => {
|
|||
|
||||
const hideProvideMnemonicsModal = () => {
|
||||
Keyboard.dismiss();
|
||||
setIsProvideMnemonicsModalVisible(false);
|
||||
provideMnemonicsModalRef.current.dismiss();
|
||||
|
||||
setImportText('');
|
||||
setAskPassphrase(false);
|
||||
};
|
||||
|
||||
const renderProvideMnemonicsModal = () => {
|
||||
return (
|
||||
<BottomModal isVisible={isProvideMnemonicsModalVisible} onClose={hideProvideMnemonicsModal}>
|
||||
<KeyboardAvoidingView enabled={!Platform.isPad} behavior={Platform.OS === 'ios' ? 'position' : null}>
|
||||
<View style={[styles.modalContent, stylesHook.modalContent]}>
|
||||
<BlueTextCentered>{loc.multisig.type_your_mnemonics}</BlueTextCentered>
|
||||
<BlueSpacing20 />
|
||||
<BlueFormMultiInput value={importText} onChangeText={setImportText} />
|
||||
{isAdvancedModeEnabled && (
|
||||
<>
|
||||
<BlueSpacing10 />
|
||||
<View style={styles.row}>
|
||||
<BlueText>{loc.wallets.import_passphrase}</BlueText>
|
||||
<Switch testID="AskPassphrase" value={askPassphrase} onValueChange={setAskPassphrase} />
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
<BlueSpacing20 />
|
||||
{isLoading ? (
|
||||
<ActivityIndicator />
|
||||
) : (
|
||||
<Button
|
||||
testID="DoImportKeyButton"
|
||||
disabled={importText.trim().length === 0}
|
||||
title={loc.wallets.import_do_import}
|
||||
onPress={useMnemonicPhrase}
|
||||
/>
|
||||
)}
|
||||
<BlueButtonLink
|
||||
testID="ScanOrOpenFile"
|
||||
ref={openScannerButton}
|
||||
disabled={isLoading}
|
||||
onPress={scanOrOpenFile}
|
||||
title={loc.wallets.import_scan_qr}
|
||||
<BottomModal onClose={hideProvideMnemonicsModal} ref={provideMnemonicsModalRef} isGrabberVisible={false}>
|
||||
<View style={[styles.modalContent, stylesHook.modalContent]}>
|
||||
<BlueTextCentered>{loc.multisig.type_your_mnemonics}</BlueTextCentered>
|
||||
<BlueSpacing20 />
|
||||
<BlueFormMultiInput value={importText} onChangeText={setImportText} />
|
||||
{isAdvancedModeEnabled && (
|
||||
<>
|
||||
<BlueSpacing10 />
|
||||
<View style={styles.row}>
|
||||
<BlueText>{loc.wallets.import_passphrase}</BlueText>
|
||||
<Switch testID="AskPassphrase" value={askPassphrase} onValueChange={setAskPassphrase} />
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
<BlueSpacing20 />
|
||||
{isLoading ? (
|
||||
<ActivityIndicator />
|
||||
) : (
|
||||
<Button
|
||||
testID="DoImportKeyButton"
|
||||
disabled={importText.trim().length === 0}
|
||||
title={loc.wallets.import_do_import}
|
||||
onPress={useMnemonicPhrase}
|
||||
/>
|
||||
</View>
|
||||
</KeyboardAvoidingView>
|
||||
)}
|
||||
<BlueButtonLink
|
||||
testID="ScanOrOpenFile"
|
||||
ref={openScannerButton}
|
||||
disabled={isLoading}
|
||||
onPress={scanOrOpenFile}
|
||||
title={loc.wallets.import_scan_qr}
|
||||
/>
|
||||
</View>
|
||||
</BottomModal>
|
||||
);
|
||||
};
|
||||
|
@ -645,37 +641,35 @@ const WalletsAddMultisigStep2 = () => {
|
|||
|
||||
const hideCosignersXpubModal = () => {
|
||||
Keyboard.dismiss();
|
||||
setIsRenderCosignersXpubModalVisible(false);
|
||||
renderCosignersXpubModalRef.current.dismiss();
|
||||
};
|
||||
|
||||
const renderCosignersXpubModal = () => {
|
||||
return (
|
||||
<BottomModal isVisible={isRenderCosignersXpubModalVisible} onClose={hideCosignersXpubModal}>
|
||||
<KeyboardAvoidingView enabled={!Platform.isPad} behavior={Platform.OS === 'ios' ? 'position' : null}>
|
||||
<View style={[styles.modalContent, stylesHook.modalContent, styles.alignItemsCenter]}>
|
||||
<Text style={[styles.headerText, stylesHook.textDestination]}>
|
||||
{loc.multisig.this_is_cosigners_xpub} {Platform.OS === 'ios' ? loc.multisig.this_is_cosigners_xpub_airdrop : ''}
|
||||
</Text>
|
||||
<BlueSpacing20 />
|
||||
<QRCodeComponent value={cosignerXpubURv2} size={260} />
|
||||
<BlueSpacing20 />
|
||||
<View style={styles.squareButtonWrapper}>
|
||||
{isLoading ? (
|
||||
<ActivityIndicator />
|
||||
) : (
|
||||
<SaveFileButton
|
||||
style={[styles.exportButton, stylesHook.exportButton]}
|
||||
fileName={cosignerXpubFilename}
|
||||
fileContent={cosignerXpub}
|
||||
beforeOnPress={exportCosignerBeforeOnPress}
|
||||
afterOnPress={exportCosignerAfterOnPress}
|
||||
>
|
||||
<SquareButton title={loc.multisig.share} />
|
||||
</SaveFileButton>
|
||||
)}
|
||||
</View>
|
||||
<BottomModal onClose={hideCosignersXpubModal} ref={renderCosignersXpubModalRef} isGrabberVisible={false}>
|
||||
<View style={[styles.modalContent, stylesHook.modalContent, styles.alignItemsCenter]}>
|
||||
<Text style={[styles.headerText, stylesHook.textDestination]}>
|
||||
{loc.multisig.this_is_cosigners_xpub} {Platform.OS === 'ios' ? loc.multisig.this_is_cosigners_xpub_airdrop : ''}
|
||||
</Text>
|
||||
<BlueSpacing20 />
|
||||
<QRCodeComponent value={cosignerXpubURv2} size={260} />
|
||||
<BlueSpacing20 />
|
||||
<View style={styles.squareButtonWrapper}>
|
||||
{isLoading ? (
|
||||
<ActivityIndicator />
|
||||
) : (
|
||||
<SaveFileButton
|
||||
style={[styles.exportButton, stylesHook.exportButton]}
|
||||
fileName={cosignerXpubFilename}
|
||||
fileContent={cosignerXpub}
|
||||
beforeOnPress={exportCosignerBeforeOnPress}
|
||||
afterOnPress={exportCosignerAfterOnPress}
|
||||
>
|
||||
<SquareButton title={loc.multisig.share} />
|
||||
</SaveFileButton>
|
||||
)}
|
||||
</View>
|
||||
</KeyboardAvoidingView>
|
||||
</View>
|
||||
</BottomModal>
|
||||
);
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue