BlueWallet/components/WalletsCarousel.js

376 lines
11 KiB
JavaScript
Raw Normal View History

2020-10-15 19:25:24 +03:00
import PropTypes from 'prop-types';
2024-05-31 13:18:01 -04:00
import React, { forwardRef, useCallback, useImperativeHandle, useRef } from 'react';
2020-10-15 19:25:24 +03:00
import {
Animated,
2024-05-20 10:54:13 +01:00
FlatList,
2021-03-18 22:30:01 -04:00
I18nManager,
2024-05-20 10:54:13 +01:00
Image,
2020-10-15 19:25:24 +03:00
Platform,
2024-05-20 10:54:13 +01:00
Pressable,
2020-10-15 19:25:24 +03:00
StyleSheet,
Text,
TouchableOpacity,
useWindowDimensions,
View,
} from 'react-native';
import LinearGradient from 'react-native-linear-gradient';
2024-05-20 10:54:13 +01:00
import { BlueSpacing10 } from '../BlueComponents';
import { LightningCustodianWallet, LightningLdkWallet, MultisigHDWallet } from '../class';
2020-10-15 19:25:24 +03:00
import WalletGradient from '../class/wallet-gradient';
2024-04-18 18:31:06 -04:00
import { useIsLargeScreen } from '../hooks/useIsLargeScreen';
2024-05-20 10:54:13 +01:00
import loc, { formatBalance, transactionTimeToReadable } from '../loc';
import { BlurredBalanceView } from './BlurredBalanceView';
2024-05-31 13:23:50 -04:00
import { useSettings } from '../hooks/context/useSettings';
2024-05-20 10:54:13 +01:00
import { useTheme } from './themes';
2024-05-31 13:18:01 -04:00
import { useStorage } from '../hooks/context/useStorage';
import { WalletTransactionsStatus } from './Context/StorageProvider';
2020-10-15 19:25:24 +03:00
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();
2021-06-15 23:21:28 -04:00
const { width } = useWindowDimensions();
const itemWidth = width * 0.82 > 375 ? 375 : width * 0.82;
2024-04-18 18:31:06 -04:00
const isLargeScreen = useIsLargeScreen();
const nStylesHooks = StyleSheet.create({
container: isLargeScreen
? {
paddingHorizontal: 24,
marginVertical: 16,
}
: { paddingVertical: 16, paddingHorizontal: 24 },
});
2021-06-15 23:21:28 -04:00
2020-10-15 19:25:24 +03:00
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 },
]}
>
2020-10-15 19:25:24 +03:00
<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({
2021-07-01 01:02:45 -04:00
root: { paddingRight: 20 },
rootLargeDevice: { marginVertical: 20 },
2020-10-15 19:25:24 +03:00
grad: {
padding: 15,
2021-03-12 15:39:40 +01:00
borderRadius: 12,
2020-10-15 19:25:24 +03:00
minHeight: 164,
},
balanceContainer: {
height: 40,
},
2020-10-15 19:25:24 +03:00
image: {
width: 99,
height: 94,
position: 'absolute',
bottom: 0,
2021-03-27 06:47:01 -04:00
right: 0,
2020-10-15 19:25:24 +03:00
},
br: {
backgroundColor: 'transparent',
},
label: {
backgroundColor: 'transparent',
fontSize: 19,
2021-03-27 06:31:05 -04:00
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
2020-10-15 19:25:24 +03:00
},
balance: {
backgroundColor: 'transparent',
fontWeight: 'bold',
fontSize: 36,
2021-03-27 06:31:05 -04:00
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
2020-10-15 19:25:24 +03:00
},
latestTx: {
backgroundColor: 'transparent',
fontSize: 13,
2021-03-27 06:31:05 -04:00
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
2020-10-15 19:25:24 +03:00
},
latestTxTime: {
backgroundColor: 'transparent',
fontWeight: 'bold',
2021-03-27 06:31:05 -04:00
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
2020-10-15 19:25:24 +03:00
fontSize: 16,
},
shadowContainer: {
...Platform.select({
ios: {
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 25 / 100,
shadowRadius: 8,
borderRadius: 12,
},
android: {
elevation: 8,
borderRadius: 12,
},
}),
},
2020-10-15 19:25:24 +03:00
});
export const WalletCarouselItem = React.memo(({ item, _, onPress, handleLongPress, isSelectedWallet, customStyle }) => {
2020-10-15 19:25:24 +03:00
const scaleValue = new Animated.Value(1.0);
const { colors } = useTheme();
const { walletTransactionUpdateStatus } = useStorage();
2021-06-15 23:21:28 -04:00
const { width } = useWindowDimensions();
const itemWidth = width * 0.82 > 375 ? 375 : width * 0.82;
2024-04-18 18:31:06 -04:00
const isLargeScreen = useIsLargeScreen();
2020-10-15 19:25:24 +03:00
const onPressedIn = () => {
2024-01-05 04:18:01 -04:00
Animated.spring(scaleValue, { duration: 50, useNativeDriver: true, toValue: 0.9 }).start();
2020-10-15 19:25:24 +03:00
};
const onPressedOut = () => {
2024-01-05 04:18:01 -04:00
Animated.spring(scaleValue, { duration: 50, useNativeDriver: true, toValue: 1.0 }).start();
2020-10-15 19:25:24 +03:00
};
const opacity = isSelectedWallet === false ? 0.5 : 1.0;
let image;
switch (item.type) {
2021-09-09 12:00:11 +01:00
case LightningLdkWallet.type:
2020-10-15 19:25:24 +03:00
case LightningCustodianWallet.type:
2021-03-27 06:31:05 -04:00
image = I18nManager.isRTL ? require('../img/lnd-shape-rtl.png') : require('../img/lnd-shape.png');
2020-10-15 19:25:24 +03:00
break;
case MultisigHDWallet.type:
2021-03-27 06:31:05 -04:00
image = I18nManager.isRTL ? require('../img/vault-shape-rtl.png') : require('../img/vault-shape.png');
2020-10-15 19:25:24 +03:00
break;
default:
2021-03-27 06:31:05 -04:00
image = I18nManager.isRTL ? require('../img/btc-shape-rtl.png') : require('../img/btc-shape.png');
2020-10-15 19:25:24 +03:00
}
2021-01-27 19:30:42 -05:00
const latestTransactionText =
2024-03-15 23:05:15 +03:00
walletTransactionUpdateStatus === WalletTransactionsStatus.ALL || walletTransactionUpdateStatus === item.getID()
2021-01-27 19:30:42 -05:00
? 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());
2021-01-27 19:30:42 -05:00
const balance = !item.hideBalance && formatBalance(Number(item.getBalance()), item.getPreferredBalanceUnit(), true);
2020-10-15 19:25:24 +03:00
return (
<Animated.View
style={[
isLargeScreen ? iStyles.rootLargeDevice : customStyle ?? { ...iStyles.root, width: itemWidth },
{ opacity, transform: [{ scale: scaleValue }] },
]}
2020-10-15 19:25:24 +03:00
>
<Pressable
accessibilityRole="button"
2020-10-15 19:25:24 +03:00
testID={item.getLabel()}
onPressIn={onPressedIn}
onPressOut={onPressedOut}
onLongPress={handleLongPress}
onPress={() => {
onPressedOut();
2024-01-05 04:18:01 -04:00
setTimeout(() => {
onPress(item); // Replace 'onPress' with your navigation function
}, 50);
2020-10-15 19:25:24 +03:00
}}
>
<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}
2020-10-15 19:25:24 +03:00
</Text>
2021-01-27 19:30:42 -05:00
<Text numberOfLines={1} style={[iStyles.latestTxTime, { color: colors.inverseForegroundColor }]}>
{latestTransactionText}
</Text>
</LinearGradient>
</View>
</Pressable>
2020-10-15 19:25:24 +03:00
</Animated.View>
);
});
2020-10-15 19:25:24 +03:00
WalletCarouselItem.propTypes = {
item: PropTypes.any,
onPress: PropTypes.func.isRequired,
handleLongPress: PropTypes.func,
2020-10-15 19:25:24 +03:00
isSelectedWallet: PropTypes.bool,
};
const cStyles = StyleSheet.create({
content: {
2021-06-15 23:21:28 -04:00
paddingTop: 16,
},
contentLargeScreen: {
paddingHorizontal: 16,
},
separatorStyle: {
width: 16,
height: 20,
},
2020-10-15 19:25:24 +03:00
});
const ListHeaderComponent = () => <View style={cStyles.separatorStyle} />;
2020-10-15 19:25:24 +03:00
const WalletsCarousel = forwardRef((props, ref) => {
2024-04-17 21:05:48 -04:00
const { preferredFiatCurrency, language } = useSettings();
2024-05-11 12:08:23 -04:00
const { horizontal, data, handleLongPress, onPress, selectedWallet, scrollEnabled } = props;
2020-10-15 19:25:24 +03:00
const renderItem = useCallback(
({ item, index }) =>
item ? (
<WalletCarouselItem
isSelectedWallet={!horizontal && selectedWallet ? selectedWallet === item.getID() : undefined}
item={item}
index={index}
handleLongPress={handleLongPress}
onPress={onPress}
/>
) : (
<NewWalletPanel onPress={onPress} />
),
// eslint-disable-next-line react-hooks/exhaustive-deps
[horizontal, selectedWallet, preferredFiatCurrency, language],
2020-10-15 19:25:24 +03:00
);
2021-06-15 23:21:28 -04:00
const flatListRef = useRef();
2020-10-15 19:25:24 +03:00
useImperativeHandle(ref, () => ({
2021-06-15 23:21:28 -04:00
scrollToItem: ({ item }) => {
2021-06-16 02:05:04 -04:00
setTimeout(() => {
flatListRef?.current?.scrollToItem({ item, viewOffset: 16 });
}, 300);
2021-06-15 23:21:28 -04:00
},
scrollToIndex: ({ index }) => {
2021-06-16 02:05:04 -04:00
setTimeout(() => {
flatListRef?.current?.scrollToIndex({ index, viewOffset: 16 });
}, 300);
},
2020-10-15 19:25:24 +03:00
}));
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) {
2021-07-24 12:05:01 -04:00
flatListRef.current.scrollToIndex({ index: error.index, animated: true });
}
}, 100);
};
2020-10-15 19:25:24 +03:00
const { width } = useWindowDimensions();
2022-01-26 10:54:32 -05:00
const sliderHeight = 195;
2021-06-15 23:21:28 -04:00
const itemWidth = width * 0.82 > 375 ? 375 : width * 0.82;
return horizontal ? (
2021-06-15 23:21:28 -04:00
<FlatList
ref={flatListRef}
renderItem={renderItem}
extraData={data}
2021-06-15 23:21:28 -04:00
keyExtractor={(_, index) => index.toString()}
showsVerticalScrollIndicator={false}
pagingEnabled
disableIntervalMomentum={horizontal}
2021-07-01 01:02:45 -04:00
snapToInterval={itemWidth} // Adjust to your content width
2021-06-15 23:21:28 -04:00
decelerationRate="fast"
contentContainerStyle={cStyles.content}
2021-06-15 23:21:28 -04:00
directionalLockEnabled
showsHorizontalScrollIndicator={false}
initialNumToRender={10}
2024-05-11 12:08:23 -04:00
scrollEnabled={scrollEnabled}
2021-07-01 01:02:45 -04:00
ListHeaderComponent={ListHeaderComponent}
style={{ minHeight: sliderHeight + 12 }}
onScrollToIndexFailed={onScrollToIndexFailed}
2021-06-15 23:21:28 -04:00
{...props}
/>
2023-03-27 00:07:46 -04:00
) : (
<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} />
),
)}
2023-03-27 00:07:46 -04:00
</View>
2020-10-15 19:25:24 +03:00
);
});
WalletsCarousel.propTypes = {
horizontal: PropTypes.bool,
selectedWallet: PropTypes.string,
2020-10-15 19:25:24 +03:00
onPress: PropTypes.func.isRequired,
handleLongPress: PropTypes.func.isRequired,
data: PropTypes.array,
2020-10-15 19:25:24 +03:00
};
export default WalletsCarousel;