mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2024-11-19 01:40:12 +01:00
Merge pull request #7165 from BlueWallet/isit
REF: IsItMyAddress to TSX
This commit is contained in:
commit
a0cb72a74f
@ -14,7 +14,7 @@ import LnurlAuth from '../screen/lnd/lnurlAuth';
|
||||
import LnurlPay from '../screen/lnd/lnurlPay';
|
||||
import LnurlPaySuccess from '../screen/lnd/lnurlPaySuccess';
|
||||
import Broadcast from '../screen/send/Broadcast';
|
||||
import IsItMyAddress from '../screen/send/isItMyAddress';
|
||||
import IsItMyAddress from '../screen/settings/IsItMyAddress';
|
||||
import Success from '../screen/send/success';
|
||||
import CPFP from '../screen/transactions/CPFP';
|
||||
import TransactionDetails from '../screen/transactions/TransactionDetails';
|
||||
@ -199,6 +199,7 @@ const DetailViewStackScreensStack = () => {
|
||||
<DetailViewStack.Screen
|
||||
name="IsItMyAddress"
|
||||
component={IsItMyAddress}
|
||||
initialParams={{ address: undefined }}
|
||||
options={navigationStyle({ title: loc.is_it_my_address.title })(theme)}
|
||||
/>
|
||||
<DetailViewStack.Screen
|
||||
|
@ -16,7 +16,7 @@ export type DetailViewStackParamList = {
|
||||
LNDViewAdditionalInvoiceInformation: { invoiceId: string };
|
||||
LNDViewAdditionalInvoicePreImage: { invoiceId: string };
|
||||
Broadcast: { scannedData?: string };
|
||||
IsItMyAddress: undefined;
|
||||
IsItMyAddress: { address?: string };
|
||||
GenerateWord: undefined;
|
||||
LnurlPay: undefined;
|
||||
LnurlPaySuccess: {
|
||||
@ -77,7 +77,7 @@ export type DetailViewStackParamList = {
|
||||
ReceiveDetailsRoot: {
|
||||
screen: 'ReceiveDetails';
|
||||
params: {
|
||||
walletID: string;
|
||||
walletID?: string;
|
||||
address: string;
|
||||
};
|
||||
};
|
||||
|
@ -1,151 +0,0 @@
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { useRoute } from '@react-navigation/native';
|
||||
import { Keyboard, StyleSheet, TextInput, View } from 'react-native';
|
||||
import { BlueButtonLink, BlueCard, BlueSpacing10, BlueSpacing20, BlueText } from '../../BlueComponents';
|
||||
import Button from '../../components/Button';
|
||||
import SafeArea from '../../components/SafeArea';
|
||||
import { useTheme } from '../../components/themes';
|
||||
import { scanQrHelper } from '../../helpers/scan-qr';
|
||||
import loc from '../../loc';
|
||||
import { useStorage } from '../../hooks/context/useStorage';
|
||||
import { useExtendedNavigation } from '../../hooks/useExtendedNavigation';
|
||||
|
||||
const IsItMyAddress = () => {
|
||||
/** @type {AbstractWallet[]} */
|
||||
const { wallets } = useStorage();
|
||||
const { navigate } = useExtendedNavigation();
|
||||
const { name } = useRoute();
|
||||
const { colors } = useTheme();
|
||||
const scanButtonRef = useRef();
|
||||
|
||||
const [address, setAddress] = useState('');
|
||||
const [result, setResult] = useState('');
|
||||
const [resultCleanAddress, setResultCleanAddress] = useState();
|
||||
|
||||
const stylesHooks = StyleSheet.create({
|
||||
input: {
|
||||
borderColor: colors.formBorder,
|
||||
borderBottomColor: colors.formBorder,
|
||||
backgroundColor: colors.inputBackgroundColor,
|
||||
},
|
||||
});
|
||||
|
||||
const handleUpdateAddress = nextValue => setAddress(nextValue.trim());
|
||||
|
||||
const checkAddress = () => {
|
||||
Keyboard.dismiss();
|
||||
const cleanAddress = address.replace('bitcoin:', '').replace('BITCOIN:', '').replace('bitcoin=', '').split('?')[0];
|
||||
const _result = [];
|
||||
for (const w of wallets) {
|
||||
if (w.weOwnAddress(cleanAddress)) {
|
||||
setResultCleanAddress(cleanAddress);
|
||||
_result.push(loc.formatString(loc.is_it_my_address.owns, { label: w.getLabel(), address: cleanAddress }));
|
||||
}
|
||||
}
|
||||
|
||||
if (_result.length === 0) {
|
||||
setResult(_result.push(loc.is_it_my_address.no_wallet_owns_address));
|
||||
setResultCleanAddress();
|
||||
}
|
||||
|
||||
setResult(_result.join('\n\n'));
|
||||
};
|
||||
|
||||
const onBarScanned = value => {
|
||||
setAddress(value);
|
||||
setResultCleanAddress(value);
|
||||
};
|
||||
|
||||
const importScan = () => {
|
||||
scanQrHelper(name, true, onBarScanned);
|
||||
};
|
||||
|
||||
const clearAddressInput = () => {
|
||||
setAddress('');
|
||||
setResult();
|
||||
setResultCleanAddress();
|
||||
};
|
||||
|
||||
const viewQRCode = () => {
|
||||
navigate('ReceiveDetailsRoot', {
|
||||
screen: 'ReceiveDetails',
|
||||
params: {
|
||||
address: resultCleanAddress,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<SafeArea style={styles.blueArea}>
|
||||
<View style={styles.wrapper}>
|
||||
<BlueCard style={styles.mainCard}>
|
||||
<View style={[styles.input, stylesHooks.input]}>
|
||||
<TextInput
|
||||
style={styles.text}
|
||||
maxHeight={100}
|
||||
minHeight={100}
|
||||
maxWidth="100%"
|
||||
minWidth="100%"
|
||||
multiline
|
||||
editable
|
||||
placeholder={loc.is_it_my_address.enter_address}
|
||||
placeholderTextColor="#81868e"
|
||||
value={address}
|
||||
onChangeText={handleUpdateAddress}
|
||||
testID="AddressInput"
|
||||
/>
|
||||
</View>
|
||||
|
||||
<BlueSpacing10 />
|
||||
<BlueButtonLink ref={scanButtonRef} title={loc.wallets.import_scan_qr} onPress={importScan} />
|
||||
<BlueSpacing10 />
|
||||
<Button title={loc.send.input_clear} onPress={clearAddressInput} />
|
||||
<BlueSpacing20 />
|
||||
{resultCleanAddress && (
|
||||
<>
|
||||
<Button title={loc.is_it_my_address.view_qrcode} onPress={viewQRCode} />
|
||||
<BlueSpacing20 />
|
||||
</>
|
||||
)}
|
||||
<Button
|
||||
disabled={address.trim().length === 0}
|
||||
title={loc.is_it_my_address.check_address}
|
||||
onPress={checkAddress}
|
||||
testID="CheckAddress"
|
||||
/>
|
||||
<BlueSpacing20 />
|
||||
<BlueText testID="Result">{result}</BlueText>
|
||||
</BlueCard>
|
||||
</View>
|
||||
</SafeArea>
|
||||
);
|
||||
};
|
||||
|
||||
export default IsItMyAddress;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
wrapper: {
|
||||
marginTop: 16,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-start',
|
||||
},
|
||||
mainCard: {
|
||||
padding: 0,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-start',
|
||||
},
|
||||
input: {
|
||||
flexDirection: 'row',
|
||||
borderWidth: 1,
|
||||
borderBottomWidth: 0.5,
|
||||
alignItems: 'center',
|
||||
borderRadius: 4,
|
||||
},
|
||||
text: {
|
||||
padding: 8,
|
||||
minHeight: 33,
|
||||
color: '#81868e',
|
||||
},
|
||||
});
|
282
screen/settings/IsItMyAddress.tsx
Normal file
282
screen/settings/IsItMyAddress.tsx
Normal file
@ -0,0 +1,282 @@
|
||||
import React, { useRef, useState, useEffect } from 'react';
|
||||
import { useRoute, useNavigation, RouteProp } from '@react-navigation/native';
|
||||
import { Keyboard, StyleSheet, TextInput, View, ScrollView, TouchableOpacity, Text } from 'react-native';
|
||||
import { BlueButtonLink, BlueCard, BlueSpacing10, BlueSpacing20, BlueSpacing40, BlueText } from '../../BlueComponents';
|
||||
import Button from '../../components/Button';
|
||||
import { useTheme } from '../../components/themes';
|
||||
import { scanQrHelper } from '../../helpers/scan-qr';
|
||||
import loc from '../../loc';
|
||||
import { useStorage } from '../../hooks/context/useStorage';
|
||||
import { TWallet } from '../../class/wallets/types';
|
||||
import { WalletCarouselItem } from '../../components/WalletsCarousel';
|
||||
import { DetailViewStackParamList } from '../../navigation/DetailViewStackParamList';
|
||||
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
import Icon from 'react-native-vector-icons/MaterialIcons';
|
||||
import { Divider } from '@rneui/themed';
|
||||
import triggerHapticFeedback, { HapticFeedbackTypes } from '../../blue_modules/hapticFeedback';
|
||||
import presentAlert from '../../components/Alert';
|
||||
|
||||
type RouteProps = RouteProp<DetailViewStackParamList, 'IsItMyAddress'>;
|
||||
type NavigationProp = NativeStackNavigationProp<DetailViewStackParamList, 'IsItMyAddress'>;
|
||||
|
||||
const IsItMyAddress: React.FC = () => {
|
||||
const { wallets } = useStorage();
|
||||
const navigation = useNavigation<NavigationProp>();
|
||||
const route = useRoute<RouteProps>();
|
||||
const { colors } = useTheme();
|
||||
const scanButtonRef = useRef<any>();
|
||||
const scrollViewRef = useRef<ScrollView>(null);
|
||||
const firstWalletRef = useRef<View>(null);
|
||||
|
||||
const [address, setAddress] = useState<string>('');
|
||||
const [matchingWallets, setMatchingWallets] = useState<TWallet[] | undefined>();
|
||||
const [resultCleanAddress, setResultCleanAddress] = useState<string | undefined>();
|
||||
|
||||
const stylesHooks = StyleSheet.create({
|
||||
input: {
|
||||
borderColor: colors.formBorder,
|
||||
borderBottomColor: colors.formBorder,
|
||||
backgroundColor: colors.inputBackgroundColor,
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (route.params?.address && route.params.address !== address) {
|
||||
setAddress(route.params.address);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [route.params?.address]);
|
||||
|
||||
useEffect(() => {
|
||||
const currentAddress = route.params?.address;
|
||||
if (currentAddress !== address) {
|
||||
navigation.setParams({ address });
|
||||
}
|
||||
}, [address, navigation, route.params?.address]);
|
||||
|
||||
const handleUpdateAddress = (nextValue: string) => setAddress(nextValue);
|
||||
|
||||
const clearAddressInput = () => {
|
||||
setAddress('');
|
||||
setResultCleanAddress(undefined);
|
||||
setMatchingWallets(undefined);
|
||||
};
|
||||
|
||||
const checkAddress = () => {
|
||||
Keyboard.dismiss();
|
||||
const cleanAddress = address.replace('bitcoin:', '').replace('BITCOIN:', '').replace('bitcoin=', '').split('?')[0];
|
||||
const matching: TWallet[] = [];
|
||||
|
||||
for (const w of wallets) {
|
||||
if (w.weOwnAddress(cleanAddress)) {
|
||||
matching.push(w);
|
||||
}
|
||||
}
|
||||
|
||||
if (matching.length > 0) {
|
||||
setMatchingWallets(matching);
|
||||
setResultCleanAddress(cleanAddress);
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationSuccess);
|
||||
} else {
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationError);
|
||||
presentAlert({
|
||||
message: loc.is_it_my_address.no_wallet_owns_address,
|
||||
buttons: [
|
||||
{
|
||||
text: loc.receive.reset,
|
||||
onPress: () => {
|
||||
clearAddressInput();
|
||||
},
|
||||
style: 'destructive',
|
||||
},
|
||||
{
|
||||
text: loc._.ok,
|
||||
onPress: () => {},
|
||||
style: 'cancel',
|
||||
},
|
||||
],
|
||||
options: { cancelable: true },
|
||||
});
|
||||
setMatchingWallets([]);
|
||||
setResultCleanAddress(undefined);
|
||||
}
|
||||
};
|
||||
|
||||
const onBarScanned = (value: string) => {
|
||||
const cleanAddress = value.replace(/^bitcoin(:|=)/i, '').split('?')[0];
|
||||
setAddress(value);
|
||||
setResultCleanAddress(cleanAddress);
|
||||
};
|
||||
|
||||
const importScan = async () => {
|
||||
const data = await scanQrHelper(route.name, true, undefined, true);
|
||||
if (data) {
|
||||
onBarScanned(data);
|
||||
}
|
||||
};
|
||||
|
||||
const viewQRCode = () => {
|
||||
if (!resultCleanAddress) return;
|
||||
navigation.navigate('ReceiveDetailsRoot', {
|
||||
screen: 'ReceiveDetails',
|
||||
params: {
|
||||
address: resultCleanAddress,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const isCheckAddressDisabled = address.trim().length === 0;
|
||||
|
||||
useEffect(() => {
|
||||
if (matchingWallets && matchingWallets.length > 0 && scrollViewRef.current && firstWalletRef.current) {
|
||||
firstWalletRef.current.measureLayout(scrollViewRef.current.getInnerViewNode(), (x, y) => {
|
||||
scrollViewRef.current?.scrollTo({ x: 0, y: y - 20, animated: true });
|
||||
});
|
||||
}
|
||||
}, [matchingWallets]);
|
||||
|
||||
const renderFormattedText = (text: string, values: { [key: string]: string }) => {
|
||||
const regex = /\{(\w+)\}/g;
|
||||
const parts = [];
|
||||
let lastIndex = 0;
|
||||
let match;
|
||||
let index = 0;
|
||||
|
||||
while ((match = regex.exec(text)) !== null) {
|
||||
if (match.index > lastIndex) {
|
||||
parts.push(<Text key={`text-${index++}`}>{text.substring(lastIndex, match.index)}</Text>);
|
||||
}
|
||||
const value = values[match[1]];
|
||||
if (value) {
|
||||
parts.push(
|
||||
<Text key={`bold-${index++}`} style={styles.boldText} selectable>
|
||||
{value}
|
||||
</Text>,
|
||||
);
|
||||
}
|
||||
lastIndex = regex.lastIndex;
|
||||
}
|
||||
if (lastIndex < text.length) {
|
||||
parts.push(<Text key={`text-${index++}`}>{text.substring(lastIndex)}</Text>);
|
||||
}
|
||||
return parts;
|
||||
};
|
||||
|
||||
return (
|
||||
<ScrollView
|
||||
ref={scrollViewRef}
|
||||
contentContainerStyle={styles.wrapper}
|
||||
automaticallyAdjustContentInsets
|
||||
automaticallyAdjustKeyboardInsets
|
||||
contentInsetAdjustmentBehavior="automatic"
|
||||
>
|
||||
<BlueCard style={styles.mainCard}>
|
||||
<View style={[styles.input, stylesHooks.input]}>
|
||||
<TextInput
|
||||
style={styles.textInput}
|
||||
multiline
|
||||
editable
|
||||
placeholder={loc.is_it_my_address.enter_address}
|
||||
placeholderTextColor={colors.placeholderTextColor}
|
||||
value={address}
|
||||
onChangeText={handleUpdateAddress}
|
||||
testID="AddressInput"
|
||||
/>
|
||||
{address.length > 0 && (
|
||||
<TouchableOpacity onPress={clearAddressInput} style={styles.clearButton}>
|
||||
<Icon name="close" size={20} color="#81868e" />
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</View>
|
||||
|
||||
<BlueSpacing10 />
|
||||
<BlueButtonLink ref={scanButtonRef} title={loc.wallets.import_scan_qr} onPress={importScan} />
|
||||
<BlueSpacing20 />
|
||||
{resultCleanAddress && (
|
||||
<>
|
||||
<Button title={loc.is_it_my_address.view_qrcode} onPress={viewQRCode} />
|
||||
<BlueSpacing20 />
|
||||
</>
|
||||
)}
|
||||
<Button disabled={isCheckAddressDisabled} title={loc.is_it_my_address.check_address} onPress={checkAddress} testID="CheckAddress" />
|
||||
<BlueSpacing40 />
|
||||
|
||||
{matchingWallets !== undefined && matchingWallets.length > 0 && (
|
||||
<>
|
||||
<Divider />
|
||||
<BlueSpacing40 />
|
||||
</>
|
||||
)}
|
||||
{matchingWallets !== undefined &&
|
||||
matchingWallets.length > 0 &&
|
||||
matchingWallets.map((wallet, index) => (
|
||||
<View key={wallet.getID()} ref={index === 0 ? firstWalletRef : undefined} style={styles.walletContainer}>
|
||||
<BlueText selectable style={styles.resultText}>
|
||||
{resultCleanAddress &&
|
||||
renderFormattedText(loc.is_it_my_address.owns, {
|
||||
label: wallet.getLabel(),
|
||||
address: resultCleanAddress,
|
||||
})}
|
||||
</BlueText>
|
||||
<BlueSpacing10 />
|
||||
<WalletCarouselItem
|
||||
item={wallet}
|
||||
onPress={item => {
|
||||
navigation.navigate('WalletTransactions', {
|
||||
walletID: item.getID(),
|
||||
walletType: item.type,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<BlueSpacing20 />
|
||||
</View>
|
||||
))}
|
||||
</BlueCard>
|
||||
</ScrollView>
|
||||
);
|
||||
};
|
||||
|
||||
export default IsItMyAddress;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
wrapper: {
|
||||
alignItems: 'center',
|
||||
},
|
||||
mainCard: {
|
||||
padding: 0,
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-start',
|
||||
width: '100%',
|
||||
},
|
||||
input: {
|
||||
flexDirection: 'row',
|
||||
borderWidth: 1,
|
||||
borderBottomWidth: 0.5,
|
||||
alignItems: 'center',
|
||||
borderRadius: 4,
|
||||
width: '100%',
|
||||
},
|
||||
textInput: {
|
||||
flex: 1,
|
||||
padding: 8,
|
||||
minHeight: 100,
|
||||
color: '#81868e',
|
||||
},
|
||||
clearButton: {
|
||||
padding: 8,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
boldText: {
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
resultText: {
|
||||
marginVertical: 10,
|
||||
textAlign: 'center',
|
||||
},
|
||||
walletContainer: {
|
||||
width: '100%',
|
||||
alignItems: 'center',
|
||||
},
|
||||
});
|
@ -153,7 +153,8 @@ describe('BlueWallet UI Tests - no wallets', () => {
|
||||
await element(by.id('IsItMyAddress')).tap();
|
||||
await element(by.id('AddressInput')).replaceText('bc1q063ctu6jhe5k4v8ka99qac8rcm2tzjjnuktyrl');
|
||||
await element(by.id('CheckAddress')).tap();
|
||||
await expect(element(by.id('Result'))).toHaveText('None of the available wallets own the provided address.');
|
||||
await expect(element(by.text('None of the available wallets own the provided address.'))).toBeVisible();
|
||||
await element(by.text('OK')).tap();
|
||||
await device.pressBack();
|
||||
await device.pressBack();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user