REF: WalletsCarousel

This commit is contained in:
Ivan 2020-10-15 19:25:24 +03:00 committed by GitHub
parent 82516d995e
commit 15484172a2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 352 additions and 324 deletions

View file

@ -29,8 +29,7 @@ import {
import Clipboard from '@react-native-community/clipboard'; import Clipboard from '@react-native-community/clipboard';
import LinearGradient from 'react-native-linear-gradient'; import LinearGradient from 'react-native-linear-gradient';
import ActionSheet from './screen/ActionSheet'; import ActionSheet from './screen/ActionSheet';
import { LightningCustodianWallet, MultisigHDWallet, PlaceholderWallet } from './class'; import { LightningCustodianWallet, MultisigHDWallet } from './class';
import Carousel from 'react-native-snap-carousel';
import { BitcoinUnit } from './models/bitcoinUnits'; import { BitcoinUnit } from './models/bitcoinUnits';
import * as NavigationService from './NavigationService'; import * as NavigationService from './NavigationService';
import WalletGradient from './class/wallet-gradient'; import WalletGradient from './class/wallet-gradient';
@ -1561,56 +1560,6 @@ export class ManageFundsBigButton extends Component {
} }
} }
export const NewWalletPanel = props => {
const { colors } = useTheme();
return (
<TouchableOpacity testID="CreateAWallet" {...props} onPress={props.onPress} style={{ marginVertical: 17, paddingRight: 10 }}>
<View
style={{
paddingHorizontal: 24,
paddingVertical: 16,
borderRadius: 10,
minHeight: Platform.OS === 'ios' ? 164 : 181,
justifyContent: 'center',
alignItems: 'flex-start',
backgroundColor: WalletGradient.createWallet(),
}}
>
<Text
style={{
fontWeight: '600',
fontSize: 24,
color: colors.foregroundColor,
marginBottom: 4,
}}
>
{loc.wallets.list_create_a_wallet}
</Text>
<Text
style={{
fontSize: 13,
color: colors.alternativeTextColor,
}}
>
{loc.wallets.list_create_a_wallet1}
</Text>
<Text
style={{
backgroundColor: 'transparent',
fontSize: 13,
color: colors.alternativeTextColor,
}}
>
{loc.wallets.list_create_a_wallet2}
</Text>
<View style={{ marginTop: 12, backgroundColor: '#007AFF', paddingHorizontal: 32, paddingVertical: 12, borderRadius: 8 }}>
<Text style={{ color: colors.brandingColor, fontWeight: '500' }}>{loc.wallets.list_create_a_button}</Text>
</View>
</View>
</TouchableOpacity>
);
};
export const BlueTransactionListItem = React.memo(({ item, itemPriceUnit = BitcoinUnit.BTC, timeElapsed }) => { export const BlueTransactionListItem = React.memo(({ item, itemPriceUnit = BitcoinUnit.BTC, timeElapsed }) => {
const [subtitleNumberOfLines, setSubtitleNumberOfLines] = useState(1); const [subtitleNumberOfLines, setSubtitleNumberOfLines] = useState(1);
const { colors } = useTheme(); const { colors } = useTheme();
@ -1824,273 +1773,6 @@ export const BlueTransactionListItem = React.memo(({ item, itemPriceUnit = Bitco
); );
}); });
const WalletCarouselItem = ({ item, index, onPress, handleLongPress, isSelectedWallet }) => {
const scaleValue = new Animated.Value(1.0);
const onPressedIn = () => {
const props = { duration: 50 };
props.useNativeDriver = true;
props.toValue = 0.9;
Animated.spring(scaleValue, props).start();
};
const onPressedOut = () => {
const props = { duration: 50 };
props.useNativeDriver = true;
props.toValue = 1.0;
Animated.spring(scaleValue, props).start();
};
if (!item) {
return (
<NewWalletPanel
onPress={() => {
onPressedOut();
onPress(index);
}}
/>
);
}
if (item.type === PlaceholderWallet.type) {
return (
<Animated.View
style={{ paddingRight: 10, marginVertical: 17, transform: [{ scale: scaleValue }] }}
shadowOpacity={40 / 100}
shadowOffset={{ width: 0, height: 0 }}
shadowRadius={5}
>
<TouchableWithoutFeedback
onPressIn={item.getIsFailure() ? onPressedIn : null}
onPressOut={item.getIsFailure() ? onPressedOut : null}
onPress={() => {
if (item.getIsFailure()) {
onPressedOut();
onPress(index);
onPressedOut();
}
}}
>
<LinearGradient
shadowColor={BlueCurrentTheme.colors.shadowColor}
colors={WalletGradient.gradientsFor(item.type)}
style={{
padding: 15,
borderRadius: 10,
minHeight: 164,
elevation: 5,
}}
>
<Image
source={require('./img/btc-shape.png')}
style={{
width: 99,
height: 94,
position: 'absolute',
bottom: 0,
right: 0,
}}
/>
<Text style={{ backgroundColor: 'transparent' }} />
<Text
numberOfLines={1}
style={{
backgroundColor: 'transparent',
fontSize: 19,
color: BlueCurrentTheme.colors.inverseForegroundColor,
}}
>
{item.getLabel()}
</Text>
{item.getIsFailure() ? (
<Text
numberOfLines={0}
style={{
backgroundColor: 'transparent',
fontSize: 19,
marginTop: 40,
color: BlueCurrentTheme.colors.inverseForegroundColor,
}}
>
{loc.wallets.list_import_error}
</Text>
) : (
<ActivityIndicator style={{ marginTop: 40 }} />
)}
</LinearGradient>
</TouchableWithoutFeedback>
</Animated.View>
);
} else {
let opacity = 1.0;
if (isSelectedWallet === false) {
opacity = 0.5;
}
return (
<Animated.View
style={{ paddingRight: 10, marginVertical: 17, transform: [{ scale: scaleValue }], opacity }}
shadowOpacity={40 / 100}
shadowOffset={{ width: 0, height: 0 }}
shadowRadius={5}
>
<TouchableWithoutFeedback
testID={item.getLabel()}
onPressIn={onPressedIn}
onPressOut={onPressedOut}
onLongPress={handleLongPress}
onPress={() => {
onPressedOut();
onPress(index);
onPressedOut();
}}
>
<LinearGradient
shadowColor={BlueCurrentTheme.colors.shadowColor}
colors={WalletGradient.gradientsFor(item.type)}
style={{
padding: 15,
borderRadius: 10,
minHeight: 164,
elevation: 5,
}}
>
<Image
source={(() => {
switch (item.type) {
case LightningCustodianWallet.type:
return require('./img/lnd-shape.png');
case MultisigHDWallet.type:
return require('./img/vault-shape.png');
default:
return require('./img/btc-shape.png');
}
})()}
style={{
width: 99,
height: 94,
position: 'absolute',
bottom: 0,
right: 0,
}}
/>
<Text style={{ backgroundColor: 'transparent' }} />
<Text
numberOfLines={1}
style={{
backgroundColor: 'transparent',
fontSize: 19,
color: BlueCurrentTheme.colors.inverseForegroundColor,
}}
>
{item.getLabel()}
</Text>
{item.hideBalance ? (
<BluePrivateBalance />
) : (
<Text
numberOfLines={1}
adjustsFontSizeToFit
style={{
backgroundColor: 'transparent',
fontWeight: 'bold',
fontSize: 36,
color: BlueCurrentTheme.colors.inverseForegroundColor,
}}
>
{formatBalance(Number(item.getBalance()), item.getPreferredBalanceUnit(), true)}
</Text>
)}
<Text style={{ backgroundColor: 'transparent' }} />
<Text
numberOfLines={1}
style={{
backgroundColor: 'transparent',
fontSize: 13,
color: BlueCurrentTheme.colors.inverseForegroundColor,
}}
>
{loc.wallets.list_latest_transaction}
</Text>
<Text
numberOfLines={1}
style={{
backgroundColor: 'transparent',
fontWeight: 'bold',
fontSize: 16,
color: BlueCurrentTheme.colors.inverseForegroundColor,
}}
>
{transactionTimeToReadable(item.getLatestTransactionTime())}
</Text>
</LinearGradient>
</TouchableWithoutFeedback>
</Animated.View>
);
}
};
const sliderWidth = width * 1;
const itemWidth = width * 0.82 > 375 ? 375 : width * 0.82;
const sliderHeight = 190;
export class WalletsCarousel extends Component {
walletsCarousel = React.createRef();
state = { isLoading: true };
_renderItem = ({ item, index }) => {
return (
<WalletCarouselItem
isSelectedWallet={this.props.vertical && this.props.selectedWallet && item ? this.props.selectedWallet === item.getID() : undefined}
item={item}
index={index}
handleLongPress={this.props.handleLongPress}
onPress={this.props.onPress}
/>
);
};
snapToItem = item => {
// eslint-disable-next-line no-unused-expressions
this.walletsCarousel?.current?.snapToItem(item);
};
onLayout = () => {
this.setState({ isLoading: false });
};
render() {
return (
<>
{this.state.isLoading && (
<View
style={{ paddingVertical: sliderHeight / 2, paddingHorizontal: sliderWidth / 2, position: 'absolute', alignItems: 'center' }}
>
<ActivityIndicator />
</View>
)}
<Carousel
ref={this.walletsCarousel}
renderItem={this._renderItem}
sliderWidth={sliderWidth}
sliderHeight={sliderHeight}
itemWidth={itemWidth}
inactiveSlideScale={1}
inactiveSlideOpacity={0.7}
activeSlideAlignment="start"
initialNumToRender={10}
onLayout={this.onLayout}
contentContainerCustomStyle={{ left: 20 }}
{...this.props}
/>
</>
);
}
}
const isDesktop = getSystemName() === 'Mac OS X'; const isDesktop = getSystemName() === 'Mac OS X';
export class BlueAddressInput extends Component { export class BlueAddressInput extends Component {
static propTypes = { static propTypes = {

View file

@ -48,7 +48,7 @@ function WatchConnectivity() {
watchEvents.on('message', handleMessages); watchEvents.on('message', handleMessages);
} }
}); });
}; }
WatchConnectivity.sendWalletsToWatch = () => { WatchConnectivity.sendWalletsToWatch = () => {
getIsWatchAppInstalled().then(installed => { getIsWatchAppInstalled().then(installed => {

View file

@ -0,0 +1,345 @@
import React, { useRef, useCallback, useState, useImperativeHandle, forwardRef } from 'react';
import PropTypes from 'prop-types';
import {
ActivityIndicator,
Animated,
Image,
Platform,
StyleSheet,
Text,
TouchableOpacity,
TouchableWithoutFeedback,
useWindowDimensions,
View,
} from 'react-native';
import { useTheme } from '@react-navigation/native';
import LinearGradient from 'react-native-linear-gradient';
import Carousel from 'react-native-snap-carousel';
import loc, { formatBalance, transactionTimeToReadable } from '../loc';
import { LightningCustodianWallet, MultisigHDWallet, PlaceholderWallet } from '../class';
import WalletGradient from '../class/wallet-gradient';
import { BlueCurrentTheme } from './themes';
import { BluePrivateBalance } from '../BlueComponents';
const nStyles = StyleSheet.create({
root: {
marginVertical: 17,
paddingRight: 10,
},
container: {
paddingHorizontal: 24,
paddingVertical: 16,
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();
return (
<TouchableOpacity testID="CreateAWallet" onPress={onPress} style={nStyles.root}>
<View style={[nStyles.container, { backgroundColor: WalletGradient.createWallet() }]}>
<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: 10,
marginVertical: 17,
},
grad: {
padding: 15,
borderRadius: 10,
minHeight: 164,
elevation: 5,
},
image: {
width: 99,
height: 94,
position: 'absolute',
bottom: 0,
right: 0,
},
br: {
backgroundColor: 'transparent',
},
label: {
backgroundColor: 'transparent',
fontSize: 19,
},
importError: {
backgroundColor: 'transparent',
fontSize: 19,
marginTop: 40,
},
activity: {
marginTop: 40,
},
balance: {
backgroundColor: 'transparent',
fontWeight: 'bold',
fontSize: 36,
},
latestTx: {
backgroundColor: 'transparent',
fontSize: 13,
},
latestTxTime: {
backgroundColor: 'transparent',
fontWeight: 'bold',
fontSize: 16,
},
});
const WalletCarouselItem = ({ item, index, onPress, handleLongPress, isSelectedWallet }) => {
const scaleValue = new Animated.Value(1.0);
const onPressedIn = () => {
const props = { duration: 50 };
props.useNativeDriver = true;
props.toValue = 0.9;
Animated.spring(scaleValue, props).start();
};
const onPressedOut = () => {
const props = { duration: 50 };
props.useNativeDriver = true;
props.toValue = 1.0;
Animated.spring(scaleValue, props).start();
};
if (!item)
return (
<NewWalletPanel
onPress={() => {
onPressedOut();
onPress(index);
}}
/>
);
if (item.type === PlaceholderWallet.type) {
return (
<Animated.View
style={[iStyles.root, { transform: [{ scale: scaleValue }] }]}
shadowOpacity={40 / 100}
shadowOffset={{ width: 0, height: 0 }}
shadowRadius={5}
>
<TouchableWithoutFeedback
onPressIn={item.getIsFailure() ? onPressedIn : null}
onPressOut={item.getIsFailure() ? onPressedOut : null}
onPress={() => {
if (item.getIsFailure()) {
onPressedOut();
onPress(index);
onPressedOut();
}
}}
>
<LinearGradient
shadowColor={BlueCurrentTheme.colors.shadowColor}
colors={WalletGradient.gradientsFor(item.type)}
style={iStyles.grad}
>
<Image source={require('../img/btc-shape.png')} style={iStyles.image} />
<Text style={iStyles.br} />
<Text numberOfLines={1} style={[iStyles.label, { color: BlueCurrentTheme.colors.inverseForegroundColor }]}>
{item.getLabel()}
</Text>
{item.getIsFailure() ? (
<Text numberOfLines={0} style={[iStyles.importError, { color: BlueCurrentTheme.colors.inverseForegroundColor }]}>
{loc.wallets.list_import_error}
</Text>
) : (
<ActivityIndicator style={iStyles.activity} />
)}
</LinearGradient>
</TouchableWithoutFeedback>
</Animated.View>
);
}
const opacity = isSelectedWallet === false ? 0.5 : 1.0;
let image;
switch (item.type) {
case LightningCustodianWallet.type:
image = require('../img/lnd-shape.png');
break;
case MultisigHDWallet.type:
image = require('../img/vault-shape.png');
break;
default:
image = require('../img/btc-shape.png');
}
return (
<Animated.View
style={[iStyles.root, { opacity, transform: [{ scale: scaleValue }] }]}
shadowOpacity={40 / 100}
shadowOffset={{ width: 0, height: 0 }}
shadowRadius={5}
>
<TouchableWithoutFeedback
testID={item.getLabel()}
onPressIn={onPressedIn}
onPressOut={onPressedOut}
onLongPress={handleLongPress}
onPress={() => {
onPressedOut();
onPress(index);
onPressedOut();
}}
>
<LinearGradient
shadowColor={BlueCurrentTheme.colors.shadowColor}
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: BlueCurrentTheme.colors.inverseForegroundColor }]}>
{item.getLabel()}
</Text>
{item.hideBalance ? (
<BluePrivateBalance />
) : (
<Text
numberOfLines={1}
adjustsFontSizeToFit
style={[iStyles.balance, { color: BlueCurrentTheme.colors.inverseForegroundColor }]}
>
{formatBalance(Number(item.getBalance()), item.getPreferredBalanceUnit(), true)}
</Text>
)}
<Text style={iStyles.br} />
<Text numberOfLines={1} style={[iStyles.latestTx, { color: BlueCurrentTheme.colors.inverseForegroundColor }]}>
{loc.wallets.list_latest_transaction}
</Text>
<Text numberOfLines={1} style={[iStyles.latestTxTime, { color: BlueCurrentTheme.colors.inverseForegroundColor }]}>
{transactionTimeToReadable(item.getLatestTransactionTime())}
</Text>
</LinearGradient>
</TouchableWithoutFeedback>
</Animated.View>
);
};
WalletCarouselItem.propTypes = {
item: PropTypes.any,
index: PropTypes.number.isRequired,
onPress: PropTypes.func.isRequired,
handleLongPress: PropTypes.func.isRequired,
isSelectedWallet: PropTypes.bool,
};
const cStyles = StyleSheet.create({
loading: {
position: 'absolute',
alignItems: 'center',
},
content: {
left: 20,
},
});
const WalletsCarousel = forwardRef((props, ref) => {
const carouselRef = useRef();
const [loading, setLoading] = useState(true);
const renderItem = useCallback(
({ item, index }) => (
<WalletCarouselItem
isSelectedWallet={props.vertical && props.selectedWallet && item ? props.selectedWallet === item.getID() : undefined}
item={item}
index={index}
handleLongPress={props.handleLongPress}
onPress={props.onPress}
/>
),
[props.vertical, props.selectedWallet, props.handleLongPress, props.onPress],
);
useImperativeHandle(ref, () => ({
snapToItem: item => carouselRef?.current?.snapToItem(item),
}));
const { width } = useWindowDimensions();
const sliderWidth = width * 1;
const itemWidth = width * 0.82 > 375 ? 375 : width * 0.82;
const sliderHeight = 190;
const onLayout = () => setLoading(false);
return (
<>
{loading && (
<View
style={[
cStyles.loading,
{
paddingVertical: sliderHeight / 2,
paddingHorizontal: sliderWidth / 2,
},
]}
>
<ActivityIndicator />
</View>
)}
<Carousel
ref={carouselRef}
renderItem={renderItem}
sliderWidth={sliderWidth}
sliderHeight={sliderHeight}
itemWidth={itemWidth}
inactiveSlideScale={1}
inactiveSlideOpacity={0.7}
activeSlideAlignment="start"
initialNumToRender={10}
onLayout={onLayout}
contentContainerCustomStyle={cStyles.content}
{...props}
/>
</>
);
});
WalletsCarousel.propTypes = {
vertical: PropTypes.bool,
selectedWallet: PropTypes.object,
onPress: PropTypes.func.isRequired,
handleLongPress: PropTypes.func.isRequired,
};
export default WalletsCarousel;

View file

@ -363,8 +363,7 @@
"import_title": "import", "import_title": "import",
"list_create_a_button": "Add now", "list_create_a_button": "Add now",
"list_create_a_wallet": "Add a wallet", "list_create_a_wallet": "Add a wallet",
"list_create_a_wallet1": "It's free and you can create", "list_create_a_wallet_text": "It's free and you can create \nas many as you like",
"list_create_a_wallet2": "as many as you like",
"list_empty_txs1": "Your transactions will appear here", "list_empty_txs1": "Your transactions will appear here",
"list_empty_txs1_lightning": "Lightning wallet should be used for your daily transactions. Fees are unfairly cheap and speed is blazing fast.", "list_empty_txs1_lightning": "Lightning wallet should be used for your daily transactions. Fees are unfairly cheap and speed is blazing fast.",
"list_empty_txs2": "Start with your wallet", "list_empty_txs2": "Start with your wallet",

View file

@ -1,7 +1,8 @@
import React, { useRef } from 'react'; import React, { useRef } from 'react';
import { StatusBar, View, TouchableOpacity, StyleSheet, Alert, useWindowDimensions } from 'react-native'; import { StatusBar, View, TouchableOpacity, StyleSheet, Alert, useWindowDimensions } from 'react-native';
import { DrawerContentScrollView } from '@react-navigation/drawer'; import { DrawerContentScrollView } from '@react-navigation/drawer';
import { WalletsCarousel, BlueNavigationStyle, BlueHeaderDefaultMain } from '../../BlueComponents'; import { BlueNavigationStyle, BlueHeaderDefaultMain } from '../../BlueComponents';
import WalletsCarousel from '../../components/WalletsCarousel';
import { Icon } from 'react-native-elements'; import { Icon } from 'react-native-elements';
import ReactNativeHapticFeedback from 'react-native-haptic-feedback'; import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';

View file

@ -13,7 +13,8 @@ import {
Dimensions, Dimensions,
useWindowDimensions, useWindowDimensions,
} from 'react-native'; } from 'react-native';
import { WalletsCarousel, BlueHeaderDefaultMain, BlueTransactionListItem } from '../../BlueComponents'; import { BlueHeaderDefaultMain, BlueTransactionListItem } from '../../BlueComponents';
import WalletsCarousel from '../../components/WalletsCarousel';
import { Icon } from 'react-native-elements'; import { Icon } from 'react-native-elements';
import DeeplinkSchemaMatch from '../../class/deeplink-schema-match'; import DeeplinkSchemaMatch from '../../class/deeplink-schema-match';
import ReactNativeHapticFeedback from 'react-native-haptic-feedback'; import ReactNativeHapticFeedback from 'react-native-haptic-feedback';