Merge pull request #8362 from BlueWallet/enhance-address
This commit is contained in:
commit
3e8906e31b
@ -1,16 +1,131 @@
|
||||
import Clipboard from '@react-native-clipboard/clipboard';
|
||||
import React, { forwardRef, useEffect, useState } from 'react';
|
||||
import { Animated, StyleSheet, TouchableOpacity, View } from 'react-native';
|
||||
import { Animated, StyleSheet, TouchableOpacity, View, Text } from 'react-native';
|
||||
|
||||
import triggerHapticFeedback, { HapticFeedbackTypes } from '../blue_modules/hapticFeedback';
|
||||
import loc from '../loc';
|
||||
import { useTheme } from './themes';
|
||||
|
||||
type CopyTextToClipboardProps = {
|
||||
text: string;
|
||||
truncated?: boolean;
|
||||
isAddress?: boolean;
|
||||
};
|
||||
|
||||
const styleCopyTextToClipboard = StyleSheet.create({
|
||||
const CopyTextToClipboard = forwardRef<React.ElementRef<typeof TouchableOpacity>, CopyTextToClipboardProps>(
|
||||
({ text, truncated, isAddress }, ref) => {
|
||||
const [hasTappedText, setHasTappedText] = useState(false);
|
||||
const [address, setAddress] = useState(text);
|
||||
const { colors } = useTheme();
|
||||
|
||||
const stylesHook = StyleSheet.create({
|
||||
addressSection: {
|
||||
color: colors.newBlue,
|
||||
fontWeight: '500',
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!hasTappedText) {
|
||||
setAddress(text);
|
||||
}
|
||||
}, [text, hasTappedText]);
|
||||
|
||||
const copyToClipboard = () => {
|
||||
setHasTappedText(true);
|
||||
Clipboard.setString(text);
|
||||
triggerHapticFeedback(HapticFeedbackTypes.Selection);
|
||||
setAddress(loc.wallets.xpub_copiedToClipboard);
|
||||
setTimeout(() => {
|
||||
setHasTappedText(false);
|
||||
setAddress(text);
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const renderHighlightedAddress = () => {
|
||||
if (address.includes(loc.wallets.xpub_copiedToClipboard)) {
|
||||
return (
|
||||
<Animated.Text
|
||||
style={styles.address}
|
||||
{...(truncated ? { numberOfLines: 1, ellipsizeMode: 'middle' } : { numberOfLines: 0 })}
|
||||
testID="AddressValue"
|
||||
>
|
||||
{address}
|
||||
</Animated.Text>
|
||||
);
|
||||
}
|
||||
if (address.toLocaleLowerCase().startsWith('bitcoin:')) {
|
||||
const prefix = address.slice(0, 8); // "bitcoin:"
|
||||
const afterPrefix = address.slice(8);
|
||||
|
||||
const qIndex = afterPrefix.indexOf('?');
|
||||
|
||||
const addrPart = qIndex === -1 ? afterPrefix : afterPrefix.slice(0, qIndex);
|
||||
|
||||
const queryPart = qIndex === -1 ? '' : afterPrefix.slice(qIndex);
|
||||
|
||||
const start = addrPart.slice(0, 6);
|
||||
const middle = addrPart.slice(6, -6);
|
||||
const end = addrPart.slice(-6);
|
||||
|
||||
return (
|
||||
<Animated.Text style={styles.address} numberOfLines={truncated ? 1 : 0} ellipsizeMode="middle" testID="AddressValue">
|
||||
<Text>{prefix}</Text>
|
||||
|
||||
<Text style={stylesHook.addressSection}>{start}</Text>
|
||||
|
||||
<Text>{middle}</Text>
|
||||
|
||||
<Text style={stylesHook.addressSection}>{end}</Text>
|
||||
|
||||
<Text>{queryPart}</Text>
|
||||
</Animated.Text>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Animated.Text
|
||||
style={styles.address}
|
||||
{...(truncated ? { numberOfLines: 1, ellipsizeMode: 'middle' } : { numberOfLines: 0 })}
|
||||
testID="AddressValue"
|
||||
>
|
||||
<Text style={stylesHook.addressSection}>{address.slice(0, 6)}</Text>
|
||||
<Text>{address.slice(6, -6)}</Text>
|
||||
<Text style={stylesHook.addressSection}>{address.slice(-6)}</Text>
|
||||
</Animated.Text>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<TouchableOpacity
|
||||
ref={ref}
|
||||
accessibilityRole="button"
|
||||
onPress={copyToClipboard}
|
||||
disabled={hasTappedText}
|
||||
testID="CopyTextToClipboard"
|
||||
>
|
||||
{isAddress ? (
|
||||
<>{renderHighlightedAddress()}</>
|
||||
) : (
|
||||
<Animated.Text
|
||||
style={styles.address}
|
||||
{...(truncated ? { numberOfLines: 1, ellipsizeMode: 'middle' } : { numberOfLines: 0 })}
|
||||
testID="AddressValue"
|
||||
>
|
||||
{address}
|
||||
</Animated.Text>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
export default CopyTextToClipboard;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: { justifyContent: 'center', alignItems: 'center', paddingHorizontal: 16 },
|
||||
address: {
|
||||
marginVertical: 32,
|
||||
fontSize: 15,
|
||||
@ -18,51 +133,3 @@ const styleCopyTextToClipboard = StyleSheet.create({
|
||||
textAlign: 'center',
|
||||
},
|
||||
});
|
||||
|
||||
const CopyTextToClipboard = forwardRef<React.ElementRef<typeof TouchableOpacity>, CopyTextToClipboardProps>(({ text, truncated }, ref) => {
|
||||
const [hasTappedText, setHasTappedText] = useState(false);
|
||||
const [address, setAddress] = useState(text);
|
||||
|
||||
useEffect(() => {
|
||||
if (!hasTappedText) {
|
||||
setAddress(text);
|
||||
}
|
||||
}, [text, hasTappedText]);
|
||||
|
||||
const copyToClipboard = () => {
|
||||
setHasTappedText(true);
|
||||
Clipboard.setString(text);
|
||||
triggerHapticFeedback(HapticFeedbackTypes.Selection);
|
||||
setAddress(loc.wallets.xpub_copiedToClipboard); // Adjust according to your localization logic
|
||||
setTimeout(() => {
|
||||
setHasTappedText(false);
|
||||
setAddress(text);
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<TouchableOpacity
|
||||
ref={ref}
|
||||
accessibilityRole="button"
|
||||
onPress={copyToClipboard}
|
||||
disabled={hasTappedText}
|
||||
testID="CopyTextToClipboard"
|
||||
>
|
||||
<Animated.Text
|
||||
style={styleCopyTextToClipboard.address}
|
||||
{...(truncated ? { numberOfLines: 1, ellipsizeMode: 'middle' } : { numberOfLines: 0 })}
|
||||
testID="AddressValue"
|
||||
>
|
||||
{address}
|
||||
</Animated.Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
});
|
||||
|
||||
export default CopyTextToClipboard;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: { justifyContent: 'center', alignItems: 'center', paddingHorizontal: 16 },
|
||||
});
|
||||
|
||||
@ -401,7 +401,7 @@ const ReceiveDetails = () => {
|
||||
<View style={styles.qrCodeContainer}>
|
||||
<QRCodeComponent value={bip21encoded} size={qrCodeSize} />
|
||||
</View>
|
||||
<CopyTextToClipboard text={isCustom ? bip21encoded : address} />
|
||||
<CopyTextToClipboard text={isCustom ? bip21encoded : address} isAddress={true} />
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
|
||||
@ -111,6 +111,9 @@ const Confirm: React.FC = () => {
|
||||
payjoinWrapper: {
|
||||
backgroundColor: colors.buttonDisabledBackgroundColor,
|
||||
},
|
||||
addressSection: {
|
||||
color: colors.newBlue,
|
||||
},
|
||||
});
|
||||
|
||||
const HeaderRightButton = useMemo(
|
||||
@ -290,7 +293,9 @@ const Confirm: React.FC = () => {
|
||||
<BlueCard>
|
||||
<Text style={[styles.transactionDetailsTitle, stylesHook.transactionDetailsTitle]}>{loc.send.create_to}</Text>
|
||||
<Text testID="TransactionAddress" style={[styles.transactionDetailsSubtitle, stylesHook.transactionDetailsSubtitle]}>
|
||||
{item.address}
|
||||
<Text style={stylesHook.addressSection}>{item?.address?.slice(0, 6)}</Text>
|
||||
<Text>{item?.address?.slice(6, -6)}</Text>
|
||||
<Text style={stylesHook.addressSection}>{item?.address?.slice(-6)}</Text>
|
||||
</Text>
|
||||
{contact ? <Text style={[styles.transactionDetailsSubtitle, stylesHook.transactionDetailsSubtitle]}>[{contact}]</Text> : null}
|
||||
</BlueCard>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user