This commit is contained in:
Marcos Rodriguez Velez 2024-07-25 01:54:51 -04:00
parent ecfd52393e
commit 9a31d729f4
No known key found for this signature in database
GPG key ID: 6030B2F48CCE86D7
3 changed files with 170 additions and 145 deletions

View file

@ -105,11 +105,128 @@ interface WalletCarouselItemProps {
customStyle?: ViewStyle;
horizontal?: boolean;
isActive?: boolean;
allowOnPressAnimation?: boolean;
searchQuery?: string;
renderHighlightedText?: (text: string, query: string) => JSX.Element;
}
export const WalletCarouselItem: React.FC<WalletCarouselItemProps> = ({
item,
onPress,
handleLongPress,
isSelectedWallet,
customStyle,
horizontal,
isActive,
searchQuery,
renderHighlightedText,
}) => {
const scaleValue = useRef(new Animated.Value(1.0)).current;
const { colors } = useTheme();
const { width } = useWindowDimensions();
const itemWidth = width * 0.82 > 375 ? 375 : width * 0.82;
const isLargeScreen = useIsLargeScreen();
const onPressedIn = useCallback(() => {
Animated.spring(scaleValue, {
toValue: 0.95,
useNativeDriver: true,
friction: 3,
tension: 100,
}).start();
}, [scaleValue]);
const onPressedOut = useCallback(() => {
Animated.spring(scaleValue, {
toValue: 1.0,
useNativeDriver: true,
friction: 3,
tension: 100,
}).start();
}, [scaleValue]);
const handlePress = useCallback(() => {
onPressedOut();
onPress(item);
}, [item, onPress, onPressedOut]);
const opacity = isSelectedWallet === false ? 0.5 : 1.0;
let image;
switch (item.type) {
case LightningLdkWallet.type:
case LightningCustodianWallet.type:
image = I18nManager.isRTL ? require('../img/lnd-shape-rtl.png') : require('../img/lnd-shape.png');
break;
case MultisigHDWallet.type:
image = I18nManager.isRTL ? require('../img/vault-shape-rtl.png') : require('../img/vault-shape.png');
break;
default:
image = I18nManager.isRTL ? require('../img/btc-shape-rtl.png') : require('../img/btc-shape.png');
}
const latestTransactionText =
item.getBalance() !== 0 && item.getLatestTransactionTime() === 0
? loc.wallets.pull_to_refresh
: item.getTransactions().find((tx: Transaction) => tx.confirmations === 0)
? loc.transactions.pending
: transactionTimeToReadable(item.getLatestTransactionTime());
const balance = !item.hideBalance && formatBalance(Number(item.getBalance()), item.getPreferredBalanceUnit(), true);
return (
<Animated.View
style={[
isLargeScreen || !horizontal ? [iStyles.rootLargeDevice, customStyle] : customStyle ?? { ...iStyles.root, width: itemWidth },
{ opacity, transform: [{ scale: scaleValue }] },
]}
>
<Pressable
accessibilityRole="button"
testID={item.getLabel()}
onPressIn={onPressedIn}
onPressOut={onPressedOut}
onLongPress={() => {
if (handleLongPress) handleLongPress();
}}
onPress={handlePress}
>
<View style={[iStyles.shadowContainer, { backgroundColor: colors.background, shadowColor: colors.shadowColor }]}>
<LinearGradient colors={WalletGradient.gradientsFor(item.type)} style={iStyles.grad}>
<Image source={image} style={iStyles.image} />
<Text style={iStyles.br} />
<Text numberOfLines={1} style={[iStyles.label, { color: colors.inverseForegroundColor }]}>
{renderHighlightedText && searchQuery ? renderHighlightedText(item.getLabel(), searchQuery) : item.getLabel()}
</Text>
<View style={iStyles.balanceContainer}>
{item.hideBalance ? (
<>
<BlueSpacing10 />
<BlurredBalanceView />
</>
) : (
<Text
numberOfLines={1}
adjustsFontSizeToFit
key={`${balance}`} // force component recreation on balance change. To fix right-to-left languages, like Farsi
style={[iStyles.balance, { color: colors.inverseForegroundColor }]}
>
{`${balance} `}
</Text>
)}
</View>
<Text style={iStyles.br} />
<Text numberOfLines={1} style={[iStyles.latestTx, { color: colors.inverseForegroundColor }]}>
{loc.wallets.list_latest_transaction}
</Text>
<Text numberOfLines={1} style={[iStyles.latestTxTime, { color: colors.inverseForegroundColor }]}>
{latestTransactionText}
</Text>
</LinearGradient>
</View>
</Pressable>
</Animated.View>
);
};
const iStyles = StyleSheet.create({
root: { paddingRight: 20 },
rootLargeDevice: { marginVertical: 20 },
@ -169,129 +286,6 @@ const iStyles = StyleSheet.create({
},
});
export const WalletCarouselItem: React.FC<WalletCarouselItemProps> = React.memo(
({
item,
onPress,
handleLongPress,
isSelectedWallet,
customStyle,
horizontal,
allowOnPressAnimation = true,
searchQuery,
renderHighlightedText,
}) => {
const scaleValue = useRef(new Animated.Value(1.0)).current;
const { colors } = useTheme();
const { walletTransactionUpdateStatus } = useStorage();
const { width } = useWindowDimensions();
const itemWidth = width * 0.82 > 375 ? 375 : width * 0.82;
const isLargeScreen = useIsLargeScreen();
const onPressedIn = useCallback(() => {
Animated.spring(scaleValue, {
toValue: 0.95,
useNativeDriver: true,
friction: 3,
tension: 100,
}).start();
}, [scaleValue]);
const onPressedOut = useCallback(() => {
Animated.spring(scaleValue, {
toValue: 1.0,
useNativeDriver: true,
friction: 3,
tension: 100,
}).start();
}, [scaleValue]);
const handlePress = useCallback(() => {
onPressedOut();
onPress(item);
}, [item, onPress, onPressedOut]);
const opacity = isSelectedWallet === false ? 0.5 : 1.0;
let image;
switch (item.type) {
case LightningLdkWallet.type:
case LightningCustodianWallet.type:
image = I18nManager.isRTL ? require('../img/lnd-shape-rtl.png') : require('../img/lnd-shape.png');
break;
case MultisigHDWallet.type:
image = I18nManager.isRTL ? require('../img/vault-shape-rtl.png') : require('../img/vault-shape.png');
break;
default:
image = I18nManager.isRTL ? require('../img/btc-shape-rtl.png') : require('../img/btc-shape.png');
}
const latestTransactionText =
walletTransactionUpdateStatus === WalletTransactionsStatus.ALL || walletTransactionUpdateStatus === item.getID()
? loc.transactions.updating
: item.getBalance() !== 0 && item.getLatestTransactionTime() === 0
? loc.wallets.pull_to_refresh
: item.getTransactions().find((tx: Transaction) => tx.confirmations === 0)
? loc.transactions.pending
: transactionTimeToReadable(item.getLatestTransactionTime());
const balance = !item.hideBalance && formatBalance(Number(item.getBalance()), item.getPreferredBalanceUnit(), true);
return (
<Animated.View
style={[
isLargeScreen || !horizontal ? [iStyles.rootLargeDevice, customStyle] : customStyle ?? { ...iStyles.root, width: itemWidth },
{ opacity, transform: [{ scale: scaleValue }] },
]}
>
<Pressable
accessibilityRole="button"
testID={item.getLabel()}
onPressIn={allowOnPressAnimation ? onPressedIn : undefined}
onPressOut={allowOnPressAnimation ? onPressedOut : undefined}
onLongPress={() => {
if (handleLongPress) handleLongPress();
}}
onPress={handlePress}
>
<View style={[iStyles.shadowContainer, { backgroundColor: colors.background, shadowColor: colors.shadowColor }]}>
<LinearGradient colors={WalletGradient.gradientsFor(item.type)} style={iStyles.grad}>
<Image source={image} style={iStyles.image} />
<Text style={iStyles.br} />
<Text numberOfLines={1} style={[iStyles.label, { color: colors.inverseForegroundColor }]}>
{renderHighlightedText ? renderHighlightedText(item.getLabel(), searchQuery ?? '') : item.getLabel()}
</Text>
<View style={iStyles.balanceContainer}>
{item.hideBalance ? (
<>
<BlueSpacing10 />
<BlurredBalanceView />
</>
) : (
<Text
numberOfLines={1}
adjustsFontSizeToFit
key={`${balance}`} // force component recreation on balance change. To fix right-to-left languages, like Farsi
style={[iStyles.balance, { color: colors.inverseForegroundColor }]}
>
{`${balance} `}
</Text>
)}
</View>
<Text style={iStyles.br} />
<Text numberOfLines={1} style={[iStyles.latestTx, { color: colors.inverseForegroundColor }]}>
{loc.wallets.list_latest_transaction}
</Text>
<Text numberOfLines={1} style={[iStyles.latestTxTime, { color: colors.inverseForegroundColor }]}>
{latestTransactionText}
</Text>
</LinearGradient>
</View>
</Pressable>
</Animated.View>
);
},
);
interface WalletsCarouselProps extends Partial<FlatListProps<any>> {
horizontal?: boolean;
selectedWallet?: string;
@ -300,7 +294,8 @@ interface WalletsCarouselProps extends Partial<FlatListProps<any>> {
handleLongPress?: () => void;
data: TWallet[];
scrollEnabled?: boolean;
showNewWalletPanel?: boolean; // New prop
renderHighlightedText?: (text: string, query: string) => JSX.Element;
searchQuery?: string;
}
type FlatListRefType = FlatList<any> & {
@ -329,7 +324,17 @@ const cStyles = StyleSheet.create({
const ListHeaderComponent: React.FC = () => <View style={cStyles.separatorStyle} />;
const WalletsCarousel = forwardRef<FlatListRefType, WalletsCarouselProps>((props, ref) => {
const { horizontal, data, handleLongPress, onPress, selectedWallet, scrollEnabled, showNewWalletPanel, onNewWalletPress } = props;
const {
horizontal,
data,
handleLongPress,
onPress,
selectedWallet,
scrollEnabled,
onNewWalletPress,
searchQuery,
renderHighlightedText,
} = props;
const renderItem = useCallback(
({ item, index }: ListRenderItemInfo<TWallet>) =>
item ? (
@ -339,9 +344,11 @@ const WalletsCarousel = forwardRef<FlatListRefType, WalletsCarouselProps>((props
handleLongPress={handleLongPress}
onPress={onPress}
horizontal={horizontal}
searchQuery={searchQuery}
renderHighlightedText={renderHighlightedText}
/>
) : null,
[horizontal, selectedWallet, handleLongPress, onPress],
[horizontal, selectedWallet, handleLongPress, onPress, searchQuery, renderHighlightedText],
);
const flatListRef = useRef<FlatList<any>>(null);
@ -406,7 +413,7 @@ const WalletsCarousel = forwardRef<FlatListRefType, WalletsCarouselProps>((props
ListHeaderComponent={ListHeaderComponent}
style={{ minHeight: sliderHeight + 12 }}
onScrollToIndexFailed={onScrollToIndexFailed}
ListFooterComponent={showNewWalletPanel && onNewWalletPress ? <NewWalletPanel onPress={onNewWalletPress} /> : null}
ListFooterComponent={onNewWalletPress ? <NewWalletPanel onPress={onNewWalletPress} /> : null}
{...props}
/>
) : (
@ -419,10 +426,12 @@ const WalletsCarousel = forwardRef<FlatListRefType, WalletsCarouselProps>((props
handleLongPress={handleLongPress}
onPress={onPress}
key={index}
searchQuery={props.searchQuery}
renderHighlightedText={props.renderHighlightedText}
/>
) : null,
)}
{showNewWalletPanel && onNewWalletPress && <NewWalletPanel onPress={onNewWalletPress} />}
{onNewWalletPress && <NewWalletPanel onPress={onNewWalletPress} />}
</View>
);
});

View file

@ -496,7 +496,7 @@
"add_ln_wallet_first": "You must first add a Lightning wallet.",
"identity_pubkey": "Identity Pubkey",
"xpub_title": "Wallet XPUB",
"search_wallets": "Search wallets, memos, and transactions"
"search_wallets": "Search wallets, memos"
},
"multisig": {
"multisig_vault": "Vault",

View file

@ -1,11 +1,10 @@
import React, { useEffect, useLayoutEffect, useRef, useReducer, useCallback, useMemo } from 'react';
import { Platform, StyleSheet, useColorScheme, TouchableOpacity, Image, Animated, Text, I18nManager } from 'react-native';
import { Platform, StyleSheet, useColorScheme, TouchableOpacity, Image, Animated, Text, I18nManager, View } from 'react-native';
// @ts-ignore: no declaration file
import DraggableFlatList, { ScaleDecorator } from 'react-native-draggable-flatlist';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import triggerHapticFeedback, { HapticFeedbackTypes } from '../../blue_modules/hapticFeedback';
import { useTheme } from '../../components/themes';
import { WalletCarouselItem } from '../../components/WalletsCarousel';
import { TransactionListItem } from '../../components/TransactionListItem';
import { useExtendedNavigation } from '../../hooks/useExtendedNavigation';
import loc from '../../loc';
@ -14,6 +13,8 @@ import useDebounce from '../../hooks/useDebounce';
import { Header } from '../../components/Header';
import { TTXMetadata } from '../../class';
import { TWallet } from '../../class/wallets/types';
import { BitcoinUnit } from '../../models/bitcoinUnits';
import { WalletCarouselItem } from '../../components/WalletsCarousel';
const SET_SEARCH_QUERY = 'SET_SEARCH_QUERY';
const SET_IS_SEARCH_FOCUSED = 'SET_IS_SEARCH_FOCUSED';
@ -86,19 +87,21 @@ const useBounceAnimation = (query: string) => {
useEffect(() => {
if (query) {
Animated.timing(bounceAnim, {
Animated.spring(bounceAnim, {
toValue: 1.2,
duration: 150,
useNativeDriver: true,
friction: 3,
tension: 100,
}).start(() => {
Animated.timing(bounceAnim, {
Animated.spring(bounceAnim, {
toValue: 1.0,
duration: 150,
useNativeDriver: true,
friction: 3,
tension: 100,
}).start();
});
}
}, [bounceAnim, query]);
}, [query]);
return bounceAnim;
};
@ -238,13 +241,16 @@ const ManageWallets: React.FC = () => {
if (item.type === 'transaction') {
return (
<TransactionListItem
item={item.data}
itemPriceUnit="BTC"
walletID={item.data.walletID}
searchQuery={state.searchQuery}
style={StyleSheet.flatten([styles.padding16, { opacity: itemOpacity }])}
/>
<View style={StyleSheet.flatten([styles.padding16, { opacity: itemOpacity }])}>
<TransactionListItem
item={item.data}
// update later to support other units
itemPriceUnit={BitcoinUnit.BTC}
walletID={item.data.walletID}
searchQuery={state.searchQuery}
renderHighlightedText={renderHighlightedText}
/>
</View>
);
}
@ -292,7 +298,9 @@ const ManageWallets: React.FC = () => {
<>
{hasWallets && <Header leftText={loc.wallets.wallets} isDrawerList />}
{hasTransactions && <Header leftText={loc.addresses.transactions} isDrawerList />}
{!hasWallets && !hasTransactions && <Text style={stylesHook.noResultsText}>{loc.wallets.no_results_found}</Text>}
{!hasWallets && !hasTransactions && (
<Text style={[styles.noResultsText, stylesHook.noResultsText]}>{loc.wallets.no_results_found}</Text>
)}
</>
);
}, [state.searchQuery, state.walletData.length, state.txMetadata, stylesHook.noResultsText]);
@ -329,6 +337,14 @@ const styles = StyleSheet.create({
button: {
padding: 16,
},
noResultsText: {
fontSize: 19,
fontWeight: 'bold',
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
textAlign: 'center',
justifyContent: 'center',
marginTop: 34,
},
});
const iStyles = StyleSheet.create({