mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-02-23 23:27:26 +01:00
Merge branch 'master' into transactionstatushook
This commit is contained in:
commit
9e28aebfb4
29 changed files with 671 additions and 705 deletions
|
@ -8,7 +8,6 @@ import {
|
|||
Alert,
|
||||
Animated,
|
||||
Dimensions,
|
||||
FlatList,
|
||||
Image,
|
||||
InputAccessoryView,
|
||||
Keyboard,
|
||||
|
@ -458,17 +457,15 @@ export class BlueWalletNavigationHeader extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
export const BlueButtonLinkHook = props => {
|
||||
export const BlueButtonLink = props => {
|
||||
const { colors } = useTheme();
|
||||
return (
|
||||
<TouchableOpacity
|
||||
style={{
|
||||
minHeight: 60,
|
||||
minWidth: 100,
|
||||
height: 60,
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
onPress={props.onPress}
|
||||
{...props}
|
||||
>
|
||||
<Text style={{ color: colors.foregroundColor, textAlign: 'center', fontSize: 16 }}>{props.title}</Text>
|
||||
|
@ -476,23 +473,6 @@ export const BlueButtonLinkHook = props => {
|
|||
);
|
||||
};
|
||||
|
||||
export class BlueButtonLink extends Component {
|
||||
render() {
|
||||
return (
|
||||
<TouchableOpacity
|
||||
style={{
|
||||
minHeight: 60,
|
||||
minWidth: 100,
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
{...this.props}
|
||||
>
|
||||
<Text style={{ color: BlueCurrentTheme.colors.foregroundColor, textAlign: 'center', fontSize: 16 }}>{this.props.title}</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const BlueAlertWalletExportReminder = ({ onSuccess = () => {}, onFailure }) => {
|
||||
Alert.alert(
|
||||
loc.wallets.details_title,
|
||||
|
@ -546,6 +526,8 @@ export const BlueNavigationStyle = (navigation, withNavigationCloseButton = fals
|
|||
};
|
||||
|
||||
export const BlueCreateTxNavigationStyle = (navigation, withAdvancedOptionsMenuButton = false, advancedOptionsMenuButtonAction) => {
|
||||
const { colors, closeImage } = useTheme();
|
||||
|
||||
let headerRight;
|
||||
if (withAdvancedOptionsMenuButton) {
|
||||
headerRight = () => (
|
||||
|
@ -554,7 +536,7 @@ export const BlueCreateTxNavigationStyle = (navigation, withAdvancedOptionsMenuB
|
|||
onPress={advancedOptionsMenuButtonAction}
|
||||
testID="advancedOptionsMenuButton"
|
||||
>
|
||||
<Icon size={22} name="kebab-horizontal" type="octicon" color={BlueCurrentTheme.colors.foregroundColor} />
|
||||
<Icon size={22} name="kebab-horizontal" type="octicon" color={colors.foregroundColor} />
|
||||
</TouchableOpacity>
|
||||
);
|
||||
} else {
|
||||
|
@ -568,9 +550,9 @@ export const BlueCreateTxNavigationStyle = (navigation, withAdvancedOptionsMenuB
|
|||
},
|
||||
headerTitleStyle: {
|
||||
fontWeight: '600',
|
||||
color: BlueCurrentTheme.colors.foregroundColor,
|
||||
color: colors.foregroundColor,
|
||||
},
|
||||
headerTintColor: BlueCurrentTheme.colors.foregroundColor,
|
||||
headerTintColor: colors.foregroundColor,
|
||||
headerLeft: () => (
|
||||
<TouchableOpacity
|
||||
style={{ minWidth: 40, height: 40, justifyContent: 'center', paddingHorizontal: 14 }}
|
||||
|
@ -579,7 +561,7 @@ export const BlueCreateTxNavigationStyle = (navigation, withAdvancedOptionsMenuB
|
|||
navigation.goBack(null);
|
||||
}}
|
||||
>
|
||||
<Image style={{}} source={BlueCurrentTheme.closeImage} />
|
||||
<Image style={{}} source={closeImage} />
|
||||
</TouchableOpacity>
|
||||
),
|
||||
headerRight,
|
||||
|
@ -675,11 +657,9 @@ export const SafeBlueArea = props => {
|
|||
return <SafeAreaView forceInset={{ horizontal: 'always' }} style={{ flex: 1, backgroundColor: colors.background }} {...props} />;
|
||||
};
|
||||
|
||||
export class BlueCard extends Component {
|
||||
render() {
|
||||
return <View {...this.props} style={{ padding: 20 }} />;
|
||||
}
|
||||
}
|
||||
export const BlueCard = props => {
|
||||
return <View {...props} style={{ padding: 20 }} />;
|
||||
};
|
||||
|
||||
export const BlueText = props => {
|
||||
const { colors } = useTheme();
|
||||
|
@ -760,122 +740,111 @@ export const BlueFormLabel = props => {
|
|||
return <Text {...props} style={{ color: colors.foregroundColor, fontWeight: '400', marginHorizontal: 20 }} />;
|
||||
};
|
||||
|
||||
export class BlueFormInput extends Component {
|
||||
render() {
|
||||
return (
|
||||
<Input
|
||||
{...this.props}
|
||||
inputStyle={{ color: BlueCurrentTheme.colors.foregroundColor, maxWidth: width - 105 }}
|
||||
containerStyle={{
|
||||
marginTop: 5,
|
||||
borderColor: BlueCurrentTheme.colors.inputBorderColor,
|
||||
borderBottomColor: BlueCurrentTheme.colors.inputBorderColor,
|
||||
borderWidth: 0.5,
|
||||
borderBottomWidth: 0.5,
|
||||
backgroundColor: BlueCurrentTheme.colors.inputBackgroundColor,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
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,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export class BlueFormMultiInput extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
selection: { start: 0, end: 0 },
|
||||
};
|
||||
}
|
||||
export const BlueFormMultiInput = props => {
|
||||
const { colors } = useTheme();
|
||||
|
||||
render() {
|
||||
return (
|
||||
<TextInput
|
||||
multiline
|
||||
underlineColorAndroid="transparent"
|
||||
numberOfLines={4}
|
||||
style={{
|
||||
paddingHorizontal: 8,
|
||||
paddingVertical: 16,
|
||||
flex: 1,
|
||||
marginTop: 5,
|
||||
marginHorizontal: 20,
|
||||
borderColor: BlueCurrentTheme.colors.formBorder,
|
||||
borderBottomColor: BlueCurrentTheme.colors.formBorder,
|
||||
borderWidth: 1,
|
||||
borderBottomWidth: 0.5,
|
||||
borderRadius: 4,
|
||||
backgroundColor: BlueCurrentTheme.colors.inputBackgroundColor,
|
||||
color: BlueCurrentTheme.colors.foregroundColor,
|
||||
textAlignVertical: 'top',
|
||||
}}
|
||||
autoCorrect={false}
|
||||
autoCapitalize="none"
|
||||
spellCheck={false}
|
||||
{...this.props}
|
||||
selectTextOnFocus={false}
|
||||
keyboardType={Platform.OS === 'android' ? 'visible-password' : 'default'}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
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'}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export class BlueHeader extends Component {
|
||||
render() {
|
||||
return (
|
||||
export const BlueHeader = props => {
|
||||
return (
|
||||
<Header
|
||||
{...props}
|
||||
backgroundColor="transparent"
|
||||
outerContainerStyles={{
|
||||
borderBottomColor: 'transparent',
|
||||
borderBottomWidth: 0,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const BlueHeaderDefaultSub = props => {
|
||||
const { colors } = useTheme();
|
||||
return (
|
||||
<SafeAreaView style={{ backgroundColor: colors.brandingColor }}>
|
||||
<Header
|
||||
{...this.props}
|
||||
backgroundColor="transparent"
|
||||
backgroundColor={colors.background}
|
||||
leftContainerStyle={{ minWidth: '100%' }}
|
||||
outerContainerStyles={{
|
||||
borderBottomColor: 'transparent',
|
||||
borderBottomWidth: 0,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class BlueHeaderDefaultSub extends Component {
|
||||
render() {
|
||||
return (
|
||||
<SafeAreaView style={{ backgroundColor: BlueCurrentTheme.colors.brandingColor }}>
|
||||
<Header
|
||||
backgroundColor={BlueCurrentTheme.colors.background}
|
||||
leftContainerStyle={{ minWidth: '100%' }}
|
||||
outerContainerStyles={{
|
||||
borderBottomColor: 'transparent',
|
||||
borderBottomWidth: 0,
|
||||
}}
|
||||
leftComponent={
|
||||
<Text
|
||||
adjustsFontSizeToFit
|
||||
style={{
|
||||
fontWeight: 'bold',
|
||||
fontSize: 30,
|
||||
color: BlueCurrentTheme.colors.foregroundColor,
|
||||
}}
|
||||
>
|
||||
{this.props.leftText}
|
||||
</Text>
|
||||
}
|
||||
rightComponent={
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
if (this.props.onClose) this.props.onClose();
|
||||
}}
|
||||
>
|
||||
<View style={stylesBlueIcon.box}>
|
||||
<View style={stylesBlueIcon.ballTransparrent}>
|
||||
<Image source={require('./img/close.png')} />
|
||||
</View>
|
||||
leftComponent={
|
||||
<Text
|
||||
adjustsFontSizeToFit
|
||||
style={{
|
||||
fontWeight: 'bold',
|
||||
fontSize: 30,
|
||||
color: colors.foregroundColor,
|
||||
}}
|
||||
>
|
||||
{props.leftText}
|
||||
</Text>
|
||||
}
|
||||
rightComponent={
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
if (props.onClose) props.onClose();
|
||||
}}
|
||||
>
|
||||
<View style={stylesBlueIcon.box}>
|
||||
<View style={stylesBlueIcon.ballTransparrent}>
|
||||
<Image source={require('./img/close.png')} />
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
}
|
||||
{...this.props}
|
||||
/>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
}
|
||||
{...props}
|
||||
/>
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
|
||||
export const BlueHeaderDefaultSubHooks = props => {
|
||||
const { colors } = useTheme();
|
||||
|
@ -938,11 +907,9 @@ export const BlueHeaderDefaultMain = props => {
|
|||
);
|
||||
};
|
||||
|
||||
export class BlueSpacing extends Component {
|
||||
render() {
|
||||
return <View {...this.props} style={{ height: 60 }} />;
|
||||
}
|
||||
}
|
||||
export const BlueSpacing = props => {
|
||||
return <View {...props} style={{ height: 60 }} />;
|
||||
};
|
||||
|
||||
export const BlueSpacing40 = props => {
|
||||
return <View {...props} style={{ height: 50 }} />;
|
||||
|
@ -972,12 +939,6 @@ export const BlueSpacing10 = props => {
|
|||
return <View {...props} style={{ height: 10, opacity: 0 }} />;
|
||||
};
|
||||
|
||||
export class BlueList extends Component {
|
||||
render() {
|
||||
return <FlatList {...this.props} />;
|
||||
}
|
||||
}
|
||||
|
||||
export class BlueUseAllFundsButton extends Component {
|
||||
static InputAccessoryViewID = 'useMaxInputAccessoryViewID';
|
||||
static propTypes = {
|
||||
|
@ -1110,19 +1071,9 @@ export class BlueDoneAndDismissKeyboardInputAccessory extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
export class BlueLoading extends Component {
|
||||
render() {
|
||||
return (
|
||||
<View style={{ flex: 1, paddingTop: 200 }} {...this.props}>
|
||||
<ActivityIndicator />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const BlueLoadingHook = () => {
|
||||
export const BlueLoading = props => {
|
||||
return (
|
||||
<View style={{ flex: 1, paddingTop: 200 }}>
|
||||
<View style={{ flex: 1, paddingTop: 200 }} {...props}>
|
||||
<ActivityIndicator />
|
||||
</View>
|
||||
);
|
||||
|
@ -1421,93 +1372,6 @@ export const BlueReceiveButtonIcon = props => {
|
|||
);
|
||||
};
|
||||
|
||||
export class BlueScanButton extends Component {
|
||||
render() {
|
||||
return (
|
||||
<TouchableOpacity {...this.props} style={{ flex: 1 }}>
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
minWidth: 130,
|
||||
backgroundColor: BlueCurrentTheme.colors.buttonBackgroundColor,
|
||||
}}
|
||||
>
|
||||
<View style={{ flex: 1, flexDirection: 'row', alignItems: 'center', justifyContent: 'center' }}>
|
||||
<View
|
||||
style={{
|
||||
minWidth: 24,
|
||||
minHeight: 30,
|
||||
backgroundColor: 'transparent',
|
||||
alignItems: 'center',
|
||||
marginBottom: -15,
|
||||
marginLeft: -8,
|
||||
}}
|
||||
>
|
||||
<Image resizeMode="stretch" source={BlueCurrentTheme.scanImage} />
|
||||
</View>
|
||||
<Text
|
||||
style={{
|
||||
color: BlueCurrentTheme.colors.buttonAlternativeTextColor,
|
||||
fontSize: sendReceiveScanButtonFontSize,
|
||||
fontWeight: '600',
|
||||
left: 5,
|
||||
backgroundColor: 'transparent',
|
||||
}}
|
||||
>
|
||||
{loc.send.details_scan}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class BlueSendButtonIcon extends Component {
|
||||
render() {
|
||||
return (
|
||||
<TouchableOpacity {...this.props} testID="SendButton" style={{ flex: 1 }}>
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
backgroundColor: BlueCurrentTheme.colors.buttonBackgroundColor,
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<View style={{ flex: 1, flexDirection: 'row', alignItems: 'center' }}>
|
||||
<View
|
||||
style={{
|
||||
left: 5,
|
||||
backgroundColor: 'transparent',
|
||||
transform: [{ rotate: '225deg' }],
|
||||
marginRight: 8,
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
{...this.props}
|
||||
name="arrow-down"
|
||||
size={sendReceiveScanButtonFontSize}
|
||||
type="font-awesome"
|
||||
color={BlueCurrentTheme.colors.buttonAlternativeTextColor}
|
||||
/>
|
||||
</View>
|
||||
<Text
|
||||
style={{
|
||||
color: BlueCurrentTheme.colors.buttonAlternativeTextColor,
|
||||
fontSize: sendReceiveScanButtonFontSize,
|
||||
fontWeight: '500',
|
||||
backgroundColor: 'transparent',
|
||||
}}
|
||||
>
|
||||
{loc.send.header}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const BlueTransactionListItem = React.memo(({ item, itemPriceUnit = BitcoinUnit.BTC, timeElapsed }) => {
|
||||
const [subtitleNumberOfLines, setSubtitleNumberOfLines] = useState(1);
|
||||
const { colors } = useTheme();
|
||||
|
|
|
@ -75,6 +75,7 @@ import DrawerList from './screen/wallets/drawerList';
|
|||
import { isTablet } from 'react-native-device-info';
|
||||
import SettingsPrivacy from './screen/settings/SettingsPrivacy';
|
||||
import LNDViewAdditionalInvoicePreImage from './screen/lnd/lndViewAdditionalInvoicePreImage';
|
||||
import PsbtMultisigQRCode from './screen/send/psbtMultisigQRCode';
|
||||
|
||||
const defaultScreenOptions =
|
||||
Platform.OS === 'ios'
|
||||
|
@ -182,6 +183,7 @@ const SendDetailsRoot = () => (
|
|||
/>
|
||||
<SendDetailsStack.Screen name="CreateTransaction" component={SendCreate} options={SendCreate.navigationOptions} />
|
||||
<SendDetailsStack.Screen name="PsbtMultisig" component={PsbtMultisig} options={PsbtMultisig.navigationOptions} />
|
||||
<SendDetailsStack.Screen name="PsbtMultisigQRCode" component={PsbtMultisigQRCode} options={PsbtMultisigQRCode.navigationOptions} />
|
||||
<SendDetailsStack.Screen
|
||||
name="Success"
|
||||
component={Success}
|
||||
|
|
|
@ -136,7 +136,7 @@ android {
|
|||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 1
|
||||
versionName "5.6.7"
|
||||
versionName "5.6.8"
|
||||
multiDexEnabled true
|
||||
missingDimensionStrategy 'react-native-camera', 'general'
|
||||
testBuildType System.getProperty('testBuildType', 'debug') // This will later be used to control the test apk build type
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* eslint react/prop-types: "off", react-native/no-inline-styles: "off" */
|
||||
import React, { Component } from 'react';
|
||||
import { Text } from 'react-native-elements';
|
||||
import { Dimensions, StyleSheet, TouchableOpacity, View } from 'react-native';
|
||||
import { Dimensions, LayoutAnimation, StyleSheet, TouchableOpacity, View } from 'react-native';
|
||||
import { encodeUR } from 'bc-ur/dist';
|
||||
import QRCode from 'react-native-qrcode-svg';
|
||||
import { BlueCurrentTheme } from '../components/themes';
|
||||
|
@ -107,6 +107,7 @@ export class DynamicQRCode extends Component {
|
|||
<TouchableOpacity
|
||||
style={animatedQRCodeStyle.qrcodeContainer}
|
||||
onPress={() => {
|
||||
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
|
||||
this.setState(prevState => ({ hideControls: !prevState.hideControls }));
|
||||
}}
|
||||
>
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1513,7 +1513,7 @@
|
|||
"$(inherited)",
|
||||
"$(PROJECT_DIR)",
|
||||
);
|
||||
MARKETING_VERSION = 5.6.7;
|
||||
MARKETING_VERSION = 5.6.8;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
|
@ -1556,7 +1556,7 @@
|
|||
"$(inherited)",
|
||||
"$(PROJECT_DIR)",
|
||||
);
|
||||
MARKETING_VERSION = 5.6.7;
|
||||
MARKETING_VERSION = 5.6.8;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
|
@ -1597,7 +1597,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 5.6.7;
|
||||
MARKETING_VERSION = 5.6.8;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.bluewallet.bluewallet.TodayExtension;
|
||||
|
@ -1636,7 +1636,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 5.6.7;
|
||||
MARKETING_VERSION = 5.6.8;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.bluewallet.bluewallet.TodayExtension;
|
||||
PRODUCT_NAME = "BlueWallet - Bitcoin Price";
|
||||
|
@ -1675,7 +1675,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 5.6.7;
|
||||
MARKETING_VERSION = 5.6.8;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.bluewallet.bluewallet.PriceWidget;
|
||||
|
@ -1717,7 +1717,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 5.6.7;
|
||||
MARKETING_VERSION = 5.6.8;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.bluewallet.bluewallet.PriceWidget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
@ -1757,7 +1757,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 5.6.7;
|
||||
MARKETING_VERSION = 5.6.8;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.bluewallet.bluewallet.MarketWidget;
|
||||
|
@ -1800,7 +1800,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 5.6.7;
|
||||
MARKETING_VERSION = 5.6.8;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.bluewallet.bluewallet.MarketWidget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
@ -1841,7 +1841,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 5.6.7;
|
||||
MARKETING_VERSION = 5.6.8;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.bluewallet.bluewallet.WalletInformationAndMarketWidget;
|
||||
|
@ -1885,7 +1885,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 5.6.7;
|
||||
MARKETING_VERSION = 5.6.8;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.bluewallet.bluewallet.WalletInformationAndMarketWidget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
@ -1926,7 +1926,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 5.6.7;
|
||||
MARKETING_VERSION = 5.6.8;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.bluewallet.bluewallet.WalletInformationWidget;
|
||||
|
@ -1969,7 +1969,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 5.6.7;
|
||||
MARKETING_VERSION = 5.6.8;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.bluewallet.bluewallet.WalletInformationWidget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
@ -2111,7 +2111,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 5.6.7;
|
||||
MARKETING_VERSION = 5.6.8;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.bluewallet.bluewallet.watch.extension;
|
||||
|
@ -2151,7 +2151,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 5.6.7;
|
||||
MARKETING_VERSION = 5.6.8;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.bluewallet.bluewallet.watch.extension;
|
||||
PRODUCT_NAME = "${TARGET_NAME}";
|
||||
|
@ -2185,7 +2185,7 @@
|
|||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
IBSC_MODULE = BlueWalletWatch_Extension;
|
||||
INFOPLIST_FILE = BlueWalletWatch/Info.plist;
|
||||
MARKETING_VERSION = 5.6.7;
|
||||
MARKETING_VERSION = 5.6.8;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.bluewallet.bluewallet.watch;
|
||||
|
@ -2221,7 +2221,7 @@
|
|||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
IBSC_MODULE = BlueWalletWatch_Extension;
|
||||
INFOPLIST_FILE = BlueWalletWatch/Info.plist;
|
||||
MARKETING_VERSION = 5.6.7;
|
||||
MARKETING_VERSION = 5.6.8;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.bluewallet.bluewallet.watch;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
|
|
@ -1,3 +1,26 @@
|
|||
v5.6.7
|
||||
======
|
||||
|
||||
* ADD: coincontrol
|
||||
* ADD: Handle fiat rate from alternate sources
|
||||
* ADD: new languages: Bulgarian, Polish, Welsh
|
||||
* ADD: UYU currency
|
||||
* FIX: PayJoin is now BIP compliant
|
||||
* FIX: better support for BRD (aka bread) wallet with segwit
|
||||
* FIX: Disregarding curent denomination on send screen, scanning address always resets it to BTC
|
||||
* FIX: import *.txn file with txhex - extra newline characted prevented it from being recognized (closes #2161)
|
||||
* FIX: locale pt_BR, cs_CZ, sl_SI, es_ES, nl_NL, fi_FI, ru
|
||||
* FIX: translate message if Bitcoin address or LN invoice is in clipboard
|
||||
* FIX: Styling for large screens
|
||||
* FIX: exclude change address from recipients for Confirm screen
|
||||
* FIX: Dont show loading indicator on launch and onsnapitem
|
||||
* FIX: Show alert if storage access is denied
|
||||
* FIX: When wallet card has balance but no txs it displays 'pull to refresh'
|
||||
* FIX: broken wallet->send->longtap send btn->choose photo
|
||||
* FIX: Use system color on widgets
|
||||
* FIX: hide provide entropy button when creating Lightning or MS wallet
|
||||
* FIX: Can't paste in address block while building tx
|
||||
|
||||
v5.6.6
|
||||
======
|
||||
|
||||
|
@ -64,12 +87,3 @@ v5.6.1
|
|||
* FIX: locales pt_BR, pt_PT, ru, sl_SI, ja_JP
|
||||
* FIX: add margin for RTL languages
|
||||
* FIX: Missing (NT) before $ sign
|
||||
|
||||
v.5.6.0
|
||||
=======
|
||||
|
||||
* FIX: some transactions displayed with 0 value
|
||||
* FIX: PSBT with HW wallets flow
|
||||
* FIX: rare crash on watch-only receive button
|
||||
* FIX: RBF cancel style
|
||||
* REF: updated languages sl_SI, de_DE, fi_FI, es_ES
|
||||
|
|
|
@ -314,6 +314,7 @@
|
|||
"details_inputs": "Inputs",
|
||||
"details_outputs": "Outputs",
|
||||
"details_received": "Received",
|
||||
"transaction_note_saved":"Transaction note has been successfully saved.",
|
||||
"details_show_in_block_explorer": "View in block explorer",
|
||||
"details_title": "Transaction",
|
||||
"details_to": "Output",
|
||||
|
|
|
@ -107,12 +107,13 @@
|
|||
"lndViewInvoice": {
|
||||
"additional_info": "Dodatne Informacije",
|
||||
"for": "Za:",
|
||||
"lightning_invoice": "Lightning Račun",
|
||||
"has_been_paid": "Ta račun je bil plačan",
|
||||
"open_direct_channel": "Odpri neposreden kanal s tem vozliščem:",
|
||||
"please_pay": "Prosim plačajte",
|
||||
"preimage": "Preimage",
|
||||
"sats": "sats",
|
||||
"wasnt_paid_and_expired": "Ta račun ni bil plačan in je potekel"
|
||||
"wasnt_paid_and_expired": "Ta račun ni bil plačan in je potekel."
|
||||
},
|
||||
"plausibledeniability": {
|
||||
"create_fake_storage": "Ustvari Šifrirano shrambo",
|
||||
|
@ -358,7 +359,7 @@
|
|||
"details_display": "prikaži na seznamu denarnic",
|
||||
"details_export_backup": "Izvozi / varnostna kopija",
|
||||
"details_marketplace": "Tržnica",
|
||||
"details_master_fingerprint": "Master fingerprint",
|
||||
"details_master_fingerprint": "Glavni prstni odtis (fingerprint)",
|
||||
"details_no_cancel": "Ne, prekliči",
|
||||
"details_save": "Shrani",
|
||||
"details_show_xpub": "Prikaži XPUB denarnice",
|
||||
|
@ -455,6 +456,10 @@
|
|||
"view_edit_cosigners": "Prikaži/uredi sopodpisnike",
|
||||
"this_cosigner_is_already_imported": "Ta sopodpisnik je že uvožen",
|
||||
"export_signed_psbt": "Izvozi podpisano PSBT",
|
||||
"input_fp": "Vnesite xfp (master key fingerprint)",
|
||||
"input_fp_explain": "preskoči in uporabi privzetega (00000000)",
|
||||
"input_path": "Vnesite pot izpeljave (derivation path)",
|
||||
"input_path_explain": "preskoči in uporabi privzeto ({default})",
|
||||
"view_edit_cosigners_title": "Urejanje sopodpisnikov"
|
||||
},
|
||||
"cc": {
|
||||
|
|
39
package-lock.json
generated
39
package-lock.json
generated
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "bluewallet",
|
||||
"version": "5.6.7",
|
||||
"version": "5.6.8",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@ -8306,12 +8306,19 @@
|
|||
}
|
||||
},
|
||||
"buffer": {
|
||||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz",
|
||||
"integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==",
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.1.tgz",
|
||||
"integrity": "sha512-rVAXBwEcEoYtxnHSO5iWyhzV/O1WMtkUYWlfdLS7FjU4PnSJJHEfHXi/uHPI5EwltmOA794gN3bm3/pzuctWjQ==",
|
||||
"requires": {
|
||||
"base64-js": "^1.0.2",
|
||||
"ieee754": "^1.1.4"
|
||||
"base64-js": "^1.3.1",
|
||||
"ieee754": "^1.2.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ieee754": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"buffer-alloc": {
|
||||
|
@ -18304,6 +18311,15 @@
|
|||
"yargs": "^13.2.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"buffer": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
|
||||
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
|
||||
"requires": {
|
||||
"base64-js": "^1.3.1",
|
||||
"ieee754": "^1.1.13"
|
||||
}
|
||||
},
|
||||
"cliui": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
|
||||
|
@ -19184,6 +19200,17 @@
|
|||
"integrity": "sha512-ToyFSYuJp0/DQ7bXd/vTwF5m3yhNSRngIpVlBFBDccDZQmp2qIo0exrObCNlJLOOHb38dil726hM+GKjR1/60w==",
|
||||
"requires": {
|
||||
"buffer": "^5.4.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"buffer": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
|
||||
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
|
||||
"requires": {
|
||||
"base64-js": "^1.3.1",
|
||||
"ieee754": "^1.1.13"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-native-tooltip": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "bluewallet",
|
||||
"version": "5.6.7",
|
||||
"version": "5.6.8",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.10.4",
|
||||
|
@ -90,7 +90,7 @@
|
|||
"bip39": "2.6.0",
|
||||
"bitcoinjs-lib": "5.2.0",
|
||||
"bolt11": "1.2.7",
|
||||
"buffer": "5.6.0",
|
||||
"buffer": "6.0.1",
|
||||
"buffer-reverse": "1.0.1",
|
||||
"coinselect": "3.1.12",
|
||||
"crypto-js": "3.1.9-1",
|
||||
|
|
|
@ -16,11 +16,11 @@ import Share from 'react-native-share';
|
|||
import Handoff from 'react-native-handoff';
|
||||
|
||||
import {
|
||||
BlueLoadingHook,
|
||||
BlueLoading,
|
||||
BlueCopyTextToClipboard,
|
||||
BlueButton,
|
||||
SecondButton,
|
||||
BlueButtonLinkHook,
|
||||
BlueButtonLink,
|
||||
is,
|
||||
BlueBitcoinAmount,
|
||||
BlueText,
|
||||
|
@ -161,7 +161,7 @@ const ReceiveDetails = () => {
|
|||
<BlueCopyTextToClipboard text={isCustom ? bip21encoded : address} />
|
||||
</View>
|
||||
<View style={styles.share}>
|
||||
<BlueButtonLinkHook title={loc.receive.details_setAmount} onPress={showCustomAmountModal} />
|
||||
<BlueButtonLink title={loc.receive.details_setAmount} onPress={showCustomAmountModal} />
|
||||
<View>
|
||||
<SecondButton onPress={handleShareButtonPressed} title={loc.receive.details_share} />
|
||||
</View>
|
||||
|
@ -345,7 +345,7 @@ const ReceiveDetails = () => {
|
|||
url={`https://blockstream.info/address/${address}`}
|
||||
/>
|
||||
)}
|
||||
{showAddress ? renderReceiveDetails() : <BlueLoadingHook />}
|
||||
{showAddress ? renderReceiveDetails() : <BlueLoading />}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, { Component } from 'react';
|
||||
import { ScrollView, View, StyleSheet } from 'react-native';
|
||||
import { BlueSpacing20, SafeBlueArea, BlueCard, BlueText, BlueNavigationStyle, BlueLoadingHook } from '../BlueComponents';
|
||||
import { BlueSpacing20, SafeBlueArea, BlueCard, BlueText, BlueNavigationStyle, BlueLoading } from '../BlueComponents';
|
||||
import PropTypes from 'prop-types';
|
||||
import { SegwitP2SHWallet, LegacyWallet, HDSegwitP2SHWallet, HDSegwitBech32Wallet } from '../class';
|
||||
import { BlueCurrentTheme } from '../components/themes';
|
||||
|
@ -210,7 +210,7 @@ export default class Selftest extends Component {
|
|||
|
||||
render() {
|
||||
if (this.state.isLoading) {
|
||||
return <BlueLoadingHook />;
|
||||
return <BlueLoading />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -7,7 +7,7 @@ import ImagePicker from 'react-native-image-picker';
|
|||
import { decodeUR, extractSingleWorkload } from 'bc-ur';
|
||||
import { useNavigation, useRoute, useIsFocused, useTheme } from '@react-navigation/native';
|
||||
import loc from '../../loc';
|
||||
import { BlueLoadingHook, BlueText, BlueButton, BlueSpacing40 } from '../../BlueComponents';
|
||||
import { BlueLoading, BlueText, BlueButton, BlueSpacing40 } from '../../BlueComponents';
|
||||
import { BlueCurrentTheme } from '../../components/themes';
|
||||
import { openPrivacyDesktopSettings } from '../../class/camera';
|
||||
const LocalQRCode = require('@remobile/react-native-qrcode-local-image');
|
||||
|
@ -231,7 +231,7 @@ const ScanQRCode = () => {
|
|||
|
||||
return isLoading ? (
|
||||
<View style={styles.root}>
|
||||
<BlueLoadingHook />
|
||||
<BlueLoading />
|
||||
</View>
|
||||
) : (
|
||||
<View style={styles.root}>
|
||||
|
|
|
@ -647,7 +647,7 @@ export default class SendDetails extends Component {
|
|||
this.props.navigation.navigate('PsbtMultisig', {
|
||||
memo: this.state.memo,
|
||||
psbtBase64: psbt.toBase64(),
|
||||
walletId: wallet.getID(),
|
||||
walletID: wallet.getID(),
|
||||
});
|
||||
this.setState({ isLoading: false });
|
||||
return;
|
||||
|
@ -976,7 +976,7 @@ export default class SendDetails extends Component {
|
|||
this.props.navigation.navigate('PsbtMultisig', {
|
||||
memo: this.state.memo,
|
||||
psbtBase64: psbt.toBase64(),
|
||||
walletId: fromWallet.getID(),
|
||||
walletID: fromWallet.getID(),
|
||||
});
|
||||
} catch (error) {
|
||||
alert(loc.send.problem_with_psbt + ': ' + error.message);
|
||||
|
|
|
@ -1,22 +1,14 @@
|
|||
/* global alert */
|
||||
import React, { useContext, useState } from 'react';
|
||||
import { FlatList, Platform, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||||
import { BlueButton, BlueButtonLink, BlueCard, BlueNavigationStyle, BlueSpacing20, BlueText, SafeBlueArea } from '../../BlueComponents';
|
||||
import { DynamicQRCode } from '../../components/DynamicQRCode';
|
||||
import { SquareButton } from '../../components/SquareButton';
|
||||
import { getSystemName } from 'react-native-device-info';
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { FlatList, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||||
import { BlueButton, BlueCard, BlueNavigationStyle, BlueText, SafeBlueArea } from '../../BlueComponents';
|
||||
import loc from '../../loc';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import ImagePicker from 'react-native-image-picker';
|
||||
import ScanQRCode from './ScanQRCode';
|
||||
import { useNavigation, useRoute, useTheme } from '@react-navigation/native';
|
||||
import { BitcoinUnit } from '../../models/bitcoinUnits';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
const bitcoin = require('bitcoinjs-lib');
|
||||
const currency = require('../../blue_modules/currency');
|
||||
const fs = require('../../blue_modules/fs');
|
||||
const LocalQRCode = require('@remobile/react-native-qrcode-local-image');
|
||||
const isDesktop = getSystemName() === 'Mac OS X';
|
||||
const BigNumber = require('bignumber.js');
|
||||
|
||||
const shortenAddress = addr => {
|
||||
|
@ -25,21 +17,21 @@ const shortenAddress = addr => {
|
|||
|
||||
const PsbtMultisig = () => {
|
||||
const { wallets } = useContext(BlueStorageContext);
|
||||
const navigation = useNavigation();
|
||||
const route = useRoute();
|
||||
const { navigate, setParams } = useNavigation();
|
||||
const { colors } = useTheme();
|
||||
const [flatListHeight, setFlatListHeight] = useState(0);
|
||||
|
||||
const walletId = route.params.walletId;
|
||||
const psbtBase64 = route.params.psbtBase64;
|
||||
const memo = route.params.memo;
|
||||
|
||||
const { walletID, psbtBase64, memo, receivedPSBTBase64 } = useRoute().params;
|
||||
/** @type MultisigHDWallet */
|
||||
const wallet = wallets.find(w => w.getID() === walletID);
|
||||
const [psbt, setPsbt] = useState(bitcoin.Psbt.fromBase64(psbtBase64));
|
||||
const [isModalVisible, setIsModalVisible] = useState(false);
|
||||
const data = new Array(wallet.getM());
|
||||
const stylesHook = StyleSheet.create({
|
||||
root: {
|
||||
backgroundColor: colors.elevated,
|
||||
},
|
||||
whitespace: {
|
||||
color: colors.elevated,
|
||||
},
|
||||
textBtc: {
|
||||
color: colors.buttonAlternativeTextColor,
|
||||
},
|
||||
|
@ -52,18 +44,12 @@ const PsbtMultisig = () => {
|
|||
textDestination: {
|
||||
color: colors.foregroundColor,
|
||||
},
|
||||
modalContentShort: {
|
||||
backgroundColor: colors.elevated,
|
||||
},
|
||||
textFiat: {
|
||||
color: colors.alternativeTextColor,
|
||||
},
|
||||
provideSignatureButton: {
|
||||
backgroundColor: colors.buttonDisabledBackgroundColor,
|
||||
},
|
||||
exportButton: {
|
||||
backgroundColor: colors.buttonDisabledBackgroundColor,
|
||||
},
|
||||
provideSignatureButtonText: {
|
||||
color: colors.buttonTextColor,
|
||||
},
|
||||
|
@ -83,8 +69,7 @@ const PsbtMultisig = () => {
|
|||
color: colors.msSuccessBG,
|
||||
},
|
||||
});
|
||||
/** @type MultisigHDWallet */
|
||||
const wallet = wallets.find(w => w.getID() === walletId);
|
||||
|
||||
let destination = [];
|
||||
let totalSat = 0;
|
||||
const targets = [];
|
||||
|
@ -98,23 +83,22 @@ const PsbtMultisig = () => {
|
|||
destination = shortenAddress(destination.join(', '));
|
||||
const totalBtc = new BigNumber(totalSat).dividedBy(100000000).toNumber();
|
||||
const totalFiat = currency.satoshiToLocalCurrency(totalSat);
|
||||
const fileName = `${Date.now()}.psbt`;
|
||||
|
||||
const howManySignaturesWeHave = () => {
|
||||
return wallet.calculateHowManySignaturesWeHaveFromPsbt(psbt);
|
||||
};
|
||||
|
||||
const getFee = () => {
|
||||
return wallet.calculateFeeFromPsbt(psbt);
|
||||
};
|
||||
|
||||
const _renderItem = el => {
|
||||
if (el.index >= howManySignaturesWeHave()) return _renderItemUnsigned(el);
|
||||
if (el.index >= howManySignaturesWeHave) return _renderItemUnsigned(el);
|
||||
else return _renderItemSigned(el);
|
||||
};
|
||||
|
||||
const navigateToPSBTMultisigQRCode = () => {
|
||||
navigate('PsbtMultisigQRCode', { walletID, psbtBase64, isShowOpenScanner: isConfirmEnabled() });
|
||||
};
|
||||
|
||||
const _renderItemUnsigned = el => {
|
||||
const renderProvideSignature = el.index === howManySignaturesWeHave();
|
||||
const renderProvideSignature = el.index === howManySignaturesWeHave;
|
||||
return (
|
||||
<View testID="ItemUnsigned">
|
||||
<View style={styles.itemUnsignedWrapper}>
|
||||
|
@ -133,9 +117,7 @@ const PsbtMultisig = () => {
|
|||
<TouchableOpacity
|
||||
testID="ProvideSignature"
|
||||
style={[styles.provideSignatureButton, stylesHook.provideSignatureButton]}
|
||||
onPress={() => {
|
||||
setIsModalVisible(true);
|
||||
}}
|
||||
onPress={navigateToPSBTMultisigQRCode}
|
||||
>
|
||||
<Text style={[styles.provideSignatureButtonText, stylesHook.provideSignatureButtonText]}>
|
||||
{loc.multisig.provide_signature}
|
||||
|
@ -162,31 +144,25 @@ const PsbtMultisig = () => {
|
|||
);
|
||||
};
|
||||
|
||||
const _combinePSBT = receivedPSBTBase64 => {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
useEffect(() => {
|
||||
if (receivedPSBTBase64) {
|
||||
_combinePSBT();
|
||||
setParams({ receivedPSBTBase64: undefined });
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [receivedPSBTBase64]);
|
||||
|
||||
const _combinePSBT = () => {
|
||||
const receivedPSBT = bitcoin.Psbt.fromBase64(receivedPSBTBase64);
|
||||
try {
|
||||
const newPsbt = psbt.combine(receivedPSBT);
|
||||
navigation.dangerouslyGetParent().pop();
|
||||
setPsbt(newPsbt);
|
||||
setIsModalVisible(false);
|
||||
} catch (error) {
|
||||
alert(error);
|
||||
}
|
||||
};
|
||||
|
||||
const onBarScanned = ret => {
|
||||
if (!ret.data) ret = { data: ret };
|
||||
if (ret.data.toUpperCase().startsWith('UR')) {
|
||||
alert('BC-UR not decoded. This should never happen');
|
||||
} else if (ret.data.indexOf('+') === -1 && ret.data.indexOf('=') === -1 && ret.data.indexOf('=') === -1) {
|
||||
// this looks like NOT base64, so maybe its transaction's hex
|
||||
// we dont support it in this flow
|
||||
} else {
|
||||
// psbt base64?
|
||||
_combinePSBT(ret.data);
|
||||
}
|
||||
};
|
||||
|
||||
const onConfirm = () => {
|
||||
try {
|
||||
psbt.finalizeAllInputs();
|
||||
|
@ -195,7 +171,7 @@ const PsbtMultisig = () => {
|
|||
try {
|
||||
const tx = psbt.extractTransaction().toHex();
|
||||
const satoshiPerByte = Math.round(getFee() / (tx.length / 2));
|
||||
navigation.navigate('Confirm', {
|
||||
navigate('Confirm', {
|
||||
fee: new BigNumber(getFee()).dividedBy(100000000).toNumber(),
|
||||
memo: memo,
|
||||
fromWallet: wallet,
|
||||
|
@ -208,83 +184,20 @@ const PsbtMultisig = () => {
|
|||
}
|
||||
};
|
||||
|
||||
const openScanner = () => {
|
||||
if (isDesktop) {
|
||||
ImagePicker.launchCamera(
|
||||
{
|
||||
title: null,
|
||||
mediaType: 'photo',
|
||||
takePhotoButtonTitle: null,
|
||||
},
|
||||
response => {
|
||||
if (response.uri) {
|
||||
const uri = Platform.OS === 'ios' ? response.uri.toString().replace('file://', '') : response.path.toString();
|
||||
LocalQRCode.decode(uri, (error, result) => {
|
||||
if (!error) {
|
||||
onBarScanned(result);
|
||||
} else {
|
||||
alert(loc.send.qr_error_no_qrcode);
|
||||
}
|
||||
});
|
||||
} else if (response.error) {
|
||||
ScanQRCode.presentCameraNotAuthorizedAlert(response.error);
|
||||
}
|
||||
},
|
||||
);
|
||||
} else {
|
||||
navigation.navigate('ScanQRCodeRoot', {
|
||||
screen: 'ScanQRCode',
|
||||
params: {
|
||||
onBarScanned: onBarScanned,
|
||||
showFileImportButton: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const exportPSBT = async () => {
|
||||
await fs.writeFileAndExport(fileName, psbt.toBase64());
|
||||
};
|
||||
|
||||
const howManySignaturesWeHave = wallet.calculateHowManySignaturesWeHaveFromPsbt(psbt);
|
||||
const isConfirmEnabled = () => {
|
||||
return howManySignaturesWeHave() >= wallet.getM();
|
||||
};
|
||||
|
||||
const renderDynamicQrCode = () => {
|
||||
return (
|
||||
<SafeBlueArea style={[styles.root, stylesHook.root]}>
|
||||
<ScrollView centerContent contentContainerStyle={styles.scrollViewContent}>
|
||||
<View style={[styles.modalContentShort, stylesHook.modalContentShort]}>
|
||||
<DynamicQRCode value={psbt.toHex()} capacity={666} />
|
||||
{!isConfirmEnabled() && (
|
||||
<>
|
||||
<BlueSpacing20 />
|
||||
<SquareButton
|
||||
testID="CosignedScanOrImportFile"
|
||||
style={[styles.exportButton, stylesHook.exportButton]}
|
||||
onPress={openScanner}
|
||||
title={loc.multisig.scan_or_import_file}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<BlueSpacing20 />
|
||||
<SquareButton style={[styles.exportButton, stylesHook.exportButton]} onPress={exportPSBT} title={loc.multisig.share} />
|
||||
<BlueSpacing20 />
|
||||
<BlueButtonLink title={loc._.cancel} onPress={() => setIsModalVisible(false)} />
|
||||
</View>
|
||||
</ScrollView>
|
||||
</SafeBlueArea>
|
||||
);
|
||||
return howManySignaturesWeHave >= wallet.getM();
|
||||
};
|
||||
|
||||
const destinationAddress = () => {
|
||||
// eslint-disable-next-line prefer-const
|
||||
let destinationAddressView = [];
|
||||
const whitespace = '_';
|
||||
const destinations = Object.entries(destination.split(','));
|
||||
for (const [index, address] of destinations) {
|
||||
if (index > 1) {
|
||||
destinationAddressView.push(
|
||||
<View style={styles.destionationTextContainer} key={`end-${index}`}>
|
||||
<View style={styles.destinationTextContainer} key={`end-${index}`}>
|
||||
<Text numberOfLines={0} style={[styles.textDestinationFirstFour, stylesHook.textFiat]}>
|
||||
and {destinations.length - 2} more...
|
||||
</Text>
|
||||
|
@ -297,13 +210,15 @@ const PsbtMultisig = () => {
|
|||
const lastFour = currentAddress.substring(currentAddress.length - 5, currentAddress.length);
|
||||
const middle = currentAddress.split(firstFour)[1].split(lastFour)[0];
|
||||
destinationAddressView.push(
|
||||
<View style={styles.destionationTextContainer} key={`${currentAddress}-${index}`}>
|
||||
<Text numberOfLines={2} style={[styles.textDestinationFirstFour, stylesHook.textBtc]}>
|
||||
{firstFour}
|
||||
<Text> </Text>
|
||||
<Text style={[styles.textDestination, stylesHook.textFiat]}>{middle}</Text>
|
||||
<Text> </Text>
|
||||
<Text style={[styles.textDestinationFirstFour, stylesHook.textBtc]}>{lastFour}</Text>
|
||||
<View style={styles.destinationTextContainer} key={`${currentAddress}-${index}`}>
|
||||
<Text style={styles.textAlignCenter}>
|
||||
<Text numberOfLines={2} style={[styles.textDestinationFirstFour, stylesHook.textBtc]}>
|
||||
{firstFour}
|
||||
<Text style={stylesHook.whitespace}>{whitespace}</Text>
|
||||
<Text style={[styles.textDestination, stylesHook.textFiat]}>{middle}</Text>
|
||||
<Text style={stylesHook.whitespace}>{whitespace}</Text>
|
||||
<Text style={[styles.textDestinationFirstFour, stylesHook.textBtc]}>{lastFour}</Text>
|
||||
</Text>
|
||||
</Text>
|
||||
</View>,
|
||||
);
|
||||
|
@ -338,13 +253,10 @@ const PsbtMultisig = () => {
|
|||
</View>
|
||||
);
|
||||
|
||||
if (isModalVisible) return renderDynamicQrCode();
|
||||
|
||||
const onLayout = e => {
|
||||
setFlatListHeight(e.nativeEvent.layout.height);
|
||||
};
|
||||
|
||||
const data = new Array(wallet.getM());
|
||||
return (
|
||||
<SafeBlueArea style={[styles.root, stylesHook.root]}>
|
||||
<View style={styles.container}>
|
||||
|
@ -367,9 +279,7 @@ const PsbtMultisig = () => {
|
|||
<TouchableOpacity
|
||||
testID="ExportSignedPsbt"
|
||||
style={[styles.provideSignatureButton, stylesHook.provideSignatureButton]}
|
||||
onPress={() => {
|
||||
setIsModalVisible(true);
|
||||
}}
|
||||
onPress={navigateToPSBTMultisigQRCode}
|
||||
>
|
||||
<Text style={[styles.provideSignatureButtonText, stylesHook.provideSignatureButtonText]}>
|
||||
{loc.multisig.export_signed_psbt}
|
||||
|
@ -422,7 +332,7 @@ const styles = StyleSheet.create({
|
|||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
destionationTextContainer: {
|
||||
destinationTextContainer: {
|
||||
flexDirection: 'row',
|
||||
marginBottom: 4,
|
||||
paddingHorizontal: 60,
|
||||
|
@ -438,6 +348,9 @@ const styles = StyleSheet.create({
|
|||
fontWeight: 'bold',
|
||||
fontSize: 30,
|
||||
},
|
||||
textAlignCenter: {
|
||||
textAlign: 'center',
|
||||
},
|
||||
textDestinationFirstFour: {
|
||||
fontSize: 14,
|
||||
},
|
||||
|
@ -447,21 +360,6 @@ const styles = StyleSheet.create({
|
|||
fontSize: 14,
|
||||
flexWrap: 'wrap',
|
||||
},
|
||||
modalContentShort: {
|
||||
marginLeft: 20,
|
||||
marginRight: 20,
|
||||
},
|
||||
copyToClipboard: {
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
exportButton: {
|
||||
height: 48,
|
||||
borderRadius: 8,
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
paddingHorizontal: 16,
|
||||
},
|
||||
provideSignatureButton: {
|
||||
marginTop: 24,
|
||||
marginLeft: 40,
|
||||
|
|
147
screen/send/psbtMultisigQRCode.js
Normal file
147
screen/send/psbtMultisigQRCode.js
Normal file
|
@ -0,0 +1,147 @@
|
|||
/* global alert */
|
||||
import React, { useState } from 'react';
|
||||
import { ActivityIndicator, Platform, ScrollView, StyleSheet, View } from 'react-native';
|
||||
import { BlueNavigationStyle, BlueSpacing20, SafeBlueArea } from '../../BlueComponents';
|
||||
import { DynamicQRCode } from '../../components/DynamicQRCode';
|
||||
import { SquareButton } from '../../components/SquareButton';
|
||||
import { getSystemName } from 'react-native-device-info';
|
||||
import loc from '../../loc';
|
||||
import ImagePicker from 'react-native-image-picker';
|
||||
import ScanQRCode from './ScanQRCode';
|
||||
import { useNavigation, useRoute, useTheme } from '@react-navigation/native';
|
||||
const bitcoin = require('bitcoinjs-lib');
|
||||
const fs = require('../../blue_modules/fs');
|
||||
const LocalQRCode = require('@remobile/react-native-qrcode-local-image');
|
||||
const isDesktop = getSystemName() === 'Mac OS X';
|
||||
|
||||
const PsbtMultisigQRCode = () => {
|
||||
const { navigate } = useNavigation();
|
||||
const { colors } = useTheme();
|
||||
const { psbtBase64, isShowOpenScanner } = useRoute().params;
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const psbt = bitcoin.Psbt.fromBase64(psbtBase64);
|
||||
const stylesHook = StyleSheet.create({
|
||||
root: {
|
||||
backgroundColor: colors.elevated,
|
||||
},
|
||||
modalContentShort: {
|
||||
backgroundColor: colors.elevated,
|
||||
},
|
||||
exportButton: {
|
||||
backgroundColor: colors.buttonDisabledBackgroundColor,
|
||||
},
|
||||
});
|
||||
const fileName = `${Date.now()}.psbt`;
|
||||
|
||||
const onBarScanned = ret => {
|
||||
if (!ret.data) ret = { data: ret };
|
||||
if (ret.data.toUpperCase().startsWith('UR')) {
|
||||
alert('BC-UR not decoded. This should never happen');
|
||||
} else if (ret.data.indexOf('+') === -1 && ret.data.indexOf('=') === -1 && ret.data.indexOf('=') === -1) {
|
||||
// this looks like NOT base64, so maybe its transaction's hex
|
||||
// we dont support it in this flow
|
||||
} else {
|
||||
// psbt base64?
|
||||
navigate('PsbtMultisig', { receivedPSBTBase64: ret.data });
|
||||
}
|
||||
};
|
||||
|
||||
const openScanner = () => {
|
||||
if (isDesktop) {
|
||||
ImagePicker.launchCamera(
|
||||
{
|
||||
title: null,
|
||||
mediaType: 'photo',
|
||||
takePhotoButtonTitle: null,
|
||||
},
|
||||
response => {
|
||||
if (response.uri) {
|
||||
const uri = Platform.OS === 'ios' ? response.uri.toString().replace('file://', '') : response.path.toString();
|
||||
LocalQRCode.decode(uri, (error, result) => {
|
||||
if (!error) {
|
||||
onBarScanned(result);
|
||||
} else {
|
||||
alert(loc.send.qr_error_no_qrcode);
|
||||
}
|
||||
});
|
||||
} else if (response.error) {
|
||||
ScanQRCode.presentCameraNotAuthorizedAlert(response.error);
|
||||
}
|
||||
},
|
||||
);
|
||||
} else {
|
||||
navigate('ScanQRCodeRoot', {
|
||||
screen: 'ScanQRCode',
|
||||
params: {
|
||||
onBarScanned: onBarScanned,
|
||||
showFileImportButton: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const exportPSBT = () => {
|
||||
setIsLoading(true);
|
||||
setTimeout(() => fs.writeFileAndExport(fileName, psbt.toBase64()).finally(() => setIsLoading(false)), 10);
|
||||
};
|
||||
|
||||
return (
|
||||
<SafeBlueArea style={[styles.root, stylesHook.root]}>
|
||||
<ScrollView centerContent contentContainerStyle={styles.scrollViewContent}>
|
||||
<View style={[styles.modalContentShort, stylesHook.modalContentShort]}>
|
||||
<DynamicQRCode value={psbt.toHex()} capacity={666} />
|
||||
{!isShowOpenScanner && (
|
||||
<>
|
||||
<BlueSpacing20 />
|
||||
<SquareButton
|
||||
testID="CosignedScanOrImportFile"
|
||||
style={[styles.exportButton, stylesHook.exportButton]}
|
||||
onPress={openScanner}
|
||||
title={loc.multisig.scan_or_import_file}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<BlueSpacing20 />
|
||||
{isLoading ? (
|
||||
<ActivityIndicator />
|
||||
) : (
|
||||
<SquareButton style={[styles.exportButton, stylesHook.exportButton]} onPress={exportPSBT} title={loc.multisig.share} />
|
||||
)}
|
||||
</View>
|
||||
</ScrollView>
|
||||
</SafeBlueArea>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
flex: 1,
|
||||
},
|
||||
scrollViewContent: {
|
||||
flexGrow: 1,
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
modalContentShort: {
|
||||
marginLeft: 20,
|
||||
marginRight: 20,
|
||||
},
|
||||
copyToClipboard: {
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
exportButton: {
|
||||
height: 48,
|
||||
borderRadius: 8,
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
paddingHorizontal: 16,
|
||||
},
|
||||
});
|
||||
|
||||
PsbtMultisigQRCode.navigationOptions = () => ({
|
||||
...BlueNavigationStyle(null, false),
|
||||
title: loc.multisig.header,
|
||||
});
|
||||
|
||||
export default PsbtMultisigQRCode;
|
|
@ -15,7 +15,7 @@ const Success = () => {
|
|||
};
|
||||
const { colors } = useTheme();
|
||||
const { dangerouslyGetParent } = useNavigation();
|
||||
const { amount = 0, fee = 0, amountUnit = BitcoinUnit.BTC, invoiceDescription = '', onDonePressed = pop } = useRoute().params;
|
||||
const { amount, fee, amountUnit = BitcoinUnit.BTC, invoiceDescription = '', onDonePressed = pop } = useRoute().params;
|
||||
const stylesHook = StyleSheet.create({
|
||||
root: {
|
||||
backgroundColor: colors.elevated,
|
||||
|
@ -76,10 +76,12 @@ export const SuccessView = ({ amount, amountUnit, fee, invoiceDescription, shoul
|
|||
<View style={styles.root}>
|
||||
<BlueCard style={styles.amount}>
|
||||
<View style={styles.view}>
|
||||
<>
|
||||
<Text style={[styles.amountValue, stylesHook.amountValue]}>{amount}</Text>
|
||||
<Text style={[styles.amountUnit, stylesHook.amountUnit]}>{' ' + amountUnit}</Text>
|
||||
</>
|
||||
{amount && (
|
||||
<>
|
||||
<Text style={[styles.amountValue, stylesHook.amountValue]}>{amount}</Text>
|
||||
<Text style={[styles.amountUnit, stylesHook.amountUnit]}>{' ' + amountUnit}</Text>
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
{fee > 0 && (
|
||||
<Text style={styles.feeText}>
|
||||
|
|
|
@ -4,7 +4,7 @@ import { ScrollView, Alert, Platform, TouchableOpacity, TouchableWithoutFeedback
|
|||
import { useNavigation } from '@react-navigation/native';
|
||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||
import {
|
||||
BlueLoadingHook,
|
||||
BlueLoading,
|
||||
SafeBlueArea,
|
||||
BlueSpacing20,
|
||||
BlueCard,
|
||||
|
@ -149,7 +149,7 @@ const EncryptStorage = () => {
|
|||
|
||||
return isLoading ? (
|
||||
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={styles.root}>
|
||||
<BlueLoadingHook />
|
||||
<BlueLoading />
|
||||
</SafeBlueArea>
|
||||
) : (
|
||||
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={styles.root}>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import { FlatList, StyleSheet } from 'react-native';
|
||||
import { SafeBlueArea, BlueListItem, BlueCard, BlueLoadingHook, BlueNavigationStyle, BlueText } from '../../BlueComponents';
|
||||
import { SafeBlueArea, BlueListItem, BlueCard, BlueLoading, BlueNavigationStyle, BlueText } from '../../BlueComponents';
|
||||
import { AvailableLanguages } from '../../loc/languages';
|
||||
import loc from '../../loc';
|
||||
|
||||
|
@ -40,7 +40,7 @@ const Language = () => {
|
|||
);
|
||||
|
||||
return isLoading ? (
|
||||
<BlueLoadingHook />
|
||||
<BlueLoading />
|
||||
) : (
|
||||
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={styles.flex}>
|
||||
<FlatList style={styles.flex} keyExtractor={(_item, index) => `${index}`} data={AvailableLanguages} renderItem={renderItem} />
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { ScrollView, StyleSheet } from 'react-native';
|
||||
import { SafeBlueArea, BlueCard, BlueText, BlueNavigationStyle, BlueSpacing20, BlueLoadingHook } from '../../BlueComponents';
|
||||
import { SafeBlueArea, BlueCard, BlueText, BlueNavigationStyle, BlueSpacing20, BlueLoading } from '../../BlueComponents';
|
||||
/** @type {AppStorage} */
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
|
@ -17,7 +17,7 @@ const Licensing = () => {
|
|||
}, []);
|
||||
|
||||
return isLoading ? (
|
||||
<BlueLoadingHook />
|
||||
<BlueLoading />
|
||||
) : (
|
||||
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={styles.root}>
|
||||
<ScrollView>
|
||||
|
|
|
@ -11,7 +11,7 @@ import {
|
|||
SafeBlueArea,
|
||||
BlueCard,
|
||||
BlueNavigationStyle,
|
||||
BlueLoadingHook,
|
||||
BlueLoading,
|
||||
BlueText,
|
||||
BlueButtonLink,
|
||||
} from '../../BlueComponents';
|
||||
|
@ -130,7 +130,7 @@ const LightningSettings = () => {
|
|||
|
||||
<BlueButtonLink title={loc.wallets.import_scan_qr} onPress={importScan} />
|
||||
<BlueSpacing20 />
|
||||
{isLoading ? <BlueLoadingHook /> : <BlueButton onPress={save} title={loc.settings.save} />}
|
||||
{isLoading ? <BlueLoading /> : <BlueButton onPress={save} title={loc.settings.save} />}
|
||||
</BlueCard>
|
||||
</SafeBlueArea>
|
||||
);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* global alert */
|
||||
import React, { Component } from 'react';
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { View, ScrollView, TouchableOpacity, Text, TextInput, Linking, StatusBar, StyleSheet, Keyboard } from 'react-native';
|
||||
import {
|
||||
SafeBlueArea,
|
||||
|
@ -12,12 +12,227 @@ import {
|
|||
} from '../../BlueComponents';
|
||||
import HandoffSettings from '../../class/handoff';
|
||||
import Handoff from 'react-native-handoff';
|
||||
import PropTypes from 'prop-types';
|
||||
import loc from '../../loc';
|
||||
import { BlueCurrentTheme } from '../../components/themes';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
import { useNavigation, useRoute, useTheme } from '@react-navigation/native';
|
||||
const dayjs = require('dayjs');
|
||||
|
||||
function onlyUnique(value, index, self) {
|
||||
return self.indexOf(value) === index;
|
||||
}
|
||||
|
||||
function arrDiff(a1, a2) {
|
||||
const ret = [];
|
||||
for (const v of a2) {
|
||||
if (a1.indexOf(v) === -1) {
|
||||
ret.push(v);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
const TransactionsDetails = () => {
|
||||
const { setOptions } = useNavigation();
|
||||
const { hash } = useRoute().params;
|
||||
const { saveToDisk, txMetadata, wallets, getTransactions } = useContext(BlueStorageContext);
|
||||
const [isHandOffUseEnabled, setIsHandOffUseEnabled] = useState(false);
|
||||
const [from, setFrom] = useState();
|
||||
const [to, setTo] = useState();
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [tx, setTX] = useState();
|
||||
const [memo, setMemo] = useState();
|
||||
const { colors } = useTheme();
|
||||
const stylesHooks = StyleSheet.create({
|
||||
rowCaption: {
|
||||
color: colors.foregroundColor,
|
||||
},
|
||||
txId: {
|
||||
color: colors.foregroundColor,
|
||||
},
|
||||
txLink: {
|
||||
color: colors.alternativeTextColor2,
|
||||
},
|
||||
saveText: {
|
||||
color: colors.alternativeTextColor2,
|
||||
},
|
||||
memoTextInput: {
|
||||
borderColor: colors.formBorder,
|
||||
borderBottomColor: colors.formBorder,
|
||||
backgroundColor: colors.inputBackgroundColor,
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setOptions({
|
||||
headerRight: () => (
|
||||
<TouchableOpacity disabled={isLoading} style={styles.save} onPress={handleOnSaveButtonTapped}>
|
||||
<Text style={stylesHooks.saveText}>{loc.wallets.details_save}</Text>
|
||||
</TouchableOpacity>
|
||||
),
|
||||
headerStyle: {
|
||||
borderBottomWidth: 0,
|
||||
elevation: 0,
|
||||
shadowOpacity: 0,
|
||||
shadowOffset: { height: 0, width: 0 },
|
||||
backgroundColor: colors.customHeader,
|
||||
},
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [colors, isLoading, memo]);
|
||||
|
||||
useEffect(() => {
|
||||
let foundTx = {};
|
||||
let from = [];
|
||||
let to = [];
|
||||
for (const tx of getTransactions()) {
|
||||
if (tx.hash === hash) {
|
||||
foundTx = tx;
|
||||
for (const input of foundTx.inputs) {
|
||||
from = from.concat(input.addresses);
|
||||
}
|
||||
for (const output of foundTx.outputs) {
|
||||
if (output.addresses) to = to.concat(output.addresses);
|
||||
if (output.scriptPubKey && output.scriptPubKey.addresses) to = to.concat(output.scriptPubKey.addresses);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const w of wallets) {
|
||||
for (const t of w.getTransactions()) {
|
||||
if (t.hash === hash) {
|
||||
console.log('tx', hash, 'belongs to', w.getLabel());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (txMetadata[foundTx.hash]) {
|
||||
if (txMetadata[foundTx.hash].memo) {
|
||||
setMemo(txMetadata[foundTx.hash].memo);
|
||||
}
|
||||
}
|
||||
|
||||
setTX(foundTx);
|
||||
setFrom(from);
|
||||
setTo(to);
|
||||
setIsLoading(false);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [hash]);
|
||||
|
||||
useEffect(() => {
|
||||
HandoffSettings.isHandoffUseEnabled().then(setIsHandOffUseEnabled);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const handleOnSaveButtonTapped = () => {
|
||||
Keyboard.dismiss();
|
||||
txMetadata[tx.hash] = { memo };
|
||||
saveToDisk().then(_success => alert(loc.transactions.transaction_note_saved));
|
||||
};
|
||||
|
||||
const handleOnOpenTransactionOnBlockExporerTapped = () => {
|
||||
const url = `https://blockstream.info/tx/${tx.hash}`;
|
||||
Linking.canOpenURL(url).then(supported => {
|
||||
if (supported) {
|
||||
Linking.openURL(url);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (isLoading || !tx) {
|
||||
return <BlueLoading />;
|
||||
}
|
||||
|
||||
return (
|
||||
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={styles.root}>
|
||||
{isHandOffUseEnabled && (
|
||||
<Handoff title={`Bitcoin Transaction ${tx.hash}`} type="io.bluewallet.bluewallet" url={`https://blockstream.info/tx/${tx.hash}`} />
|
||||
)}
|
||||
<StatusBar barStyle="default" />
|
||||
<ScrollView style={styles.scroll}>
|
||||
<BlueCard>
|
||||
<View>
|
||||
<TextInput
|
||||
placeholder={loc.send.details_note_placeholder}
|
||||
value={memo}
|
||||
placeholderTextColor="#81868e"
|
||||
style={[styles.memoTextInput, stylesHooks.memoTextInput]}
|
||||
onChangeText={setMemo}
|
||||
/>
|
||||
<BlueSpacing20 />
|
||||
</View>
|
||||
|
||||
{from && (
|
||||
<>
|
||||
<View style={styles.rowHeader}>
|
||||
<BlueText style={[styles.rowCaption, stylesHooks.rowCaption]}>{loc.transactions.details_from}</BlueText>
|
||||
<BlueCopyToClipboardButton stringToCopy={from.filter(onlyUnique).join(', ')} />
|
||||
</View>
|
||||
<BlueText style={styles.rowValue}>{from.filter(onlyUnique).join(', ')}</BlueText>
|
||||
</>
|
||||
)}
|
||||
|
||||
{to && (
|
||||
<>
|
||||
<View style={styles.rowHeader}>
|
||||
<BlueText style={[styles.rowCaption, stylesHooks.rowCaption]}>{loc.transactions.details_to}</BlueText>
|
||||
<BlueCopyToClipboardButton stringToCopy={to.filter(onlyUnique).join(', ')} />
|
||||
</View>
|
||||
<BlueText style={styles.rowValue}>{arrDiff(from, to.filter(onlyUnique)).join(', ')}</BlueText>
|
||||
</>
|
||||
)}
|
||||
|
||||
{tx.fee && (
|
||||
<>
|
||||
<BlueText style={[styles.rowCaption, stylesHooks.rowCaption]}>{loc.send.create_fee}</BlueText>
|
||||
<BlueText style={styles.rowValue}>{tx.fee + ' sats'}</BlueText>
|
||||
</>
|
||||
)}
|
||||
|
||||
{tx.hash && (
|
||||
<>
|
||||
<View style={styles.rowHeader}>
|
||||
<BlueText style={[styles.txId, stylesHooks.txId]}>Txid</BlueText>
|
||||
<BlueCopyToClipboardButton stringToCopy={tx.hash} />
|
||||
</View>
|
||||
<BlueText style={styles.txHash}>{tx.hash}</BlueText>
|
||||
<TouchableOpacity onPress={handleOnOpenTransactionOnBlockExporerTapped}>
|
||||
<BlueText style={[styles.txLink, stylesHooks.txLink]}>{loc.transactions.details_show_in_block_explorer}</BlueText>
|
||||
</TouchableOpacity>
|
||||
</>
|
||||
)}
|
||||
|
||||
{tx.received && (
|
||||
<>
|
||||
<BlueText style={[styles.rowCaption, stylesHooks.rowCaption]}>{loc.transactions.details_received}</BlueText>
|
||||
<BlueText style={styles.rowValue}>{dayjs(tx.received).format('MM/DD/YYYY h:mm A')}</BlueText>
|
||||
</>
|
||||
)}
|
||||
|
||||
{tx.block_height > 0 && (
|
||||
<>
|
||||
<BlueText style={[styles.rowCaption, stylesHooks.rowCaption]}>{loc.transactions.details_block}</BlueText>
|
||||
<BlueText style={styles.rowValue}>{tx.block_height}</BlueText>
|
||||
</>
|
||||
)}
|
||||
|
||||
{tx.inputs && (
|
||||
<>
|
||||
<BlueText style={[styles.rowCaption, stylesHooks.rowCaption]}>{loc.transactions.details_inputs}</BlueText>
|
||||
<BlueText style={styles.rowValue}>{tx.inputs.length}</BlueText>
|
||||
</>
|
||||
)}
|
||||
|
||||
{tx.outputs.length > 0 && (
|
||||
<>
|
||||
<BlueText style={[styles.rowCaption, stylesHooks.rowCaption]}>{loc.transactions.details_outputs}</BlueText>
|
||||
<BlueText style={styles.rowValue}>{tx.outputs.length}</BlueText>
|
||||
</>
|
||||
)}
|
||||
</BlueCard>
|
||||
</ScrollView>
|
||||
</SafeBlueArea>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
flex: 1,
|
||||
|
@ -35,7 +250,6 @@ const styles = StyleSheet.create({
|
|||
fontSize: 16,
|
||||
fontWeight: '500',
|
||||
marginBottom: 4,
|
||||
color: BlueCurrentTheme.colors.foregroundColor,
|
||||
},
|
||||
rowValue: {
|
||||
marginBottom: 26,
|
||||
|
@ -44,7 +258,6 @@ const styles = StyleSheet.create({
|
|||
txId: {
|
||||
fontSize: 16,
|
||||
fontWeight: '500',
|
||||
color: BlueCurrentTheme.colors.foregroundColor,
|
||||
},
|
||||
txHash: {
|
||||
marginBottom: 8,
|
||||
|
@ -52,23 +265,16 @@ const styles = StyleSheet.create({
|
|||
},
|
||||
txLink: {
|
||||
marginBottom: 26,
|
||||
color: BlueCurrentTheme.colors.alternativeTextColor2,
|
||||
},
|
||||
save: {
|
||||
marginHorizontal: 16,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
saveText: {
|
||||
color: BlueCurrentTheme.colors.alternativeTextColor2,
|
||||
},
|
||||
memoTextInput: {
|
||||
flexDirection: 'row',
|
||||
borderColor: BlueCurrentTheme.colors.formBorder,
|
||||
borderBottomColor: BlueCurrentTheme.colors.formBorder,
|
||||
borderWidth: 1,
|
||||
borderBottomWidth: 0.5,
|
||||
backgroundColor: BlueCurrentTheme.colors.inputBackgroundColor,
|
||||
minHeight: 44,
|
||||
height: 44,
|
||||
alignItems: 'center',
|
||||
|
@ -79,221 +285,9 @@ const styles = StyleSheet.create({
|
|||
},
|
||||
});
|
||||
|
||||
function onlyUnique(value, index, self) {
|
||||
return self.indexOf(value) === index;
|
||||
}
|
||||
export default TransactionsDetails;
|
||||
|
||||
function arrDiff(a1, a2) {
|
||||
const ret = [];
|
||||
for (const v of a2) {
|
||||
if (a1.indexOf(v) === -1) {
|
||||
ret.push(v);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
export default class TransactionsDetails extends Component {
|
||||
static contextType = BlueStorageContext;
|
||||
|
||||
constructor(props, context) {
|
||||
super(props);
|
||||
const hash = props.route.params.hash;
|
||||
let foundTx = {};
|
||||
let from = [];
|
||||
let to = [];
|
||||
for (const tx of context.getTransactions()) {
|
||||
if (tx.hash === hash) {
|
||||
foundTx = tx;
|
||||
for (const input of foundTx.inputs) {
|
||||
from = from.concat(input.addresses);
|
||||
}
|
||||
for (const output of foundTx.outputs) {
|
||||
if (output.addresses) to = to.concat(output.addresses);
|
||||
if (output.scriptPubKey && output.scriptPubKey.addresses) to = to.concat(output.scriptPubKey.addresses);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let wallet = false;
|
||||
for (const w of context.wallets) {
|
||||
for (const t of w.getTransactions()) {
|
||||
if (t.hash === hash) {
|
||||
console.log('tx', hash, 'belongs to', w.getLabel());
|
||||
wallet = w;
|
||||
}
|
||||
}
|
||||
}
|
||||
let memo = '';
|
||||
if (context.txMetadata[foundTx.hash]) {
|
||||
if (context.txMetadata[foundTx.hash].memo) {
|
||||
memo = context.txMetadata[foundTx.hash].memo;
|
||||
}
|
||||
}
|
||||
this.state = {
|
||||
isLoading: true,
|
||||
tx: foundTx,
|
||||
from,
|
||||
to,
|
||||
wallet,
|
||||
isHandOffUseEnabled: false,
|
||||
memo,
|
||||
};
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
console.log('transactions/details - componentDidMount');
|
||||
this.props.navigation.setParams({ handleOnSaveButtonTapped: this.handleOnSaveButtonTapped });
|
||||
const isHandOffUseEnabled = await HandoffSettings.isHandoffUseEnabled();
|
||||
this.setState({
|
||||
isLoading: false,
|
||||
isHandOffUseEnabled,
|
||||
});
|
||||
}
|
||||
|
||||
handleOnSaveButtonTapped = () => {
|
||||
Keyboard.dismiss();
|
||||
this.context.txMetadata[this.state.tx.hash] = { memo: this.state.memo };
|
||||
this.context.saveToDisk().then(_success => alert('Transaction note has been successfully saved.'));
|
||||
};
|
||||
|
||||
handleOnMemoChangeText = value => {
|
||||
this.setState({ memo: value });
|
||||
};
|
||||
|
||||
render() {
|
||||
if (this.state.isLoading || !('tx' in this.state)) {
|
||||
return <BlueLoading />;
|
||||
}
|
||||
|
||||
return (
|
||||
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={styles.root}>
|
||||
{this.state.isHandOffUseEnabled && (
|
||||
<Handoff
|
||||
title={`Bitcoin Transaction ${this.state.tx.hash}`}
|
||||
type="io.bluewallet.bluewallet"
|
||||
url={`https://blockstream.info/tx/${this.state.tx.hash}`}
|
||||
/>
|
||||
)}
|
||||
<StatusBar barStyle="default" />
|
||||
<ScrollView style={styles.scroll}>
|
||||
<BlueCard>
|
||||
<View>
|
||||
<TextInput
|
||||
placeholder={loc.send.details_note_placeholder}
|
||||
value={this.state.memo}
|
||||
placeholderTextColor="#81868e"
|
||||
style={styles.memoTextInput}
|
||||
onChangeText={this.handleOnMemoChangeText}
|
||||
/>
|
||||
<BlueSpacing20 />
|
||||
</View>
|
||||
|
||||
{'from' in this.state && (
|
||||
<>
|
||||
<View style={styles.rowHeader}>
|
||||
<BlueText style={styles.rowCaption}>{loc.transactions.details_from}</BlueText>
|
||||
<BlueCopyToClipboardButton stringToCopy={this.state.from.filter(onlyUnique).join(', ')} />
|
||||
</View>
|
||||
<BlueText style={styles.rowValue}>{this.state.from.filter(onlyUnique).join(', ')}</BlueText>
|
||||
</>
|
||||
)}
|
||||
|
||||
{'to' in this.state && (
|
||||
<>
|
||||
<View style={styles.rowHeader}>
|
||||
<BlueText style={styles.rowCaption}>{loc.transactions.details_to}</BlueText>
|
||||
<BlueCopyToClipboardButton stringToCopy={this.state.to.filter(onlyUnique).join(', ')} />
|
||||
</View>
|
||||
<BlueText style={styles.rowValue}>{arrDiff(this.state.from, this.state.to.filter(onlyUnique)).join(', ')}</BlueText>
|
||||
</>
|
||||
)}
|
||||
|
||||
{'fee' in this.state.tx && (
|
||||
<>
|
||||
<BlueText style={styles.rowCaption}>{loc.send.create_fee}</BlueText>
|
||||
<BlueText style={styles.rowValue}>{this.state.tx.fee + ' sats'}</BlueText>
|
||||
</>
|
||||
)}
|
||||
|
||||
{'hash' in this.state.tx && (
|
||||
<>
|
||||
<View style={styles.rowHeader}>
|
||||
<BlueText style={styles.txId}>Txid</BlueText>
|
||||
<BlueCopyToClipboardButton stringToCopy={this.state.tx.hash} />
|
||||
</View>
|
||||
<BlueText style={styles.txHash}>{this.state.tx.hash}</BlueText>
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
const url = `https://blockstream.info/tx/${this.state.tx.hash}`;
|
||||
Linking.canOpenURL(url).then(supported => {
|
||||
if (supported) {
|
||||
Linking.openURL(url);
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
<BlueText style={styles.txLink}>{loc.transactions.details_show_in_block_explorer}</BlueText>
|
||||
</TouchableOpacity>
|
||||
</>
|
||||
)}
|
||||
|
||||
{'received' in this.state.tx && (
|
||||
<>
|
||||
<BlueText style={styles.rowCaption}>{loc.transactions.details_received}</BlueText>
|
||||
<BlueText style={styles.rowValue}>{dayjs(this.state.tx.received).format('MM/DD/YYYY h:mm A')}</BlueText>
|
||||
</>
|
||||
)}
|
||||
|
||||
{'block_height' in this.state.tx && this.state.tx.block_height > 0 && (
|
||||
<>
|
||||
<BlueText style={styles.rowCaption}>{loc.transactions.details_block}</BlueText>
|
||||
<BlueText style={styles.rowValue}>{this.state.tx.block_height}</BlueText>
|
||||
</>
|
||||
)}
|
||||
|
||||
{'inputs' in this.state.tx && (
|
||||
<>
|
||||
<BlueText style={styles.rowCaption}>{loc.transactions.details_inputs}</BlueText>
|
||||
<BlueText style={styles.rowValue}>{this.state.tx.inputs.length}</BlueText>
|
||||
</>
|
||||
)}
|
||||
|
||||
{'outputs' in this.state.tx && this.state.tx.outputs.length > 0 && (
|
||||
<>
|
||||
<BlueText style={styles.rowCaption}>{loc.transactions.details_outputs}</BlueText>
|
||||
<BlueText style={styles.rowValue}>{this.state.tx.outputs.length}</BlueText>
|
||||
</>
|
||||
)}
|
||||
</BlueCard>
|
||||
</ScrollView>
|
||||
</SafeBlueArea>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TransactionsDetails.propTypes = {
|
||||
route: PropTypes.shape({
|
||||
name: PropTypes.string,
|
||||
params: PropTypes.shape({
|
||||
hash: PropTypes.string,
|
||||
}),
|
||||
}),
|
||||
navigation: PropTypes.shape({
|
||||
setParams: PropTypes.func,
|
||||
}),
|
||||
};
|
||||
|
||||
TransactionsDetails.navigationOptions = ({ navigation, route }) => ({
|
||||
TransactionsDetails.navigationOptions = () => ({
|
||||
...BlueNavigationStyle(),
|
||||
title: loc.transactions.details_title,
|
||||
headerStyle: {
|
||||
...BlueNavigationStyle().headerStyle,
|
||||
backgroundColor: BlueCurrentTheme.colors.customHeader,
|
||||
},
|
||||
headerRight: () => (
|
||||
<TouchableOpacity disabled={route.params.isLoading === true} style={styles.save} onPress={route.params.handleOnSaveButtonTapped}>
|
||||
<Text style={styles.saveText}>{loc.wallets.details_save}</Text>
|
||||
</TouchableOpacity>
|
||||
),
|
||||
});
|
||||
|
|
|
@ -22,7 +22,7 @@ import {
|
|||
BlueFormLabel,
|
||||
BlueButton,
|
||||
BlueNavigationStyle,
|
||||
BlueButtonLinkHook,
|
||||
BlueButtonLink,
|
||||
BlueSpacing20,
|
||||
} from '../../BlueComponents';
|
||||
import { HDSegwitBech32Wallet, SegwitP2SHWallet, HDSegwitP2SHWallet, LightningCustodianWallet, AppStorage } from '../../class';
|
||||
|
@ -310,7 +310,7 @@ const WalletsAdd = () => {
|
|||
}
|
||||
})()}
|
||||
{isAdvancedOptionsEnabled && selectedWalletType === ButtonSelected.ONCHAIN && !isLoading && (
|
||||
<BlueButtonLinkHook style={styles.import} title={entropyButtonText} onPress={navigateToEntropy} />
|
||||
<BlueButtonLink style={styles.import} title={entropyButtonText} onPress={navigateToEntropy} />
|
||||
)}
|
||||
<BlueSpacing20 />
|
||||
<View style={styles.createButton}>
|
||||
|
@ -321,7 +321,7 @@ const WalletsAdd = () => {
|
|||
)}
|
||||
</View>
|
||||
{!isLoading && (
|
||||
<BlueButtonLinkHook
|
||||
<BlueButtonLink
|
||||
testID="ImportWallet"
|
||||
style={styles.import}
|
||||
title={loc.wallets.add_import_wallet}
|
||||
|
|
|
@ -15,9 +15,9 @@ import {
|
|||
} from 'react-native';
|
||||
import {
|
||||
BlueButton,
|
||||
BlueButtonLinkHook,
|
||||
BlueButtonLink,
|
||||
BlueFormMultiInput,
|
||||
BlueLoadingHook,
|
||||
BlueLoading,
|
||||
BlueNavigationStyle,
|
||||
BlueSpacing10,
|
||||
BlueSpacing20,
|
||||
|
@ -290,6 +290,13 @@ const WalletsAddMultisigStep2 = () => {
|
|||
if (cosignersCopy.length === n) setIsOnCreateButtonEnabled(true);
|
||||
setIsProvideMnemonicsModalVisible(false);
|
||||
setIsLoading(false);
|
||||
setImportText('');
|
||||
};
|
||||
|
||||
const isValidMnemonicSeed = mnemonicSeed => {
|
||||
const hd = new HDSegwitBech32Wallet();
|
||||
hd.setSecret(mnemonicSeed);
|
||||
return hd.validateMnemonic();
|
||||
};
|
||||
|
||||
const onBarScanned = ret => {
|
||||
|
@ -298,6 +305,9 @@ const WalletsAddMultisigStep2 = () => {
|
|||
if (!ret.data) ret = { data: ret };
|
||||
if (ret.data.toUpperCase().startsWith('UR')) {
|
||||
alert('BC-UR not decoded. This should never happen');
|
||||
} else if (isValidMnemonicSeed(ret.data)) {
|
||||
setIsProvideMnemonicsModalVisible(true);
|
||||
setImportText(ret.data);
|
||||
} else {
|
||||
let cosigner = new MultisigCosigner(ret.data);
|
||||
if (!cosigner.isValid()) return alert(loc.multisig.invalid_cosigner);
|
||||
|
@ -572,7 +582,7 @@ const WalletsAddMultisigStep2 = () => {
|
|||
) : (
|
||||
<BlueButton disabled={importText.trim().length === 0} title={loc.wallets.import_do_import} onPress={useMnemonicPhrase} />
|
||||
)}
|
||||
<BlueButtonLinkHook disabled={isLoading} onPress={scanOrOpenFile} title={loc.wallets.import_scan_qr} />
|
||||
<BlueButtonLink disabled={isLoading} onPress={scanOrOpenFile} title={loc.wallets.import_scan_qr} />
|
||||
</View>
|
||||
</KeyboardAvoidingView>
|
||||
</BottomModal>
|
||||
|
@ -620,7 +630,7 @@ const WalletsAddMultisigStep2 = () => {
|
|||
);
|
||||
};
|
||||
const footer = isLoading ? (
|
||||
<BlueLoadingHook />
|
||||
<BlueLoading />
|
||||
) : (
|
||||
<View style={styles.buttonBottom}>
|
||||
<BlueButton title={loc.multisig.create} onPress={onCreate} disabled={!isOnCreateButtonEnabled} />
|
||||
|
|
|
@ -16,7 +16,7 @@ import {
|
|||
StatusBar,
|
||||
PermissionsAndroid,
|
||||
} from 'react-native';
|
||||
import { SecondButton, SafeBlueArea, BlueCard, BlueSpacing20, BlueNavigationStyle, BlueText, BlueLoadingHook } from '../../BlueComponents';
|
||||
import { SecondButton, SafeBlueArea, BlueCard, BlueSpacing20, BlueNavigationStyle, BlueText, BlueLoading } from '../../BlueComponents';
|
||||
import { LightningCustodianWallet } from '../../class/wallets/lightning-custodian-wallet';
|
||||
import { HDLegacyBreadwalletWallet } from '../../class/wallets/hd-legacy-breadwallet-wallet';
|
||||
import { HDLegacyP2PKHWallet } from '../../class/wallets/hd-legacy-p2pkh-wallet';
|
||||
|
@ -368,7 +368,7 @@ const WalletDetails = () => {
|
|||
|
||||
return isLoading ? (
|
||||
<View style={styles.root}>
|
||||
<BlueLoadingHook />
|
||||
<BlueLoading />
|
||||
</View>
|
||||
) : (
|
||||
<SafeBlueArea style={styles.root}>
|
||||
|
|
|
@ -140,6 +140,15 @@ const WalletTransactions = () => {
|
|||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [walletID]);
|
||||
|
||||
// if balance of the wallet positive and there are no transactions, then
|
||||
// it'a freshly impoted wallet and we need to refresh transactions
|
||||
useEffect(() => {
|
||||
if (dataSource.length === 0 && wallet.current.getBalance() > 0) {
|
||||
refreshTransactions();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
// if description of transaction has been changed we want to show new one
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
|
|
|
@ -22,9 +22,9 @@ import ImagePicker from 'react-native-image-picker';
|
|||
|
||||
import {
|
||||
BlueButton,
|
||||
BlueButtonLinkHook,
|
||||
BlueButtonLink,
|
||||
BlueFormMultiInput,
|
||||
BlueLoadingHook,
|
||||
BlueLoading,
|
||||
BlueNavigationStyle,
|
||||
BlueSpacing10,
|
||||
BlueSpacing20,
|
||||
|
@ -441,7 +441,7 @@ const ViewEditMultisigCosigners = () => {
|
|||
onPress={handleUseMnemonicPhrase}
|
||||
/>
|
||||
)}
|
||||
<BlueButtonLinkHook disabled={isLoading} onPress={scanOrOpenFile} title={loc.wallets.import_scan_qr} />
|
||||
<BlueButtonLink disabled={isLoading} onPress={scanOrOpenFile} title={loc.wallets.import_scan_qr} />
|
||||
</View>
|
||||
</KeyboardAvoidingView>
|
||||
</BottomModal>
|
||||
|
@ -451,7 +451,7 @@ const ViewEditMultisigCosigners = () => {
|
|||
if (isLoading)
|
||||
return (
|
||||
<SafeAreaView style={[styles.root, stylesHook.root]}>
|
||||
<BlueLoadingHook />
|
||||
<BlueLoading />
|
||||
</SafeAreaView>
|
||||
);
|
||||
|
||||
|
|
|
@ -1,12 +1,4 @@
|
|||
vim ios/BlueWallet/Info.plist
|
||||
vim ios/BlueWalletWatch/Info.plist
|
||||
vim "ios/BlueWalletWatch Extension/Info.plist"
|
||||
vim "ios/TodayExtension/Info.plist"
|
||||
vim ios/BlueWallet.xcodeproj/project.pbxproj
|
||||
vim ios/WalletInformationWidget/Widgets/MarketWidget/Info.plist
|
||||
vim ios/WalletInformationWidget/Widgets/WalletInformationAndMarketWidget/Info.plist
|
||||
vim ios/WalletInformationWidget/Info.plist
|
||||
vim ios/WalletInformationWidget/Widgets/PriceWidget/Info.plist
|
||||
vim android/app/build.gradle
|
||||
vim package.json
|
||||
vim package-lock.json
|
||||
|
|
Loading…
Add table
Reference in a new issue