mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-03-13 11:09:20 +01:00
Merge branch 'master' into modal
This commit is contained in:
commit
502c52bf11
7 changed files with 425 additions and 403 deletions
|
@ -1,378 +0,0 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React, { forwardRef, useCallback, useImperativeHandle, useRef } from 'react';
|
||||
import {
|
||||
Animated,
|
||||
FlatList,
|
||||
I18nManager,
|
||||
Image,
|
||||
Platform,
|
||||
Pressable,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
useWindowDimensions,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import LinearGradient from 'react-native-linear-gradient';
|
||||
import { BlueSpacing10 } from '../BlueComponents';
|
||||
import { LightningCustodianWallet, LightningLdkWallet, MultisigHDWallet } from '../class';
|
||||
import WalletGradient from '../class/wallet-gradient';
|
||||
import { useIsLargeScreen } from '../hooks/useIsLargeScreen';
|
||||
import loc, { formatBalance, transactionTimeToReadable } from '../loc';
|
||||
import { BlurredBalanceView } from './BlurredBalanceView';
|
||||
import { useSettings } from '../hooks/context/useSettings';
|
||||
import { useTheme } from './themes';
|
||||
import { useStorage } from '../hooks/context/useStorage';
|
||||
import { WalletTransactionsStatus } from './Context/StorageProvider';
|
||||
|
||||
const nStyles = StyleSheet.create({
|
||||
container: {
|
||||
borderRadius: 10,
|
||||
minHeight: Platform.OS === 'ios' ? 164 : 181,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'flex-start',
|
||||
},
|
||||
addAWAllet: {
|
||||
fontWeight: '600',
|
||||
fontSize: 24,
|
||||
marginBottom: 4,
|
||||
},
|
||||
addLine: {
|
||||
fontSize: 13,
|
||||
},
|
||||
button: {
|
||||
marginTop: 12,
|
||||
backgroundColor: '#007AFF',
|
||||
paddingHorizontal: 32,
|
||||
paddingVertical: 12,
|
||||
borderRadius: 8,
|
||||
},
|
||||
buttonText: {
|
||||
fontWeight: '500',
|
||||
},
|
||||
});
|
||||
|
||||
const NewWalletPanel = ({ onPress }) => {
|
||||
const { colors } = useTheme();
|
||||
const { width } = useWindowDimensions();
|
||||
const itemWidth = width * 0.82 > 375 ? 375 : width * 0.82;
|
||||
const isLargeScreen = useIsLargeScreen();
|
||||
const nStylesHooks = StyleSheet.create({
|
||||
container: isLargeScreen
|
||||
? {
|
||||
paddingHorizontal: 24,
|
||||
marginVertical: 16,
|
||||
}
|
||||
: { paddingVertical: 16, paddingHorizontal: 24 },
|
||||
});
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
testID="CreateAWallet"
|
||||
onPress={onPress}
|
||||
style={isLargeScreen ? {} : { width: itemWidth * 1.2 }}
|
||||
>
|
||||
<View
|
||||
style={[
|
||||
nStyles.container,
|
||||
nStylesHooks.container,
|
||||
{ backgroundColor: WalletGradient.createWallet() },
|
||||
isLargeScreen ? {} : { width: itemWidth },
|
||||
]}
|
||||
>
|
||||
<Text style={[nStyles.addAWAllet, { color: colors.foregroundColor }]}>{loc.wallets.list_create_a_wallet}</Text>
|
||||
<Text style={[nStyles.addLine, { color: colors.alternativeTextColor }]}>{loc.wallets.list_create_a_wallet_text}</Text>
|
||||
<View style={nStyles.button}>
|
||||
<Text style={[nStyles.buttonText, { color: colors.brandingColor }]}>{loc.wallets.list_create_a_button}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
NewWalletPanel.propTypes = {
|
||||
onPress: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
const iStyles = StyleSheet.create({
|
||||
root: { paddingRight: 20 },
|
||||
rootLargeDevice: { marginVertical: 20 },
|
||||
grad: {
|
||||
padding: 15,
|
||||
borderRadius: 12,
|
||||
minHeight: 164,
|
||||
},
|
||||
balanceContainer: {
|
||||
height: 40,
|
||||
},
|
||||
image: {
|
||||
width: 99,
|
||||
height: 94,
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
},
|
||||
br: {
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
label: {
|
||||
backgroundColor: 'transparent',
|
||||
fontSize: 19,
|
||||
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
|
||||
},
|
||||
balance: {
|
||||
backgroundColor: 'transparent',
|
||||
fontWeight: 'bold',
|
||||
fontSize: 36,
|
||||
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
|
||||
},
|
||||
latestTx: {
|
||||
backgroundColor: 'transparent',
|
||||
fontSize: 13,
|
||||
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
|
||||
},
|
||||
latestTxTime: {
|
||||
backgroundColor: 'transparent',
|
||||
fontWeight: 'bold',
|
||||
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
|
||||
fontSize: 16,
|
||||
},
|
||||
shadowContainer: {
|
||||
...Platform.select({
|
||||
ios: {
|
||||
shadowOffset: { width: 0, height: 4 },
|
||||
shadowOpacity: 25 / 100,
|
||||
shadowRadius: 8,
|
||||
borderRadius: 12,
|
||||
},
|
||||
android: {
|
||||
elevation: 8,
|
||||
borderRadius: 12,
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
export const WalletCarouselItem = React.memo(({ item, _, onPress, handleLongPress, isSelectedWallet, customStyle, horizontal }) => {
|
||||
const scaleValue = new Animated.Value(1.0);
|
||||
const { colors } = useTheme();
|
||||
const { walletTransactionUpdateStatus } = useStorage();
|
||||
const { width } = useWindowDimensions();
|
||||
const itemWidth = width * 0.82 > 375 ? 375 : width * 0.82;
|
||||
const isLargeScreen = useIsLargeScreen();
|
||||
const onPressedIn = () => {
|
||||
Animated.spring(scaleValue, { duration: 50, useNativeDriver: true, toValue: 0.9 }).start();
|
||||
};
|
||||
|
||||
const onPressedOut = () => {
|
||||
Animated.spring(scaleValue, { duration: 50, useNativeDriver: true, toValue: 1.0 }).start();
|
||||
};
|
||||
|
||||
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 => 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={() => {
|
||||
handleLongPress();
|
||||
}}
|
||||
onPress={() => {
|
||||
onPressedOut();
|
||||
setTimeout(() => {
|
||||
onPress(item); // Replace 'onPress' with your navigation function
|
||||
}, 50);
|
||||
}}
|
||||
>
|
||||
<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 }]}>
|
||||
{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>
|
||||
);
|
||||
});
|
||||
|
||||
WalletCarouselItem.propTypes = {
|
||||
item: PropTypes.any,
|
||||
onPress: PropTypes.func.isRequired,
|
||||
handleLongPress: PropTypes.func,
|
||||
isSelectedWallet: PropTypes.bool,
|
||||
};
|
||||
|
||||
const cStyles = StyleSheet.create({
|
||||
content: {
|
||||
paddingTop: 16,
|
||||
},
|
||||
contentLargeScreen: {
|
||||
paddingHorizontal: 16,
|
||||
},
|
||||
separatorStyle: {
|
||||
width: 16,
|
||||
height: 20,
|
||||
},
|
||||
});
|
||||
|
||||
const ListHeaderComponent = () => <View style={cStyles.separatorStyle} />;
|
||||
|
||||
const WalletsCarousel = forwardRef((props, ref) => {
|
||||
const { preferredFiatCurrency, language } = useSettings();
|
||||
const { horizontal, data, handleLongPress, onPress, selectedWallet, scrollEnabled } = props;
|
||||
const renderItem = useCallback(
|
||||
({ item, index }) =>
|
||||
item ? (
|
||||
<WalletCarouselItem
|
||||
isSelectedWallet={!horizontal && selectedWallet ? selectedWallet === item.getID() : undefined}
|
||||
item={item}
|
||||
index={index}
|
||||
handleLongPress={handleLongPress}
|
||||
onPress={onPress}
|
||||
horizontal={horizontal}
|
||||
/>
|
||||
) : (
|
||||
<NewWalletPanel onPress={onPress} />
|
||||
),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[horizontal, selectedWallet, preferredFiatCurrency, language],
|
||||
);
|
||||
const flatListRef = useRef();
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
scrollToItem: ({ item }) => {
|
||||
setTimeout(() => {
|
||||
flatListRef?.current?.scrollToItem({ item, viewOffset: 16 });
|
||||
}, 300);
|
||||
},
|
||||
scrollToIndex: ({ index }) => {
|
||||
setTimeout(() => {
|
||||
flatListRef?.current?.scrollToIndex({ index, viewOffset: 16 });
|
||||
}, 300);
|
||||
},
|
||||
}));
|
||||
|
||||
const onScrollToIndexFailed = error => {
|
||||
console.log('onScrollToIndexFailed');
|
||||
console.log(error);
|
||||
flatListRef.current.scrollToOffset({ offset: error.averageItemLength * error.index, animated: true });
|
||||
setTimeout(() => {
|
||||
if (data.length !== 0 && flatListRef.current !== null) {
|
||||
flatListRef.current.scrollToIndex({ index: error.index, animated: true });
|
||||
}
|
||||
}, 100);
|
||||
};
|
||||
|
||||
const { width } = useWindowDimensions();
|
||||
const sliderHeight = 195;
|
||||
const itemWidth = width * 0.82 > 375 ? 375 : width * 0.82;
|
||||
return horizontal ? (
|
||||
<FlatList
|
||||
ref={flatListRef}
|
||||
renderItem={renderItem}
|
||||
extraData={data}
|
||||
keyExtractor={(_, index) => index.toString()}
|
||||
showsVerticalScrollIndicator={false}
|
||||
pagingEnabled
|
||||
disableIntervalMomentum={horizontal}
|
||||
snapToInterval={itemWidth} // Adjust to your content width
|
||||
decelerationRate="fast"
|
||||
contentContainerStyle={cStyles.content}
|
||||
directionalLockEnabled
|
||||
showsHorizontalScrollIndicator={false}
|
||||
initialNumToRender={10}
|
||||
scrollEnabled={scrollEnabled}
|
||||
ListHeaderComponent={ListHeaderComponent}
|
||||
style={{ minHeight: sliderHeight + 12 }}
|
||||
onScrollToIndexFailed={onScrollToIndexFailed}
|
||||
{...props}
|
||||
/>
|
||||
) : (
|
||||
<View style={cStyles.contentLargeScreen}>
|
||||
{data.map((item, index) =>
|
||||
item ? (
|
||||
<WalletCarouselItem
|
||||
isSelectedWallet={!horizontal && selectedWallet ? selectedWallet === item.getID() : undefined}
|
||||
item={item}
|
||||
index={index}
|
||||
handleLongPress={handleLongPress}
|
||||
onPress={onPress}
|
||||
key={index}
|
||||
/>
|
||||
) : (
|
||||
<NewWalletPanel key={index} onPress={onPress} />
|
||||
),
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
});
|
||||
|
||||
WalletsCarousel.propTypes = {
|
||||
horizontal: PropTypes.bool,
|
||||
selectedWallet: PropTypes.string,
|
||||
onPress: PropTypes.func.isRequired,
|
||||
handleLongPress: PropTypes.func.isRequired,
|
||||
data: PropTypes.array,
|
||||
};
|
||||
|
||||
export default WalletsCarousel;
|
414
components/WalletsCarousel.tsx
Normal file
414
components/WalletsCarousel.tsx
Normal file
|
@ -0,0 +1,414 @@
|
|||
import React, { forwardRef, useCallback, useImperativeHandle, useRef } from 'react';
|
||||
import {
|
||||
Animated,
|
||||
FlatList,
|
||||
I18nManager,
|
||||
Image,
|
||||
Platform,
|
||||
Pressable,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
useWindowDimensions,
|
||||
View,
|
||||
FlatListProps,
|
||||
ListRenderItemInfo,
|
||||
ViewStyle,
|
||||
} from 'react-native';
|
||||
import LinearGradient from 'react-native-linear-gradient';
|
||||
import { BlueSpacing10 } from '../BlueComponents';
|
||||
import { LightningCustodianWallet, LightningLdkWallet, MultisigHDWallet } from '../class';
|
||||
import WalletGradient from '../class/wallet-gradient';
|
||||
import { useIsLargeScreen } from '../hooks/useIsLargeScreen';
|
||||
import loc, { formatBalance, transactionTimeToReadable } from '../loc';
|
||||
import { BlurredBalanceView } from './BlurredBalanceView';
|
||||
import { useTheme } from './themes';
|
||||
import { useStorage } from '../hooks/context/useStorage';
|
||||
import { WalletTransactionsStatus } from './Context/StorageProvider';
|
||||
import { Transaction, TWallet } from '../class/wallets/types';
|
||||
|
||||
interface NewWalletPanelProps {
|
||||
onPress: () => void;
|
||||
}
|
||||
|
||||
const nStyles = StyleSheet.create({
|
||||
container: {
|
||||
borderRadius: 10,
|
||||
minHeight: Platform.OS === 'ios' ? 164 : 181,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'flex-start',
|
||||
},
|
||||
addAWAllet: {
|
||||
fontWeight: '600',
|
||||
fontSize: 24,
|
||||
marginBottom: 4,
|
||||
},
|
||||
addLine: {
|
||||
fontSize: 13,
|
||||
},
|
||||
button: {
|
||||
marginTop: 12,
|
||||
backgroundColor: '#007AFF',
|
||||
paddingHorizontal: 32,
|
||||
paddingVertical: 12,
|
||||
borderRadius: 8,
|
||||
},
|
||||
buttonText: {
|
||||
fontWeight: '500',
|
||||
},
|
||||
});
|
||||
|
||||
const NewWalletPanel: React.FC<NewWalletPanelProps> = ({ onPress }) => {
|
||||
const { colors } = useTheme();
|
||||
const { width } = useWindowDimensions();
|
||||
const itemWidth = width * 0.82 > 375 ? 375 : width * 0.82;
|
||||
const isLargeScreen = useIsLargeScreen();
|
||||
const nStylesHooks = StyleSheet.create({
|
||||
container: isLargeScreen
|
||||
? {
|
||||
paddingHorizontal: 24,
|
||||
marginVertical: 16,
|
||||
}
|
||||
: { paddingVertical: 16, paddingHorizontal: 24 },
|
||||
});
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
testID="CreateAWallet"
|
||||
onPress={onPress}
|
||||
style={isLargeScreen ? {} : { width: itemWidth * 1.2 }}
|
||||
>
|
||||
<View
|
||||
style={[
|
||||
nStyles.container,
|
||||
nStylesHooks.container,
|
||||
{ backgroundColor: WalletGradient.createWallet() },
|
||||
isLargeScreen ? {} : { width: itemWidth },
|
||||
]}
|
||||
>
|
||||
<Text style={[nStyles.addAWAllet, { color: colors.foregroundColor }]}>{loc.wallets.list_create_a_wallet}</Text>
|
||||
<Text style={[nStyles.addLine, { color: colors.alternativeTextColor }]}>{loc.wallets.list_create_a_wallet_text}</Text>
|
||||
<View style={nStyles.button}>
|
||||
<Text style={[nStyles.buttonText, { color: colors.brandingColor }]}>{loc.wallets.list_create_a_button}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
interface WalletCarouselItemProps {
|
||||
item: TWallet;
|
||||
onPress: (item: TWallet) => void;
|
||||
handleLongPress?: () => void;
|
||||
isSelectedWallet?: boolean;
|
||||
customStyle?: ViewStyle;
|
||||
horizontal?: boolean;
|
||||
isActive?: boolean;
|
||||
allowOnPressAnimation?: boolean;
|
||||
}
|
||||
|
||||
const iStyles = StyleSheet.create({
|
||||
root: { paddingRight: 20 },
|
||||
rootLargeDevice: { marginVertical: 20 },
|
||||
grad: {
|
||||
padding: 15,
|
||||
borderRadius: 12,
|
||||
minHeight: 164,
|
||||
},
|
||||
balanceContainer: {
|
||||
height: 40,
|
||||
},
|
||||
image: {
|
||||
width: 99,
|
||||
height: 94,
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
},
|
||||
br: {
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
label: {
|
||||
backgroundColor: 'transparent',
|
||||
fontSize: 19,
|
||||
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
|
||||
},
|
||||
balance: {
|
||||
backgroundColor: 'transparent',
|
||||
fontWeight: 'bold',
|
||||
fontSize: 36,
|
||||
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
|
||||
},
|
||||
latestTx: {
|
||||
backgroundColor: 'transparent',
|
||||
fontSize: 13,
|
||||
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
|
||||
},
|
||||
latestTxTime: {
|
||||
backgroundColor: 'transparent',
|
||||
fontWeight: 'bold',
|
||||
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
|
||||
fontSize: 16,
|
||||
},
|
||||
shadowContainer: {
|
||||
...Platform.select({
|
||||
ios: {
|
||||
shadowOffset: { width: 0, height: 4 },
|
||||
shadowOpacity: 25 / 100,
|
||||
shadowRadius: 8,
|
||||
borderRadius: 12,
|
||||
},
|
||||
android: {
|
||||
elevation: 8,
|
||||
borderRadius: 12,
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
export const WalletCarouselItem: React.FC<WalletCarouselItemProps> = React.memo(
|
||||
({ item, onPress, handleLongPress, isSelectedWallet, customStyle, horizontal, allowOnPressAnimation = true }) => {
|
||||
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 }]}>
|
||||
{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;
|
||||
onPress: (item: TWallet) => void;
|
||||
onNewWalletPress?: () => void;
|
||||
handleLongPress?: () => void;
|
||||
data: TWallet[];
|
||||
scrollEnabled?: boolean;
|
||||
showNewWalletPanel?: boolean; // New prop
|
||||
}
|
||||
|
||||
type FlatListRefType = FlatList<any> & {
|
||||
scrollToEnd(params?: { animated?: boolean | null }): void;
|
||||
scrollToIndex(params: { animated?: boolean | null; index: number; viewOffset?: number; viewPosition?: number }): void;
|
||||
scrollToItem(params: { animated?: boolean | null; item: TWallet; viewPosition?: number }): void;
|
||||
scrollToOffset(params: { animated?: boolean | null; offset: number }): void;
|
||||
recordInteraction(): void;
|
||||
flashScrollIndicators(): void;
|
||||
getNativeScrollRef(): View;
|
||||
};
|
||||
|
||||
const cStyles = StyleSheet.create({
|
||||
content: {
|
||||
paddingTop: 16,
|
||||
},
|
||||
contentLargeScreen: {
|
||||
paddingHorizontal: 16,
|
||||
},
|
||||
separatorStyle: {
|
||||
width: 16,
|
||||
height: 20,
|
||||
},
|
||||
});
|
||||
|
||||
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 renderItem = useCallback(
|
||||
({ item, index }: ListRenderItemInfo<TWallet>) =>
|
||||
item ? (
|
||||
<WalletCarouselItem
|
||||
isSelectedWallet={!horizontal && selectedWallet ? selectedWallet === item.getID() : undefined}
|
||||
item={item}
|
||||
handleLongPress={handleLongPress}
|
||||
onPress={onPress}
|
||||
horizontal={horizontal}
|
||||
/>
|
||||
) : null,
|
||||
[horizontal, selectedWallet, handleLongPress, onPress],
|
||||
);
|
||||
|
||||
const flatListRef = useRef<FlatList<any>>(null);
|
||||
|
||||
useImperativeHandle(ref, (): any => {
|
||||
return {
|
||||
scrollToEnd: (params: { animated?: boolean | null | undefined } | undefined) => flatListRef.current?.scrollToEnd(params),
|
||||
scrollToIndex: (params: {
|
||||
animated?: boolean | null | undefined;
|
||||
index: number;
|
||||
viewOffset?: number | undefined;
|
||||
viewPosition?: number | undefined;
|
||||
}) => flatListRef.current?.scrollToIndex(params),
|
||||
scrollToItem: (params: {
|
||||
animated?: boolean | null | undefined;
|
||||
item: any;
|
||||
viewOffset?: number | undefined;
|
||||
viewPosition?: number | undefined;
|
||||
}) => flatListRef.current?.scrollToItem(params),
|
||||
scrollToOffset: (params: { animated?: boolean | null | undefined; offset: number }) => flatListRef.current?.scrollToOffset(params),
|
||||
recordInteraction: () => flatListRef.current?.recordInteraction(),
|
||||
flashScrollIndicators: () => flatListRef.current?.flashScrollIndicators(),
|
||||
getNativeScrollRef: () => flatListRef.current?.getNativeScrollRef(),
|
||||
};
|
||||
}, []);
|
||||
|
||||
const onScrollToIndexFailed = (error: { averageItemLength: number; index: number }): void => {
|
||||
console.log('onScrollToIndexFailed');
|
||||
console.log(error);
|
||||
flatListRef.current?.scrollToOffset({ offset: error.averageItemLength * error.index, animated: true });
|
||||
setTimeout(() => {
|
||||
if (data.length !== 0 && flatListRef.current !== null) {
|
||||
flatListRef.current.scrollToIndex({ index: error.index, animated: true });
|
||||
}
|
||||
}, 100);
|
||||
};
|
||||
|
||||
const { width } = useWindowDimensions();
|
||||
const sliderHeight = 195;
|
||||
const itemWidth = width * 0.82 > 375 ? 375 : width * 0.82;
|
||||
|
||||
return horizontal ? (
|
||||
<FlatList
|
||||
ref={flatListRef}
|
||||
renderItem={renderItem}
|
||||
extraData={data}
|
||||
keyExtractor={(_, index) => index.toString()}
|
||||
showsVerticalScrollIndicator={false}
|
||||
pagingEnabled
|
||||
disableIntervalMomentum={horizontal}
|
||||
snapToInterval={itemWidth}
|
||||
decelerationRate="fast"
|
||||
contentContainerStyle={cStyles.content}
|
||||
directionalLockEnabled
|
||||
showsHorizontalScrollIndicator={false}
|
||||
initialNumToRender={10}
|
||||
scrollEnabled={scrollEnabled}
|
||||
ListHeaderComponent={ListHeaderComponent}
|
||||
style={{ minHeight: sliderHeight + 12 }}
|
||||
onScrollToIndexFailed={onScrollToIndexFailed}
|
||||
ListFooterComponent={showNewWalletPanel && onNewWalletPress ? <NewWalletPanel onPress={onNewWalletPress} /> : null}
|
||||
{...props}
|
||||
/>
|
||||
) : (
|
||||
<View style={cStyles.contentLargeScreen}>
|
||||
{data.map((item, index) =>
|
||||
item ? (
|
||||
<WalletCarouselItem
|
||||
isSelectedWallet={!horizontal && selectedWallet ? selectedWallet === item.getID() : undefined}
|
||||
item={item}
|
||||
handleLongPress={handleLongPress}
|
||||
onPress={onPress}
|
||||
key={index}
|
||||
/>
|
||||
) : null,
|
||||
)}
|
||||
{showNewWalletPanel && onNewWalletPress && <NewWalletPanel onPress={onNewWalletPress} />}
|
||||
</View>
|
||||
);
|
||||
});
|
||||
|
||||
export default WalletsCarousel;
|
|
@ -277,7 +277,7 @@ export const saveLanguage = async (lang: string) => {
|
|||
await setDateTimeLocale();
|
||||
};
|
||||
|
||||
export const transactionTimeToReadable = (time: number) => {
|
||||
export const transactionTimeToReadable = (time: number | string) => {
|
||||
if (time === -1) {
|
||||
return 'unknown';
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { DrawerContentScrollView } from '@react-navigation/drawer';
|
||||
import { NavigationProp, ParamListBase, useIsFocused } from '@react-navigation/native';
|
||||
import React, { memo, useCallback, useEffect, useMemo, useReducer, useRef } from 'react';
|
||||
import { FlatList, InteractionManager, LayoutAnimation, StyleSheet, ViewStyle } from 'react-native';
|
||||
import { InteractionManager, LayoutAnimation, StyleSheet, ViewStyle } from 'react-native';
|
||||
|
||||
import triggerHapticFeedback, { HapticFeedbackTypes } from '../../blue_modules/hapticFeedback';
|
||||
import { TWallet } from '../../class/wallets/types';
|
||||
|
@ -83,7 +83,7 @@ const DrawerList: React.FC<DrawerListProps> = memo(({ navigation }) => {
|
|||
};
|
||||
|
||||
const [state, dispatch] = useReducer(walletReducer, initialState);
|
||||
const walletsCarousel = useRef<FlatList<TWallet>>(null);
|
||||
const walletsCarousel = useRef(null);
|
||||
const { wallets, selectedWalletID } = useStorage();
|
||||
const { colors } = useTheme();
|
||||
const isFocused = useIsFocused();
|
||||
|
@ -103,7 +103,7 @@ const DrawerList: React.FC<DrawerListProps> = memo(({ navigation }) => {
|
|||
}, [wallets, isFocused]);
|
||||
|
||||
const handleClick = useCallback(
|
||||
(item: TWallet) => {
|
||||
(item?: TWallet) => {
|
||||
if (item?.getID) {
|
||||
const walletID = item.getID();
|
||||
const walletType = item.type;
|
||||
|
@ -144,8 +144,8 @@ const DrawerList: React.FC<DrawerListProps> = memo(({ navigation }) => {
|
|||
>
|
||||
<Header leftText={loc.wallets.list_title} onNewWalletPress={onNewWalletPress} isDrawerList />
|
||||
<WalletsCarousel
|
||||
// @ts-ignore: refactor later
|
||||
data={state.wallets.concat(false as any)}
|
||||
data={state.wallets}
|
||||
showNewWalletPanel
|
||||
extraData={[state.wallets]}
|
||||
onPress={handleClick}
|
||||
handleLongPress={handleLongPress}
|
||||
|
|
|
@ -127,12 +127,11 @@ const ReorderWallets: React.FC = () => {
|
|||
return (
|
||||
<ScaleDecorator>
|
||||
<WalletCarouselItem
|
||||
// @ts-ignore: fix later
|
||||
item={item}
|
||||
handleLongPress={isDraggingDisabled ? null : drag}
|
||||
isActive={isActive}
|
||||
onPress={navigateToWallet}
|
||||
customStyle={[styles.padding16, { opacity: itemOpacity }]}
|
||||
customStyle={StyleSheet.flatten([styles.padding16, { opacity: itemOpacity }])}
|
||||
/>
|
||||
</ScaleDecorator>
|
||||
);
|
||||
|
|
|
@ -81,10 +81,6 @@ const SelectWallet: React.FC = () => {
|
|||
}
|
||||
};
|
||||
|
||||
const handleLongPress = (item: TWallet) => {
|
||||
// place holder. remove once WalletsCarousel is TSX
|
||||
};
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<View style={[styles.loading, stylesHook.loading]}>
|
||||
|
@ -102,17 +98,7 @@ const SelectWallet: React.FC = () => {
|
|||
</SafeArea>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<WalletsCarousel
|
||||
// @ts-ignore: refactor later
|
||||
data={data}
|
||||
onPress={onPress}
|
||||
handleLongPress={handleLongPress}
|
||||
ref={walletsCarousel}
|
||||
testID="WalletsList"
|
||||
horizontal={false}
|
||||
/>
|
||||
);
|
||||
return <WalletsCarousel data={data} onPress={onPress} ref={walletsCarousel} testID="WalletsList" horizontal={false} />;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -247,13 +247,14 @@ const WalletsList: React.FC = () => {
|
|||
const renderWalletsCarousel = useCallback(() => {
|
||||
return (
|
||||
<WalletsCarousel
|
||||
// @ts-ignore: Fix later
|
||||
data={wallets.concat(false)}
|
||||
data={wallets}
|
||||
extraData={[wallets]}
|
||||
onPress={handleClick}
|
||||
handleLongPress={handleLongPress}
|
||||
onMomentumScrollEnd={onSnapToItem}
|
||||
ref={walletsCarousel}
|
||||
showNewWalletPanel
|
||||
onNewWalletPress={handleClick}
|
||||
testID="WalletsList"
|
||||
horizontal
|
||||
scrollEnabled={isFocused}
|
||||
|
|
Loading…
Add table
Reference in a new issue