BlueWallet/BlueComponents.js

1200 lines
36 KiB
JavaScript
Raw Normal View History

/* eslint react/prop-types: "off", react-native/no-inline-styles: "off" */
import React, { Component, forwardRef } from 'react';
import PropTypes from 'prop-types';
2020-09-22 03:53:28 +02:00
import { Icon, Input, Text, Header, ListItem, Avatar } from 'react-native-elements';
2018-12-12 04:33:28 +01:00
import {
ActivityIndicator,
2020-09-09 19:55:13 +02:00
Alert,
Animated,
2018-12-12 04:33:28 +01:00
Dimensions,
Image,
2019-01-31 07:52:21 +01:00
InputAccessoryView,
2020-09-09 19:55:13 +02:00
Keyboard,
KeyboardAvoidingView,
PixelRatio,
2018-12-12 04:33:28 +01:00
Platform,
2020-10-08 09:55:17 +02:00
PlatformColor,
2020-09-09 19:55:13 +02:00
SafeAreaView,
StyleSheet,
2020-09-22 03:53:28 +02:00
Switch,
TextInput,
2020-09-09 19:55:13 +02:00
TouchableOpacity,
View,
2021-03-19 03:30:01 +01:00
I18nManager,
ImageBackground,
2018-12-12 04:33:28 +01:00
} from 'react-native';
2021-02-28 06:13:37 +01:00
import Clipboard from '@react-native-clipboard/clipboard';
2020-09-14 12:49:08 +02:00
import NetworkTransactionFees, { NetworkTransactionFee, NetworkTransactionFeeType } from './models/networkTransactionFees';
2020-12-16 04:15:57 +01:00
import AsyncStorage from '@react-native-async-storage/async-storage';
import { useTheme } from '@react-navigation/native';
2020-07-15 19:32:59 +02:00
import { BlueCurrentTheme } from './components/themes';
import loc, { formatStringAddTwoWhiteSpaces } from './loc';
2018-05-12 22:27:34 +02:00
const { height, width } = Dimensions.get('window');
const aspectRatio = height / width;
let isIpad;
if (aspectRatio > 1.6) {
isIpad = false;
} else {
isIpad = true;
}
2020-10-07 14:24:36 +02:00
// eslint-disable-next-line no-unused-expressions
Platform.OS === 'android' ? (ActivityIndicator.defaultProps.color = PlatformColor('?attr/colorControlActivated')) : null;
2018-01-30 23:42:38 +01:00
2020-10-06 19:54:35 +02:00
export const BlueButton = props => {
const { colors } = useTheme();
let backgroundColor = props.backgroundColor ? props.backgroundColor : colors.mainColor || BlueCurrentTheme.colors.mainColor;
let fontColor = props.buttonTextColor || colors.buttonTextColor;
2020-10-06 19:54:35 +02:00
if (props.disabled === true) {
backgroundColor = colors.buttonDisabledBackgroundColor;
fontColor = colors.buttonDisabledTextColor;
}
2020-10-06 19:54:35 +02:00
return (
<TouchableOpacity
style={{
borderWidth: 0.7,
borderColor: 'transparent',
backgroundColor: backgroundColor,
minHeight: 45,
height: 45,
maxHeight: 45,
borderRadius: 25,
justifyContent: 'center',
alignItems: 'center',
2020-11-22 09:48:16 +01:00
paddingHorizontal: 16,
2021-09-17 14:01:12 +02:00
flexGrow: 1,
2020-10-06 19:54:35 +02:00
}}
accessibilityRole="button"
2020-10-06 19:54:35 +02:00
{...props}
>
<View style={{ flexDirection: 'row', justifyContent: 'center', alignItems: 'center' }}>
{props.icon && <Icon name={props.icon.name} type={props.icon.type} color={props.icon.color} />}
{props.title && <Text style={{ marginHorizontal: 8, fontSize: 16, color: fontColor, fontWeight: '500' }}>{props.title}</Text>}
2020-10-06 19:54:35 +02:00
</View>
</TouchableOpacity>
);
};
2020-07-15 19:32:59 +02:00
export const SecondButton = forwardRef((props, ref) => {
const { colors } = useTheme();
let backgroundColor = props.backgroundColor ? props.backgroundColor : colors.buttonBlueBackgroundColor;
let fontColor = colors.buttonTextColor;
if (props.disabled === true) {
backgroundColor = colors.buttonDisabledBackgroundColor;
fontColor = colors.buttonDisabledTextColor;
2018-07-14 20:54:27 +02:00
}
2020-11-22 09:48:16 +01:00
return (
<TouchableOpacity
accessibilityRole="button"
style={{
borderWidth: 0.7,
borderColor: 'transparent',
backgroundColor: backgroundColor,
minHeight: 45,
height: 45,
maxHeight: 45,
borderRadius: 25,
justifyContent: 'center',
alignItems: 'center',
2021-09-17 14:01:12 +02:00
paddingHorizontal: 16,
flexGrow: 1,
}}
{...props}
ref={ref}
>
<View style={{ flexDirection: 'row', justifyContent: 'center', alignItems: 'center' }}>
{props.icon && <Icon name={props.icon.name} type={props.icon.type} color={props.icon.color} />}
{props.title && <Text style={{ marginHorizontal: 8, fontSize: 16, color: fontColor }}>{props.title}</Text>}
</View>
</TouchableOpacity>
);
});
2018-07-14 20:54:27 +02:00
export const BitcoinButton = props => {
const { colors } = useTheme();
return (
<TouchableOpacity accessibilityRole="button" testID={props.testID} onPress={props.onPress}>
<View
style={{
borderColor: (props.active && colors.newBlue) || colors.buttonDisabledBackgroundColor,
borderWidth: 1.5,
borderRadius: 8,
backgroundColor: colors.buttonDisabledBackgroundColor,
minWidth: props.style.width,
minHeight: props.style.height,
height: props.style.height,
flex: 1,
marginBottom: 8,
2018-07-14 20:54:27 +02:00
}}
>
<View style={{ marginHorizontal: 16, marginVertical: 10, flexDirection: 'row', alignItems: 'center' }}>
<View>
<Image style={{ width: 34, height: 34, marginRight: 8 }} source={require('./img/addWallet/bitcoin.png')} />
</View>
<View style={{ flex: 1 }}>
2021-05-22 05:36:34 +02:00
<Text style={{ color: colors.newBlue, fontWeight: 'bold', fontSize: 18, writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr' }}>
{loc.wallets.add_bitcoin}
</Text>
<Text
style={{
color: colors.alternativeTextColor,
fontSize: 13,
fontWeight: '500',
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
}}
>
{loc.wallets.add_bitcoin_explain}
</Text>
</View>
</View>
</View>
</TouchableOpacity>
);
};
export const VaultButton = props => {
const { colors } = useTheme();
return (
<TouchableOpacity accessibilityRole="button" testID={props.testID} onPress={props.onPress}>
<View
style={{
borderColor: (props.active && colors.foregroundColor) || colors.buttonDisabledBackgroundColor,
borderWidth: 1.5,
borderRadius: 8,
backgroundColor: colors.buttonDisabledBackgroundColor,
minWidth: props.style.width,
minHeight: props.style.height,
height: props.style.height,
flex: 1,
}}
>
<View style={{ marginHorizontal: 16, marginVertical: 10, flexDirection: 'row', alignItems: 'center' }}>
<View>
<Image style={{ width: 34, height: 34, marginRight: 8 }} source={require('./img/addWallet/vault.png')} />
</View>
<View style={{ flex: 1 }}>
2021-05-22 05:36:34 +02:00
<Text
style={{
color: colors.foregroundColor,
fontWeight: 'bold',
fontSize: 18,
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
}}
>
{loc.multisig.multisig_vault}
</Text>
<Text
style={{
color: colors.alternativeTextColor,
fontSize: 13,
fontWeight: '500',
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
}}
>
{loc.multisig.multisig_vault_explain}
</Text>
</View>
2018-07-14 20:54:27 +02:00
</View>
</View>
</TouchableOpacity>
);
};
2018-07-14 20:54:27 +02:00
export const LightningButton = props => {
const { colors } = useTheme();
return (
<TouchableOpacity accessibilityRole="button" onPress={props.onPress}>
<View
style={{
borderColor: (props.active && colors.lnborderColor) || colors.buttonDisabledBackgroundColor,
borderWidth: 1.5,
borderRadius: 8,
backgroundColor: colors.buttonDisabledBackgroundColor,
minWidth: props.style.width,
minHeight: props.style.height,
height: props.style.height,
flex: 1,
marginBottom: 8,
2018-07-14 20:54:27 +02:00
}}
>
<View style={{ marginHorizontal: 16, marginVertical: 10, flexDirection: 'row', alignItems: 'center' }}>
<View>
<Image style={{ width: 34, height: 34, marginRight: 8 }} source={require('./img/addWallet/lightning.png')} />
</View>
<View style={{ flex: 1 }}>
2021-05-22 05:36:34 +02:00
<Text
style={{ color: colors.lnborderColor, fontWeight: 'bold', fontSize: 18, writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr' }}
>
{loc.wallets.add_lightning}
</Text>
<Text
style={{
color: colors.alternativeTextColor,
fontSize: 13,
fontWeight: '500',
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
}}
>
{loc.wallets.add_lightning_explain}
</Text>
</View>
2018-07-14 20:54:27 +02:00
</View>
</View>
</TouchableOpacity>
);
};
2018-07-14 20:54:27 +02:00
/**
* TODO: remove this comment once this file gets properly converted to typescript.
*
* @type {React.FC<any>}
*/
export const BlueButtonLink = forwardRef((props, ref) => {
2020-07-15 19:32:59 +02:00
const { colors } = useTheme();
return (
<TouchableOpacity
accessibilityRole="button"
2020-07-15 19:32:59 +02:00
style={{
minHeight: 60,
minWidth: 100,
justifyContent: 'center',
}}
{...props}
ref={ref}
2020-07-15 19:32:59 +02:00
>
<Text style={{ color: colors.foregroundColor, textAlign: 'center', fontSize: 16 }}>{props.title}</Text>
2020-07-15 19:32:59 +02:00
</TouchableOpacity>
);
});
2020-07-15 19:32:59 +02:00
export const BlueAlertWalletExportReminder = ({ onSuccess = () => {}, onFailure }) => {
Alert.alert(
2020-07-23 15:09:48 +02:00
loc.wallets.details_title,
loc.pleasebackup.ask,
[
2020-07-23 15:09:48 +02:00
{ text: loc.pleasebackup.ask_yes, onPress: onSuccess, style: 'cancel' },
{ text: loc.pleasebackup.ask_no, onPress: onFailure },
],
{ cancelable: false },
);
};
2019-08-03 23:29:15 +02:00
export const BluePrivateBalance = () => {
return (
<View style={{ flexDirection: 'row', alignItems: 'center', marginTop: 13, borderRadius: 9 }}>
<ImageBackground
blurRadius={6}
style={{ backgroundColor: '#FFFFFF', opacity: 0.5, height: 30, width: 110, marginRight: 8, borderRadius: 9 }}
/>
<Icon name="eye-slash" type="font-awesome" color="#FFFFFF" />
</View>
);
2019-08-03 23:29:15 +02:00
};
2019-09-27 16:49:56 +02:00
export const BlueCopyToClipboardButton = ({ stringToCopy, displayText = false }) => {
2018-09-29 06:58:09 +02:00
return (
<TouchableOpacity accessibilityRole="button" onPress={() => Clipboard.setString(stringToCopy)}>
2020-07-20 15:38:46 +02:00
<Text style={{ fontSize: 13, fontWeight: '400', color: '#68bbe1' }}>{displayText || loc.transactions.details_copy}</Text>
2018-09-29 06:58:09 +02:00
</TouchableOpacity>
);
};
2019-01-21 14:55:39 +01:00
export class BlueCopyTextToClipboard extends Component {
static propTypes = {
text: PropTypes.string,
truncated: PropTypes.bool,
2019-01-21 14:55:39 +01:00
};
static defaultProps = {
text: '',
truncated: false,
2019-01-21 14:55:39 +01:00
};
2019-02-02 04:45:18 +01:00
constructor(props) {
super(props);
this.state = { hasTappedText: false, address: props.text };
}
2019-05-02 22:33:03 +02:00
static getDerivedStateFromProps(props, state) {
if (state.hasTappedText) {
return { hasTappedText: state.hasTappedText, address: state.address, truncated: props.truncated };
2019-05-02 22:33:03 +02:00
} else {
return { hasTappedText: state.hasTappedText, address: props.text, truncated: props.truncated };
2019-05-02 22:33:03 +02:00
}
}
2019-01-21 14:55:39 +01:00
copyToClipboard = () => {
2019-02-02 04:45:18 +01:00
this.setState({ hasTappedText: true }, () => {
2019-01-21 14:55:39 +01:00
Clipboard.setString(this.props.text);
2020-07-20 15:38:46 +02:00
this.setState({ address: loc.wallets.xpub_copiedToClipboard }, () => {
2019-02-02 04:45:18 +01:00
setTimeout(() => {
this.setState({ hasTappedText: false, address: this.props.text });
}, 1000);
});
2019-01-21 14:55:39 +01:00
});
};
render() {
return (
<View style={{ justifyContent: 'center', alignItems: 'center', paddingHorizontal: 16 }}>
<TouchableOpacity
accessibilityRole="button"
onPress={this.copyToClipboard}
disabled={this.state.hasTappedText}
testID="BlueCopyTextToClipboard"
>
<Animated.Text
style={styleCopyTextToClipboard.address}
{...(this.props.truncated ? { numberOfLines: 1, ellipsizeMode: 'middle' } : { numberOfLines: 0 })}
testID="AddressValue"
>
2019-02-02 04:45:18 +01:00
{this.state.address}
</Animated.Text>
2019-01-21 14:55:39 +01:00
</TouchableOpacity>
</View>
);
}
}
const styleCopyTextToClipboard = StyleSheet.create({
address: {
marginVertical: 32,
fontSize: 15,
color: '#9aa0aa',
2019-01-21 14:55:39 +01:00
textAlign: 'center',
},
});
2020-11-25 04:03:11 +01:00
export const SafeBlueArea = props => {
const { style, ...nonStyleProps } = props;
2020-11-25 04:03:11 +01:00
const { colors } = useTheme();
2021-03-23 23:39:19 +01:00
const baseStyle = { flex: 1, backgroundColor: colors.background };
return <SafeAreaView forceInset={{ horizontal: 'always' }} style={[baseStyle, style]} {...nonStyleProps} />;
2020-11-25 04:03:11 +01:00
};
2018-01-30 23:42:38 +01:00
2020-11-30 05:18:54 +01:00
export const BlueCard = props => {
return <View {...props} style={{ padding: 20 }} />;
};
2018-01-30 23:42:38 +01:00
2020-11-22 09:04:04 +01:00
export const BlueText = props => {
2020-07-15 19:32:59 +02:00
const { colors } = useTheme();
const style = StyleSheet.compose({ color: colors.foregroundColor, writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr' }, props.style);
return <Text {...props} style={style} />;
2020-07-15 19:32:59 +02:00
};
2018-01-30 23:42:38 +01:00
2020-11-25 04:01:51 +01:00
export const BlueTextCentered = props => {
const { colors } = useTheme();
return <Text {...props} style={{ color: colors.foregroundColor, textAlign: 'center' }} />;
};
2020-09-22 03:53:28 +02:00
export const BlueListItem = React.memo(props => {
2020-07-15 19:32:59 +02:00
const { colors } = useTheme();
2021-03-16 03:07:52 +01:00
2020-07-15 19:32:59 +02:00
return (
<ListItem
2020-09-22 03:53:28 +02:00
containerStyle={props.containerStyle ?? { backgroundColor: 'transparent' }}
Component={props.Component ?? TouchableOpacity}
bottomDivider={props.bottomDivider !== undefined ? props.bottomDivider : true}
topDivider={props.topDivider !== undefined ? props.topDivider : false}
2020-09-22 03:53:28 +02:00
testID={props.testID}
onPress={props.onPress}
onLongPress={props.onLongPress}
2020-11-10 01:51:27 +01:00
disabled={props.disabled}
2021-03-16 03:07:52 +01:00
accessible={props.switch === undefined}
2020-09-22 03:53:28 +02:00
>
{props.leftAvatar && <Avatar>{props.leftAvatar}</Avatar>}
{props.leftIcon && <Avatar icon={props.leftIcon} />}
<ListItem.Content>
<ListItem.Title
style={{
color: props.disabled ? colors.buttonDisabledTextColor : colors.foregroundColor,
fontSize: 16,
fontWeight: '500',
2021-08-26 20:00:23 +02:00
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
2020-09-22 03:53:28 +02:00
}}
numberOfLines={0}
2021-03-16 03:07:52 +01:00
accessible={props.switch === undefined}
2020-09-22 03:53:28 +02:00
>
{props.title}
</ListItem.Title>
{props.subtitle && (
<ListItem.Subtitle
2021-02-27 00:26:18 +01:00
numberOfLines={props.subtitleNumberOfLines ?? 1}
2021-03-16 03:07:52 +01:00
accessible={props.switch === undefined}
2021-08-26 20:00:23 +02:00
style={{
flexWrap: 'wrap',
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
color: colors.alternativeTextColor,
fontWeight: '400',
fontSize: 14,
}}
2020-09-22 03:53:28 +02:00
>
{props.subtitle}
</ListItem.Subtitle>
)}
</ListItem.Content>
2021-09-09 13:00:11 +02:00
{props.rightTitle && (
<ListItem.Content right>
2020-09-22 03:53:28 +02:00
<ListItem.Title style={props.rightTitleStyle} numberOfLines={0} right>
{props.rightTitle}
</ListItem.Title>
2021-09-09 13:00:11 +02:00
</ListItem.Content>
)}
{props.isLoading ? (
<ActivityIndicator />
) : (
<>
2021-03-19 03:30:01 +01:00
{props.chevron && <ListItem.Chevron iconStyle={{ transform: [{ scaleX: I18nManager.isRTL ? -1 : 1 }] }} />}
{props.rightIcon && <Avatar icon={props.rightIcon} />}
2021-03-16 03:07:52 +01:00
{props.switch && <Switch {...props.switch} accessibilityLabel={props.title} accessible accessibilityRole="switch" />}
{props.checkmark && <ListItem.CheckBox iconType="octaicon" checkedColor="#0070FF" checkedIcon="check" checked />}
</>
)}
2020-09-22 03:53:28 +02:00
</ListItem>
2020-07-15 19:32:59 +02:00
);
2020-09-22 03:53:28 +02:00
});
2020-07-15 19:32:59 +02:00
export const BlueFormLabel = props => {
const { colors } = useTheme();
2021-05-22 05:36:34 +02:00
return (
<Text
{...props}
style={{
color: colors.foregroundColor,
fontWeight: '400',
marginHorizontal: 20,
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
}}
/>
);
};
2018-01-30 23:42:38 +01:00
2020-11-30 05:18:54 +01:00
export const BlueFormInput = props => {
const { colors } = useTheme();
return (
<Input
{...props}
inputStyle={{ color: colors.foregroundColor, maxWidth: width - 105 }}
containerStyle={{
marginTop: 5,
borderColor: colors.inputBorderColor,
borderBottomColor: colors.inputBorderColor,
borderWidth: 0.5,
borderBottomWidth: 0.5,
backgroundColor: colors.inputBackgroundColor,
}}
/>
);
};
2020-11-30 05:18:54 +01:00
export const BlueFormMultiInput = props => {
const { colors } = useTheme();
2020-11-30 05:18:54 +01:00
return (
<TextInput
multiline
underlineColorAndroid="transparent"
numberOfLines={4}
style={{
paddingHorizontal: 8,
paddingVertical: 16,
flex: 1,
marginTop: 5,
marginHorizontal: 20,
borderColor: colors.formBorder,
borderBottomColor: colors.formBorder,
borderWidth: 1,
borderBottomWidth: 0.5,
borderRadius: 4,
backgroundColor: colors.inputBackgroundColor,
color: colors.foregroundColor,
textAlignVertical: 'top',
}}
autoCorrect={false}
autoCapitalize="none"
spellCheck={false}
{...props}
selectTextOnFocus={false}
keyboardType={Platform.OS === 'android' ? 'visible-password' : 'default'}
/>
);
};
2018-05-06 19:12:14 +02:00
2020-11-30 05:18:54 +01:00
export const BlueHeader = props => {
return (
<Header
{...props}
backgroundColor="transparent"
outerContainerStyles={{
borderBottomColor: 'transparent',
borderBottomWidth: 0,
}}
/>
);
};
export const BlueHeaderDefaultSub = props => {
const { colors } = useTheme();
2020-07-15 19:32:59 +02:00
return (
<SafeAreaView>
<Header
backgroundColor={colors.background}
leftContainerStyle={{ minWidth: '100%' }}
outerContainerStyles={{
borderBottomColor: 'transparent',
borderBottomWidth: 0,
}}
leftComponent={
<Text
adjustsFontSizeToFit
style={{
fontWeight: 'bold',
fontSize: 30,
color: colors.foregroundColor,
}}
>
{props.leftText}
</Text>
}
{...props}
/>
</SafeAreaView>
);
};
export const BlueHeaderDefaultMain = props => {
const { colors } = useTheme();
2020-10-07 22:53:50 +02:00
const { isDrawerList } = props;
return (
2021-09-07 23:13:15 +02:00
<View
style={{
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
backgroundColor: isDrawerList ? colors.elevated : colors.background,
paddingHorizontal: 16,
2020-10-07 22:53:50 +02:00
borderTopColor: isDrawerList ? colors.elevated : colors.background,
borderBottomColor: isDrawerList ? colors.elevated : colors.background,
marginBottom: 8,
}}
2021-09-07 23:13:15 +02:00
>
<Text
style={{
textAlign: 'left',
fontWeight: 'bold',
fontSize: 34,
color: colors.foregroundColor,
}}
>
{props.leftText}
</Text>
<BluePlusIcon onPress={props.onNewWalletPress} Component={TouchableOpacity} />
2021-09-07 23:13:15 +02:00
</View>
);
};
2018-01-30 23:42:38 +01:00
2020-11-30 05:18:54 +01:00
export const BlueSpacing = props => {
return <View {...props} style={{ height: 60 }} />;
};
2018-06-29 00:17:14 +02:00
2020-11-28 04:24:20 +01:00
export const BlueSpacing40 = props => {
return <View {...props} style={{ height: 50 }} />;
};
2018-01-30 23:42:38 +01:00
export const BlueSpacingVariable = props => {
if (isIpad) {
return <BlueSpacing40 {...props} />;
} else {
return <BlueSpacing {...props} />;
2018-06-29 00:17:14 +02:00
}
};
2018-06-29 00:17:14 +02:00
export class is {
static ipad() {
return isIpad;
}
}
2020-11-28 04:24:20 +01:00
export const BlueSpacing20 = props => {
2021-09-09 13:00:11 +02:00
const { horizontal = false } = props;
return <View {...props} style={{ height: horizontal ? 0 : 20, width: horizontal ? 20 : 0, opacity: 0 }} />;
2020-11-28 04:24:20 +01:00
};
2018-06-25 00:22:46 +02:00
2020-11-28 04:24:20 +01:00
export const BlueSpacing10 = props => {
return <View {...props} style={{ height: 10, opacity: 0 }} />;
};
2019-05-22 14:08:31 +02:00
export const BlueDismissKeyboardInputAccessory = () => {
const { colors } = useTheme();
BlueDismissKeyboardInputAccessory.InputAccessoryViewID = 'BlueDismissKeyboardInputAccessory';
2019-08-29 06:18:32 +02:00
return Platform.OS !== 'ios' ? null : (
<InputAccessoryView nativeID={BlueDismissKeyboardInputAccessory.InputAccessoryViewID}>
2019-08-29 06:18:32 +02:00
<View
style={{
backgroundColor: colors.inputBackgroundColor,
height: 44,
flex: 1,
2019-08-29 06:18:32 +02:00
flexDirection: 'row',
justifyContent: 'flex-end',
alignItems: 'center',
}}
>
<BlueButtonLink title={loc.send.input_done} onPress={Keyboard.dismiss} />
2019-08-29 06:18:32 +02:00
</View>
</InputAccessoryView>
);
};
2019-08-29 06:18:32 +02:00
export const BlueDoneAndDismissKeyboardInputAccessory = props => {
const { colors } = useTheme();
BlueDoneAndDismissKeyboardInputAccessory.InputAccessoryViewID = 'BlueDoneAndDismissKeyboardInputAccessory';
const onPasteTapped = async () => {
const clipboard = await Clipboard.getString();
props.onPasteTapped(clipboard);
};
const inputView = (
<View
style={{
backgroundColor: colors.inputBackgroundColor,
flexDirection: 'row',
justifyContent: 'flex-end',
alignItems: 'center',
maxHeight: 44,
}}
>
<BlueButtonLink title={loc.send.input_clear} onPress={props.onClearTapped} />
<BlueButtonLink title={loc.send.input_paste} onPress={onPasteTapped} />
<BlueButtonLink title={loc.send.input_done} onPress={Keyboard.dismiss} />
</View>
);
if (Platform.OS === 'ios') {
return <InputAccessoryView nativeID={BlueDoneAndDismissKeyboardInputAccessory.InputAccessoryViewID}>{inputView}</InputAccessoryView>;
} else {
return <KeyboardAvoidingView>{inputView}</KeyboardAvoidingView>;
2019-08-29 06:18:32 +02:00
}
};
2019-08-29 06:18:32 +02:00
2020-11-30 05:18:54 +01:00
export const BlueLoading = props => {
2020-07-15 19:32:59 +02:00
return (
2021-04-29 16:32:48 +02:00
<View style={{ flex: 1, justifyContent: 'center' }} {...props}>
2020-07-15 19:32:59 +02:00
<ActivityIndicator />
</View>
);
};
2018-06-25 00:22:46 +02:00
const stylesBlueIcon = StyleSheet.create({
container: {
flex: 1,
},
box1: {
position: 'relative',
top: 15,
},
2018-06-28 03:43:28 +02:00
box: {
alignSelf: 'flex-end',
paddingHorizontal: 14,
paddingTop: 8,
2018-06-28 03:43:28 +02:00
},
2019-01-31 07:52:21 +01:00
boxIncoming: {
2018-06-25 00:22:46 +02:00
position: 'relative',
},
ball: {
width: 30,
height: 30,
borderRadius: 15,
},
2019-01-31 07:52:21 +01:00
ballIncoming: {
2018-06-25 00:22:46 +02:00
width: 30,
height: 30,
borderRadius: 15,
2018-12-31 23:29:36 +01:00
transform: [{ rotate: '-45deg' }],
2020-05-03 20:17:49 +02:00
justifyContent: 'center',
2018-12-31 23:29:36 +01:00
},
2019-01-31 07:52:21 +01:00
ballIncomingWithoutRotate: {
2018-12-31 23:29:36 +01:00
width: 30,
height: 30,
borderRadius: 15,
2018-06-25 00:22:46 +02:00
},
ballReceive: {
width: 30,
height: 30,
borderBottomLeftRadius: 15,
transform: [{ rotate: '-45deg' }],
},
ballOutgoing: {
width: 30,
height: 30,
borderRadius: 15,
2018-12-31 23:29:36 +01:00
transform: [{ rotate: '225deg' }],
2020-05-03 20:17:49 +02:00
justifyContent: 'center',
2018-06-25 00:22:46 +02:00
},
2018-12-28 22:43:38 +01:00
ballOutgoingWithoutRotate: {
width: 30,
height: 30,
borderRadius: 15,
},
ballOutgoingExpired: {
width: 30,
height: 30,
borderRadius: 15,
2020-05-03 20:17:49 +02:00
justifyContent: 'center',
},
2018-06-25 00:22:46 +02:00
ballTransparrent: {
width: 30,
height: 30,
borderRadius: 15,
backgroundColor: 'transparent',
},
ballDimmed: {
width: 30,
height: 30,
borderRadius: 15,
backgroundColor: 'gray',
},
});
2020-09-09 01:36:35 +02:00
export const BluePlusIcon = props => {
const { colors } = useTheme();
const stylesBlueIconHooks = StyleSheet.create({
ball: {
backgroundColor: colors.buttonBackgroundColor,
},
});
return (
<Avatar
rounded
containerStyle={[stylesBlueIcon.ball, stylesBlueIconHooks.ball]}
icon={{ name: 'add', size: 22, type: 'ionicons', color: colors.foregroundColor }}
{...props}
/>
2020-09-09 01:36:35 +02:00
);
};
2018-01-30 23:42:38 +01:00
2020-09-09 01:36:35 +02:00
export const BlueTransactionIncomingIcon = props => {
const { colors } = useTheme();
const stylesBlueIconHooks = StyleSheet.create({
ballIncoming: {
backgroundColor: colors.ballReceive,
},
});
return (
<View {...props}>
<View style={stylesBlueIcon.boxIncoming}>
<View style={[stylesBlueIcon.ballIncoming, stylesBlueIconHooks.ballIncoming]}>
<Icon {...props} name="arrow-down" size={16} type="font-awesome" color={colors.incomingForegroundColor} />
2018-01-30 23:42:38 +01:00
</View>
2018-06-25 00:22:46 +02:00
</View>
2020-09-09 01:36:35 +02:00
</View>
);
};
2018-06-25 00:22:46 +02:00
2020-09-09 01:36:35 +02:00
export const BlueTransactionPendingIcon = props => {
const { colors } = useTheme();
const stylesBlueIconHooks = StyleSheet.create({
ball: {
backgroundColor: colors.buttonBackgroundColor,
},
});
return (
<View {...props}>
<View style={stylesBlueIcon.boxIncoming}>
<View style={[stylesBlueIcon.ball, stylesBlueIconHooks.ball]}>
<Icon
{...props}
name="kebab-horizontal"
size={16}
type="octicon"
color={colors.foregroundColor}
iconStyle={{ left: 0, top: 7 }}
/>
2018-06-25 00:22:46 +02:00
</View>
</View>
2020-09-09 01:36:35 +02:00
</View>
);
};
2018-06-25 00:22:46 +02:00
2020-09-09 01:36:35 +02:00
export const BlueTransactionExpiredIcon = props => {
const { colors } = useTheme();
const stylesBlueIconHooks = StyleSheet.create({
ballOutgoingExpired: {
backgroundColor: colors.ballOutgoingExpired,
},
});
return (
<View {...props}>
<View style={stylesBlueIcon.boxIncoming}>
<View style={[stylesBlueIcon.ballOutgoingExpired, stylesBlueIconHooks.ballOutgoingExpired]}>
<Icon {...props} name="clock" size={16} type="octicon" color="#9AA0AA" iconStyle={{ left: 0, top: 0 }} />
2018-12-28 22:43:38 +01:00
</View>
</View>
2020-09-09 01:36:35 +02:00
</View>
);
};
2018-12-28 22:43:38 +01:00
2020-09-09 01:36:35 +02:00
export const BlueTransactionOnchainIcon = props => {
const { colors } = useTheme();
const stylesBlueIconHooks = StyleSheet.create({
ballIncoming: {
backgroundColor: colors.ballReceive,
},
});
return (
<View {...props}>
<View style={stylesBlueIcon.boxIncoming}>
<View style={[stylesBlueIcon.ballIncoming, stylesBlueIconHooks.ballIncoming]}>
<Icon
{...props}
name="link"
size={16}
type="font-awesome"
color={colors.incomingForegroundColor}
iconStyle={{ left: 0, top: 0, transform: [{ rotate: '-45deg' }] }}
/>
</View>
</View>
2020-09-09 01:36:35 +02:00
</View>
);
};
2020-09-09 01:36:35 +02:00
export const BlueTransactionOffchainIcon = props => {
const { colors } = useTheme();
const stylesBlueIconHooks = StyleSheet.create({
ballOutgoingWithoutRotate: {
backgroundColor: colors.ballOutgoing,
},
});
return (
<View {...props}>
<View style={stylesBlueIcon.boxIncoming}>
<View style={[stylesBlueIcon.ballOutgoingWithoutRotate, stylesBlueIconHooks.ballOutgoingWithoutRotate]}>
<Icon
{...props}
name="bolt"
size={16}
type="font-awesome"
color={colors.outgoingForegroundColor}
iconStyle={{ left: 0, marginTop: 6 }}
/>
2018-09-05 00:18:24 +02:00
</View>
</View>
2020-09-09 01:36:35 +02:00
</View>
);
};
2018-09-05 00:18:24 +02:00
2020-09-09 01:36:35 +02:00
export const BlueTransactionOffchainIncomingIcon = props => {
const { colors } = useTheme();
const stylesBlueIconHooks = StyleSheet.create({
ballIncomingWithoutRotate: {
backgroundColor: colors.ballReceive,
},
});
return (
<View {...props}>
<View style={stylesBlueIcon.boxIncoming}>
<View style={[stylesBlueIcon.ballIncomingWithoutRotate, stylesBlueIconHooks.ballIncomingWithoutRotate]}>
<Icon
{...props}
name="bolt"
size={16}
type="font-awesome"
color={colors.incomingForegroundColor}
iconStyle={{ left: 0, marginTop: 6 }}
/>
2018-09-05 00:18:24 +02:00
</View>
</View>
2020-09-09 01:36:35 +02:00
</View>
);
};
2018-09-05 00:18:24 +02:00
2020-09-09 01:36:35 +02:00
export const BlueTransactionOutgoingIcon = props => {
const { colors } = useTheme();
const stylesBlueIconHooks = StyleSheet.create({
ballOutgoing: {
backgroundColor: colors.ballOutgoing,
},
});
return (
<View {...props}>
<View style={stylesBlueIcon.boxIncoming}>
<View style={[stylesBlueIcon.ballOutgoing, stylesBlueIconHooks.ballOutgoing]}>
<Icon {...props} name="arrow-down" size={16} type="font-awesome" color={colors.outgoingForegroundColor} />
2018-06-25 00:22:46 +02:00
</View>
</View>
2020-09-09 01:36:35 +02:00
</View>
);
};
2018-06-25 00:22:46 +02:00
2020-08-21 00:50:38 +02:00
const sendReceiveScanButtonFontSize =
PixelRatio.roundToNearestPixel(Dimensions.get('window').width / 26) > 22
? 22
: PixelRatio.roundToNearestPixel(Dimensions.get('window').width / 26);
2020-09-09 01:36:35 +02:00
export const BlueReceiveButtonIcon = props => {
const { colors } = useTheme();
return (
<TouchableOpacity accessibilityRole="button" {...props} style={{ flex: 1 }}>
2020-09-09 01:36:35 +02:00
<View
style={{
flex: 1,
backgroundColor: colors.buttonBackgroundColor,
}}
>
<View style={{ flex: 1, flexDirection: 'row', alignItems: 'center', justifyContent: 'center' }}>
<View
style={{
left: 5,
backgroundColor: 'transparent',
transform: [{ rotate: '-45deg' }],
alignItems: 'center',
marginRight: 8,
}}
>
<Icon
{...props}
name="arrow-down"
size={sendReceiveScanButtonFontSize}
type="font-awesome"
color={colors.buttonAlternativeTextColor}
/>
2018-06-25 00:22:46 +02:00
</View>
2020-09-09 01:36:35 +02:00
<Text
style={{
color: colors.buttonAlternativeTextColor,
fontWeight: '500',
fontSize: sendReceiveScanButtonFontSize,
left: 5,
backgroundColor: 'transparent',
}}
>
2021-08-01 21:05:21 +02:00
{formatStringAddTwoWhiteSpaces(loc.receive.header)}
2020-09-09 01:36:35 +02:00
</Text>
2018-06-25 00:22:46 +02:00
</View>
2020-09-09 01:36:35 +02:00
</View>
</TouchableOpacity>
);
};
2018-06-25 00:22:46 +02:00
2019-08-27 06:05:27 +02:00
export class BlueReplaceFeeSuggestions extends Component {
static propTypes = {
onFeeSelected: PropTypes.func.isRequired,
transactionMinimum: PropTypes.number.isRequired,
};
static defaultProps = {
transactionMinimum: 1,
};
2020-09-14 12:49:08 +02:00
state = {
customFeeValue: '1',
};
2019-08-27 06:05:27 +02:00
async componentDidMount() {
2020-09-14 12:49:08 +02:00
try {
const cachedNetworkTransactionFees = JSON.parse(await AsyncStorage.getItem(NetworkTransactionFee.StorageKey));
if (cachedNetworkTransactionFees && 'fastestFee' in cachedNetworkTransactionFees) {
this.setState({ networkFees: cachedNetworkTransactionFees }, () => this.onFeeSelected(NetworkTransactionFeeType.FAST));
2020-09-14 12:49:08 +02:00
}
} catch (_) {}
2019-08-27 06:05:27 +02:00
const networkFees = await NetworkTransactionFees.recommendedFees();
this.setState({ networkFees }, () => this.onFeeSelected(NetworkTransactionFeeType.FAST));
2019-08-27 06:05:27 +02:00
}
onFeeSelected = selectedFeeType => {
if (selectedFeeType !== NetworkTransactionFeeType.CUSTOM) {
Keyboard.dismiss();
}
if (selectedFeeType === NetworkTransactionFeeType.FAST) {
this.props.onFeeSelected(this.state.networkFees.fastestFee);
this.setState({ selectedFeeType }, () => this.props.onFeeSelected(this.state.networkFees.fastestFee));
} else if (selectedFeeType === NetworkTransactionFeeType.MEDIUM) {
2020-05-18 16:45:31 +02:00
this.setState({ selectedFeeType }, () => this.props.onFeeSelected(this.state.networkFees.mediumFee));
2019-08-27 06:05:27 +02:00
} else if (selectedFeeType === NetworkTransactionFeeType.SLOW) {
2020-05-18 16:45:31 +02:00
this.setState({ selectedFeeType }, () => this.props.onFeeSelected(this.state.networkFees.slowFee));
2019-08-27 06:05:27 +02:00
} else if (selectedFeeType === NetworkTransactionFeeType.CUSTOM) {
2020-09-14 12:49:08 +02:00
this.props.onFeeSelected(Number(this.state.customFeeValue));
2019-08-27 06:05:27 +02:00
}
};
onCustomFeeTextChange = customFee => {
2020-09-14 12:49:08 +02:00
const customFeeValue = customFee.replace(/[^0-9]/g, '');
this.setState({ customFeeValue, selectedFeeType: NetworkTransactionFeeType.CUSTOM }, () => {
2019-08-27 06:05:27 +02:00
this.onFeeSelected(NetworkTransactionFeeType.CUSTOM);
});
};
render() {
2020-09-14 12:49:08 +02:00
const { networkFees, selectedFeeType } = this.state;
2019-08-27 06:05:27 +02:00
return (
<View>
2020-09-14 12:49:08 +02:00
{networkFees &&
[
{
label: loc.send.fee_fast,
time: loc.send.fee_10m,
type: NetworkTransactionFeeType.FAST,
rate: networkFees.fastestFee,
active: selectedFeeType === NetworkTransactionFeeType.FAST,
},
{
2021-08-01 21:05:21 +02:00
label: formatStringAddTwoWhiteSpaces(loc.send.fee_medium),
2020-09-14 12:49:08 +02:00
time: loc.send.fee_3h,
type: NetworkTransactionFeeType.MEDIUM,
rate: networkFees.mediumFee,
active: selectedFeeType === NetworkTransactionFeeType.MEDIUM,
},
{
label: loc.send.fee_slow,
time: loc.send.fee_1d,
type: NetworkTransactionFeeType.SLOW,
rate: networkFees.slowFee,
active: selectedFeeType === NetworkTransactionFeeType.SLOW,
},
].map(({ label, type, time, rate, active }, index) => (
<TouchableOpacity
accessibilityRole="button"
2020-09-14 12:49:08 +02:00
key={label}
onPress={() => this.onFeeSelected(type)}
style={[
{ paddingHorizontal: 16, paddingVertical: 8, marginBottom: 10 },
active && { borderRadius: 8, backgroundColor: BlueCurrentTheme.colors.incomingBackgroundColor },
]}
>
<View style={{ justifyContent: 'space-between', flexDirection: 'row', alignItems: 'center' }}>
<Text style={{ fontSize: 22, color: BlueCurrentTheme.colors.successColor, fontWeight: '600' }}>{label}</Text>
<View
style={{
backgroundColor: BlueCurrentTheme.colors.successColor,
borderRadius: 5,
paddingHorizontal: 6,
paddingVertical: 3,
}}
>
<Text style={{ color: BlueCurrentTheme.colors.background }}>~{time}</Text>
</View>
</View>
<View style={{ justifyContent: 'flex-end', flexDirection: 'row', alignItems: 'center' }}>
<Text style={{ color: BlueCurrentTheme.colors.successColor }}>{rate} sat/byte</Text>
</View>
</TouchableOpacity>
))}
<TouchableOpacity
accessibilityRole="button"
2020-09-14 12:49:08 +02:00
onPress={() => this.customTextInput.focus()}
style={[
{ paddingHorizontal: 16, paddingVertical: 8, marginBottom: 10 },
selectedFeeType === NetworkTransactionFeeType.CUSTOM && {
borderRadius: 8,
backgroundColor: BlueCurrentTheme.colors.incomingBackgroundColor,
},
]}
>
<View style={{ justifyContent: 'space-between', flexDirection: 'row', alignItems: 'center' }}>
2021-08-01 21:05:21 +02:00
<Text style={{ fontSize: 22, color: BlueCurrentTheme.colors.successColor, fontWeight: '600' }}>
{formatStringAddTwoWhiteSpaces(loc.send.fee_custom)}
</Text>
2020-09-14 12:49:08 +02:00
</View>
<View style={{ justifyContent: 'space-between', flexDirection: 'row', alignItems: 'center', marginTop: 5 }}>
<TextInput
onChangeText={this.onCustomFeeTextChange}
keyboardType="numeric"
value={this.state.customFeeValue}
ref={ref => (this.customTextInput = ref)}
maxLength={9}
style={{
2020-09-14 12:49:08 +02:00
backgroundColor: BlueCurrentTheme.colors.inputBackgroundColor,
borderBottomColor: BlueCurrentTheme.colors.formBorder,
borderBottomWidth: 0.5,
borderColor: BlueCurrentTheme.colors.formBorder,
borderRadius: 4,
borderWidth: 1.0,
color: '#81868e',
flex: 1,
marginRight: 10,
minHeight: 33,
paddingRight: 5,
paddingLeft: 5,
}}
2020-09-14 12:49:08 +02:00
onFocus={() => this.onCustomFeeTextChange(this.state.customFeeValue)}
defaultValue={`${this.props.transactionMinimum}`}
placeholder={loc.send.fee_satvbyte}
2020-09-14 12:49:08 +02:00
placeholderTextColor="#81868e"
inputAccessoryViewID={BlueDismissKeyboardInputAccessory.InputAccessoryViewID}
/>
<Text style={{ color: BlueCurrentTheme.colors.successColor }}>sat/byte</Text>
</View>
</TouchableOpacity>
2020-07-15 19:32:59 +02:00
<BlueText style={{ color: BlueCurrentTheme.colors.alternativeTextColor }}>
2021-09-15 16:24:51 +02:00
{loc.formatString(loc.send.fee_replace_minvb, { min: this.props.transactionMinimum })}
</BlueText>
</View>
2019-08-27 06:05:27 +02:00
);
}
}
2020-04-28 18:27:35 +02:00
export function BlueBigCheckmark({ style }) {
const defaultStyles = {
backgroundColor: '#ccddf9',
width: 120,
height: 120,
borderRadius: 60,
alignSelf: 'center',
justifyContent: 'center',
marginTop: 0,
marginBottom: 0,
};
const mergedStyles = { ...defaultStyles, ...style };
return (
<View style={mergedStyles}>
<Icon name="check" size={50} type="font-awesome" color="#0f5cc0" />
</View>
);
}
2020-06-29 14:58:43 +02:00
const tabsStyles = StyleSheet.create({
root: {
flexDirection: 'row',
height: 50,
borderColor: '#e3e3e3',
borderBottomWidth: 1,
},
tabRoot: {
flex: 1,
2020-06-29 14:58:43 +02:00
justifyContent: 'center',
alignItems: 'center',
borderColor: 'white',
borderBottomWidth: 2,
},
});
export const BlueTabs = ({ active, onSwitch, tabs }) => (
<View style={[tabsStyles.root, isIpad && { marginBottom: 30 }]}>
2020-06-29 14:58:43 +02:00
{tabs.map((Tab, i) => (
<TouchableOpacity
key={i}
accessibilityRole="button"
2020-06-29 14:58:43 +02:00
onPress={() => onSwitch(i)}
style={[
tabsStyles.tabRoot,
active === i && {
2020-07-15 19:32:59 +02:00
borderColor: BlueCurrentTheme.colors.buttonAlternativeTextColor,
2020-06-29 14:58:43 +02:00
borderBottomWidth: 2,
},
]}
>
<Tab active={active === i} />
</TouchableOpacity>
))}
</View>
);