mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-02-22 23:08:07 +01:00
commit
47673a4ae0
4 changed files with 112 additions and 144 deletions
|
@ -7,24 +7,18 @@ import { GROUP_IO_BLUEWALLET } from '../blue_modules/currency';
|
||||||
import { BlueApp } from '../class';
|
import { BlueApp } from '../class';
|
||||||
import { HandOffComponentProps } from './types';
|
import { HandOffComponentProps } from './types';
|
||||||
|
|
||||||
const HandOffComponent: React.FC<HandOffComponentProps> = React.memo(
|
const HandOffComponent: React.FC<HandOffComponentProps> = props => {
|
||||||
props => {
|
const { isHandOffUseEnabled } = useSettings();
|
||||||
const { isHandOffUseEnabled } = useSettings();
|
if (!props || !props.type || !props.userInfo || Object.keys(props.userInfo).length === 0) {
|
||||||
|
console.debug('HandOffComponent: Missing required type or userInfo data');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const userInfo = JSON.stringify(props.userInfo);
|
||||||
|
console.debug(`HandOffComponent is rendering. Type: ${props.type}, UserInfo: ${userInfo}...`);
|
||||||
|
return isHandOffUseEnabled ? <Handoff {...props} /> : null;
|
||||||
|
};
|
||||||
|
|
||||||
if (!props || !props.type || !props.userInfo || Object.keys(props.userInfo).length === 0) {
|
const MemoizedHandOffComponent = React.memo(HandOffComponent);
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return isHandOffUseEnabled ? <Handoff {...props} /> : null;
|
|
||||||
},
|
|
||||||
(prevProps, nextProps) => {
|
|
||||||
return (
|
|
||||||
prevProps.type === nextProps.type &&
|
|
||||||
JSON.stringify(prevProps.userInfo) === JSON.stringify(nextProps.userInfo) &&
|
|
||||||
prevProps.title === nextProps.title
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
export const setIsHandOffUseEnabled = async (value: boolean) => {
|
export const setIsHandOffUseEnabled = async (value: boolean) => {
|
||||||
try {
|
try {
|
||||||
|
@ -50,4 +44,4 @@ export const getIsHandOffUseEnabled = async (): Promise<boolean> => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default HandOffComponent;
|
export default MemoizedHandOffComponent;
|
||||||
|
|
|
@ -40,9 +40,7 @@ const AddressItem = ({ item, balanceUnit, walletID, allowSignVerifyMessage }: Ad
|
||||||
borderBottomColor: colors.lightBorder,
|
borderBottomColor: colors.lightBorder,
|
||||||
backgroundColor: colors.elevated,
|
backgroundColor: colors.elevated,
|
||||||
},
|
},
|
||||||
list: {
|
|
||||||
color: colors.buttonTextColor,
|
|
||||||
},
|
|
||||||
index: {
|
index: {
|
||||||
color: colors.alternativeTextColor,
|
color: colors.alternativeTextColor,
|
||||||
},
|
},
|
||||||
|
@ -156,19 +154,23 @@ const AddressItem = ({ item, balanceUnit, walletID, allowSignVerifyMessage }: Ad
|
||||||
isButton
|
isButton
|
||||||
>
|
>
|
||||||
<ListItem key={item.key} containerStyle={stylesHook.container}>
|
<ListItem key={item.key} containerStyle={stylesHook.container}>
|
||||||
<ListItem.Content style={stylesHook.list}>
|
<ListItem.Content>
|
||||||
<ListItem.Title style={stylesHook.list} numberOfLines={1} ellipsizeMode="middle">
|
<View style={styles.row}>
|
||||||
<Text style={[styles.index, stylesHook.index]}>{item.index + 1}</Text>{' '}
|
<View style={styles.leftSection}>
|
||||||
<Text style={[stylesHook.address, styles.address]}>{item.address}</Text>
|
<Text style={[styles.index, stylesHook.index]}>{item.index}</Text>
|
||||||
</ListItem.Title>
|
</View>
|
||||||
<View style={styles.subtitle}>
|
<View style={styles.middleSection}>
|
||||||
<Text style={[stylesHook.list, styles.balance, stylesHook.balance]}>{balance}</Text>
|
<Text style={[stylesHook.address, styles.address]} numberOfLines={1} ellipsizeMode="middle">
|
||||||
|
{item.address}
|
||||||
|
</Text>
|
||||||
|
<Text style={[stylesHook.balance, styles.balance]}>{balance}</Text>
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</ListItem.Content>
|
</ListItem.Content>
|
||||||
<View>
|
<View style={styles.rightContainer}>
|
||||||
<AddressTypeBadge isInternal={item.isInternal} hasTransactions={hasTransactions} />
|
<AddressTypeBadge isInternal={item.isInternal} hasTransactions={hasTransactions} />
|
||||||
<Text style={[stylesHook.list, styles.balance, stylesHook.balance]}>
|
<Text style={[stylesHook.balance, styles.balance]}>
|
||||||
{loc.addresses.transactions}: {item.transactions}
|
{loc.addresses.transactions}: {item.transactions ?? 0}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
@ -179,20 +181,27 @@ const AddressItem = ({ item, balanceUnit, walletID, allowSignVerifyMessage }: Ad
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
address: {
|
address: {
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
marginHorizontal: 40,
|
marginHorizontal: 4,
|
||||||
},
|
},
|
||||||
index: {
|
index: {
|
||||||
fontSize: 15,
|
fontSize: 15,
|
||||||
},
|
},
|
||||||
balance: {
|
balance: {
|
||||||
marginTop: 8,
|
marginTop: 4,
|
||||||
marginLeft: 14,
|
|
||||||
},
|
},
|
||||||
subtitle: {
|
row: {
|
||||||
flex: 1,
|
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
justifyContent: 'space-between',
|
alignItems: 'center',
|
||||||
width: '100%',
|
},
|
||||||
|
leftSection: {
|
||||||
|
marginRight: 8,
|
||||||
|
},
|
||||||
|
middleSection: {
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
rightContainer: {
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'flex-end',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -31,37 +31,6 @@ import TipBox from '../../components/TipBox';
|
||||||
|
|
||||||
const segmentControlValues = [loc.wallets.details_address, loc.bip47.payment_code];
|
const segmentControlValues = [loc.wallets.details_address, loc.bip47.payment_code];
|
||||||
|
|
||||||
const TabContent = React.memo(
|
|
||||||
({ currentTab, bip21encoded, wallet, address, showAddress, isCustom, handleShareButtonPressed, renderReceiveDetails }) => {
|
|
||||||
const qrValue = useMemo(
|
|
||||||
() => (currentTab === segmentControlValues[0] ? bip21encoded : wallet?.getBIP47PaymentCode()),
|
|
||||||
[currentTab, bip21encoded, wallet],
|
|
||||||
);
|
|
||||||
|
|
||||||
if (currentTab === segmentControlValues[0]) {
|
|
||||||
return <View style={styles.container}>{address && renderReceiveDetails()}</View>;
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<View style={styles.container}>
|
|
||||||
{!qrValue && <Text>{loc.bip47.not_found}</Text>}
|
|
||||||
{qrValue && (
|
|
||||||
<>
|
|
||||||
<TipBox description={loc.receive.bip47_explanation} containerStyle={styles.tip} />
|
|
||||||
<QRCodeComponent value={qrValue} />
|
|
||||||
<CopyTextToClipboard text={qrValue} truncated={false} />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
const MemoizedHandoffComponent = React.memo(
|
|
||||||
({ address }) => <HandOffComponent title={loc.send.details_address} type={HandOffActivityType.ReceiveOnchain} userInfo={{ address }} />,
|
|
||||||
(prevProps, nextProps) => prevProps.address === nextProps.address,
|
|
||||||
);
|
|
||||||
|
|
||||||
const ReceiveDetails = () => {
|
const ReceiveDetails = () => {
|
||||||
const { walletID, address } = useRoute().params;
|
const { walletID, address } = useRoute().params;
|
||||||
const { wallets, saveToDisk, sleep, fetchAndSaveWalletTransactions } = useStorage();
|
const { wallets, saveToDisk, sleep, fetchAndSaveWalletTransactions } = useStorage();
|
||||||
|
@ -459,37 +428,25 @@ const ReceiveDetails = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
|
||||||
|
|
||||||
const handleTabChange = useCallback(index => {
|
|
||||||
setIsLoading(true);
|
|
||||||
// Use requestAnimationFrame to prevent UI blocking (better than InteractionManager)
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
setCurrentTab(segmentControlValues[index]);
|
|
||||||
// Add a small delay to allow the UI to update (sadly needed hack)
|
|
||||||
setTimeout(() => {
|
|
||||||
setIsLoading(false);
|
|
||||||
}, 100);
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const renderTabContent = () => {
|
const renderTabContent = () => {
|
||||||
if (isLoading) {
|
const qrValue = currentTab === segmentControlValues[0] ? bip21encoded : wallet.getBIP47PaymentCode();
|
||||||
return <BlueLoading />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
if (currentTab === segmentControlValues[0]) {
|
||||||
<TabContent
|
return <View style={styles.container}>{address && renderReceiveDetails()}</View>;
|
||||||
currentTab={currentTab}
|
} else {
|
||||||
bip21encoded={bip21encoded}
|
return (
|
||||||
wallet={wallet}
|
<View style={styles.container}>
|
||||||
address={address}
|
{!qrValue && <Text>{loc.bip47.not_found}</Text>}
|
||||||
showAddress={showAddress}
|
{qrValue && (
|
||||||
isCustom={isCustom}
|
<>
|
||||||
handleShareButtonPressed={handleShareButtonPressed}
|
<TipBox description={loc.receive.bip47_explanation} containerStyle={styles.tip} />
|
||||||
renderReceiveDetails={renderReceiveDetails}
|
<QRCodeComponent value={qrValue} />
|
||||||
/>
|
<CopyTextToClipboard text={qrValue} truncated={false} />
|
||||||
);
|
</>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -504,12 +461,16 @@ const ReceiveDetails = () => {
|
||||||
<SegmentedControl
|
<SegmentedControl
|
||||||
values={segmentControlValues}
|
values={segmentControlValues}
|
||||||
selectedIndex={segmentControlValues.findIndex(tab => tab === currentTab)}
|
selectedIndex={segmentControlValues.findIndex(tab => tab === currentTab)}
|
||||||
onChange={handleTabChange}
|
onChange={index => {
|
||||||
|
setCurrentTab(segmentControlValues[index]);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
{showAddress && renderTabContent()}
|
{showAddress && renderTabContent()}
|
||||||
{address !== undefined && showAddress && <MemoizedHandoffComponent address={address} />}
|
{address !== undefined && showAddress && (
|
||||||
|
<HandOffComponent title={loc.send.details_address} type={HandOffActivityType.ReceiveOnchain} userInfo={{ address }} />
|
||||||
|
)}
|
||||||
{showConfirmedBalance ? renderConfirmedBalance() : null}
|
{showConfirmedBalance ? renderConfirmedBalance() : null}
|
||||||
{showPendingBalance ? renderPendingBalance() : null}
|
{showPendingBalance ? renderPendingBalance() : null}
|
||||||
{!showAddress && !showPendingBalance && !showConfirmedBalance ? <BlueLoading /> : null}
|
{!showAddress && !showPendingBalance && !showConfirmedBalance ? <BlueLoading /> : null}
|
||||||
|
|
|
@ -139,6 +139,25 @@ const WalletAddresses: React.FC = () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const getAddresses = useMemo(() => {
|
||||||
|
if (!walletInstance) return [];
|
||||||
|
const newAddresses: Address[] = [];
|
||||||
|
// @ts-ignore: idk what to do
|
||||||
|
for (let index = 0; index <= (walletInstance?.next_free_change_address_index ?? 0); index++) {
|
||||||
|
newAddresses.push(getAddress(walletInstance, index, true));
|
||||||
|
}
|
||||||
|
// @ts-ignore: idk what to do
|
||||||
|
for (let index = 0; index < (walletInstance?.next_free_address_index ?? 0) + (walletInstance?.gap_limit ?? 0); index++) {
|
||||||
|
newAddresses.push(getAddress(walletInstance, index, false));
|
||||||
|
}
|
||||||
|
return newAddresses;
|
||||||
|
}, [walletInstance]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch({ type: SET_ADDRESSES, payload: getAddresses });
|
||||||
|
dispatch({ type: SET_SHOW_ADDRESSES, payload: true });
|
||||||
|
}, [getAddresses]);
|
||||||
|
|
||||||
const filteredAddresses = useMemo(
|
const filteredAddresses = useMemo(
|
||||||
() => addresses.filter(address => filterByAddressType(TABS.INTERNAL, address.isInternal, currentTab)).sort(sortByAddressIndex),
|
() => addresses.filter(address => filterByAddressType(TABS.INTERNAL, address.isInternal, currentTab)).sort(sortByAddressIndex),
|
||||||
[addresses, currentTab],
|
[addresses, currentTab],
|
||||||
|
@ -158,36 +177,20 @@ const WalletAddresses: React.FC = () => {
|
||||||
});
|
});
|
||||||
}, [setOptions]);
|
}, [setOptions]);
|
||||||
|
|
||||||
const getAddresses = useCallback(() => {
|
|
||||||
const newAddresses: Address[] = [];
|
|
||||||
// @ts-ignore: idk what to do
|
|
||||||
for (let index = 0; index <= (walletInstance?.next_free_change_address_index ?? 0); index++) {
|
|
||||||
const address = getAddress(walletInstance, index, true);
|
|
||||||
newAddresses.push(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
// @ts-ignore: idk what to do
|
|
||||||
for (let index = 0; index < (walletInstance?.next_free_address_index ?? 0) + (walletInstance?.gap_limit ?? 0); index++) {
|
|
||||||
const address = getAddress(walletInstance, index, false);
|
|
||||||
newAddresses.push(address);
|
|
||||||
}
|
|
||||||
dispatch({ type: SET_ADDRESSES, payload: newAddresses });
|
|
||||||
dispatch({ type: SET_SHOW_ADDRESSES, payload: true });
|
|
||||||
}, [walletInstance]);
|
|
||||||
|
|
||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
if (!isDesktop) disallowScreenshot(true);
|
if (!isDesktop) disallowScreenshot(true);
|
||||||
getAddresses();
|
|
||||||
return () => {
|
return () => {
|
||||||
if (!isDesktop) disallowScreenshot(false);
|
if (!isDesktop) disallowScreenshot(false);
|
||||||
};
|
};
|
||||||
}, [getAddresses]),
|
}, []),
|
||||||
);
|
);
|
||||||
|
|
||||||
const data =
|
const data =
|
||||||
search.length > 0 ? filteredAddresses.filter(item => item.address.toLowerCase().includes(search.toLowerCase())) : filteredAddresses;
|
search.length > 0 ? filteredAddresses.filter(item => item.address.toLowerCase().includes(search.toLowerCase())) : filteredAddresses;
|
||||||
|
|
||||||
|
const keyExtractor = useCallback((item: Address) => item.key, []);
|
||||||
|
|
||||||
const renderRow = useCallback(
|
const renderRow = useCallback(
|
||||||
({ item }: { item: Address }) => {
|
({ item }: { item: Address }) => {
|
||||||
return (
|
return (
|
||||||
|
@ -206,31 +209,32 @@ const WalletAddresses: React.FC = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.root, stylesHook.root]}>
|
<FlatList
|
||||||
<FlatList
|
contentContainerStyle={stylesHook.root}
|
||||||
contentContainerStyle={stylesHook.root}
|
ref={addressList}
|
||||||
ref={addressList}
|
data={data}
|
||||||
data={data}
|
extraData={data}
|
||||||
extraData={data}
|
style={styles.root}
|
||||||
initialNumToRender={20}
|
keyExtractor={keyExtractor}
|
||||||
renderItem={renderRow}
|
initialNumToRender={20}
|
||||||
ListEmptyComponent={search.length > 0 ? null : <ActivityIndicator />}
|
renderItem={renderRow}
|
||||||
centerContent={!showAddresses}
|
ListEmptyComponent={search.length > 0 ? null : <ActivityIndicator />}
|
||||||
contentInsetAdjustmentBehavior="automatic"
|
centerContent={!showAddresses}
|
||||||
ListHeaderComponent={
|
contentInsetAdjustmentBehavior="automatic"
|
||||||
<SegmentedControl
|
automaticallyAdjustContentInsets
|
||||||
values={Object.values(TABS).map(tab => loc.addresses[`type_${tab}`])}
|
automaticallyAdjustsScrollIndicatorInsets
|
||||||
selectedIndex={Object.values(TABS).findIndex(tab => tab === currentTab)}
|
automaticallyAdjustKeyboardInsets
|
||||||
onChange={index => {
|
ListHeaderComponent={
|
||||||
const tabKey = Object.keys(TABS)[index] as TabKey;
|
<SegmentedControl
|
||||||
dispatch({ type: SET_CURRENT_TAB, payload: TABS[tabKey] });
|
values={Object.values(TABS).map(tab => loc.addresses[`type_${tab}`])}
|
||||||
}}
|
selectedIndex={Object.values(TABS).findIndex(tab => tab === currentTab)}
|
||||||
|
onChange={index => {
|
||||||
// style={{ marginVertical: 10 }}
|
const tabKey = Object.keys(TABS)[index] as TabKey;
|
||||||
/>
|
dispatch({ type: SET_CURRENT_TAB, payload: TABS[tabKey] });
|
||||||
}
|
}}
|
||||||
/>
|
/>
|
||||||
</View>
|
}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue