mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2024-11-20 02:09:10 +01:00
Merge branch 'master' into opsrn
This commit is contained in:
commit
024e744f35
@ -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,
|
||||
@ -505,88 +485,6 @@ export const BlueAlertWalletExportReminder = ({ onSuccess = () => {}, onFailure
|
||||
);
|
||||
};
|
||||
|
||||
export const BlueNavigationStyle = (navigation, withNavigationCloseButton = false, customCloseButtonFunction = undefined) => {
|
||||
let headerRight;
|
||||
const { colors, closeImage } = useTheme();
|
||||
if (withNavigationCloseButton) {
|
||||
headerRight = () => (
|
||||
<TouchableOpacity
|
||||
style={{ width: 40, height: 40, padding: 14 }}
|
||||
onPress={
|
||||
customCloseButtonFunction === undefined
|
||||
? () => {
|
||||
Keyboard.dismiss();
|
||||
navigation.goBack(null);
|
||||
}
|
||||
: customCloseButtonFunction
|
||||
}
|
||||
>
|
||||
<Image style={{ alignSelf: 'center' }} source={closeImage} />
|
||||
</TouchableOpacity>
|
||||
);
|
||||
} else {
|
||||
headerRight = null;
|
||||
}
|
||||
|
||||
return {
|
||||
headerStyle: {
|
||||
borderBottomWidth: 0,
|
||||
elevation: 0,
|
||||
shadowOpacity: 0,
|
||||
shadowOffset: { height: 0, width: 0 },
|
||||
},
|
||||
headerTitleStyle: {
|
||||
fontWeight: '600',
|
||||
color: colors.foregroundColor,
|
||||
},
|
||||
headerRight,
|
||||
headerBackTitleVisible: false,
|
||||
headerTintColor: colors.foregroundColor,
|
||||
};
|
||||
};
|
||||
|
||||
export const BlueCreateTxNavigationStyle = (navigation, withAdvancedOptionsMenuButton = false, advancedOptionsMenuButtonAction) => {
|
||||
let headerRight;
|
||||
if (withAdvancedOptionsMenuButton) {
|
||||
headerRight = () => (
|
||||
<TouchableOpacity
|
||||
style={{ minWidth: 40, height: 40, justifyContent: 'center' }}
|
||||
onPress={advancedOptionsMenuButtonAction}
|
||||
testID="advancedOptionsMenuButton"
|
||||
>
|
||||
<Icon size={22} name="kebab-horizontal" type="octicon" color={BlueCurrentTheme.colors.foregroundColor} />
|
||||
</TouchableOpacity>
|
||||
);
|
||||
} else {
|
||||
headerRight = null;
|
||||
}
|
||||
return {
|
||||
headerStyle: {
|
||||
borderBottomWidth: 0,
|
||||
elevation: 0,
|
||||
shadowOffset: { height: 0, width: 0 },
|
||||
},
|
||||
headerTitleStyle: {
|
||||
fontWeight: '600',
|
||||
color: BlueCurrentTheme.colors.foregroundColor,
|
||||
},
|
||||
headerTintColor: BlueCurrentTheme.colors.foregroundColor,
|
||||
headerLeft: () => (
|
||||
<TouchableOpacity
|
||||
style={{ minWidth: 40, height: 40, justifyContent: 'center', paddingHorizontal: 14 }}
|
||||
onPress={() => {
|
||||
Keyboard.dismiss();
|
||||
navigation.goBack(null);
|
||||
}}
|
||||
>
|
||||
<Image style={{}} source={BlueCurrentTheme.closeImage} />
|
||||
</TouchableOpacity>
|
||||
),
|
||||
headerRight,
|
||||
headerBackTitle: null,
|
||||
};
|
||||
};
|
||||
|
||||
export const BluePrivateBalance = () => {
|
||||
return Platform.select({
|
||||
ios: (
|
||||
@ -675,11 +573,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 +656,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 +823,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 +855,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 +987,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 +1288,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();
|
||||
|
458
Navigation.js
458
Navigation.js
@ -2,6 +2,7 @@ import React from 'react';
|
||||
import { createStackNavigator, TransitionPresets } from '@react-navigation/stack';
|
||||
import { createDrawerNavigator } from '@react-navigation/drawer';
|
||||
import { Platform, useWindowDimensions, Dimensions } from 'react-native';
|
||||
import { useTheme } from '@react-navigation/native';
|
||||
|
||||
import Settings from './screen/settings/settings';
|
||||
import About from './screen/settings/about';
|
||||
@ -75,6 +76,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'
|
||||
@ -106,133 +108,191 @@ const defaultStackScreenOptions =
|
||||
};
|
||||
|
||||
const WalletsStack = createStackNavigator();
|
||||
const WalletsRoot = () => (
|
||||
<WalletsStack.Navigator {...(Platform.OS === 'android' ? { screenOptions: defaultScreenOptions } : null)}>
|
||||
<WalletsStack.Screen name="WalletsList" component={WalletsList} />
|
||||
<WalletsStack.Screen name="WalletTransactions" component={WalletTransactions} options={WalletTransactions.navigationOptions} />
|
||||
<WalletsStack.Screen name="WalletDetails" component={WalletDetails} options={WalletDetails.navigationOptions} />
|
||||
<WalletsStack.Screen name="TransactionDetails" component={TransactionDetails} options={TransactionDetails.navigationOptions} />
|
||||
<WalletsStack.Screen name="TransactionStatus" component={TransactionStatus} options={TransactionStatus.navigationOptions} />
|
||||
<WalletsStack.Screen name="HodlHodl" component={HodlHodl} options={HodlHodl.navigationOptions} />
|
||||
<WalletsStack.Screen name="CPFP" component={CPFP} options={CPFP.navigationOptions} />
|
||||
<WalletsStack.Screen name="RBFBumpFee" component={RBFBumpFee} options={RBFBumpFee.navigationOptions} />
|
||||
<WalletsStack.Screen name="RBFCancel" component={RBFCancel} options={RBFCancel.navigationOptions} />
|
||||
<WalletsStack.Screen name="Settings" component={Settings} options={Settings.navigationOptions} />
|
||||
<WalletsStack.Screen name="SelectWallet" component={SelectWallet} options={SelectWallet.navigationOptions} />
|
||||
<WalletsStack.Screen name="Currency" component={Currency} options={Currency.navigationOptions} />
|
||||
<WalletsStack.Screen name="About" component={About} options={About.navigationOptions} />
|
||||
<WalletsStack.Screen name="ReleaseNotes" component={ReleaseNotes} options={ReleaseNotes.navigationOptions} />
|
||||
<WalletsStack.Screen name="Selftest" component={Selftest} options={Selftest.navigationOptions} />
|
||||
<WalletsStack.Screen name="Licensing" component={Licensing} options={Licensing.navigationOptions} />
|
||||
<WalletsStack.Screen name="DefaultView" component={DefaultView} options={DefaultView.navigationOptions} />
|
||||
<WalletsStack.Screen name="Language" component={Language} options={Language.navigationOptions} />
|
||||
<WalletsStack.Screen name="EncryptStorage" component={EncryptStorage} options={EncryptStorage.navigationOptions} />
|
||||
<WalletsStack.Screen name="GeneralSettings" component={GeneralSettings} options={GeneralSettings.navigationOptions} />
|
||||
<WalletsStack.Screen name="NetworkSettings" component={NetworkSettings} options={NetworkSettings.navigationOptions} />
|
||||
<WalletsStack.Screen name="NotificationSettings" component={NotificationSettings} options={NotificationSettings.navigationOptions} />
|
||||
<WalletsStack.Screen name="PlausibleDeniability" component={PlausibleDeniability} options={PlausibleDeniability.navigationOptions} />
|
||||
<WalletsStack.Screen name="LightningSettings" component={LightningSettings} options={LightningSettings.navigationOptions} />
|
||||
<WalletsStack.Screen name="ElectrumSettings" component={ElectrumSettings} options={ElectrumSettings.navigationOptions} />
|
||||
<WalletsStack.Screen name="SettingsPrivacy" component={SettingsPrivacy} options={SettingsPrivacy.navigationOptions} />
|
||||
<WalletsStack.Screen name="LNDViewInvoice" component={LNDViewInvoice} options={LNDViewInvoice.navigationOptions} />
|
||||
<WalletsStack.Screen
|
||||
name="LNDViewAdditionalInvoiceInformation"
|
||||
component={LNDViewAdditionalInvoiceInformation}
|
||||
options={LNDViewAdditionalInvoiceInformation.navigationOptions}
|
||||
/>
|
||||
<WalletsStack.Screen
|
||||
name="LNDViewAdditionalInvoicePreImage"
|
||||
component={LNDViewAdditionalInvoicePreImage}
|
||||
options={LNDViewAdditionalInvoicePreImage.navigationOptions}
|
||||
/>
|
||||
<WalletsStack.Screen name="HodlHodlViewOffer" component={HodlHodlViewOffer} options={HodlHodlViewOffer.navigationOptions} />
|
||||
<WalletsStack.Screen name="Broadcast" component={Broadcast} options={Broadcast.navigationOptions} />
|
||||
<WalletsStack.Screen name="LnurlPay" component={LnurlPay} options={LnurlPay.navigationOptions} />
|
||||
<WalletsStack.Screen name="LnurlPaySuccess" component={LnurlPaySuccess} options={LnurlPaySuccess.navigationOptions} />
|
||||
</WalletsStack.Navigator>
|
||||
);
|
||||
|
||||
const WalletsRoot = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<WalletsStack.Navigator {...(Platform.OS === 'android' ? { screenOptions: defaultScreenOptions } : null)}>
|
||||
<WalletsStack.Screen name="WalletsList" component={WalletsList} />
|
||||
<WalletsStack.Screen name="WalletTransactions" component={WalletTransactions} options={WalletTransactions.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="WalletDetails" component={WalletDetails} options={WalletDetails.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="TransactionDetails" component={TransactionDetails} options={TransactionDetails.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="TransactionStatus" component={TransactionStatus} options={TransactionStatus.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="HodlHodl" component={HodlHodl} options={HodlHodl.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="HodlHodlViewOffer" component={HodlHodlViewOffer} options={HodlHodlViewOffer.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="CPFP" component={CPFP} options={CPFP.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="RBFBumpFee" component={RBFBumpFee} options={RBFBumpFee.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="RBFCancel" component={RBFCancel} options={RBFCancel.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="Settings" component={Settings} options={Settings.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="SelectWallet" component={SelectWallet} options={SelectWallet.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="Currency" component={Currency} options={Currency.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="About" component={About} options={About.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="ReleaseNotes" component={ReleaseNotes} options={ReleaseNotes.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="Selftest" component={Selftest} options={Selftest.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="Licensing" component={Licensing} options={Licensing.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="DefaultView" component={DefaultView} options={DefaultView.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="Language" component={Language} options={Language.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="EncryptStorage" component={EncryptStorage} options={EncryptStorage.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="GeneralSettings" component={GeneralSettings} options={GeneralSettings.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="NetworkSettings" component={NetworkSettings} options={NetworkSettings.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen
|
||||
name="NotificationSettings"
|
||||
component={NotificationSettings}
|
||||
options={NotificationSettings.navigationOptions(theme)}
|
||||
/>
|
||||
<WalletsStack.Screen
|
||||
name="PlausibleDeniability"
|
||||
component={PlausibleDeniability}
|
||||
options={PlausibleDeniability.navigationOptions(theme)}
|
||||
/>
|
||||
<WalletsStack.Screen name="LightningSettings" component={LightningSettings} options={LightningSettings.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="ElectrumSettings" component={ElectrumSettings} options={ElectrumSettings.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="SettingsPrivacy" component={SettingsPrivacy} options={SettingsPrivacy.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="LNDViewInvoice" component={LNDViewInvoice} options={LNDViewInvoice.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen
|
||||
name="LNDViewAdditionalInvoiceInformation"
|
||||
component={LNDViewAdditionalInvoiceInformation}
|
||||
options={LNDViewAdditionalInvoiceInformation.navigationOptions(theme)}
|
||||
/>
|
||||
<WalletsStack.Screen
|
||||
name="LNDViewAdditionalInvoicePreImage"
|
||||
component={LNDViewAdditionalInvoicePreImage}
|
||||
options={LNDViewAdditionalInvoicePreImage.navigationOptions(theme)}
|
||||
/>
|
||||
<WalletsStack.Screen name="Broadcast" component={Broadcast} options={Broadcast.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="LnurlPay" component={LnurlPay} options={LnurlPay.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="LnurlPaySuccess" component={LnurlPaySuccess} options={LnurlPaySuccess.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen
|
||||
name="Success"
|
||||
component={Success}
|
||||
options={{
|
||||
headerShown: false,
|
||||
gestureEnabled: false,
|
||||
}}
|
||||
/>
|
||||
</WalletsStack.Navigator>
|
||||
);
|
||||
};
|
||||
|
||||
const AddWalletStack = createStackNavigator();
|
||||
const AddWalletRoot = () => (
|
||||
<AddWalletStack.Navigator screenOptions={defaultStackScreenOptions}>
|
||||
<AddWalletStack.Screen name="AddWallet" component={AddWallet} options={AddWallet.navigationOptions} />
|
||||
<AddWalletStack.Screen name="ImportWallet" component={ImportWallet} options={ImportWallet.navigationOptions} />
|
||||
<AddWalletStack.Screen name="PleaseBackup" component={PleaseBackup} options={PleaseBackup.navigationOptions} />
|
||||
<AddWalletStack.Screen name="PleaseBackupLNDHub" component={PleaseBackupLNDHub} options={PleaseBackupLNDHub.navigationOptions} />
|
||||
<AddWalletStack.Screen name="ProvideEntropy" component={ProvideEntropy} options={ProvideEntropy.navigationOptions} />
|
||||
<AddWalletStack.Screen name="WalletsAddMultisig" component={WalletsAddMultisig} options={WalletsAddMultisig.navigationOptions} />
|
||||
<AddWalletStack.Screen
|
||||
name="WalletsAddMultisigStep2"
|
||||
component={WalletsAddMultisigStep2}
|
||||
options={WalletsAddMultisigStep2.navigationOptions}
|
||||
/>
|
||||
</AddWalletStack.Navigator>
|
||||
);
|
||||
const AddWalletRoot = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<AddWalletStack.Navigator screenOptions={defaultStackScreenOptions}>
|
||||
<AddWalletStack.Screen name="AddWallet" component={AddWallet} options={AddWallet.navigationOptions(theme)} />
|
||||
<AddWalletStack.Screen name="ImportWallet" component={ImportWallet} options={ImportWallet.navigationOptions(theme)} />
|
||||
<AddWalletStack.Screen name="PleaseBackup" component={PleaseBackup} options={PleaseBackup.navigationOptions(theme)} />
|
||||
<AddWalletStack.Screen
|
||||
name="PleaseBackupLNDHub"
|
||||
component={PleaseBackupLNDHub}
|
||||
options={PleaseBackupLNDHub.navigationOptions(theme)}
|
||||
/>
|
||||
<AddWalletStack.Screen name="ProvideEntropy" component={ProvideEntropy} options={ProvideEntropy.navigationOptions(theme)} />
|
||||
<AddWalletStack.Screen
|
||||
name="WalletsAddMultisig"
|
||||
component={WalletsAddMultisig}
|
||||
options={WalletsAddMultisig.navigationOptions(theme)}
|
||||
/>
|
||||
<AddWalletStack.Screen
|
||||
name="WalletsAddMultisigStep2"
|
||||
component={WalletsAddMultisigStep2}
|
||||
options={WalletsAddMultisigStep2.navigationOptions(theme)}
|
||||
/>
|
||||
</AddWalletStack.Navigator>
|
||||
);
|
||||
};
|
||||
|
||||
// CreateTransactionStackNavigator === SendDetailsStack
|
||||
const SendDetailsStack = createStackNavigator();
|
||||
const SendDetailsRoot = () => (
|
||||
<SendDetailsStack.Navigator screenOptions={defaultStackScreenOptions}>
|
||||
<SendDetailsStack.Screen name="SendDetails" component={SendDetails} options={SendDetails.navigationOptions} />
|
||||
<SendDetailsStack.Screen name="Confirm" component={Confirm} options={Confirm.navigationOptions} />
|
||||
<SendDetailsStack.Screen
|
||||
name="PsbtWithHardwareWallet"
|
||||
component={PsbtWithHardwareWallet}
|
||||
options={PsbtWithHardwareWallet.navigationOptions}
|
||||
/>
|
||||
<SendDetailsStack.Screen name="CreateTransaction" component={SendCreate} options={SendCreate.navigationOptions} />
|
||||
<SendDetailsStack.Screen name="PsbtMultisig" component={PsbtMultisig} options={PsbtMultisig.navigationOptions} />
|
||||
<SendDetailsStack.Screen
|
||||
name="Success"
|
||||
component={Success}
|
||||
options={{
|
||||
headerShown: false,
|
||||
gestureEnabled: false,
|
||||
}}
|
||||
/>
|
||||
<SendDetailsStack.Screen name="SelectWallet" component={SelectWallet} options={SelectWallet.navigationOptions} />
|
||||
<SendDetailsStack.Screen name="CoinControl" component={CoinControl} options={CoinControl.navigationOptions} />
|
||||
</SendDetailsStack.Navigator>
|
||||
);
|
||||
const SendDetailsRoot = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<SendDetailsStack.Navigator screenOptions={defaultStackScreenOptions}>
|
||||
<SendDetailsStack.Screen name="SendDetails" component={SendDetails} options={SendDetails.navigationOptions(theme)} />
|
||||
<SendDetailsStack.Screen name="Confirm" component={Confirm} options={Confirm.navigationOptions(theme)} />
|
||||
<SendDetailsStack.Screen
|
||||
name="PsbtWithHardwareWallet"
|
||||
component={PsbtWithHardwareWallet}
|
||||
options={PsbtWithHardwareWallet.navigationOptions(theme)}
|
||||
/>
|
||||
<SendDetailsStack.Screen name="CreateTransaction" component={SendCreate} options={SendCreate.navigationOptions(theme)} />
|
||||
<SendDetailsStack.Screen name="PsbtMultisig" component={PsbtMultisig} options={PsbtMultisig.navigationOptions(theme)} />
|
||||
<SendDetailsStack.Screen
|
||||
name="PsbtMultisigQRCode"
|
||||
component={PsbtMultisigQRCode}
|
||||
options={PsbtMultisigQRCode.navigationOptions(theme)}
|
||||
/>
|
||||
<SendDetailsStack.Screen
|
||||
name="Success"
|
||||
component={Success}
|
||||
options={{
|
||||
headerShown: false,
|
||||
gestureEnabled: false,
|
||||
}}
|
||||
/>
|
||||
<SendDetailsStack.Screen name="SelectWallet" component={SelectWallet} options={SelectWallet.navigationOptions(theme)} />
|
||||
<SendDetailsStack.Screen name="CoinControl" component={CoinControl} options={CoinControl.navigationOptions(theme)} />
|
||||
</SendDetailsStack.Navigator>
|
||||
);
|
||||
};
|
||||
|
||||
const LNDCreateInvoiceStack = createStackNavigator();
|
||||
const LNDCreateInvoiceRoot = () => (
|
||||
<LNDCreateInvoiceStack.Navigator screenOptions={defaultStackScreenOptions}>
|
||||
<LNDCreateInvoiceStack.Screen name="LNDCreateInvoice" component={LNDCreateInvoice} options={LNDCreateInvoice.navigationOptions} />
|
||||
<LNDCreateInvoiceStack.Screen name="SelectWallet" component={SelectWallet} options={SelectWallet.navigationOptions} />
|
||||
<LNDCreateInvoiceStack.Screen name="LNDViewInvoice" component={LNDViewInvoice} options={LNDViewInvoice.navigationOptions} />
|
||||
<LNDCreateInvoiceStack.Screen
|
||||
name="LNDViewAdditionalInvoiceInformation"
|
||||
component={LNDViewAdditionalInvoiceInformation}
|
||||
options={LNDViewAdditionalInvoiceInformation.navigationOptions}
|
||||
/>
|
||||
<LNDCreateInvoiceStack.Screen
|
||||
name="LNDViewAdditionalInvoicePreImage"
|
||||
component={LNDViewAdditionalInvoicePreImage}
|
||||
options={LNDViewAdditionalInvoicePreImage.navigationOptions}
|
||||
/>
|
||||
</LNDCreateInvoiceStack.Navigator>
|
||||
);
|
||||
const LNDCreateInvoiceRoot = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<LNDCreateInvoiceStack.Navigator screenOptions={defaultStackScreenOptions}>
|
||||
<LNDCreateInvoiceStack.Screen
|
||||
name="LNDCreateInvoice"
|
||||
component={LNDCreateInvoice}
|
||||
options={LNDCreateInvoice.navigationOptions(theme)}
|
||||
/>
|
||||
<LNDCreateInvoiceStack.Screen name="SelectWallet" component={SelectWallet} options={SelectWallet.navigationOptions(theme)} />
|
||||
<LNDCreateInvoiceStack.Screen name="LNDViewInvoice" component={LNDViewInvoice} options={LNDViewInvoice.navigationOptions(theme)} />
|
||||
<LNDCreateInvoiceStack.Screen
|
||||
name="LNDViewAdditionalInvoiceInformation"
|
||||
component={LNDViewAdditionalInvoiceInformation}
|
||||
options={LNDViewAdditionalInvoiceInformation.navigationOptions(theme)}
|
||||
/>
|
||||
<LNDCreateInvoiceStack.Screen
|
||||
name="LNDViewAdditionalInvoicePreImage"
|
||||
component={LNDViewAdditionalInvoicePreImage}
|
||||
options={LNDViewAdditionalInvoicePreImage.navigationOptions(theme)}
|
||||
/>
|
||||
</LNDCreateInvoiceStack.Navigator>
|
||||
);
|
||||
};
|
||||
|
||||
// LightningScanInvoiceStackNavigator === ScanLndInvoiceStack
|
||||
const ScanLndInvoiceStack = createStackNavigator();
|
||||
const ScanLndInvoiceRoot = () => (
|
||||
<ScanLndInvoiceStack.Navigator screenOptions={defaultStackScreenOptions}>
|
||||
<ScanLndInvoiceStack.Screen name="ScanLndInvoice" component={ScanLndInvoice} options={ScanLndInvoice.navigationOptions} />
|
||||
<ScanLndInvoiceStack.Screen name="SelectWallet" component={SelectWallet} options={SelectWallet.navigationOptions} />
|
||||
<ScanLndInvoiceStack.Screen name="Success" component={Success} options={{ headerShown: false, gestureEnabled: false }} />
|
||||
<ScanLndInvoiceStack.Screen name="LnurlPay" component={LnurlPay} options={LnurlPay.navigationOptions} />
|
||||
<ScanLndInvoiceStack.Screen name="LnurlPaySuccess" component={LnurlPaySuccess} options={LnurlPaySuccess.navigationOptions} />
|
||||
</ScanLndInvoiceStack.Navigator>
|
||||
);
|
||||
const ScanLndInvoiceRoot = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<ScanLndInvoiceStack.Navigator screenOptions={defaultStackScreenOptions}>
|
||||
<ScanLndInvoiceStack.Screen name="ScanLndInvoice" component={ScanLndInvoice} options={ScanLndInvoice.navigationOptions(theme)} />
|
||||
<ScanLndInvoiceStack.Screen name="SelectWallet" component={SelectWallet} options={SelectWallet.navigationOptions(theme)} />
|
||||
<ScanLndInvoiceStack.Screen name="Success" component={Success} options={{ headerShown: false, gestureEnabled: false }} />
|
||||
<ScanLndInvoiceStack.Screen name="LnurlPay" component={LnurlPay} options={LnurlPay.navigationOptions(theme)} />
|
||||
<ScanLndInvoiceStack.Screen name="LnurlPaySuccess" component={LnurlPaySuccess} options={LnurlPaySuccess.navigationOptions(theme)} />
|
||||
</ScanLndInvoiceStack.Navigator>
|
||||
);
|
||||
};
|
||||
|
||||
const AztecoRedeemStack = createStackNavigator();
|
||||
const AztecoRedeemRoot = () => (
|
||||
<AztecoRedeemStack.Navigator screenOptions={defaultStackScreenOptions}>
|
||||
<AztecoRedeemStack.Screen name="AztecoRedeem" component={AztecoRedeem} options={AztecoRedeem.navigationOptions} />
|
||||
<AztecoRedeemStack.Screen name="SelectWallet" component={SelectWallet} options={{ headerLeft: null }} />
|
||||
</AztecoRedeemStack.Navigator>
|
||||
);
|
||||
const AztecoRedeemRoot = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<AztecoRedeemStack.Navigator screenOptions={defaultStackScreenOptions}>
|
||||
<AztecoRedeemStack.Screen name="AztecoRedeem" component={AztecoRedeem} options={AztecoRedeem.navigationOptions(theme)} />
|
||||
<AztecoRedeemStack.Screen name="SelectWallet" component={SelectWallet} options={{ headerLeft: null }} />
|
||||
</AztecoRedeemStack.Navigator>
|
||||
);
|
||||
};
|
||||
|
||||
const ScanQRCodeStack = createStackNavigator();
|
||||
const ScanQRCodeRoot = () => (
|
||||
@ -256,18 +316,26 @@ const UnlockWithScreenRoot = () => (
|
||||
);
|
||||
|
||||
const HodlHodlLoginStack = createStackNavigator();
|
||||
const HodlHodlLoginRoot = () => (
|
||||
<HodlHodlLoginStack.Navigator name="HodlHodlLoginRoot" screenOptions={defaultStackScreenOptions}>
|
||||
<HodlHodlLoginStack.Screen name="HodlHodlLogin" component={HodlHodlLogin} options={HodlHodlLogin.navigationOptions} />
|
||||
</HodlHodlLoginStack.Navigator>
|
||||
);
|
||||
const HodlHodlLoginRoot = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<HodlHodlLoginStack.Navigator name="HodlHodlLoginRoot" screenOptions={defaultStackScreenOptions}>
|
||||
<HodlHodlLoginStack.Screen name="HodlHodlLogin" component={HodlHodlLogin} options={HodlHodlLogin.navigationOptions(theme)} />
|
||||
</HodlHodlLoginStack.Navigator>
|
||||
);
|
||||
};
|
||||
|
||||
const ReorderWalletsStack = createStackNavigator();
|
||||
const ReorderWalletsStackRoot = () => (
|
||||
<ReorderWalletsStack.Navigator name="ReorderWalletsRoot" screenOptions={defaultStackScreenOptions}>
|
||||
<ReorderWalletsStack.Screen name="ReorderWallets" component={ReorderWallets} options={ReorderWallets.navigationOptions} />
|
||||
</ReorderWalletsStack.Navigator>
|
||||
);
|
||||
const ReorderWalletsStackRoot = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<ReorderWalletsStack.Navigator name="ReorderWalletsRoot" screenOptions={defaultStackScreenOptions}>
|
||||
<ReorderWalletsStack.Screen name="ReorderWallets" component={ReorderWallets} options={ReorderWallets.navigationOptions(theme)} />
|
||||
</ReorderWalletsStack.Navigator>
|
||||
);
|
||||
};
|
||||
|
||||
const Drawer = createDrawerNavigator();
|
||||
function DrawerRoot() {
|
||||
@ -287,32 +355,48 @@ function DrawerRoot() {
|
||||
}
|
||||
|
||||
const ReceiveDetailsStack = createStackNavigator();
|
||||
const ReceiveDetailsStackRoot = () => (
|
||||
<ReceiveDetailsStack.Navigator name="ReceiveDetailsRoot" screenOptions={defaultStackScreenOptions} initialRouteName="ReceiveDetails">
|
||||
<RootStack.Screen name="ReceiveDetails" component={ReceiveDetails} options={ReceiveDetails.navigationOptions} />
|
||||
</ReceiveDetailsStack.Navigator>
|
||||
);
|
||||
const ReceiveDetailsStackRoot = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<ReceiveDetailsStack.Navigator name="ReceiveDetailsRoot" screenOptions={defaultStackScreenOptions} initialRouteName="ReceiveDetails">
|
||||
<RootStack.Screen name="ReceiveDetails" component={ReceiveDetails} options={ReceiveDetails.navigationOptions(theme)} />
|
||||
</ReceiveDetailsStack.Navigator>
|
||||
);
|
||||
};
|
||||
|
||||
const WalletXpubStack = createStackNavigator();
|
||||
const WalletXpubStackRoot = () => (
|
||||
<WalletXpubStack.Navigator name="WalletXpubRoot" screenOptions={defaultStackScreenOptions} initialRouteName="WalletXpub">
|
||||
<WalletXpubStack.Screen name="WalletXpub" component={WalletXpub} options={WalletXpub.navigationOptions} />
|
||||
</WalletXpubStack.Navigator>
|
||||
);
|
||||
const WalletXpubStackRoot = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<WalletXpubStack.Navigator name="WalletXpubRoot" screenOptions={defaultStackScreenOptions} initialRouteName="WalletXpub">
|
||||
<WalletXpubStack.Screen name="WalletXpub" component={WalletXpub} options={WalletXpub.navigationOptions(theme)} />
|
||||
</WalletXpubStack.Navigator>
|
||||
);
|
||||
};
|
||||
|
||||
const WalletExportStack = createStackNavigator();
|
||||
const WalletExportStackRoot = () => (
|
||||
<WalletExportStack.Navigator name="WalletExportRoot" screenOptions={defaultStackScreenOptions} initialRouteName="WalletExport">
|
||||
<WalletExportStack.Screen name="WalletExport" component={WalletExport} options={WalletExport.navigationOptions} />
|
||||
</WalletExportStack.Navigator>
|
||||
);
|
||||
const WalletExportStackRoot = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<WalletExportStack.Navigator name="WalletExportRoot" screenOptions={defaultStackScreenOptions} initialRouteName="WalletExport">
|
||||
<WalletExportStack.Screen name="WalletExport" component={WalletExport} options={WalletExport.navigationOptions(theme)} />
|
||||
</WalletExportStack.Navigator>
|
||||
);
|
||||
};
|
||||
|
||||
const LappBrowserStack = createStackNavigator();
|
||||
const LappBrowserStackRoot = () => (
|
||||
<LappBrowserStack.Navigator name="LappBrowserRoot" screenOptions={defaultStackScreenOptions} initialRouteName="LappBrowser">
|
||||
<LappBrowserStack.Screen name="LappBrowser" component={LappBrowser} options={LappBrowser.navigationOptions} />
|
||||
</LappBrowserStack.Navigator>
|
||||
);
|
||||
const LappBrowserStackRoot = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<LappBrowserStack.Navigator name="LappBrowserRoot" screenOptions={defaultStackScreenOptions} initialRouteName="LappBrowser">
|
||||
<LappBrowserStack.Screen name="LappBrowser" component={LappBrowser} options={LappBrowser.navigationOptions(theme)} />
|
||||
</LappBrowserStack.Navigator>
|
||||
);
|
||||
};
|
||||
|
||||
const InitStack = createStackNavigator();
|
||||
const InitRoot = () => (
|
||||
@ -329,47 +413,51 @@ const InitRoot = () => (
|
||||
);
|
||||
|
||||
const RootStack = createStackNavigator();
|
||||
const Navigation = () => (
|
||||
<RootStack.Navigator mode="modal" screenOptions={defaultScreenOptions} initialRouteName="LoadingScreenRoot">
|
||||
{/* stacks */}
|
||||
<RootStack.Screen name="WalletsRoot" component={WalletsRoot} options={{ headerShown: false }} />
|
||||
<RootStack.Screen name="AddWalletRoot" component={AddWalletRoot} options={{ headerShown: false }} />
|
||||
<RootStack.Screen name="SendDetailsRoot" component={SendDetailsRoot} options={{ headerShown: false }} />
|
||||
<RootStack.Screen name="LNDCreateInvoiceRoot" component={LNDCreateInvoiceRoot} options={{ headerShown: false }} />
|
||||
<RootStack.Screen name="ScanLndInvoiceRoot" component={ScanLndInvoiceRoot} options={{ headerShown: false }} />
|
||||
<RootStack.Screen name="AztecoRedeemRoot" component={AztecoRedeemRoot} options={{ headerShown: false }} />
|
||||
<RootStack.Screen name="HodlHodlLoginRoot" component={HodlHodlLoginRoot} options={{ headerShown: false }} />
|
||||
<RootStack.Screen name="HodlHodlMyContracts" component={HodlHodlMyContracts} options={HodlHodlMyContracts.navigationOptions} />
|
||||
<RootStack.Screen name="HodlHodlWebview" component={HodlHodlWebview} options={HodlHodlWebview.navigationOptions} />
|
||||
const Navigation = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
{/* screens */}
|
||||
<RootStack.Screen name="WalletExportRoot" component={WalletExportStackRoot} options={{ headerShown: false }} />
|
||||
<RootStack.Screen
|
||||
name="ExportMultisigCoordinationSetup"
|
||||
component={ExportMultisigCoordinationSetup}
|
||||
options={ExportMultisigCoordinationSetup.navigationOptions}
|
||||
/>
|
||||
<RootStack.Screen
|
||||
name="ViewEditMultisigCosigners"
|
||||
component={ViewEditMultisigCosigners}
|
||||
options={ViewEditMultisigCosigners.navigationOptions}
|
||||
/>
|
||||
<RootStack.Screen name="WalletXpubRoot" component={WalletXpubStackRoot} options={{ headerShown: false }} />
|
||||
<RootStack.Screen name="BuyBitcoin" component={BuyBitcoin} options={BuyBitcoin.navigationOptions} />
|
||||
<RootStack.Screen name="Marketplace" component={Marketplace} options={Marketplace.navigationOptions} />
|
||||
<RootStack.Screen name="SelectWallet" component={SelectWallet} options={{ headerLeft: null }} />
|
||||
<RootStack.Screen name="ReceiveDetailsRoot" component={ReceiveDetailsStackRoot} options={{ headerShown: false }} />
|
||||
<RootStack.Screen name="LappBrowserRoot" component={LappBrowserStackRoot} options={{ headerShown: false }} />
|
||||
return (
|
||||
<RootStack.Navigator mode="modal" screenOptions={defaultScreenOptions} initialRouteName="LoadingScreenRoot">
|
||||
{/* stacks */}
|
||||
<RootStack.Screen name="WalletsRoot" component={WalletsRoot} options={{ headerShown: false }} />
|
||||
<RootStack.Screen name="AddWalletRoot" component={AddWalletRoot} options={{ headerShown: false }} />
|
||||
<RootStack.Screen name="SendDetailsRoot" component={SendDetailsRoot} options={{ headerShown: false }} />
|
||||
<RootStack.Screen name="LNDCreateInvoiceRoot" component={LNDCreateInvoiceRoot} options={{ headerShown: false }} />
|
||||
<RootStack.Screen name="ScanLndInvoiceRoot" component={ScanLndInvoiceRoot} options={{ headerShown: false }} />
|
||||
<RootStack.Screen name="AztecoRedeemRoot" component={AztecoRedeemRoot} options={{ headerShown: false }} />
|
||||
<RootStack.Screen name="HodlHodlLoginRoot" component={HodlHodlLoginRoot} options={{ headerShown: false }} />
|
||||
<RootStack.Screen name="HodlHodlMyContracts" component={HodlHodlMyContracts} options={HodlHodlMyContracts.navigationOptions(theme)} />
|
||||
<RootStack.Screen name="HodlHodlWebview" component={HodlHodlWebview} options={HodlHodlWebview.navigationOptions(theme)} />
|
||||
|
||||
<RootStack.Screen
|
||||
name="ScanQRCodeRoot"
|
||||
component={ScanQRCodeRoot}
|
||||
options={{
|
||||
...(Platform.OS === 'ios' ? TransitionPresets.ModalTransition : TransitionPresets.ScaleFromCenterAndroid),
|
||||
headerShown: false,
|
||||
}}
|
||||
/>
|
||||
</RootStack.Navigator>
|
||||
);
|
||||
{/* screens */}
|
||||
<RootStack.Screen name="WalletExportRoot" component={WalletExportStackRoot} options={{ headerShown: false }} />
|
||||
<RootStack.Screen
|
||||
name="ExportMultisigCoordinationSetup"
|
||||
component={ExportMultisigCoordinationSetup}
|
||||
options={ExportMultisigCoordinationSetup.navigationOptions(theme)}
|
||||
/>
|
||||
<RootStack.Screen
|
||||
name="ViewEditMultisigCosigners"
|
||||
component={ViewEditMultisigCosigners}
|
||||
options={ViewEditMultisigCosigners.navigationOptions(theme)}
|
||||
/>
|
||||
<RootStack.Screen name="WalletXpubRoot" component={WalletXpubStackRoot} options={{ headerShown: false }} />
|
||||
<RootStack.Screen name="BuyBitcoin" component={BuyBitcoin} options={BuyBitcoin.navigationOptions(theme)} />
|
||||
<RootStack.Screen name="Marketplace" component={Marketplace} options={Marketplace.navigationOptions(theme)} />
|
||||
<RootStack.Screen name="SelectWallet" component={SelectWallet} options={{ headerLeft: null }} />
|
||||
<RootStack.Screen name="ReceiveDetailsRoot" component={ReceiveDetailsStackRoot} options={{ headerShown: false }} />
|
||||
<RootStack.Screen name="LappBrowserRoot" component={LappBrowserStackRoot} options={{ headerShown: false }} />
|
||||
|
||||
<RootStack.Screen
|
||||
name="ScanQRCodeRoot"
|
||||
component={ScanQRCodeRoot}
|
||||
options={{
|
||||
...(Platform.OS === 'ios' ? TransitionPresets.ModalTransition : TransitionPresets.ScaleFromCenterAndroid),
|
||||
headerShown: false,
|
||||
}}
|
||||
/>
|
||||
</RootStack.Navigator>
|
||||
);
|
||||
};
|
||||
|
||||
export default InitRoot;
|
||||
|
@ -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
|
||||
|
@ -82,7 +82,7 @@ class DeeplinkSchemaMatch {
|
||||
},
|
||||
]);
|
||||
} else if (action === 'openReceive') {
|
||||
completionHandler(['LNDCreateInvoiceRoot', { screen: 'LNDCreateInvoice', params: { fromWallet: wallet } }]);
|
||||
completionHandler(['LNDCreateInvoiceRoot', { screen: 'LNDCreateInvoice', params: { walletID: wallet.getID() } }]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 }));
|
||||
}}
|
||||
>
|
||||
|
92
components/navigationStyle.js
Normal file
92
components/navigationStyle.js
Normal file
@ -0,0 +1,92 @@
|
||||
import React from 'react';
|
||||
import { Image, Keyboard, TouchableOpacity, StyleSheet } from 'react-native';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
button: {
|
||||
minWidth: 40,
|
||||
height: 40,
|
||||
justifyContent: 'center',
|
||||
paddingHorizontal: 14,
|
||||
},
|
||||
});
|
||||
|
||||
const navigationStyle = ({ closeButton = false, closeButtonFunc, ...opts }, formatter) => {
|
||||
return theme => ({ navigation, route }) => {
|
||||
let headerRight = null;
|
||||
if (closeButton) {
|
||||
const handleClose = closeButtonFunc
|
||||
? () => closeButtonFunc({ navigation, route })
|
||||
: () => {
|
||||
Keyboard.dismiss();
|
||||
navigation.goBack(null);
|
||||
};
|
||||
headerRight = () => (
|
||||
<TouchableOpacity style={styles.button} onPress={handleClose}>
|
||||
<Image source={theme.closeImage} />
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
|
||||
let options = {
|
||||
headerStyle: {
|
||||
borderBottomWidth: 0,
|
||||
elevation: 0,
|
||||
shadowOpacity: 0,
|
||||
shadowOffset: { height: 0, width: 0 },
|
||||
},
|
||||
headerTitleStyle: {
|
||||
fontWeight: '600',
|
||||
color: theme.colors.foregroundColor,
|
||||
},
|
||||
headerRight,
|
||||
headerBackTitleVisible: false,
|
||||
headerTintColor: theme.colors.foregroundColor,
|
||||
...opts,
|
||||
};
|
||||
|
||||
if (formatter) {
|
||||
options = formatter(options, { theme, navigation, route });
|
||||
}
|
||||
|
||||
return options;
|
||||
};
|
||||
};
|
||||
|
||||
export default navigationStyle;
|
||||
|
||||
export const navigationStyleTx = (opts, formatter) => {
|
||||
return theme => ({ navigation, route }) => {
|
||||
let options = {
|
||||
headerStyle: {
|
||||
borderBottomWidth: 0,
|
||||
elevation: 0,
|
||||
shadowOffset: { height: 0, width: 0 },
|
||||
},
|
||||
headerTitleStyle: {
|
||||
fontWeight: '600',
|
||||
color: theme.colors.foregroundColor,
|
||||
},
|
||||
// headerBackTitle: null,
|
||||
headerBackTitleVisible: false,
|
||||
headerTintColor: theme.colors.foregroundColor,
|
||||
headerLeft: () => (
|
||||
<TouchableOpacity
|
||||
style={styles.button}
|
||||
onPress={() => {
|
||||
Keyboard.dismiss();
|
||||
navigation.goBack(null);
|
||||
}}
|
||||
>
|
||||
<Image source={theme.closeImage} />
|
||||
</TouchableOpacity>
|
||||
),
|
||||
...opts,
|
||||
};
|
||||
|
||||
if (formatter) {
|
||||
options = formatter(options, { theme, navigation, route });
|
||||
}
|
||||
|
||||
return options;
|
||||
};
|
||||
};
|
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)";
|
||||
|
@ -157,6 +157,14 @@
|
||||
<string>₩</string>
|
||||
<key>locale</key>
|
||||
<string>ko-KR</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>LBP</string>
|
||||
<key>symbol</key>
|
||||
<string>ل.ل.</string>
|
||||
<key>locale</key>
|
||||
<string>ar-LB</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
|
@ -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
|
||||
|
@ -75,6 +75,7 @@
|
||||
"item_rating": "{rating} trades",
|
||||
"item_rating_no": "No rating",
|
||||
"login": "Login",
|
||||
"logout": "logout",
|
||||
"mycont": "My contracts",
|
||||
"offer_accept": "Accept offer",
|
||||
"offer_account_finish": "Looks like you didn't finish setting up account on HodlHodl, would you like to finish setup now?",
|
||||
@ -145,6 +146,8 @@
|
||||
"header": "Receive"
|
||||
},
|
||||
"send": {
|
||||
"broadcast_success_screen_msg": "Success! You transaction has been broadcasted!",
|
||||
"broadcast_success_screen_open": "Open link in explorer",
|
||||
"broadcastButton": "BROADCAST",
|
||||
"broadcastError": "error",
|
||||
"broadcastNone": "Input transaction hash",
|
||||
@ -314,6 +317,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",
|
||||
@ -403,6 +407,7 @@
|
||||
"take_photo": "Take Photo",
|
||||
"xpub_copiedToClipboard": "Copied to clipboard.",
|
||||
"pull_to_refresh": "pull to refresh",
|
||||
"warning_do_not_disclose": "Warning! Do not disclose",
|
||||
"xpub_title": "wallet XPUB"
|
||||
},
|
||||
"multisig": {
|
||||
|
@ -107,12 +107,13 @@
|
||||
"lndViewInvoice": {
|
||||
"additional_info": "Lisäinformaatio",
|
||||
"for": "Kenelle:",
|
||||
"lightning_invoice": "Lightning-lasku",
|
||||
"has_been_paid": "Tämä lasku on maksettu",
|
||||
"open_direct_channel": "Avaa suora kanava tällä solmulla:",
|
||||
"please_pay": "Ole hyvä ja maksa",
|
||||
"preimage": "Alkukuva",
|
||||
"sats": "sats",
|
||||
"wasnt_paid_and_expired": "Tätä laskua ei maksettu, ja se on vanhentunut"
|
||||
"wasnt_paid_and_expired": "Tätä laskua ei maksettu, ja se on vanhentunut."
|
||||
},
|
||||
"plausibledeniability": {
|
||||
"create_fake_storage": "Luo Salattu tallennustila",
|
||||
@ -313,6 +314,7 @@
|
||||
"details_inputs": "Syötteet",
|
||||
"details_outputs": "Ulostulot",
|
||||
"details_received": "Vastaanotettu",
|
||||
"transaction_note_saved":"Siirtotapahtumailmoitus on tallennettu.",
|
||||
"details_show_in_block_explorer": "Näytä lohkoketjuselaimessa",
|
||||
"details_title": "Siirtotapahtuma",
|
||||
"details_to": "Ulostulo",
|
||||
@ -455,6 +457,10 @@
|
||||
"view_edit_cosigners": "Tarkastele/muokkaa allekirjoittajia",
|
||||
"this_cosigner_is_already_imported": "Tämä allekirjoittaja on jo tuotu.",
|
||||
"export_signed_psbt": "Vie Allekirjoitettu PSBT",
|
||||
"input_fp": "Syötä sormenjälki",
|
||||
"input_fp_explain": "ohita käyttääksesi oletusarvoa (00000000)",
|
||||
"input_path": "Syöte johtamisen polku",
|
||||
"input_path_explain": "ohita käyttääksesi oletusarvoa ({default})",
|
||||
"view_edit_cosigners_title": "Muokkaa Allekirjoittajia"
|
||||
},
|
||||
"cc": {
|
||||
|
@ -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",
|
||||
@ -146,7 +147,7 @@
|
||||
"send": {
|
||||
"broadcastButton": "Objavi v omrežju",
|
||||
"broadcastError": "napaka",
|
||||
"broadcastNone": "Vnesite zgoščeno vrednost transakcije",
|
||||
"broadcastNone": "Vnesite zgoščeno vrednost transakcije (hash)",
|
||||
"broadcastPending": "v teku",
|
||||
"broadcastSuccess": "uspešno",
|
||||
"confirm_header": "Potrditev",
|
||||
@ -313,6 +314,7 @@
|
||||
"details_inputs": "Vhodi",
|
||||
"details_outputs": "Izhodi",
|
||||
"details_received": "Prejeto",
|
||||
"transaction_note_saved":"Opomba transakcije je bila uspešno shranjena.",
|
||||
"details_show_in_block_explorer": "Prikaži v raziskovalcu blokov",
|
||||
"details_title": "Transakcija",
|
||||
"details_to": "Izhod",
|
||||
@ -358,7 +360,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 +457,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": {
|
||||
|
@ -27,6 +27,7 @@ export const FiatUnit = Object.freeze({
|
||||
JPY: { endPointKey: 'JPY', symbol: '¥', locale: 'ja-JP', source: FiatUnitSource.CoinDesk },
|
||||
KES: { endPointKey: 'KES', symbol: 'Ksh', locale: 'en-KE', source: FiatUnitSource.CoinDesk },
|
||||
KRW: { endPointKey: 'KRW', symbol: '₩', locale: 'ko-KR', source: FiatUnitSource.CoinDesk },
|
||||
LBP: { endPointKey: 'LBP', symbol: 'ل.ل.', locale: 'ar-LB', source: FiatUnitSource.CoinDesk },
|
||||
MXN: { endPointKey: 'MXN', symbol: '$', locale: 'es-MX', source: FiatUnitSource.CoinDesk },
|
||||
MYR: { endPointKey: 'MYR', symbol: 'RM', locale: 'ms-MY', source: FiatUnitSource.CoinDesk },
|
||||
NGN: { endPointKey: 'NGN', symbol: '₦', locale: 'en-NG', source: FiatUnitSource.CoinDesk },
|
||||
|
51
package-lock.json
generated
51
package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "bluewallet",
|
||||
"version": "5.6.7",
|
||||
"version": "5.6.8",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@ -6475,9 +6475,9 @@
|
||||
}
|
||||
},
|
||||
"@react-navigation/drawer": {
|
||||
"version": "5.10.2",
|
||||
"resolved": "https://registry.npmjs.org/@react-navigation/drawer/-/drawer-5.10.2.tgz",
|
||||
"integrity": "sha512-P9Sf9csztbdJAZpz3FhYg5TFZXxRwSyv5xIgFyT55F1KqB4ocRjrtj+AsIoOzdfQ7wC0nHJacOjp9noDk6QHqw==",
|
||||
"version": "5.11.2",
|
||||
"resolved": "https://registry.npmjs.org/@react-navigation/drawer/-/drawer-5.11.2.tgz",
|
||||
"integrity": "sha512-xCD/Q9Yne5CYsvKr+eMMNx4riQJMy5ghi5dEnvntsdJFx2Lq+UETyvFeTH7iXVXo2JOOxNddMvZJrds0B7+nCg==",
|
||||
"requires": {
|
||||
"color": "^3.1.3",
|
||||
"react-native-iphone-x-helper": "^1.3.0"
|
||||
@ -6529,9 +6529,9 @@
|
||||
}
|
||||
},
|
||||
"@react-navigation/stack": {
|
||||
"version": "5.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-navigation/stack/-/stack-5.11.1.tgz",
|
||||
"integrity": "sha512-wxGxnQnktf0ByicDAVAQnf6bazC7FynvPYY3o5Zf31i1Ucb+xJcSesDbl5wyeaW1YGiCfFs/K8fUVko3K7fxQA==",
|
||||
"version": "5.12.6",
|
||||
"resolved": "https://registry.npmjs.org/@react-navigation/stack/-/stack-5.12.6.tgz",
|
||||
"integrity": "sha512-pf9AigAIVtCQuCpZAZqBux4kNqQwj98ngvd6JEryFrqTQ1CYsUH6jfpQE7SKyHggVRFSQVMf24aCgwtRixBvjw==",
|
||||
"requires": {
|
||||
"color": "^3.1.3",
|
||||
"react-native-iphone-x-helper": "^1.3.0"
|
||||
@ -8380,12 +8380,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": {
|
||||
@ -18382,6 +18389,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",
|
||||
@ -19261,6 +19277,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",
|
||||
@ -73,9 +73,9 @@
|
||||
"@react-native-community/masked-view": "0.1.10",
|
||||
"@react-native-community/push-notification-ios": "1.7.1",
|
||||
"@react-native-community/slider": "3.0.3",
|
||||
"@react-navigation/drawer": "5.10.2",
|
||||
"@react-navigation/drawer": "5.11.2",
|
||||
"@react-navigation/native": "5.8.2",
|
||||
"@react-navigation/stack": "5.11.1",
|
||||
"@react-navigation/stack": "5.12.6",
|
||||
"@remobile/react-native-qrcode-local-image": "git+https://github.com/BlueWallet/react-native-qrcode-local-image.git",
|
||||
"@sentry/react-native": "1.9.0",
|
||||
"amplitude-js": "7.3.0",
|
||||
@ -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",
|
||||
|
@ -1,4 +1,6 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Ionicons from 'react-native-vector-icons/Ionicons';
|
||||
import {
|
||||
TouchableOpacity,
|
||||
ActivityIndicator,
|
||||
@ -12,9 +14,9 @@ import {
|
||||
StyleSheet,
|
||||
} from 'react-native';
|
||||
import { WebView } from 'react-native-webview';
|
||||
import { BlueNavigationStyle, SafeBlueArea } from '../../BlueComponents';
|
||||
import Ionicons from 'react-native-vector-icons/Ionicons';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { SafeBlueArea } from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import Notifications from '../../blue_modules/notifications';
|
||||
|
||||
let processedInvoices = {};
|
||||
@ -517,8 +519,8 @@ Browser.propTypes = {
|
||||
}),
|
||||
};
|
||||
|
||||
Browser.navigationOptions = ({ navigation }) => ({
|
||||
...BlueNavigationStyle(navigation, true),
|
||||
Browser.navigationOptions = navigationStyle({
|
||||
closeButton: true,
|
||||
title: 'Lapp Browser',
|
||||
headerLeft: null,
|
||||
});
|
||||
|
@ -1,38 +1,402 @@
|
||||
/* global alert */
|
||||
import React, { Component } from 'react';
|
||||
import React, { useCallback, useContext, useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
ActivityIndicator,
|
||||
View,
|
||||
TextInput,
|
||||
KeyboardAvoidingView,
|
||||
Keyboard,
|
||||
StatusBar,
|
||||
TouchableWithoutFeedback,
|
||||
TouchableOpacity,
|
||||
Text,
|
||||
StyleSheet,
|
||||
Image,
|
||||
Keyboard,
|
||||
KeyboardAvoidingView,
|
||||
StatusBar,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TextInput,
|
||||
TouchableOpacity,
|
||||
TouchableWithoutFeedback,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import {
|
||||
BlueNavigationStyle,
|
||||
BlueButton,
|
||||
BlueBitcoinAmount,
|
||||
BlueDismissKeyboardInputAccessory,
|
||||
BlueAlertWalletExportReminder,
|
||||
} from '../../BlueComponents';
|
||||
import { LightningCustodianWallet } from '../../class/wallets/lightning-custodian-wallet';
|
||||
import PropTypes from 'prop-types';
|
||||
import { BitcoinUnit, Chain } from '../../models/bitcoinUnits';
|
||||
import * as NavigationService from '../../NavigationService';
|
||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import { useFocusEffect, useNavigation, useRoute, useTheme } from '@react-navigation/native';
|
||||
|
||||
import { BlueAlertWalletExportReminder, BlueBitcoinAmount, BlueButton, BlueDismissKeyboardInputAccessory } from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import * as NavigationService from '../../NavigationService';
|
||||
import { LightningCustodianWallet } from '../../class/wallets/lightning-custodian-wallet';
|
||||
import { BitcoinUnit, Chain } from '../../models/bitcoinUnits';
|
||||
import loc, { formatBalanceWithoutSuffix, formatBalancePlain } from '../../loc';
|
||||
import { BlueCurrentTheme } from '../../components/themes';
|
||||
import Lnurl from '../../class/lnurl';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
import Notifications from '../../blue_modules/notifications';
|
||||
const currency = require('../../blue_modules/currency');
|
||||
|
||||
const LNDCreateInvoice = () => {
|
||||
const { wallets, saveToDisk, setSelectedWallet } = useContext(BlueStorageContext);
|
||||
const { walletID, uri } = useRoute().params;
|
||||
const wallet = useRef(
|
||||
wallets.find(item => item.getID() === walletID) || wallets.find(item => item.type === LightningCustodianWallet.type),
|
||||
);
|
||||
const { name } = useRoute();
|
||||
const { colors } = useTheme();
|
||||
const { navigate, dangerouslyGetParent, goBack, pop, setParams } = useNavigation();
|
||||
const [unit, setUnit] = useState(wallet.current.getPreferredBalanceUnit());
|
||||
const [amount, setAmount] = useState();
|
||||
const [renderWalletSelectionButtonHidden, setRenderWalletSelectionButtonHidden] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [description, setDescription] = useState('');
|
||||
const [lnurlParams, setLNURLParams] = useState();
|
||||
const styleHooks = StyleSheet.create({
|
||||
scanRoot: {
|
||||
backgroundColor: colors.scanLabel,
|
||||
},
|
||||
scanClick: {
|
||||
color: colors.inverseForegroundColor,
|
||||
},
|
||||
walletNameText: {
|
||||
color: colors.buttonAlternativeTextColor,
|
||||
},
|
||||
walletNameBalance: {
|
||||
color: colors.buttonAlternativeTextColor,
|
||||
},
|
||||
walletNameSats: {
|
||||
color: colors.buttonAlternativeTextColor,
|
||||
},
|
||||
root: {
|
||||
backgroundColor: colors.elevated,
|
||||
},
|
||||
amount: {
|
||||
backgroundColor: colors.elevated,
|
||||
},
|
||||
fiat: {
|
||||
borderColor: colors.formBorder,
|
||||
borderBottomColor: colors.formBorder,
|
||||
backgroundColor: colors.inputBackgroundColor,
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
// console.log(params)
|
||||
Keyboard.addListener('keyboardDidShow', _keyboardDidShow);
|
||||
Keyboard.addListener('keyboardDidHide', _keyboardDidHide);
|
||||
return () => {
|
||||
Keyboard.removeListener('keyboardDidShow', _keyboardDidShow);
|
||||
Keyboard.removeListener('keyboardDidHide', _keyboardDidHide);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const renderReceiveDetails = async () => {
|
||||
try {
|
||||
wallet.current.setUserHasSavedExport(true);
|
||||
await saveToDisk();
|
||||
if (uri) {
|
||||
processLnurl(uri);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (wallet.current && wallet.current.getID() !== walletID) {
|
||||
const newWallet = wallets.find(w => w.getID() === walletID);
|
||||
if (newWallet) {
|
||||
wallet.current = newWallet;
|
||||
setSelectedWallet(newWallet.getID());
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [walletID]);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
if (wallet.current) {
|
||||
setSelectedWallet(walletID);
|
||||
if (wallet.current.getUserHasSavedExport()) {
|
||||
renderReceiveDetails();
|
||||
} else {
|
||||
BlueAlertWalletExportReminder({
|
||||
onSuccess: () => renderReceiveDetails(),
|
||||
onFailure: () => {
|
||||
dangerouslyGetParent().pop();
|
||||
NavigationService.navigate('WalletExportRoot', {
|
||||
screen: 'WalletExport',
|
||||
params: {
|
||||
walletID,
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [wallet]),
|
||||
[],
|
||||
);
|
||||
|
||||
const _keyboardDidShow = () => {
|
||||
setRenderWalletSelectionButtonHidden(true);
|
||||
};
|
||||
|
||||
const _keyboardDidHide = () => {
|
||||
setRenderWalletSelectionButtonHidden(false);
|
||||
};
|
||||
|
||||
const createInvoice = async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
let invoiceAmount = amount;
|
||||
switch (unit) {
|
||||
case BitcoinUnit.SATS:
|
||||
invoiceAmount = parseInt(invoiceAmount); // basically nop
|
||||
break;
|
||||
case BitcoinUnit.BTC:
|
||||
invoiceAmount = currency.btcToSatoshi(invoiceAmount);
|
||||
break;
|
||||
case BitcoinUnit.LOCAL_CURRENCY:
|
||||
// trying to fetch cached sat equivalent for this fiat amount
|
||||
invoiceAmount = BlueBitcoinAmount.getCachedSatoshis(invoiceAmount) || currency.btcToSatoshi(currency.fiatToBTC(invoiceAmount));
|
||||
break;
|
||||
}
|
||||
|
||||
const invoiceRequest = await wallet.current.addInvoice(invoiceAmount, description);
|
||||
ReactNativeHapticFeedback.trigger('notificationSuccess', { ignoreAndroidSystemSettings: false });
|
||||
|
||||
// lets decode payreq and subscribe groundcontrol so we can receive push notification when our invoice is paid
|
||||
/** @type LightningCustodianWallet */
|
||||
const decoded = await wallet.current.decodeInvoice(invoiceRequest);
|
||||
await Notifications.tryToObtainPermissions();
|
||||
Notifications.majorTomToGroundControl([], [decoded.payment_hash], []);
|
||||
|
||||
// send to lnurl-withdraw callback url if that exists
|
||||
if (lnurlParams) {
|
||||
const { callback, k1 } = lnurlParams;
|
||||
const callbackUrl = callback + (callback.indexOf('?') !== -1 ? '&' : '?') + 'k1=' + k1 + '&pr=' + invoiceRequest;
|
||||
const resp = await fetch(callbackUrl, { method: 'GET' });
|
||||
if (resp.status >= 300) {
|
||||
const text = await resp.text();
|
||||
throw new Error(text);
|
||||
}
|
||||
const reply = await resp.json();
|
||||
if (reply.status === 'ERROR') {
|
||||
throw new Error('Reply from server: ' + reply.reason);
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(async () => {
|
||||
// wallet object doesnt have this fresh invoice in its internals, so we refetch it and only then save
|
||||
await wallet.current.fetchUserInvoices(1);
|
||||
await saveToDisk();
|
||||
}, 1000);
|
||||
|
||||
navigate('LNDViewInvoice', {
|
||||
invoice: invoiceRequest,
|
||||
walletID,
|
||||
isModal: true,
|
||||
});
|
||||
} catch (Err) {
|
||||
ReactNativeHapticFeedback.trigger('notificationError', { ignoreAndroidSystemSettings: false });
|
||||
setIsLoading(false);
|
||||
alert(Err.message);
|
||||
}
|
||||
};
|
||||
|
||||
const processLnurl = async data => {
|
||||
setIsLoading(true);
|
||||
if (!wallet) {
|
||||
ReactNativeHapticFeedback.trigger('notificationError', { ignoreAndroidSystemSettings: false });
|
||||
alert('Before paying a Lightning invoice, you must first add a Lightning wallet.');
|
||||
return goBack();
|
||||
}
|
||||
|
||||
// decoding the lnurl
|
||||
const url = Lnurl.getUrlFromLnurl(data);
|
||||
|
||||
// calling the url
|
||||
try {
|
||||
const resp = await fetch(url, { method: 'GET' });
|
||||
if (resp.status >= 300) {
|
||||
throw new Error('Bad response from server');
|
||||
}
|
||||
const reply = await resp.json();
|
||||
if (reply.status === 'ERROR') {
|
||||
throw new Error('Reply from server: ' + reply.reason);
|
||||
}
|
||||
|
||||
if (reply.tag === Lnurl.TAG_PAY_REQUEST) {
|
||||
// we are here by mistake. user wants to SEND to lnurl-pay, but he is on a screen that creates
|
||||
// invoices (including through lnurl-withdraw)
|
||||
navigate('ScanLndInvoiceRoot', {
|
||||
screen: 'LnurlPay',
|
||||
params: {
|
||||
lnurl: data,
|
||||
fromWalletID: walletID,
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (reply.tag !== Lnurl.TAG_WITHDRAW_REQUEST) {
|
||||
throw new Error('Unsupported lnurl');
|
||||
}
|
||||
|
||||
// amount that comes from lnurl is always in sats
|
||||
let amount = (reply.maxWithdrawable / 1000).toString();
|
||||
const sats = amount;
|
||||
switch (unit) {
|
||||
case BitcoinUnit.SATS:
|
||||
// nop
|
||||
break;
|
||||
case BitcoinUnit.BTC:
|
||||
amount = currency.satoshiToBTC(amount);
|
||||
break;
|
||||
case BitcoinUnit.LOCAL_CURRENCY:
|
||||
amount = formatBalancePlain(amount, BitcoinUnit.LOCAL_CURRENCY);
|
||||
BlueBitcoinAmount.setCachedSatoshis(amount, sats);
|
||||
break;
|
||||
}
|
||||
|
||||
// setting the invoice creating screen with the parameters
|
||||
setLNURLParams({
|
||||
k1: reply.k1,
|
||||
callback: reply.callback,
|
||||
fixed: reply.minWithdrawable === reply.maxWithdrawable,
|
||||
min: (reply.minWithdrawable || 0) / 1000,
|
||||
max: reply.maxWithdrawable / 1000,
|
||||
});
|
||||
setAmount(amount);
|
||||
setDescription(reply.defaultDescription);
|
||||
setIsLoading(false);
|
||||
} catch (Err) {
|
||||
Keyboard.dismiss();
|
||||
setIsLoading(false);
|
||||
ReactNativeHapticFeedback.trigger('notificationError', { ignoreAndroidSystemSettings: false });
|
||||
alert(Err.message);
|
||||
}
|
||||
};
|
||||
|
||||
const renderCreateButton = () => {
|
||||
return (
|
||||
<View style={styles.createButton}>
|
||||
{isLoading ? (
|
||||
<ActivityIndicator />
|
||||
) : (
|
||||
<BlueButton disabled={!(amount > 0)} onPress={createInvoice} title={loc.send.details_create} />
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const navigateToScanQRCode = () => {
|
||||
NavigationService.navigate('ScanQRCodeRoot', {
|
||||
screen: 'ScanQRCode',
|
||||
params: {
|
||||
onBarScanned: processLnurl,
|
||||
launchedBy: name,
|
||||
},
|
||||
});
|
||||
Keyboard.dismiss();
|
||||
};
|
||||
|
||||
const renderScanClickable = () => {
|
||||
return (
|
||||
<TouchableOpacity disabled={isLoading} onPress={navigateToScanQRCode} style={[styles.scanRoot, styleHooks.scanRoot]}>
|
||||
<Image style={{}} source={require('../../img/scan-white.png')} />
|
||||
<Text style={[styles.scanClick, styleHooks.scanClick]}>{loc.send.details_scan}</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
const navigateToSelectWallet = () => {
|
||||
navigate('SelectWallet', { onWalletSelect: onWalletSelect, chainType: Chain.OFFCHAIN });
|
||||
};
|
||||
|
||||
const renderWalletSelectionButton = () => {
|
||||
if (renderWalletSelectionButtonHidden) return;
|
||||
return (
|
||||
<View style={styles.walletRoot}>
|
||||
{!isLoading && (
|
||||
<TouchableOpacity style={styles.walletChooseWrap} onPress={navigateToSelectWallet}>
|
||||
<Text style={styles.walletChooseText}>{loc.wallets.select_wallet.toLowerCase()}</Text>
|
||||
<Icon name="angle-right" size={18} type="font-awesome" color="#9aa0aa" />
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
<View style={styles.walletNameWrap}>
|
||||
<TouchableOpacity style={styles.walletNameTouch} onPress={navigateToSelectWallet}>
|
||||
<Text style={[styles.walletNameText, styleHooks.walletNameText]}>{wallet.current.getLabel()}</Text>
|
||||
<Text style={[styles.walletNameBalance, styleHooks.walletNameBalance]}>
|
||||
{formatBalanceWithoutSuffix(wallet.current.getBalance(), BitcoinUnit.SATS, false)}
|
||||
</Text>
|
||||
<Text style={[styles.walletNameSats, styleHooks.walletNameSats]}>{BitcoinUnit.SATS}</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const onWalletSelect = selectedWallet => {
|
||||
setParams({ walletID: selectedWallet.getID() });
|
||||
pop();
|
||||
};
|
||||
|
||||
if (wallet.current === undefined || !walletID) {
|
||||
return (
|
||||
<View style={styles.error}>
|
||||
<Text>System error: Source wallet not found (this should never happen)</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<TouchableWithoutFeedback onPress={Keyboard.dismiss} accessible={false}>
|
||||
<View style={[styles.root, styleHooks.root]}>
|
||||
<StatusBar barStyle="light-content" />
|
||||
<View style={[styles.amount, styleHooks.amount]}>
|
||||
<KeyboardAvoidingView behavior="position">
|
||||
<BlueBitcoinAmount
|
||||
isLoading={isLoading}
|
||||
amount={amount}
|
||||
onAmountUnitChange={setUnit}
|
||||
onChangeText={text => {
|
||||
if (lnurlParams) {
|
||||
// in this case we prevent the user from changing the amount to < min or > max
|
||||
const { min, max } = lnurlParams;
|
||||
const nextAmount = parseInt(text);
|
||||
if (nextAmount < min) {
|
||||
text = min.toString();
|
||||
} else if (nextAmount > max) {
|
||||
text = max.toString();
|
||||
}
|
||||
}
|
||||
|
||||
setAmount(text);
|
||||
}}
|
||||
disabled={isLoading || (lnurlParams && lnurlParams.fixed)}
|
||||
unit={unit}
|
||||
inputAccessoryViewID={BlueDismissKeyboardInputAccessory.InputAccessoryViewID}
|
||||
/>
|
||||
<View style={[styles.fiat, styleHooks.fiat]}>
|
||||
<TextInput
|
||||
onChangeText={setDescription}
|
||||
placeholder={loc.receive.details_label}
|
||||
value={description}
|
||||
numberOfLines={1}
|
||||
placeholderTextColor="#81868e"
|
||||
style={styles.fiat2}
|
||||
editable={!isLoading}
|
||||
onSubmitEditing={Keyboard.dismiss}
|
||||
inputAccessoryViewID={BlueDismissKeyboardInputAccessory.InputAccessoryViewID}
|
||||
/>
|
||||
{lnurlParams ? null : renderScanClickable()}
|
||||
</View>
|
||||
<BlueDismissKeyboardInputAccessory />
|
||||
{renderCreateButton()}
|
||||
</KeyboardAvoidingView>
|
||||
</View>
|
||||
{renderWalletSelectionButton()}
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
createButton: {
|
||||
marginHorizontal: 16,
|
||||
@ -44,7 +408,6 @@ const styles = StyleSheet.create({
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
backgroundColor: BlueCurrentTheme.colors.scanLabel,
|
||||
borderRadius: 4,
|
||||
paddingVertical: 4,
|
||||
paddingHorizontal: 8,
|
||||
@ -52,7 +415,6 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
scanClick: {
|
||||
marginLeft: 4,
|
||||
color: BlueCurrentTheme.colors.inverseForegroundColor,
|
||||
},
|
||||
walletRoot: {
|
||||
marginBottom: 16,
|
||||
@ -78,18 +440,15 @@ const styles = StyleSheet.create({
|
||||
alignItems: 'center',
|
||||
},
|
||||
walletNameText: {
|
||||
color: BlueCurrentTheme.colors.buttonAlternativeTextColor,
|
||||
fontSize: 14,
|
||||
},
|
||||
walletNameBalance: {
|
||||
color: BlueCurrentTheme.colors.buttonAlternativeTextColor,
|
||||
fontSize: 14,
|
||||
fontWeight: '600',
|
||||
marginLeft: 8,
|
||||
marginRight: 4,
|
||||
},
|
||||
walletNameSats: {
|
||||
color: BlueCurrentTheme.colors.buttonAlternativeTextColor,
|
||||
fontSize: 11,
|
||||
fontWeight: '600',
|
||||
textAlignVertical: 'bottom',
|
||||
@ -102,19 +461,14 @@ const styles = StyleSheet.create({
|
||||
root: {
|
||||
flex: 1,
|
||||
justifyContent: 'space-between',
|
||||
backgroundColor: BlueCurrentTheme.colors.elevated,
|
||||
},
|
||||
amount: {
|
||||
flex: 1,
|
||||
backgroundColor: BlueCurrentTheme.colors.elevated,
|
||||
},
|
||||
fiat: {
|
||||
flexDirection: 'row',
|
||||
borderColor: BlueCurrentTheme.colors.formBorder,
|
||||
borderBottomColor: BlueCurrentTheme.colors.formBorder,
|
||||
borderWidth: 1.0,
|
||||
borderBottomWidth: 0.5,
|
||||
backgroundColor: BlueCurrentTheme.colors.inputBackgroundColor,
|
||||
minHeight: 44,
|
||||
height: 44,
|
||||
marginHorizontal: 20,
|
||||
@ -130,377 +484,10 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
});
|
||||
|
||||
export default class LNDCreateInvoice extends Component {
|
||||
static contextType = BlueStorageContext;
|
||||
constructor(props, context) {
|
||||
super(props);
|
||||
this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this._keyboardDidShow);
|
||||
this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this._keyboardDidHide);
|
||||
/** @type LightningCustodianWallet */
|
||||
let fromWallet;
|
||||
if (props.route.params.fromWallet) fromWallet = props.route.params.fromWallet;
|
||||
export default LNDCreateInvoice;
|
||||
|
||||
// fallback to first wallet if it exists
|
||||
if (!fromWallet) {
|
||||
const lightningWallets = context.wallets.filter(item => item.type === LightningCustodianWallet.type);
|
||||
if (lightningWallets.length > 0) {
|
||||
fromWallet = lightningWallets[0];
|
||||
console.warn('warning: using ln wallet index 0');
|
||||
}
|
||||
}
|
||||
|
||||
this.state = {
|
||||
fromWallet,
|
||||
amount: '',
|
||||
unit: fromWallet.preferredBalanceUnit,
|
||||
description: '',
|
||||
lnurl: '',
|
||||
lnurlParams: null,
|
||||
isLoading: true,
|
||||
renderWalletSelectionButtonHidden: false,
|
||||
};
|
||||
}
|
||||
|
||||
renderReceiveDetails = async () => {
|
||||
try {
|
||||
this.state.fromWallet.setUserHasSavedExport(true);
|
||||
await this.context.saveToDisk();
|
||||
if (this.props.route.params.uri) {
|
||||
this.processLnurl(this.props.route.params.uri);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
this.setState({ isLoading: false });
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
console.log('lnd/lndCreateInvoice mounted');
|
||||
this.context.setSelectedWallet(this.state.fromWallet.getID());
|
||||
if (this.state.fromWallet.getUserHasSavedExport()) {
|
||||
this.renderReceiveDetails();
|
||||
} else {
|
||||
BlueAlertWalletExportReminder({
|
||||
onSuccess: this.renderReceiveDetails,
|
||||
onFailure: () => {
|
||||
this.props.navigation.dangerouslyGetParent().pop();
|
||||
this.props.navigation.navigate('WalletExportRoot', {
|
||||
screen: 'WalletExportRoot',
|
||||
params: {
|
||||
walletID: this.state.fromWallet.getID(),
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.keyboardDidShowListener.remove();
|
||||
this.keyboardDidHideListener.remove();
|
||||
}
|
||||
|
||||
_keyboardDidShow = () => {
|
||||
this.setState({ renderWalletSelectionButtonHidden: true });
|
||||
};
|
||||
|
||||
_keyboardDidHide = () => {
|
||||
this.setState({ renderWalletSelectionButtonHidden: false });
|
||||
};
|
||||
|
||||
async createInvoice() {
|
||||
this.setState({ isLoading: true }, async () => {
|
||||
try {
|
||||
let amount = this.state.amount;
|
||||
switch (this.state.unit) {
|
||||
case BitcoinUnit.SATS:
|
||||
amount = parseInt(amount); // basically nop
|
||||
break;
|
||||
case BitcoinUnit.BTC:
|
||||
amount = currency.btcToSatoshi(amount);
|
||||
break;
|
||||
case BitcoinUnit.LOCAL_CURRENCY:
|
||||
// trying to fetch cached sat equivalent for this fiat amount
|
||||
amount = BlueBitcoinAmount.getCachedSatoshis(amount) || currency.btcToSatoshi(currency.fiatToBTC(amount));
|
||||
break;
|
||||
}
|
||||
|
||||
const invoiceRequest = await this.state.fromWallet.addInvoice(amount, this.state.description);
|
||||
ReactNativeHapticFeedback.trigger('notificationSuccess', { ignoreAndroidSystemSettings: false });
|
||||
|
||||
// lets decode payreq and subscribe groundcontrol so we can receive push notification when our invoice is paid
|
||||
/** @type LightningCustodianWallet */
|
||||
const fromWallet = this.state.fromWallet;
|
||||
const decoded = await fromWallet.decodeInvoice(invoiceRequest);
|
||||
await Notifications.tryToObtainPermissions();
|
||||
Notifications.majorTomToGroundControl([], [decoded.payment_hash], []);
|
||||
|
||||
// send to lnurl-withdraw callback url if that exists
|
||||
if (this.state.lnurlParams) {
|
||||
const { callback, k1 } = this.state.lnurlParams;
|
||||
const callbackUrl = callback + (callback.indexOf('?') !== -1 ? '&' : '?') + 'k1=' + k1 + '&pr=' + invoiceRequest;
|
||||
const resp = await fetch(callbackUrl, { method: 'GET' });
|
||||
if (resp.status >= 300) {
|
||||
const text = await resp.text();
|
||||
throw new Error(text);
|
||||
}
|
||||
const reply = await resp.json();
|
||||
if (reply.status === 'ERROR') {
|
||||
throw new Error('Reply from server: ' + reply.reason);
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(async () => {
|
||||
// wallet object doesnt have this fresh invoice in its internals, so we refetch it and only then save
|
||||
await fromWallet.fetchUserInvoices(1);
|
||||
await this.context.saveToDisk();
|
||||
}, 1000);
|
||||
|
||||
this.props.navigation.navigate('LNDViewInvoice', {
|
||||
invoice: invoiceRequest,
|
||||
walletID: this.state.fromWallet.getID(),
|
||||
isModal: true,
|
||||
});
|
||||
} catch (Err) {
|
||||
ReactNativeHapticFeedback.trigger('notificationError', { ignoreAndroidSystemSettings: false });
|
||||
this.setState({ isLoading: false });
|
||||
alert(Err.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
processLnurl = data => {
|
||||
this.setState({ isLoading: true }, async () => {
|
||||
if (!this.state.fromWallet) {
|
||||
ReactNativeHapticFeedback.trigger('notificationError', { ignoreAndroidSystemSettings: false });
|
||||
alert('Before paying a Lightning invoice, you must first add a Lightning wallet.');
|
||||
return this.props.navigation.goBack();
|
||||
}
|
||||
|
||||
// decoding the lnurl
|
||||
const url = Lnurl.getUrlFromLnurl(data);
|
||||
|
||||
// calling the url
|
||||
try {
|
||||
const resp = await fetch(url, { method: 'GET' });
|
||||
if (resp.status >= 300) {
|
||||
throw new Error('Bad response from server');
|
||||
}
|
||||
const reply = await resp.json();
|
||||
if (reply.status === 'ERROR') {
|
||||
throw new Error('Reply from server: ' + reply.reason);
|
||||
}
|
||||
|
||||
if (reply.tag === Lnurl.TAG_PAY_REQUEST) {
|
||||
// we are here by mistake. user wants to SEND to lnurl-pay, but he is on a screen that creates
|
||||
// invoices (including through lnurl-withdraw)
|
||||
this.props.navigation.navigate('ScanLndInvoiceRoot', {
|
||||
screen: 'LnurlPay',
|
||||
params: {
|
||||
lnurl: data,
|
||||
fromWalletID: this.state.fromWallet.getID(),
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (reply.tag !== Lnurl.TAG_WITHDRAW_REQUEST) {
|
||||
throw new Error('Unsupported lnurl');
|
||||
}
|
||||
|
||||
// amount that comes from lnurl is always in sats
|
||||
let amount = (reply.maxWithdrawable / 1000).toString();
|
||||
const sats = amount;
|
||||
switch (this.state.unit) {
|
||||
case BitcoinUnit.SATS:
|
||||
// nop
|
||||
break;
|
||||
case BitcoinUnit.BTC:
|
||||
amount = currency.satoshiToBTC(amount);
|
||||
break;
|
||||
case BitcoinUnit.LOCAL_CURRENCY:
|
||||
amount = formatBalancePlain(amount, BitcoinUnit.LOCAL_CURRENCY);
|
||||
BlueBitcoinAmount.setCachedSatoshis(amount, sats);
|
||||
break;
|
||||
}
|
||||
|
||||
// setting the invoice creating screen with the parameters
|
||||
this.setState({
|
||||
isLoading: false,
|
||||
lnurlParams: {
|
||||
k1: reply.k1,
|
||||
callback: reply.callback,
|
||||
fixed: reply.minWithdrawable === reply.maxWithdrawable,
|
||||
min: (reply.minWithdrawable || 0) / 1000,
|
||||
max: reply.maxWithdrawable / 1000,
|
||||
},
|
||||
amount,
|
||||
description: reply.defaultDescription,
|
||||
});
|
||||
} catch (Err) {
|
||||
Keyboard.dismiss();
|
||||
this.setState({ isLoading: false });
|
||||
ReactNativeHapticFeedback.trigger('notificationError', { ignoreAndroidSystemSettings: false });
|
||||
alert(Err.message);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
renderCreateButton = () => {
|
||||
return (
|
||||
<View style={styles.createButton}>
|
||||
{this.state.isLoading ? (
|
||||
<ActivityIndicator />
|
||||
) : (
|
||||
<BlueButton disabled={!(this.state.amount > 0)} onPress={() => this.createInvoice()} title={loc.send.details_create} />
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
renderScanClickable = () => {
|
||||
return (
|
||||
<TouchableOpacity
|
||||
disabled={this.state.isLoading}
|
||||
onPress={() => {
|
||||
NavigationService.navigate('ScanQRCodeRoot', {
|
||||
screen: 'ScanQRCode',
|
||||
params: {
|
||||
onBarScanned: this.processLnurl,
|
||||
launchedBy: this.props.route.name,
|
||||
},
|
||||
});
|
||||
Keyboard.dismiss();
|
||||
}}
|
||||
style={styles.scanRoot}
|
||||
>
|
||||
<Image style={{}} source={require('../../img/scan-white.png')} />
|
||||
<Text style={styles.scanClick}>{loc.send.details_scan}</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
renderWalletSelectionButton = () => {
|
||||
if (this.state.renderWalletSelectionButtonHidden) return;
|
||||
return (
|
||||
<View style={styles.walletRoot}>
|
||||
{!this.state.isLoading && (
|
||||
<TouchableOpacity
|
||||
style={styles.walletChooseWrap}
|
||||
onPress={() =>
|
||||
this.props.navigation.navigate('SelectWallet', { onWalletSelect: this.onWalletSelect, chainType: Chain.OFFCHAIN })
|
||||
}
|
||||
>
|
||||
<Text style={styles.walletChooseText}>{loc.wallets.select_wallet.toLowerCase()}</Text>
|
||||
<Icon name="angle-right" size={18} type="font-awesome" color="#9aa0aa" />
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
<View style={styles.walletNameWrap}>
|
||||
<TouchableOpacity
|
||||
style={styles.walletNameTouch}
|
||||
onPress={() =>
|
||||
this.props.navigation.navigate('SelectWallet', { onWalletSelect: this.onWalletSelect, chainType: Chain.OFFCHAIN })
|
||||
}
|
||||
>
|
||||
<Text style={styles.walletNameText}>{this.state.fromWallet.getLabel()}</Text>
|
||||
<Text style={styles.walletNameBalance}>
|
||||
{formatBalanceWithoutSuffix(this.state.fromWallet.getBalance(), BitcoinUnit.SATS, false)}
|
||||
</Text>
|
||||
<Text style={styles.walletNameSats}>{BitcoinUnit.SATS}</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
onWalletSelect = wallet => {
|
||||
this.setState({ fromWallet: wallet }, () => {
|
||||
this.context.setSelectedWallet(wallet.getID());
|
||||
this.props.navigation.pop();
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
if (!this.state.fromWallet) {
|
||||
return (
|
||||
<View style={styles.error}>
|
||||
<Text>System error: Source wallet not found (this should never happen)</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<TouchableWithoutFeedback onPress={Keyboard.dismiss} accessible={false}>
|
||||
<View style={styles.root}>
|
||||
<StatusBar barStyle="light-content" />
|
||||
<View style={styles.amount}>
|
||||
<KeyboardAvoidingView behavior="position">
|
||||
<BlueBitcoinAmount
|
||||
isLoading={this.state.isLoading}
|
||||
amount={this.state.amount}
|
||||
onAmountUnitChange={unit => {
|
||||
this.setState({ unit });
|
||||
}}
|
||||
onChangeText={text => {
|
||||
if (this.state.lnurlParams) {
|
||||
// in this case we prevent the user from changing the amount to < min or > max
|
||||
const { min, max } = this.state.lnurlParams;
|
||||
const nextAmount = parseInt(text);
|
||||
if (nextAmount < min) {
|
||||
text = min.toString();
|
||||
} else if (nextAmount > max) {
|
||||
text = max.toString();
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({ amount: text });
|
||||
}}
|
||||
disabled={this.state.isLoading || (this.state.lnurlParams && this.state.lnurlParams.fixed)}
|
||||
unit={this.state.unit}
|
||||
inputAccessoryViewID={BlueDismissKeyboardInputAccessory.InputAccessoryViewID}
|
||||
/>
|
||||
<View style={styles.fiat}>
|
||||
<TextInput
|
||||
onChangeText={text => this.setState({ description: text })}
|
||||
placeholder={loc.receive.details_label}
|
||||
value={this.state.description}
|
||||
numberOfLines={1}
|
||||
placeholderTextColor="#81868e"
|
||||
style={styles.fiat2}
|
||||
editable={!this.state.isLoading}
|
||||
onSubmitEditing={Keyboard.dismiss}
|
||||
inputAccessoryViewID={BlueDismissKeyboardInputAccessory.InputAccessoryViewID}
|
||||
/>
|
||||
{this.state.lnurlParams ? null : this.renderScanClickable()}
|
||||
</View>
|
||||
<BlueDismissKeyboardInputAccessory />
|
||||
{this.renderCreateButton()}
|
||||
</KeyboardAvoidingView>
|
||||
</View>
|
||||
{this.renderWalletSelectionButton()}
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
LNDCreateInvoice.propTypes = {
|
||||
navigation: PropTypes.shape({
|
||||
goBack: PropTypes.func,
|
||||
dangerouslyGetParent: PropTypes.func,
|
||||
navigate: PropTypes.func,
|
||||
pop: PropTypes.func,
|
||||
}),
|
||||
route: PropTypes.shape({
|
||||
name: PropTypes.string,
|
||||
params: PropTypes.shape({
|
||||
uri: PropTypes.string,
|
||||
fromWallet: PropTypes.shape({}),
|
||||
}),
|
||||
}),
|
||||
};
|
||||
LNDCreateInvoice.navigationOptions = ({ navigation }) => ({
|
||||
...BlueNavigationStyle(navigation, true),
|
||||
LNDCreateInvoice.navigationOptions = navigationStyle({
|
||||
closeButton: true,
|
||||
headerTitle: loc.receive.header,
|
||||
headerLeft: null,
|
||||
});
|
||||
|
@ -1,22 +1,15 @@
|
||||
/* global alert */
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { View, Share, StyleSheet } from 'react-native';
|
||||
import {
|
||||
BlueLoading,
|
||||
BlueCopyTextToClipboard,
|
||||
SafeBlueArea,
|
||||
BlueButton,
|
||||
BlueNavigationStyle,
|
||||
BlueText,
|
||||
BlueSpacing20,
|
||||
} from '../../BlueComponents';
|
||||
import QRCode from 'react-native-qrcode-svg';
|
||||
import loc from '../../loc';
|
||||
import { useNavigation, useRoute, useTheme } from '@react-navigation/native';
|
||||
|
||||
import { BlueButton, BlueCopyTextToClipboard, BlueLoading, BlueSpacing20, BlueText, SafeBlueArea } from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import loc from '../../loc';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
|
||||
const LNDViewAdditionalInvoiceInformation = () => {
|
||||
// state = { walletInfo: undefined };
|
||||
const { walletID } = useRoute().params;
|
||||
const { wallets } = useContext(BlueStorageContext);
|
||||
const wallet = wallets.find(w => w.getID() === walletID);
|
||||
@ -123,7 +116,6 @@ const styles = StyleSheet.create({
|
||||
|
||||
export default LNDViewAdditionalInvoiceInformation;
|
||||
|
||||
LNDViewAdditionalInvoiceInformation.navigationOptions = () => ({
|
||||
...BlueNavigationStyle(),
|
||||
LNDViewAdditionalInvoiceInformation.navigationOptions = navigationStyle({
|
||||
title: loc.lndViewInvoice.additional_info,
|
||||
});
|
||||
|
@ -1,10 +1,12 @@
|
||||
import React from 'react';
|
||||
import { View, StyleSheet } from 'react-native';
|
||||
import { BlueCopyTextToClipboard, SafeBlueArea, BlueNavigationStyle, BlueSpacing20, BlueTextCentered } from '../../BlueComponents';
|
||||
import QRCode from 'react-native-qrcode-svg';
|
||||
import loc from '../../loc';
|
||||
import { useRoute, useTheme } from '@react-navigation/native';
|
||||
|
||||
import { BlueCopyTextToClipboard, SafeBlueArea, BlueSpacing20, BlueTextCentered } from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import loc from '../../loc';
|
||||
|
||||
const LNDViewAdditionalInvoicePreImage = () => {
|
||||
// state = { walletInfo: undefined };
|
||||
const { colors } = useTheme();
|
||||
@ -59,7 +61,6 @@ const styles = StyleSheet.create({
|
||||
|
||||
export default LNDViewAdditionalInvoicePreImage;
|
||||
|
||||
LNDViewAdditionalInvoicePreImage.navigationOptions = () => ({
|
||||
...BlueNavigationStyle(),
|
||||
LNDViewAdditionalInvoicePreImage.navigationOptions = navigationStyle({
|
||||
title: loc.lndViewInvoice.additional_info,
|
||||
});
|
||||
|
@ -1,6 +1,11 @@
|
||||
import React, { useContext, useEffect, useRef, useState } from 'react';
|
||||
import { View, Text, StatusBar, ScrollView, BackHandler, TouchableOpacity, StyleSheet, useWindowDimensions } from 'react-native';
|
||||
import Share from 'react-native-share';
|
||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import QRCode from 'react-native-qrcode-svg';
|
||||
import { useNavigation, useRoute, useTheme } from '@react-navigation/native';
|
||||
|
||||
import {
|
||||
BlueLoading,
|
||||
BlueText,
|
||||
@ -8,16 +13,12 @@ import {
|
||||
BlueButton,
|
||||
SecondButton,
|
||||
BlueCopyTextToClipboard,
|
||||
BlueNavigationStyle,
|
||||
BlueSpacing20,
|
||||
BlueTextCentered,
|
||||
} from '../../BlueComponents';
|
||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import QRCode from 'react-native-qrcode-svg';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import loc from '../../loc';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
import { useNavigation, useRoute, useTheme } from '@react-navigation/native';
|
||||
import { BitcoinUnit } from '../../models/bitcoinUnits';
|
||||
import { SuccessView } from '../send/success';
|
||||
|
||||
@ -356,23 +357,22 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
});
|
||||
|
||||
export default LNDViewInvoice;
|
||||
LNDViewInvoice.navigationOptions = navigationStyle(
|
||||
{
|
||||
title: loc.lndViewInvoice.lightning_invoice,
|
||||
closeButton: true,
|
||||
closeButtonFunc: ({ navigation }) => navigation.dangerouslyGetParent().pop(),
|
||||
},
|
||||
(options, { theme, navigation, route }) => {
|
||||
return route.params.isModal === true
|
||||
? {
|
||||
headerLeft: null,
|
||||
gestureEnabled: false,
|
||||
}
|
||||
: {
|
||||
headerRight: null,
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
LNDViewInvoice.navigationOptions = ({ navigation, route }) =>
|
||||
route.params.isModal === true
|
||||
? {
|
||||
...BlueNavigationStyle(navigation, true, () => navigation.dangerouslyGetParent().pop()),
|
||||
title: loc.lndViewInvoice.lightning_invoice,
|
||||
headerLeft: null,
|
||||
headerStyle: {
|
||||
...BlueNavigationStyle().headerStyle,
|
||||
},
|
||||
gestureEnabled: false,
|
||||
}
|
||||
: {
|
||||
...BlueNavigationStyle(),
|
||||
title: loc.lndViewInvoice.lightning_invoice,
|
||||
headerStyle: {
|
||||
...BlueNavigationStyle().headerStyle,
|
||||
},
|
||||
};
|
||||
export default LNDViewInvoice;
|
||||
|
@ -1,26 +1,27 @@
|
||||
/* global alert */
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||
import AsyncStorage from '@react-native-community/async-storage';
|
||||
import { Image, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||||
import { Icon } from 'react-native-elements';
|
||||
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import {
|
||||
BlueBitcoinAmount,
|
||||
BlueButton,
|
||||
BlueCard,
|
||||
BlueDismissKeyboardInputAccessory,
|
||||
BlueLoading,
|
||||
BlueNavigationStyle,
|
||||
BlueSpacing20,
|
||||
BlueText,
|
||||
SafeBlueArea,
|
||||
} from '../../BlueComponents';
|
||||
import { BlueCurrentTheme } from '../../components/themes';
|
||||
import Lnurl from '../../class/lnurl';
|
||||
import { Image, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||||
import { BitcoinUnit, Chain } from '../../models/bitcoinUnits';
|
||||
import loc, { formatBalanceWithoutSuffix } from '../../loc';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import Biometric from '../../class/biometrics';
|
||||
import PropTypes from 'prop-types';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
const currency = require('../../blue_modules/currency');
|
||||
|
||||
@ -260,10 +261,9 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
});
|
||||
|
||||
LnurlPay.navigationOptions = ({ navigation, route }) => {
|
||||
return {
|
||||
...BlueNavigationStyle(navigation, true, () => navigation.dangerouslyGetParent().popToTop()),
|
||||
title: '',
|
||||
headerLeft: null,
|
||||
};
|
||||
};
|
||||
LnurlPay.navigationOptions = navigationStyle({
|
||||
title: '',
|
||||
closeButton: true,
|
||||
closeButtonFunc: ({ navigation }) => navigation.dangerouslyGetParent().popToTop(),
|
||||
headerLeft: null,
|
||||
});
|
||||
|
@ -1,19 +1,12 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import LottieView from 'lottie-react-native';
|
||||
import { View, Text, Linking, StyleSheet, Image, ScrollView } from 'react-native';
|
||||
import AsyncStorage from '@react-native-community/async-storage';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import {
|
||||
BlueButton,
|
||||
BlueButtonLink,
|
||||
BlueNavigationStyle,
|
||||
SafeBlueArea,
|
||||
BlueCard,
|
||||
BlueLoading,
|
||||
BlueText,
|
||||
BlueSpacing20,
|
||||
} from '../../BlueComponents';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { BlueButton, BlueButtonLink, BlueCard, BlueLoading, BlueSpacing20, BlueText, SafeBlueArea } from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import Lnurl from '../../class/lnurl';
|
||||
import loc from '../../loc';
|
||||
|
||||
@ -201,10 +194,9 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
});
|
||||
|
||||
LnurlPaySuccess.navigationOptions = ({ navigation, route }) => {
|
||||
return {
|
||||
...BlueNavigationStyle(navigation, true, () => navigation.dangerouslyGetParent().popToTop()),
|
||||
title: '',
|
||||
headerLeft: null,
|
||||
};
|
||||
};
|
||||
LnurlPaySuccess.navigationOptions = navigationStyle({
|
||||
title: '',
|
||||
closeButton: true,
|
||||
closeButtonFunc: ({ navigation }) => navigation.dangerouslyGetParent().popToTop(),
|
||||
headerLeft: null,
|
||||
});
|
||||
|
@ -12,21 +12,22 @@ import {
|
||||
StyleSheet,
|
||||
} from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||
|
||||
import {
|
||||
BlueButton,
|
||||
SafeBlueArea,
|
||||
BlueCard,
|
||||
BlueDismissKeyboardInputAccessory,
|
||||
BlueNavigationStyle,
|
||||
BlueAddressInput,
|
||||
BlueBitcoinAmount,
|
||||
BlueLoading,
|
||||
} from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import { LightningCustodianWallet } from '../../class/wallets/lightning-custodian-wallet';
|
||||
import Lnurl from '../../class/lnurl';
|
||||
import { BitcoinUnit, Chain } from '../../models/bitcoinUnits';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||
import Biometric from '../../class/biometrics';
|
||||
import loc, { formatBalanceWithoutSuffix } from '../../loc';
|
||||
import { BlueCurrentTheme } from '../../components/themes';
|
||||
@ -468,8 +469,8 @@ ScanLndInvoice.propTypes = {
|
||||
}),
|
||||
};
|
||||
|
||||
ScanLndInvoice.navigationOptions = ({ navigation }) => ({
|
||||
...BlueNavigationStyle(navigation, true),
|
||||
ScanLndInvoice.navigationOptions = navigationStyle({
|
||||
closeButton: true,
|
||||
title: loc.send.header,
|
||||
headerLeft: null,
|
||||
});
|
||||
|
@ -1,11 +1,13 @@
|
||||
/* global alert */
|
||||
import React, { useContext, useState } from 'react';
|
||||
import { ScrollView, StyleSheet } from 'react-native';
|
||||
import { BlueLoading, BlueButton, SafeBlueArea, BlueCard, BlueText, BlueNavigationStyle, BlueSpacing20 } from '../BlueComponents';
|
||||
import { useNavigation, useTheme } from '@react-navigation/native';
|
||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||
|
||||
import navigationStyle from '../components/navigationStyle';
|
||||
import { BlueLoading, BlueButton, SafeBlueArea, BlueCard, BlueText, BlueSpacing20 } from '../BlueComponents';
|
||||
import loc from '../loc';
|
||||
import { BlueStorageContext } from '../blue_modules/storage-context';
|
||||
import { useNavigation, useTheme } from '@react-navigation/native';
|
||||
const prompt = require('../blue_modules/prompt');
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
@ -85,7 +87,6 @@ const PlausibleDeniability = () => {
|
||||
|
||||
export default PlausibleDeniability;
|
||||
|
||||
PlausibleDeniability.navigationOptions = () => ({
|
||||
...BlueNavigationStyle(),
|
||||
PlausibleDeniability.navigationOptions = navigationStyle({
|
||||
title: loc.plausibledeniability.title,
|
||||
});
|
||||
|
@ -3,8 +3,9 @@ import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Keyboard, Text, TouchableOpacity, StatusBar, TouchableWithoutFeedback, View, StyleSheet } from 'react-native';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import { BlueButton, BlueCreateTxNavigationStyle, BlueLoading, BlueSpacing, BlueText } from '../../BlueComponents';
|
||||
|
||||
import { BlueButton, BlueLoading, BlueSpacing, BlueText } from '../../BlueComponents';
|
||||
import { navigationStyleTx } from '../../components/navigationStyle';
|
||||
import loc from '../../loc';
|
||||
import { PlaceholderWallet } from '../../class';
|
||||
import Azteco from '../../class/azteco';
|
||||
@ -183,7 +184,6 @@ AztecoRedeem.propTypes = {
|
||||
}),
|
||||
};
|
||||
|
||||
AztecoRedeem.navigationOptions = ({ navigation }) => ({
|
||||
...BlueCreateTxNavigationStyle(navigation),
|
||||
AztecoRedeem.navigationOptions = navigationStyleTx({
|
||||
title: loc.azteco.title,
|
||||
});
|
||||
|
@ -16,18 +16,18 @@ import Share from 'react-native-share';
|
||||
import Handoff from 'react-native-handoff';
|
||||
|
||||
import {
|
||||
BlueLoadingHook,
|
||||
BlueLoading,
|
||||
BlueCopyTextToClipboard,
|
||||
BlueButton,
|
||||
SecondButton,
|
||||
BlueButtonLinkHook,
|
||||
BlueButtonLink,
|
||||
is,
|
||||
BlueBitcoinAmount,
|
||||
BlueText,
|
||||
BlueSpacing20,
|
||||
BlueAlertWalletExportReminder,
|
||||
BlueNavigationStyle,
|
||||
} from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import BottomModal from '../../components/BottomModal';
|
||||
import Privacy from '../../Privacy';
|
||||
import { Chain, BitcoinUnit } from '../../models/bitcoinUnits';
|
||||
@ -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,13 +345,13 @@ const ReceiveDetails = () => {
|
||||
url={`https://blockstream.info/address/${address}`}
|
||||
/>
|
||||
)}
|
||||
{showAddress ? renderReceiveDetails() : <BlueLoadingHook />}
|
||||
{showAddress ? renderReceiveDetails() : <BlueLoading />}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
ReceiveDetails.navigationOptions = ({ navigation }) => ({
|
||||
...BlueNavigationStyle(navigation, true),
|
||||
ReceiveDetails.navigationOptions = navigationStyle({
|
||||
closeButton: true,
|
||||
title: loc.receive.header,
|
||||
headerLeft: null,
|
||||
});
|
||||
|
@ -1,7 +1,8 @@
|
||||
import React, { Component } from 'react';
|
||||
import { ScrollView, View, StyleSheet } from 'react-native';
|
||||
import { BlueSpacing20, SafeBlueArea, BlueCard, BlueText, BlueNavigationStyle, BlueLoadingHook } from '../BlueComponents';
|
||||
import PropTypes from 'prop-types';
|
||||
import { ScrollView, View, StyleSheet } from 'react-native';
|
||||
import { BlueSpacing20, SafeBlueArea, BlueCard, BlueText, BlueLoading } from '../BlueComponents';
|
||||
import navigationStyle from '../components/navigationStyle';
|
||||
import { SegwitP2SHWallet, LegacyWallet, HDSegwitP2SHWallet, HDSegwitBech32Wallet } from '../class';
|
||||
import { BlueCurrentTheme } from '../components/themes';
|
||||
const bitcoin = require('bitcoinjs-lib');
|
||||
@ -210,7 +211,7 @@ export default class Selftest extends Component {
|
||||
|
||||
render() {
|
||||
if (this.state.isLoading) {
|
||||
return <BlueLoadingHook />;
|
||||
return <BlueLoading />;
|
||||
}
|
||||
|
||||
return (
|
||||
@ -259,7 +260,6 @@ Selftest.propTypes = {
|
||||
}),
|
||||
};
|
||||
|
||||
Selftest.navigationOptions = () => ({
|
||||
...BlueNavigationStyle(),
|
||||
Selftest.navigationOptions = navigationStyle({
|
||||
title: 'Self test',
|
||||
});
|
||||
|
@ -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}>
|
||||
@ -330,8 +330,4 @@ const ScanQRCode = () => {
|
||||
);
|
||||
};
|
||||
|
||||
ScanQRCode.navigationOptions = {
|
||||
headerShown: false,
|
||||
};
|
||||
|
||||
export default ScanQRCode;
|
||||
|
@ -2,8 +2,10 @@ import React, { useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { ActivityIndicator, Linking, StyleSheet, View, KeyboardAvoidingView, Platform, Text, TextInput } from 'react-native';
|
||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||
|
||||
import loc from '../../loc';
|
||||
import { HDSegwitBech32Wallet } from '../../class';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import {
|
||||
SafeBlueArea,
|
||||
BlueCard,
|
||||
@ -13,7 +15,6 @@ import {
|
||||
BlueFormLabel,
|
||||
BlueTextCentered,
|
||||
BlueBigCheckmark,
|
||||
BlueNavigationStyle,
|
||||
} from '../../BlueComponents';
|
||||
import { BlueCurrentTheme } from '../../components/themes';
|
||||
import BlueElectrum from '../../blue_modules/BlueElectrum';
|
||||
@ -110,8 +111,7 @@ const Broadcast = () => {
|
||||
};
|
||||
|
||||
export default Broadcast;
|
||||
Broadcast.navigationOptions = () => ({
|
||||
...BlueNavigationStyle(),
|
||||
Broadcast.navigationOptions = navigationStyle({
|
||||
title: loc.send.create_broadcast,
|
||||
});
|
||||
|
||||
@ -180,10 +180,10 @@ function SuccessScreen({ tx }) {
|
||||
<View style={styles.broadcastResultWrapper}>
|
||||
<BlueBigCheckmark />
|
||||
<BlueSpacing20 />
|
||||
<BlueTextCentered>Success! You transaction has been broadcasted!</BlueTextCentered>
|
||||
<BlueTextCentered>{loc.send.broadcast_success_screen_msg}</BlueTextCentered>
|
||||
<BlueSpacing10 />
|
||||
<Text style={styles.link} onPress={() => Linking.openURL(`https://blockstream.info/tx/${tx}`)}>
|
||||
Open link in explorer
|
||||
{loc.send.broadcast_success_screen_open}
|
||||
</Text>
|
||||
</View>
|
||||
</BlueCard>
|
||||
|
@ -16,9 +16,10 @@ import {
|
||||
} from 'react-native';
|
||||
import { useRoute, useTheme, useNavigation } from '@react-navigation/native';
|
||||
|
||||
import loc, { formatBalanceWithoutSuffix } from '../../loc';
|
||||
import loc, { formatBalance } from '../../loc';
|
||||
import { BitcoinUnit } from '../../models/bitcoinUnits';
|
||||
import { BlueNavigationStyle, SafeBlueArea, BlueSpacing10, BlueSpacing20, BlueButton, BlueListItem } from '../../BlueComponents';
|
||||
import { SafeBlueArea, BlueSpacing10, BlueSpacing20, BlueButton, BlueListItem } from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import BottomModal from '../../components/BottomModal';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
|
||||
@ -35,7 +36,15 @@ const debounce = (func, wait) => {
|
||||
};
|
||||
};
|
||||
|
||||
const Output = ({ item: { address, txid, value, vout }, oMemo, frozen, change = false, full = false, onPress }) => {
|
||||
const Output = ({
|
||||
item: { address, txid, value, vout },
|
||||
balanceUnit = BitcoinUnit.BTC,
|
||||
oMemo,
|
||||
frozen,
|
||||
change = false,
|
||||
full = false,
|
||||
onPress,
|
||||
}) => {
|
||||
const { colors } = useTheme();
|
||||
const { txMetadata } = useContext(BlueStorageContext);
|
||||
const cs = useColorScheme();
|
||||
@ -43,7 +52,7 @@ const Output = ({ item: { address, txid, value, vout }, oMemo, frozen, change =
|
||||
const fullId = `${txid}:${vout}`;
|
||||
const shortId = `${address.substring(0, 9)}...${address.substr(address.length - 9)}`;
|
||||
const color = `#${txid.substring(0, 6)}`;
|
||||
const amount = formatBalanceWithoutSuffix(value, BitcoinUnit.BTC, true);
|
||||
const amount = formatBalance(value, balanceUnit, true);
|
||||
|
||||
const oStyles = StyleSheet.create({
|
||||
containerFull: { paddingHorizontal: 0 },
|
||||
@ -102,6 +111,7 @@ Output.propTypes = {
|
||||
value: PropTypes.number.isRequired,
|
||||
vout: PropTypes.number.isRequired,
|
||||
}),
|
||||
balanceUnit: PropTypes.string,
|
||||
oMemo: PropTypes.string,
|
||||
frozen: PropTypes.bool,
|
||||
change: PropTypes.bool,
|
||||
@ -148,7 +158,7 @@ const OutputModalContent = ({ output, wallet, onUseCoin }) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Output item={output} full />
|
||||
<Output item={output} balanceUnit={wallet.getPreferredBalanceUnit()} full />
|
||||
<BlueSpacing20 />
|
||||
<TextInput
|
||||
testID="OutputMemo"
|
||||
@ -223,7 +233,16 @@ const CoinControl = () => {
|
||||
const renderItem = p => {
|
||||
const { memo, frozen } = wallet.getUTXOMetadata(p.item.txid, p.item.vout);
|
||||
const change = wallet.addressIsChange(p.item.address);
|
||||
return <Output item={p.item} oMemo={memo} frozen={frozen} change={change} onPress={() => handleChoose(p.item)} />;
|
||||
return (
|
||||
<Output
|
||||
balanceUnit={wallet.getPreferredBalanceUnit()}
|
||||
item={p.item}
|
||||
oMemo={memo}
|
||||
frozen={frozen}
|
||||
change={change}
|
||||
onPress={() => handleChoose(p.item)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
@ -290,8 +309,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
});
|
||||
|
||||
CoinControl.navigationOptions = () => ({
|
||||
...BlueNavigationStyle(null, false),
|
||||
CoinControl.navigationOptions = navigationStyle({
|
||||
title: loc.cc.header,
|
||||
});
|
||||
|
||||
|
@ -1,13 +1,15 @@
|
||||
/* global alert */
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { ActivityIndicator, FlatList, TouchableOpacity, StyleSheet, Switch, View } from 'react-native';
|
||||
import { Text } from 'react-native-elements';
|
||||
import { PayjoinClient } from 'payjoin-client';
|
||||
import PayjoinTransaction from '../../class/payjoin-transaction';
|
||||
import { BlueButton, BlueText, SafeBlueArea, BlueCard, BlueSpacing40, BlueNavigationStyle } from '../../BlueComponents';
|
||||
import { BitcoinUnit } from '../../models/bitcoinUnits';
|
||||
import PropTypes from 'prop-types';
|
||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||
|
||||
import PayjoinTransaction from '../../class/payjoin-transaction';
|
||||
import { BlueButton, BlueText, SafeBlueArea, BlueCard, BlueSpacing40 } from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import { BitcoinUnit } from '../../models/bitcoinUnits';
|
||||
import Biometric from '../../class/biometrics';
|
||||
import loc, { formatBalance, formatBalanceWithoutSuffix } from '../../loc';
|
||||
import { BlueCurrentTheme } from '../../components/themes';
|
||||
@ -91,14 +93,15 @@ export default class Confirm extends Component {
|
||||
|
||||
amount = formatBalanceWithoutSuffix(amount, BitcoinUnit.BTC, false);
|
||||
|
||||
this.context.fetchAndSaveWalletTransactions(this.state.fromWallet.getID());
|
||||
this.props.navigation.navigate('Success', {
|
||||
fee: Number(this.state.fee),
|
||||
amount,
|
||||
dismissModal: () => this.props.navigation.dangerouslyGetParent().pop(),
|
||||
});
|
||||
|
||||
this.setState({ isLoading: false });
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 3000)); // sleep to make sure network propagates
|
||||
this.context.fetchAndSaveWalletTransactions(this.state.fromWallet.getID());
|
||||
} catch (error) {
|
||||
ReactNativeHapticFeedback.trigger('notificationError', {
|
||||
ignoreAndroidSystemSettings: false,
|
||||
@ -323,7 +326,6 @@ Confirm.propTypes = {
|
||||
}),
|
||||
};
|
||||
|
||||
Confirm.navigationOptions = () => ({
|
||||
...BlueNavigationStyle(null, false),
|
||||
Confirm.navigationOptions = navigationStyle({
|
||||
title: loc.send.confirm_header,
|
||||
});
|
||||
|
@ -20,13 +20,14 @@ import Clipboard from '@react-native-community/clipboard';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import Share from 'react-native-share';
|
||||
import RNFS from 'react-native-fs';
|
||||
import isCatalyst from 'react-native-is-catalyst';
|
||||
|
||||
import { BlueNavigationStyle, SafeBlueArea, BlueCard, BlueText } from '../../BlueComponents';
|
||||
import { SafeBlueArea, BlueCard, BlueText } from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import Privacy from '../../Privacy';
|
||||
import { BitcoinUnit } from '../../models/bitcoinUnits';
|
||||
import loc from '../../loc';
|
||||
import { BlueCurrentTheme } from '../../components/themes';
|
||||
import isCatalyst from 'react-native-is-catalyst';
|
||||
const currency = require('../../blue_modules/currency');
|
||||
|
||||
export default class SendCreate extends Component {
|
||||
@ -246,21 +247,25 @@ SendCreate.propTypes = {
|
||||
}),
|
||||
};
|
||||
|
||||
SendCreate.navigationOptions = ({ navigation, route }) => {
|
||||
let headerRight;
|
||||
if (route.params.exportTXN) {
|
||||
headerRight = () => (
|
||||
<TouchableOpacity style={styles.export} onPress={route.params.exportTXN}>
|
||||
<Icon size={22} name="share-alternative" type="entypo" color={BlueCurrentTheme.colors.foregroundColor} />
|
||||
</TouchableOpacity>
|
||||
);
|
||||
} else {
|
||||
headerRight = null;
|
||||
}
|
||||
|
||||
return {
|
||||
...BlueNavigationStyle(),
|
||||
SendCreate.navigationOptions = navigationStyle(
|
||||
{
|
||||
title: loc.send.create_details,
|
||||
headerRight,
|
||||
};
|
||||
};
|
||||
},
|
||||
(options, { theme, navigation, route }) => {
|
||||
let headerRight;
|
||||
if (route.params.exportTXN) {
|
||||
headerRight = () => (
|
||||
<TouchableOpacity style={styles.export} onPress={route.params.exportTXN}>
|
||||
<Icon size={22} name="share-alternative" type="entypo" color={BlueCurrentTheme.colors.foregroundColor} />
|
||||
</TouchableOpacity>
|
||||
);
|
||||
} else {
|
||||
headerRight = null;
|
||||
}
|
||||
|
||||
return {
|
||||
...options,
|
||||
headerRight,
|
||||
};
|
||||
},
|
||||
);
|
||||
|
@ -20,8 +20,12 @@ import {
|
||||
} from 'react-native';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import AsyncStorage from '@react-native-community/async-storage';
|
||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||
import RNFS from 'react-native-fs';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import * as bitcoin from 'bitcoinjs-lib';
|
||||
|
||||
import {
|
||||
BlueCreateTxNavigationStyle,
|
||||
BlueButton,
|
||||
BlueBitcoinAmount,
|
||||
BlueAddressInput,
|
||||
@ -31,11 +35,7 @@ import {
|
||||
BlueListItem,
|
||||
BlueText,
|
||||
} from '../../BlueComponents';
|
||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import RNFS from 'react-native-fs';
|
||||
import * as bitcoin from 'bitcoinjs-lib';
|
||||
|
||||
import { navigationStyleTx } from '../../components/navigationStyle';
|
||||
import NetworkTransactionFees, { NetworkTransactionFee } from '../../models/networkTransactionFees';
|
||||
import { BitcoinUnit, Chain } from '../../models/bitcoinUnits';
|
||||
import { HDSegwitBech32Wallet, LightningCustodianWallet, MultisigHDWallet, WatchOnlyWallet } from '../../class';
|
||||
@ -205,6 +205,11 @@ const styles = StyleSheet.create({
|
||||
feeValue: {
|
||||
color: BlueCurrentTheme.colors.feeValue,
|
||||
},
|
||||
advancedOptions: {
|
||||
minWidth: 40,
|
||||
height: 40,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
});
|
||||
|
||||
export default class SendDetails extends Component {
|
||||
@ -647,7 +652,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;
|
||||
@ -923,7 +928,6 @@ export default class SendDetails extends Component {
|
||||
} else if (DeeplinkSchemaMatch.isTXNFile(res.uri)) {
|
||||
// plain text file with txhex ready to broadcast
|
||||
const file = (await RNFS.readFile(res.uri, 'ascii')).replace('\n', '').replace('\r', '');
|
||||
console.warn(JSON.stringify(file));
|
||||
this.props.navigation.navigate('PsbtWithHardwareWallet', {
|
||||
memo: this.state.memo,
|
||||
fromWallet: this.state.fromWallet,
|
||||
@ -976,7 +980,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);
|
||||
@ -1470,7 +1474,28 @@ SendDetails.propTypes = {
|
||||
}),
|
||||
};
|
||||
|
||||
SendDetails.navigationOptions = ({ navigation, route }) => ({
|
||||
...BlueCreateTxNavigationStyle(navigation, route.params.withAdvancedOptionsMenuButton, route.params.advancedOptionsMenuButtonAction),
|
||||
title: loc.send.header,
|
||||
});
|
||||
SendDetails.navigationOptions = navigationStyleTx(
|
||||
{
|
||||
title: loc.send.header,
|
||||
},
|
||||
(options, { theme, navigation, route }) => {
|
||||
let headerRight;
|
||||
if (route.params.withAdvancedOptionsMenuButton) {
|
||||
headerRight = () => (
|
||||
<TouchableOpacity
|
||||
style={styles.advancedOptions}
|
||||
onPress={route.params.advancedOptionsMenuButtonAction}
|
||||
testID="advancedOptionsMenuButton"
|
||||
>
|
||||
<Icon size={22} name="kebab-horizontal" type="octicon" color={theme.colors.foregroundColor} />
|
||||
</TouchableOpacity>
|
||||
);
|
||||
} else {
|
||||
headerRight = null;
|
||||
}
|
||||
return {
|
||||
...options,
|
||||
headerRight,
|
||||
};
|
||||
},
|
||||
);
|
||||
|
@ -1,23 +1,17 @@
|
||||
/* 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 loc from '../../loc';
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { FlatList, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||||
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 { BlueButton, BlueCard, BlueText, SafeBlueArea } from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import loc from '../../loc';
|
||||
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 currency = require('../../blue_modules/currency');
|
||||
|
||||
const shortenAddress = addr => {
|
||||
return addr.substr(0, Math.floor(addr.length / 2) - 1) + '\n' + addr.substr(Math.floor(addr.length / 2) - 1, addr.length);
|
||||
@ -25,21 +19,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 +46,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 +71,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 +85,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 +119,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 +146,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 +173,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 +186,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 +212,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 +255,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 +281,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 +334,7 @@ const styles = StyleSheet.create({
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
destionationTextContainer: {
|
||||
destinationTextContainer: {
|
||||
flexDirection: 'row',
|
||||
marginBottom: 4,
|
||||
paddingHorizontal: 60,
|
||||
@ -438,6 +350,9 @@ const styles = StyleSheet.create({
|
||||
fontWeight: 'bold',
|
||||
fontSize: 30,
|
||||
},
|
||||
textAlignCenter: {
|
||||
textAlign: 'center',
|
||||
},
|
||||
textDestinationFirstFour: {
|
||||
fontSize: 14,
|
||||
},
|
||||
@ -447,21 +362,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,
|
||||
@ -503,8 +403,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
});
|
||||
|
||||
PsbtMultisig.navigationOptions = () => ({
|
||||
...BlueNavigationStyle(null, false),
|
||||
PsbtMultisig.navigationOptions = navigationStyle({
|
||||
title: loc.multisig.header,
|
||||
});
|
||||
|
||||
|
148
screen/send/psbtMultisigQRCode.js
Normal file
148
screen/send/psbtMultisigQRCode.js
Normal file
@ -0,0 +1,148 @@
|
||||
/* global alert */
|
||||
import React, { useState } from 'react';
|
||||
import { ActivityIndicator, Platform, ScrollView, StyleSheet, View } from 'react-native';
|
||||
import { getSystemName } from 'react-native-device-info';
|
||||
import ImagePicker from 'react-native-image-picker';
|
||||
import { useNavigation, useRoute, useTheme } from '@react-navigation/native';
|
||||
|
||||
import { BlueSpacing20, SafeBlueArea } from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import { DynamicQRCode } from '../../components/DynamicQRCode';
|
||||
import { SquareButton } from '../../components/SquareButton';
|
||||
import loc from '../../loc';
|
||||
import ScanQRCode from './ScanQRCode';
|
||||
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 = navigationStyle({
|
||||
title: loc.multisig.header,
|
||||
});
|
||||
|
||||
export default PsbtMultisigQRCode;
|
@ -15,27 +15,28 @@ import {
|
||||
} from 'react-native';
|
||||
import ImagePicker from 'react-native-image-picker';
|
||||
import Clipboard from '@react-native-community/clipboard';
|
||||
import Share from 'react-native-share';
|
||||
import { getSystemName } from 'react-native-device-info';
|
||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||
import DocumentPicker from 'react-native-document-picker';
|
||||
import { useNavigation, useRoute, useTheme } from '@react-navigation/native';
|
||||
import isCatalyst from 'react-native-is-catalyst';
|
||||
import RNFS from 'react-native-fs';
|
||||
|
||||
import {
|
||||
SecondButton,
|
||||
BlueText,
|
||||
SafeBlueArea,
|
||||
BlueCard,
|
||||
BlueNavigationStyle,
|
||||
BlueSpacing20,
|
||||
BlueCopyToClipboardButton,
|
||||
DynamicQRCode,
|
||||
} from '../../BlueComponents';
|
||||
import Share from 'react-native-share';
|
||||
import { getSystemName } from 'react-native-device-info';
|
||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||
import RNFS from 'react-native-fs';
|
||||
import DocumentPicker from 'react-native-document-picker';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import loc from '../../loc';
|
||||
import ScanQRCode from './ScanQRCode';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
import Notifications from '../../blue_modules/notifications';
|
||||
import { useNavigation, useRoute, useTheme } from '@react-navigation/native';
|
||||
import isCatalyst from 'react-native-is-catalyst';
|
||||
const BlueElectrum = require('../../blue_modules/BlueElectrum');
|
||||
/** @type {AppStorage} */
|
||||
const bitcoin = require('bitcoinjs-lib');
|
||||
@ -336,8 +337,7 @@ const PsbtWithHardwareWallet = () => {
|
||||
|
||||
export default PsbtWithHardwareWallet;
|
||||
|
||||
PsbtWithHardwareWallet.navigationOptions = () => ({
|
||||
...BlueNavigationStyle(null, false),
|
||||
PsbtWithHardwareWallet.navigationOptions = navigationStyle({
|
||||
title: loc.send.header,
|
||||
});
|
||||
|
||||
|
@ -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}>
|
||||
|
@ -1,6 +1,8 @@
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { ScrollView, Platform, TouchableWithoutFeedback, TouchableOpacity, StyleSheet } from 'react-native';
|
||||
import { BlueLoading, BlueText, BlueSpacing20, BlueListItem, BlueNavigationStyle, BlueCard } from '../../BlueComponents';
|
||||
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import { BlueLoading, BlueText, BlueSpacing20, BlueListItem, BlueCard } from '../../BlueComponents';
|
||||
import { useNavigation, useTheme } from '@react-navigation/native';
|
||||
import HandoffSettings from '../../class/handoff';
|
||||
import loc from '../../loc';
|
||||
@ -88,8 +90,7 @@ const GeneralSettings = () => {
|
||||
);
|
||||
};
|
||||
|
||||
GeneralSettings.navigationOptions = () => ({
|
||||
...BlueNavigationStyle(),
|
||||
GeneralSettings.navigationOptions = navigationStyle({
|
||||
title: loc.settings.general,
|
||||
});
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
import React from 'react';
|
||||
import { ScrollView, StyleSheet } from 'react-native';
|
||||
import { SafeBlueArea, BlueListItem, BlueNavigationStyle } from '../../BlueComponents';
|
||||
import { useNavigation, useTheme } from '@react-navigation/native';
|
||||
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import { SafeBlueArea, BlueListItem } from '../../BlueComponents';
|
||||
import loc from '../../loc';
|
||||
|
||||
const NetworkSettings = () => {
|
||||
@ -36,8 +38,9 @@ const NetworkSettings = () => {
|
||||
</SafeBlueArea>
|
||||
);
|
||||
};
|
||||
NetworkSettings.navigationOptions = () => ({
|
||||
...BlueNavigationStyle(),
|
||||
|
||||
NetworkSettings.navigationOptions = navigationStyle({
|
||||
title: loc.settings.network,
|
||||
});
|
||||
|
||||
export default NetworkSettings;
|
||||
|
@ -1,7 +1,9 @@
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { ScrollView, TouchableWithoutFeedback, StyleSheet, Linking } from 'react-native';
|
||||
import { BlueText, BlueSpacing20, BlueListItem, BlueNavigationStyle, BlueCard } from '../../BlueComponents';
|
||||
import { useTheme } from '@react-navigation/native';
|
||||
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import { BlueText, BlueSpacing20, BlueListItem, BlueCard } from '../../BlueComponents';
|
||||
import loc from '../../loc';
|
||||
import BlueClipboard from '../../blue_modules/clipboard';
|
||||
import DeviceQuickActions from '../../class/quick-actions';
|
||||
@ -100,8 +102,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
});
|
||||
|
||||
SettingsPrivacy.navigationOptions = () => ({
|
||||
...BlueNavigationStyle(),
|
||||
SettingsPrivacy.navigationOptions = navigationStyle({
|
||||
title: loc.settings.privacy,
|
||||
});
|
||||
|
||||
|
@ -1,17 +1,11 @@
|
||||
import React from 'react';
|
||||
import { ScrollView, Linking, Image, View, Text, StyleSheet, useWindowDimensions } from 'react-native';
|
||||
import { useNavigation, useTheme } from '@react-navigation/native';
|
||||
import {
|
||||
BlueTextCentered,
|
||||
BlueSpacing20,
|
||||
BlueButton,
|
||||
SafeBlueArea,
|
||||
BlueCard,
|
||||
BlueListItem,
|
||||
BlueNavigationStyle,
|
||||
} from '../../BlueComponents';
|
||||
import { getApplicationName, getVersion, getBundleId, getBuildNumber } from 'react-native-device-info';
|
||||
import Rate, { AndroidMarket } from 'react-native-rate';
|
||||
|
||||
import { BlueButton, BlueCard, BlueListItem, BlueSpacing20, BlueTextCentered, SafeBlueArea } from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import loc from '../../loc';
|
||||
|
||||
const About = () => {
|
||||
@ -193,8 +187,7 @@ const About = () => {
|
||||
);
|
||||
};
|
||||
|
||||
About.navigationOptions = () => ({
|
||||
...BlueNavigationStyle(),
|
||||
About.navigationOptions = navigationStyle({
|
||||
headerTitle: loc.settings.about,
|
||||
});
|
||||
export default About;
|
||||
|
@ -1,10 +1,11 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { FlatList, ActivityIndicator, View, StyleSheet } from 'react-native';
|
||||
import { SafeBlueArea, BlueListItem, BlueText, BlueCard, BlueNavigationStyle } from '../../BlueComponents';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTheme } from '@react-navigation/native';
|
||||
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import { SafeBlueArea, BlueListItem, BlueText, BlueCard } from '../../BlueComponents';
|
||||
import { FiatUnit, FiatUnitSource } from '../../models/fiatUnit';
|
||||
import loc from '../../loc';
|
||||
import { useTheme } from '@react-navigation/native';
|
||||
const currency = require('../../blue_modules/currency');
|
||||
|
||||
const data = Object.values(FiatUnit);
|
||||
@ -82,15 +83,8 @@ const Currency = () => {
|
||||
);
|
||||
};
|
||||
|
||||
Currency.propTypes = {
|
||||
navigation: PropTypes.shape({
|
||||
navigate: PropTypes.func,
|
||||
goBack: PropTypes.func,
|
||||
}),
|
||||
};
|
||||
|
||||
Currency.navigationOptions = () => ({
|
||||
...BlueNavigationStyle(),
|
||||
Currency.navigationOptions = navigationStyle({
|
||||
title: loc.settings.currency,
|
||||
});
|
||||
|
||||
export default Currency;
|
||||
|
@ -1,7 +1,9 @@
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { View, TouchableWithoutFeedback, StyleSheet } from 'react-native';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { SafeBlueArea, BlueCard, BlueNavigationStyle, BlueListItem, BlueText } from '../../BlueComponents';
|
||||
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import { SafeBlueArea, BlueCard, BlueListItem, BlueText } from '../../BlueComponents';
|
||||
import OnAppLaunch from '../../class/on-app-launch';
|
||||
import loc from '../../loc';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
@ -78,8 +80,7 @@ const DefaultView = () => {
|
||||
);
|
||||
};
|
||||
|
||||
DefaultView.navigationOptions = () => ({
|
||||
...BlueNavigationStyle(),
|
||||
DefaultView.navigationOptions = navigationStyle({
|
||||
title: loc.settings.default_title,
|
||||
});
|
||||
|
||||
|
@ -1,24 +1,17 @@
|
||||
/* global alert */
|
||||
import React, { Component } from 'react';
|
||||
import { View, TextInput, StyleSheet } from 'react-native';
|
||||
import { AppStorage } from '../../class';
|
||||
import AsyncStorage from '@react-native-community/async-storage';
|
||||
import { ScrollView } from 'react-native-gesture-handler';
|
||||
import {
|
||||
BlueLoading,
|
||||
BlueSpacing20,
|
||||
BlueButton,
|
||||
SafeBlueArea,
|
||||
BlueCard,
|
||||
BlueText,
|
||||
BlueNavigationStyle,
|
||||
BlueButtonLink,
|
||||
} from '../../BlueComponents';
|
||||
import { BlueCurrentTheme } from '../../components/themes';
|
||||
import PropTypes from 'prop-types';
|
||||
import loc from '../../loc';
|
||||
import { View, TextInput, StyleSheet } from 'react-native';
|
||||
import DefaultPreference from 'react-native-default-preference';
|
||||
import RNWidgetCenter from 'react-native-widget-center';
|
||||
import AsyncStorage from '@react-native-community/async-storage';
|
||||
import { ScrollView } from 'react-native-gesture-handler';
|
||||
|
||||
import { AppStorage } from '../../class';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import { BlueButton, BlueButtonLink, BlueCard, BlueLoading, BlueSpacing20, BlueText, SafeBlueArea } from '../../BlueComponents';
|
||||
import { BlueCurrentTheme } from '../../components/themes';
|
||||
import loc from '../../loc';
|
||||
const BlueElectrum = require('../../blue_modules/BlueElectrum');
|
||||
|
||||
export default class ElectrumSettings extends Component {
|
||||
@ -219,8 +212,7 @@ ElectrumSettings.propTypes = {
|
||||
}),
|
||||
};
|
||||
|
||||
ElectrumSettings.navigationOptions = () => ({
|
||||
...BlueNavigationStyle(),
|
||||
ElectrumSettings.navigationOptions = navigationStyle({
|
||||
title: loc.settings.electrum_settings,
|
||||
});
|
||||
|
||||
|
@ -3,19 +3,20 @@ import React, { useEffect, useState, useCallback, useContext } from 'react';
|
||||
import { ScrollView, Alert, Platform, TouchableOpacity, TouchableWithoutFeedback, StyleSheet } from 'react-native';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||
import { colors } from 'react-native-elements';
|
||||
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import {
|
||||
BlueLoadingHook,
|
||||
BlueLoading,
|
||||
SafeBlueArea,
|
||||
BlueSpacing20,
|
||||
BlueCard,
|
||||
BlueListItem,
|
||||
BlueHeaderDefaultSubHooks,
|
||||
BlueText,
|
||||
BlueNavigationStyle,
|
||||
} from '../../BlueComponents';
|
||||
import Biometric from '../../class/biometrics';
|
||||
import loc from '../../loc';
|
||||
import { colors } from 'react-native-elements';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
const prompt = require('../../blue_modules/prompt');
|
||||
|
||||
@ -149,7 +150,7 @@ const EncryptStorage = () => {
|
||||
|
||||
return isLoading ? (
|
||||
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={styles.root}>
|
||||
<BlueLoadingHook />
|
||||
<BlueLoading />
|
||||
</SafeBlueArea>
|
||||
) : (
|
||||
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={styles.root}>
|
||||
@ -202,7 +203,6 @@ const EncryptStorage = () => {
|
||||
};
|
||||
|
||||
export default EncryptStorage;
|
||||
EncryptStorage.navigationOptions = () => ({
|
||||
...BlueNavigationStyle(),
|
||||
EncryptStorage.navigationOptions = navigationStyle({
|
||||
headerTitle: loc.settings.encrypt_title,
|
||||
});
|
||||
|
@ -1,6 +1,8 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import { FlatList, StyleSheet } from 'react-native';
|
||||
import { SafeBlueArea, BlueListItem, BlueCard, BlueLoadingHook, BlueNavigationStyle, BlueText } from '../../BlueComponents';
|
||||
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import { SafeBlueArea, BlueListItem, BlueCard, BlueLoading, BlueText } from '../../BlueComponents';
|
||||
import { AvailableLanguages } from '../../loc/languages';
|
||||
import loc from '../../loc';
|
||||
|
||||
@ -40,7 +42,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} />
|
||||
@ -51,8 +53,7 @@ const Language = () => {
|
||||
);
|
||||
};
|
||||
|
||||
Language.navigationOptions = () => ({
|
||||
...BlueNavigationStyle(),
|
||||
Language.navigationOptions = navigationStyle({
|
||||
headerTitle: loc.settings.language,
|
||||
});
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { ScrollView, StyleSheet } from 'react-native';
|
||||
import { SafeBlueArea, BlueCard, BlueText, BlueNavigationStyle, BlueSpacing20, BlueLoadingHook } from '../../BlueComponents';
|
||||
/** @type {AppStorage} */
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import { SafeBlueArea, BlueCard, BlueText, BlueSpacing20, BlueLoading } from '../../BlueComponents';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
@ -17,7 +17,7 @@ const Licensing = () => {
|
||||
}, []);
|
||||
|
||||
return isLoading ? (
|
||||
<BlueLoadingHook />
|
||||
<BlueLoading />
|
||||
) : (
|
||||
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={styles.root}>
|
||||
<ScrollView>
|
||||
@ -51,8 +51,7 @@ const Licensing = () => {
|
||||
);
|
||||
};
|
||||
|
||||
Licensing.navigationOptions = () => ({
|
||||
...BlueNavigationStyle(),
|
||||
Licensing.navigationOptions = navigationStyle({
|
||||
title: 'License',
|
||||
});
|
||||
|
||||
|
@ -3,18 +3,11 @@ import React, { useState, useEffect, useCallback } from 'react';
|
||||
import { View, TextInput, Linking, StyleSheet } from 'react-native';
|
||||
import { Button } from 'react-native-elements';
|
||||
import { useTheme, useNavigation, useRoute } from '@react-navigation/native';
|
||||
import { AppStorage } from '../../class';
|
||||
import AsyncStorage from '@react-native-community/async-storage';
|
||||
import {
|
||||
BlueSpacing20,
|
||||
BlueButton,
|
||||
SafeBlueArea,
|
||||
BlueCard,
|
||||
BlueNavigationStyle,
|
||||
BlueLoadingHook,
|
||||
BlueText,
|
||||
BlueButtonLink,
|
||||
} from '../../BlueComponents';
|
||||
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import { BlueButton, BlueButtonLink, BlueCard, BlueLoading, BlueSpacing20, BlueText, SafeBlueArea } from '../../BlueComponents';
|
||||
import { AppStorage } from '../../class';
|
||||
import { LightningCustodianWallet } from '../../class/wallets/lightning-custodian-wallet';
|
||||
import loc from '../../loc';
|
||||
import { BlueCurrentTheme } from '../../components/themes';
|
||||
@ -130,14 +123,14 @@ 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>
|
||||
);
|
||||
};
|
||||
|
||||
LightningSettings.navigationOptions = () => ({
|
||||
...BlueNavigationStyle(),
|
||||
LightningSettings.navigationOptions = navigationStyle({
|
||||
title: loc.settings.lightning_settings,
|
||||
});
|
||||
|
||||
export default LightningSettings;
|
||||
|
@ -1,19 +1,12 @@
|
||||
/* global alert */
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { ScrollView, TouchableWithoutFeedback, StyleSheet, Linking, View, TextInput } from 'react-native';
|
||||
import {
|
||||
BlueLoading,
|
||||
BlueText,
|
||||
BlueSpacing20,
|
||||
BlueListItem,
|
||||
BlueNavigationStyle,
|
||||
BlueCard,
|
||||
BlueButton,
|
||||
BlueCopyToClipboardButton,
|
||||
} from '../../BlueComponents';
|
||||
import { useTheme } from '@react-navigation/native';
|
||||
import loc from '../../loc';
|
||||
import { Button } from 'react-native-elements';
|
||||
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import { BlueButton, BlueCard, BlueCopyToClipboardButton, BlueListItem, BlueLoading, BlueSpacing20, BlueText } from '../../BlueComponents';
|
||||
import loc from '../../loc';
|
||||
import { BlueCurrentTheme } from '../../components/themes';
|
||||
import Notifications from '../../blue_modules/notifications';
|
||||
|
||||
@ -163,8 +156,7 @@ const NotificationSettings = () => {
|
||||
);
|
||||
};
|
||||
|
||||
NotificationSettings.navigationOptions = () => ({
|
||||
...BlueNavigationStyle(),
|
||||
NotificationSettings.navigationOptions = navigationStyle({
|
||||
title: loc.settings.notifications,
|
||||
});
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
import React from 'react';
|
||||
import { ScrollView, StyleSheet } from 'react-native';
|
||||
import { SafeBlueArea, BlueCard, BlueNavigationStyle, BlueText } from '../../BlueComponents';
|
||||
import { useTheme } from '@react-navigation/native';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import { SafeBlueArea, BlueCard, BlueText } from '../../BlueComponents';
|
||||
import loc from '../../loc';
|
||||
|
||||
const ReleaseNotes = () => {
|
||||
@ -25,8 +26,7 @@ const ReleaseNotes = () => {
|
||||
);
|
||||
};
|
||||
|
||||
ReleaseNotes.navigationOptions = () => ({
|
||||
...BlueNavigationStyle(),
|
||||
ReleaseNotes.navigationOptions = navigationStyle({
|
||||
title: loc.settings.about_release_notes,
|
||||
});
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
import React from 'react';
|
||||
import { ScrollView, TouchableOpacity, StyleSheet, StatusBar } from 'react-native';
|
||||
import { BlueListItem, BlueNavigationStyle, BlueHeaderDefaultSubHooks } from '../../BlueComponents';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import { BlueListItem, BlueHeaderDefaultSubHooks } from '../../BlueComponents';
|
||||
import loc from '../../loc';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
@ -47,7 +48,6 @@ const Settings = () => {
|
||||
};
|
||||
|
||||
export default Settings;
|
||||
Settings.navigationOptions = () => ({
|
||||
...BlueNavigationStyle(),
|
||||
Settings.navigationOptions = navigationStyle({
|
||||
headerTitle: '',
|
||||
});
|
||||
|
@ -5,16 +5,9 @@ import { ActivityIndicator, View, TextInput, TouchableOpacity, Linking, ScrollVi
|
||||
import Clipboard from '@react-native-community/clipboard';
|
||||
import { Text } from 'react-native-elements';
|
||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||
import {
|
||||
BlueSpacing20,
|
||||
BlueReplaceFeeSuggestions,
|
||||
BlueButton,
|
||||
SafeBlueArea,
|
||||
BlueCard,
|
||||
BlueText,
|
||||
BlueSpacing,
|
||||
BlueNavigationStyle,
|
||||
} from '../../BlueComponents';
|
||||
|
||||
import { BlueButton, BlueCard, BlueReplaceFeeSuggestions, BlueSpacing, BlueSpacing20, BlueText, SafeBlueArea } from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import { BlueCurrentTheme } from '../../components/themes';
|
||||
import { HDSegwitBech32Transaction, HDSegwitBech32Wallet } from '../../class';
|
||||
import loc from '../../loc';
|
||||
@ -117,6 +110,7 @@ export default class CPFP extends Component {
|
||||
onSuccessBroadcast() {
|
||||
this.context.txMetadata[this.state.newTxid] = { memo: 'Child pays for parent (CPFP)' };
|
||||
Notifications.majorTomToGroundControl([], [], [this.state.newTxid]);
|
||||
this.context.sleep(4000).then(() => this.context.fetchAndSaveWalletTransactions(this.state.wallet.getID()));
|
||||
this.props.navigation.navigate('Success', { onDonePressed: () => this.props.navigation.popToTop(), amount: undefined });
|
||||
}
|
||||
|
||||
@ -249,7 +243,6 @@ CPFP.propTypes = {
|
||||
}),
|
||||
}),
|
||||
};
|
||||
CPFP.navigationOptions = () => ({
|
||||
...BlueNavigationStyle(null, false),
|
||||
CPFP.navigationOptions = navigationStyle({
|
||||
title: loc.transactions.cpfp_title,
|
||||
});
|
||||
|
@ -2,7 +2,8 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { ActivityIndicator, View, ScrollView, StyleSheet } from 'react-native';
|
||||
import { BlueSpacing20, SafeBlueArea, BlueText, BlueNavigationStyle } from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import { BlueSpacing20, SafeBlueArea, BlueText } from '../../BlueComponents';
|
||||
import { HDSegwitBech32Transaction, HDSegwitBech32Wallet } from '../../class';
|
||||
import CPFP from './CPFP';
|
||||
import loc from '../../loc';
|
||||
@ -65,6 +66,7 @@ export default class RBFBumpFee extends CPFP {
|
||||
if (this.context.txMetadata[this.state.txid]) {
|
||||
this.context.txMetadata[this.state.newTxid] = this.context.txMetadata[this.state.txid];
|
||||
}
|
||||
this.context.sleep(4000).then(() => this.context.fetchAndSaveWalletTransactions(this.state.wallet.getID()));
|
||||
this.props.navigation.navigate('Success', { onDonePressed: () => this.props.navigation.popToTop(), amount: undefined });
|
||||
}
|
||||
|
||||
@ -115,8 +117,6 @@ RBFBumpFee.propTypes = {
|
||||
}),
|
||||
}),
|
||||
};
|
||||
|
||||
RBFBumpFee.navigationOptions = () => ({
|
||||
...BlueNavigationStyle(null, false),
|
||||
RBFBumpFee.navigationOptions = navigationStyle({
|
||||
title: loc.transactions.rbf_title,
|
||||
});
|
||||
|
@ -1,8 +1,9 @@
|
||||
/* global alert */
|
||||
import React from 'react';
|
||||
import { ActivityIndicator, View, StyleSheet, ScrollView } from 'react-native';
|
||||
import { BlueSpacing20, SafeBlueArea, BlueText, BlueNavigationStyle } from '../../BlueComponents';
|
||||
import PropTypes from 'prop-types';
|
||||
import { ActivityIndicator, View, StyleSheet, ScrollView } from 'react-native';
|
||||
import { BlueSpacing20, SafeBlueArea, BlueText } from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import { HDSegwitBech32Transaction, HDSegwitBech32Wallet } from '../../class';
|
||||
import CPFP from './CPFP';
|
||||
import loc from '../../loc';
|
||||
@ -75,6 +76,7 @@ export default class RBFCancel extends CPFP {
|
||||
} else {
|
||||
this.context.txMetadata[this.state.newTxid].memo = 'Cancelled transaction';
|
||||
}
|
||||
this.context.sleep(4000).then(() => this.context.fetchAndSaveWalletTransactions(this.state.wallet.getID()));
|
||||
this.props.navigation.navigate('Success', { onDonePressed: () => this.props.navigation.popToTop(), amount: undefined });
|
||||
}
|
||||
|
||||
@ -126,7 +128,6 @@ RBFCancel.propTypes = {
|
||||
}),
|
||||
};
|
||||
|
||||
RBFCancel.navigationOptions = () => ({
|
||||
...BlueNavigationStyle(null, false),
|
||||
RBFCancel.navigationOptions = navigationStyle({
|
||||
title: loc.transactions.cancel_title,
|
||||
});
|
||||
|
@ -1,23 +1,231 @@
|
||||
/* 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,
|
||||
BlueCard,
|
||||
BlueText,
|
||||
BlueLoading,
|
||||
BlueSpacing20,
|
||||
BlueCopyToClipboardButton,
|
||||
BlueNavigationStyle,
|
||||
} from '../../BlueComponents';
|
||||
import HandoffSettings from '../../class/handoff';
|
||||
import { useNavigation, useRoute, useTheme } from '@react-navigation/native';
|
||||
import Handoff from 'react-native-handoff';
|
||||
import PropTypes from 'prop-types';
|
||||
import { BlueCard, BlueCopyToClipboardButton, BlueLoading, BlueSpacing20, BlueText, SafeBlueArea } from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import HandoffSettings from '../../class/handoff';
|
||||
import loc from '../../loc';
|
||||
import { BlueCurrentTheme } from '../../components/themes';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
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 +243,6 @@ const styles = StyleSheet.create({
|
||||
fontSize: 16,
|
||||
fontWeight: '500',
|
||||
marginBottom: 4,
|
||||
color: BlueCurrentTheme.colors.foregroundColor,
|
||||
},
|
||||
rowValue: {
|
||||
marginBottom: 26,
|
||||
@ -44,7 +251,6 @@ const styles = StyleSheet.create({
|
||||
txId: {
|
||||
fontSize: 16,
|
||||
fontWeight: '500',
|
||||
color: BlueCurrentTheme.colors.foregroundColor,
|
||||
},
|
||||
txHash: {
|
||||
marginBottom: 8,
|
||||
@ -52,23 +258,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 +278,8 @@ 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 }) => ({
|
||||
...BlueNavigationStyle(),
|
||||
TransactionsDetails.navigationOptions = navigationStyle({
|
||||
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>
|
||||
),
|
||||
});
|
||||
|
@ -1,32 +1,362 @@
|
||||
import React, { Component } from 'react';
|
||||
import React, { useContext, useEffect, useRef, useState } from 'react';
|
||||
import { View, ActivityIndicator, Text, TouchableOpacity, StyleSheet, StatusBar } from 'react-native';
|
||||
import {
|
||||
BlueButton,
|
||||
SafeBlueArea,
|
||||
BlueTransactionOutgoingIcon,
|
||||
BlueTransactionPendingIcon,
|
||||
BlueTransactionIncomingIcon,
|
||||
BlueCard,
|
||||
BlueText,
|
||||
BlueLoading,
|
||||
BlueSpacing20,
|
||||
BlueNavigationStyle,
|
||||
} from '../../BlueComponents';
|
||||
import PropTypes from 'prop-types';
|
||||
import { HDSegwitBech32Transaction } from '../../class';
|
||||
import { BitcoinUnit } from '../../models/bitcoinUnits';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import Handoff from 'react-native-handoff';
|
||||
import { useNavigation, useRoute, useTheme } from '@react-navigation/native';
|
||||
|
||||
import {
|
||||
BlueButton,
|
||||
BlueCard,
|
||||
BlueLoading,
|
||||
BlueSpacing10,
|
||||
BlueSpacing20,
|
||||
BlueText,
|
||||
BlueTransactionIncomingIcon,
|
||||
BlueTransactionOutgoingIcon,
|
||||
BlueTransactionPendingIcon,
|
||||
SafeBlueArea,
|
||||
} from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import { HDSegwitBech32Transaction } from '../../class';
|
||||
import { BitcoinUnit } from '../../models/bitcoinUnits';
|
||||
import HandoffSettings from '../../class/handoff';
|
||||
import loc, { formatBalanceWithoutSuffix } from '../../loc';
|
||||
import { BlueCurrentTheme } from '../../components/themes';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
|
||||
const buttonStatus = Object.freeze({
|
||||
possible: 1,
|
||||
unknown: 2,
|
||||
notPossible: 3,
|
||||
});
|
||||
|
||||
const TransactionsStatus = () => {
|
||||
const { setSelectedWallet, wallets, txMetadata, getTransactions } = useContext(BlueStorageContext);
|
||||
const [isHandOffUseEnabled, setIsHandOffUseEnabled] = useState(false);
|
||||
const { hash } = useRoute().params;
|
||||
const { navigate, setOptions } = useNavigation();
|
||||
const { colors } = useTheme();
|
||||
const wallet = useRef();
|
||||
const [isCPFPPossible, setIsCPFPPossible] = useState();
|
||||
const [isRBFBumpFeePossible, setIsRBFBumpFeePossible] = useState();
|
||||
const [isRBFCancelPossible, setIsRBFCancelPossible] = useState();
|
||||
const [tx, setTX] = useState();
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const stylesHook = StyleSheet.create({
|
||||
root: {
|
||||
backgroundColor: colors.background,
|
||||
},
|
||||
value: {
|
||||
color: colors.alternativeTextColor2,
|
||||
},
|
||||
valueUnit: {
|
||||
color: colors.alternativeTextColor2,
|
||||
},
|
||||
iconRoot: {
|
||||
backgroundColor: colors.success,
|
||||
},
|
||||
confirmations: {
|
||||
backgroundColor: colors.lightButton,
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setIsCPFPPossible(buttonStatus.unknown);
|
||||
setIsRBFBumpFeePossible(buttonStatus.unknown);
|
||||
setIsRBFCancelPossible(buttonStatus.unknown);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setOptions({
|
||||
headerStyle: {
|
||||
borderBottomWidth: 0,
|
||||
elevation: 0,
|
||||
shadowOpacity: 0,
|
||||
shadowOffset: { height: 0, width: 0 },
|
||||
backgroundColor: colors.customHeader,
|
||||
},
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [colors]);
|
||||
|
||||
useEffect(() => {
|
||||
for (const w of wallets) {
|
||||
for (const t of w.getTransactions()) {
|
||||
if (t.hash === hash) {
|
||||
console.log('tx', hash, 'belongs to', w.getLabel());
|
||||
wallet.current = w;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const tx of getTransactions()) {
|
||||
if (tx.hash === hash) {
|
||||
setTX(tx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [hash]);
|
||||
|
||||
const initialState = async () => {
|
||||
try {
|
||||
await checkPossibilityOfCPFP();
|
||||
await checkPossibilityOfRBFBumpFee();
|
||||
await checkPossibilityOfRBFCancel();
|
||||
} catch (e) {
|
||||
setIsCPFPPossible(buttonStatus.notPossible);
|
||||
setIsRBFBumpFeePossible(buttonStatus.notPossible);
|
||||
setIsRBFCancelPossible(buttonStatus.notPossible);
|
||||
}
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
initialState();
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [tx]);
|
||||
|
||||
useEffect(() => {
|
||||
if (wallet) {
|
||||
setSelectedWallet(wallet.current.getID());
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [wallet]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log('transactions/details - useEffect');
|
||||
HandoffSettings.isHandoffUseEnabled().then(setIsHandOffUseEnabled);
|
||||
}, []);
|
||||
|
||||
const checkPossibilityOfCPFP = async () => {
|
||||
if (!wallet.current.allowRBF()) {
|
||||
return setIsCPFPPossible(buttonStatus.notPossible);
|
||||
}
|
||||
|
||||
const cpfbTx = new HDSegwitBech32Transaction(null, tx.hash, wallet.current);
|
||||
if ((await cpfbTx.isToUsTransaction()) && (await cpfbTx.getRemoteConfirmationsNum()) === 0) {
|
||||
return setIsCPFPPossible(buttonStatus.possible);
|
||||
} else {
|
||||
return setIsCPFPPossible(buttonStatus.notPossible);
|
||||
}
|
||||
};
|
||||
|
||||
const checkPossibilityOfRBFBumpFee = async () => {
|
||||
if (!wallet.current.allowRBF()) {
|
||||
return setIsRBFBumpFeePossible(buttonStatus.notPossible);
|
||||
}
|
||||
|
||||
const rbfTx = new HDSegwitBech32Transaction(null, tx.hash, wallet.current);
|
||||
if (
|
||||
(await rbfTx.isOurTransaction()) &&
|
||||
(await rbfTx.getRemoteConfirmationsNum()) === 0 &&
|
||||
(await rbfTx.isSequenceReplaceable()) &&
|
||||
(await rbfTx.canBumpTx())
|
||||
) {
|
||||
return setIsRBFBumpFeePossible(buttonStatus.possible);
|
||||
} else {
|
||||
return setIsRBFBumpFeePossible(buttonStatus.notPossible);
|
||||
}
|
||||
};
|
||||
|
||||
const checkPossibilityOfRBFCancel = async () => {
|
||||
if (!wallet.current.allowRBF()) {
|
||||
return setIsRBFCancelPossible(buttonStatus.notPossible);
|
||||
}
|
||||
|
||||
const rbfTx = new HDSegwitBech32Transaction(null, tx.hash, wallet.current);
|
||||
if (
|
||||
(await rbfTx.isOurTransaction()) &&
|
||||
(await rbfTx.getRemoteConfirmationsNum()) === 0 &&
|
||||
(await rbfTx.isSequenceReplaceable()) &&
|
||||
(await rbfTx.canCancelTx())
|
||||
) {
|
||||
return setIsRBFCancelPossible(buttonStatus.possible);
|
||||
} else {
|
||||
return setIsRBFCancelPossible(buttonStatus.notPossible);
|
||||
}
|
||||
};
|
||||
|
||||
const navigateToRBFBumpFee = () => {
|
||||
navigate('RBFBumpFee', {
|
||||
txid: tx.hash,
|
||||
wallet: wallet.current,
|
||||
});
|
||||
};
|
||||
|
||||
const navigateToRBFCancel = () => {
|
||||
navigate('RBFCancel', {
|
||||
txid: tx.hash,
|
||||
wallet: wallet.current,
|
||||
});
|
||||
};
|
||||
|
||||
const navigateToCPFP = () => {
|
||||
navigate('CPFP', {
|
||||
txid: tx.hash,
|
||||
wallet: wallet.current,
|
||||
});
|
||||
};
|
||||
const navigateToTransactionDetials = () => {
|
||||
navigate('TransactionDetails', { hash: tx.hash });
|
||||
};
|
||||
|
||||
const renderCPFP = () => {
|
||||
if (isCPFPPossible === buttonStatus.unknown) {
|
||||
return (
|
||||
<>
|
||||
<ActivityIndicator />
|
||||
<BlueSpacing20 />
|
||||
</>
|
||||
);
|
||||
} else if (isCPFPPossible === buttonStatus.possible) {
|
||||
return (
|
||||
<>
|
||||
<BlueButton onPress={navigateToCPFP} title={loc.transactions.status_bump} />
|
||||
<BlueSpacing10 />
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const renderRBFCancel = () => {
|
||||
if (isRBFCancelPossible === buttonStatus.unknown) {
|
||||
return (
|
||||
<>
|
||||
<ActivityIndicator />
|
||||
</>
|
||||
);
|
||||
} else if (isRBFCancelPossible === buttonStatus.possible) {
|
||||
return (
|
||||
<>
|
||||
<TouchableOpacity style={styles.cancel}>
|
||||
<Text onPress={navigateToRBFCancel} style={styles.cancelText}>
|
||||
{loc.transactions.status_cancel}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<BlueSpacing10 />
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const renderRBFBumpFee = () => {
|
||||
if (isRBFBumpFeePossible === buttonStatus.unknown) {
|
||||
return (
|
||||
<>
|
||||
<ActivityIndicator />
|
||||
<BlueSpacing20 />
|
||||
</>
|
||||
);
|
||||
} else if (isRBFBumpFeePossible === buttonStatus.possible) {
|
||||
return (
|
||||
<>
|
||||
<BlueButton onPress={navigateToRBFBumpFee} title={loc.transactions.status_bump} />
|
||||
<BlueSpacing10 />
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const renderTXMetadata = () => {
|
||||
if (txMetadata[tx.hash]) {
|
||||
if (txMetadata[tx.hash].memo) {
|
||||
return (
|
||||
<View style={styles.memo}>
|
||||
<Text style={styles.memoText}>{txMetadata[tx.hash].memo}</Text>
|
||||
<BlueSpacing20 />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (isLoading || !tx) {
|
||||
return (
|
||||
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={[styles.root, stylesHook.root]}>
|
||||
<BlueLoading />
|
||||
</SafeBlueArea>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={[styles.root, stylesHook.root]}>
|
||||
{isHandOffUseEnabled && (
|
||||
<Handoff title={`Bitcoin Transaction ${tx.hash}`} type="io.bluewallet.bluewallet" url={`https://blockstream.info/tx/${tx.hash}`} />
|
||||
)}
|
||||
<StatusBar barStyle="default" />
|
||||
<View style={styles.container}>
|
||||
<BlueCard>
|
||||
<View style={styles.center}>
|
||||
<Text style={[styles.value, stylesHook.value]}>
|
||||
{formatBalanceWithoutSuffix(tx.value, wallet.current.preferredBalanceUnit, true)}{' '}
|
||||
{wallet.current.preferredBalanceUnit !== BitcoinUnit.LOCAL_CURRENCY && (
|
||||
<Text style={[styles.valueUnit, stylesHook.valueUnit]}>{wallet.current.preferredBalanceUnit}</Text>
|
||||
)}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
{renderTXMetadata()}
|
||||
|
||||
<View style={[styles.iconRoot, stylesHook.iconRoot]}>
|
||||
<View>
|
||||
<Icon name="check" size={50} type="font-awesome" color={colors.successCheck} />
|
||||
</View>
|
||||
<View style={[styles.iconWrap, styles.margin]}>
|
||||
{(() => {
|
||||
if (!tx.confirmations) {
|
||||
return (
|
||||
<View style={styles.icon}>
|
||||
<BlueTransactionPendingIcon />
|
||||
</View>
|
||||
);
|
||||
} else if (tx.value < 0) {
|
||||
return (
|
||||
<View style={styles.icon}>
|
||||
<BlueTransactionOutgoingIcon />
|
||||
</View>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<View style={styles.icon}>
|
||||
<BlueTransactionIncomingIcon />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
})()}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{tx.fee && (
|
||||
<View style={styles.fee}>
|
||||
<BlueText style={styles.feeText}>
|
||||
{loc.send.create_fee.toLowerCase()} {formatBalanceWithoutSuffix(tx.fee, wallet.current.preferredBalanceUnit, true)}{' '}
|
||||
{wallet.current.preferredBalanceUnit !== BitcoinUnit.LOCAL_CURRENCY && wallet.current.preferredBalanceUnit}
|
||||
</BlueText>
|
||||
</View>
|
||||
)}
|
||||
|
||||
<View style={[styles.confirmations, stylesHook.confirmations]}>
|
||||
<Text style={styles.confirmationsText}>{tx.confirmations > 6 ? '6+' : tx.confirmations} confirmations</Text>
|
||||
</View>
|
||||
</BlueCard>
|
||||
|
||||
<View style={styles.actions}>
|
||||
{renderCPFP()}
|
||||
{renderRBFBumpFee()}
|
||||
{renderRBFCancel()}
|
||||
<TouchableOpacity style={styles.details} onPress={navigateToTransactionDetials}>
|
||||
<Text style={styles.detailsText}>{loc.send.create_details.toLowerCase()}</Text>
|
||||
<Icon name="angle-right" size={18} type="font-awesome" color="#9aa0aa" />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</SafeBlueArea>
|
||||
);
|
||||
};
|
||||
|
||||
export default TransactionsStatus;
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
flex: 1,
|
||||
@ -39,12 +369,10 @@ const styles = StyleSheet.create({
|
||||
alignItems: 'center',
|
||||
},
|
||||
value: {
|
||||
color: BlueCurrentTheme.colors.alternativeTextColor2,
|
||||
fontSize: 36,
|
||||
fontWeight: '600',
|
||||
},
|
||||
valueUnit: {
|
||||
color: BlueCurrentTheme.colors.alternativeTextColor2,
|
||||
fontSize: 16,
|
||||
fontWeight: '600',
|
||||
},
|
||||
@ -57,7 +385,6 @@ const styles = StyleSheet.create({
|
||||
fontSize: 14,
|
||||
},
|
||||
iconRoot: {
|
||||
backgroundColor: BlueCurrentTheme.colors.success,
|
||||
width: 120,
|
||||
height: 120,
|
||||
borderRadius: 60,
|
||||
@ -93,7 +420,6 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
confirmations: {
|
||||
borderRadius: 11,
|
||||
backgroundColor: BlueCurrentTheme.colors.lightButton,
|
||||
width: 109,
|
||||
height: 21,
|
||||
alignSelf: 'center',
|
||||
@ -130,321 +456,6 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
});
|
||||
|
||||
export default class TransactionsStatus 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;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.state = {
|
||||
isLoading: true,
|
||||
tx: foundTx,
|
||||
from,
|
||||
to,
|
||||
wallet,
|
||||
isCPFPpossible: buttonStatus.unknown,
|
||||
isRBFBumpFeePossible: buttonStatus.unknown,
|
||||
isRBFCancelPossible: buttonStatus.unknown,
|
||||
isHandOffUseEnabled: false,
|
||||
};
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
console.log('transactions/details - componentDidMount');
|
||||
const isHandOffUseEnabled = await HandoffSettings.isHandoffUseEnabled();
|
||||
this.setState({
|
||||
isLoading: false,
|
||||
isHandOffUseEnabled,
|
||||
});
|
||||
|
||||
try {
|
||||
await this.checkPossibilityOfCPFP();
|
||||
await this.checkPossibilityOfRBFBumpFee();
|
||||
await this.checkPossibilityOfRBFCancel();
|
||||
} catch (_) {
|
||||
this.setState({
|
||||
isCPFPpossible: buttonStatus.notPossible,
|
||||
isRBFBumpFeePossible: buttonStatus.notPossible,
|
||||
isRBFCancelPossible: buttonStatus.notPossible,
|
||||
});
|
||||
}
|
||||
this.context.setSelectedWallet(this.state.wallet.getID());
|
||||
}
|
||||
|
||||
async checkPossibilityOfCPFP() {
|
||||
if (!this.state.wallet.allowRBF()) {
|
||||
return this.setState({ isCPFPpossible: buttonStatus.notPossible });
|
||||
}
|
||||
|
||||
const tx = new HDSegwitBech32Transaction(null, this.state.tx.hash, this.state.wallet);
|
||||
if ((await tx.isToUsTransaction()) && (await tx.getRemoteConfirmationsNum()) === 0) {
|
||||
return this.setState({ isCPFPpossible: buttonStatus.possible });
|
||||
} else {
|
||||
return this.setState({ isCPFPpossible: buttonStatus.notPossible });
|
||||
}
|
||||
}
|
||||
|
||||
async checkPossibilityOfRBFBumpFee() {
|
||||
if (!this.state.wallet.allowRBF()) {
|
||||
return this.setState({ isRBFBumpFeePossible: buttonStatus.notPossible });
|
||||
}
|
||||
|
||||
const tx = new HDSegwitBech32Transaction(null, this.state.tx.hash, this.state.wallet);
|
||||
if (
|
||||
(await tx.isOurTransaction()) &&
|
||||
(await tx.getRemoteConfirmationsNum()) === 0 &&
|
||||
(await tx.isSequenceReplaceable()) &&
|
||||
(await tx.canBumpTx())
|
||||
) {
|
||||
return this.setState({ isRBFBumpFeePossible: buttonStatus.possible });
|
||||
} else {
|
||||
return this.setState({ isRBFBumpFeePossible: buttonStatus.notPossible });
|
||||
}
|
||||
}
|
||||
|
||||
async checkPossibilityOfRBFCancel() {
|
||||
if (!this.state.wallet.allowRBF()) {
|
||||
return this.setState({ isRBFCancelPossible: buttonStatus.notPossible });
|
||||
}
|
||||
|
||||
const tx = new HDSegwitBech32Transaction(null, this.state.tx.hash, this.state.wallet);
|
||||
if (
|
||||
(await tx.isOurTransaction()) &&
|
||||
(await tx.getRemoteConfirmationsNum()) === 0 &&
|
||||
(await tx.isSequenceReplaceable()) &&
|
||||
(await tx.canCancelTx())
|
||||
) {
|
||||
return this.setState({ isRBFCancelPossible: buttonStatus.possible });
|
||||
} else {
|
||||
return this.setState({ isRBFCancelPossible: buttonStatus.notPossible });
|
||||
}
|
||||
}
|
||||
|
||||
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" />
|
||||
<View style={styles.container}>
|
||||
<BlueCard>
|
||||
<View style={styles.center}>
|
||||
<Text style={styles.value}>
|
||||
{formatBalanceWithoutSuffix(this.state.tx.value, this.state.wallet.preferredBalanceUnit, true)}{' '}
|
||||
{this.state.wallet.preferredBalanceUnit !== BitcoinUnit.LOCAL_CURRENCY && (
|
||||
<Text style={styles.valueUnit}>{this.state.wallet.preferredBalanceUnit}</Text>
|
||||
)}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
{(() => {
|
||||
if (this.context.txMetadata[this.state.tx.hash]) {
|
||||
if (this.context.txMetadata[this.state.tx.hash].memo) {
|
||||
return (
|
||||
<View style={styles.memo}>
|
||||
<Text style={styles.memoText}>{this.context.txMetadata[this.state.tx.hash].memo}</Text>
|
||||
<BlueSpacing20 />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
})()}
|
||||
|
||||
<View style={styles.iconRoot}>
|
||||
<View>
|
||||
<Icon name="check" size={50} type="font-awesome" color={BlueCurrentTheme.colors.successCheck} />
|
||||
</View>
|
||||
<View style={[styles.iconWrap, styles.margin]}>
|
||||
{(() => {
|
||||
if (!this.state.tx.confirmations) {
|
||||
return (
|
||||
<View style={styles.icon}>
|
||||
<BlueTransactionPendingIcon />
|
||||
</View>
|
||||
);
|
||||
} else if (this.state.tx.value < 0) {
|
||||
return (
|
||||
<View style={styles.icon}>
|
||||
<BlueTransactionOutgoingIcon />
|
||||
</View>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<View style={styles.icon}>
|
||||
<BlueTransactionIncomingIcon />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
})()}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{'fee' in this.state.tx && (
|
||||
<View style={styles.fee}>
|
||||
<BlueText style={styles.feeText}>
|
||||
{loc.send.create_fee.toLowerCase()}{' '}
|
||||
{formatBalanceWithoutSuffix(this.state.tx.fee, this.state.wallet.preferredBalanceUnit, true)}{' '}
|
||||
{this.state.wallet.preferredBalanceUnit !== BitcoinUnit.LOCAL_CURRENCY && this.state.wallet.preferredBalanceUnit}
|
||||
</BlueText>
|
||||
</View>
|
||||
)}
|
||||
|
||||
<View style={styles.confirmations}>
|
||||
<Text style={styles.confirmationsText}>
|
||||
{this.state.tx.confirmations > 6 ? '6+' : this.state.tx.confirmations} confirmations
|
||||
</Text>
|
||||
</View>
|
||||
</BlueCard>
|
||||
|
||||
<View style={styles.actions}>
|
||||
{(() => {
|
||||
if (this.state.isCPFPpossible === buttonStatus.unknown) {
|
||||
return (
|
||||
<>
|
||||
<ActivityIndicator />
|
||||
<BlueSpacing20 />
|
||||
</>
|
||||
);
|
||||
} else if (this.state.isCPFPpossible === buttonStatus.possible) {
|
||||
return (
|
||||
<>
|
||||
<BlueButton
|
||||
onPress={() =>
|
||||
this.props.navigation.navigate('CPFP', {
|
||||
txid: this.state.tx.hash,
|
||||
wallet: this.state.wallet,
|
||||
})
|
||||
}
|
||||
title={loc.transactions.status_bump}
|
||||
/>
|
||||
<BlueSpacing20 />
|
||||
</>
|
||||
);
|
||||
}
|
||||
})()}
|
||||
|
||||
{(() => {
|
||||
if (this.state.isRBFBumpFeePossible === buttonStatus.unknown) {
|
||||
return (
|
||||
<>
|
||||
<ActivityIndicator />
|
||||
<BlueSpacing20 />
|
||||
</>
|
||||
);
|
||||
} else if (this.state.isRBFBumpFeePossible === buttonStatus.possible) {
|
||||
return (
|
||||
<>
|
||||
<BlueButton
|
||||
onPress={() =>
|
||||
this.props.navigation.navigate('RBFBumpFee', {
|
||||
txid: this.state.tx.hash,
|
||||
wallet: this.state.wallet,
|
||||
})
|
||||
}
|
||||
title={loc.transactions.status_bump}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
})()}
|
||||
{(() => {
|
||||
if (this.state.isRBFCancelPossible === buttonStatus.unknown) {
|
||||
return (
|
||||
<>
|
||||
<ActivityIndicator />
|
||||
</>
|
||||
);
|
||||
} else if (this.state.isRBFCancelPossible === buttonStatus.possible) {
|
||||
return (
|
||||
<>
|
||||
<TouchableOpacity style={styles.cancel}>
|
||||
<Text
|
||||
onPress={() =>
|
||||
this.props.navigation.navigate('RBFCancel', {
|
||||
txid: this.state.tx.hash,
|
||||
wallet: this.state.wallet,
|
||||
})
|
||||
}
|
||||
style={styles.cancelText}
|
||||
>
|
||||
{loc.transactions.status_cancel}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</>
|
||||
);
|
||||
}
|
||||
})()}
|
||||
|
||||
<TouchableOpacity
|
||||
style={styles.details}
|
||||
onPress={() => this.props.navigation.navigate('TransactionDetails', { hash: this.state.tx.hash })}
|
||||
>
|
||||
<Text style={styles.detailsText}>{loc.send.create_details.toLowerCase()}</Text>
|
||||
<Icon name="angle-right" size={18} type="font-awesome" color="#9aa0aa" />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</SafeBlueArea>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TransactionsStatus.propTypes = {
|
||||
navigation: PropTypes.shape({
|
||||
goBack: PropTypes.func,
|
||||
navigate: PropTypes.func,
|
||||
state: PropTypes.shape({
|
||||
params: PropTypes.shape({
|
||||
hash: PropTypes.string,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
route: PropTypes.shape({
|
||||
params: PropTypes.object,
|
||||
}),
|
||||
};
|
||||
|
||||
TransactionsStatus.navigationOptions = () => ({
|
||||
...BlueNavigationStyle(),
|
||||
TransactionsStatus.navigationOptions = navigationStyle({
|
||||
title: '',
|
||||
headerStyle: {
|
||||
...BlueNavigationStyle().headerStyle,
|
||||
backgroundColor: BlueCurrentTheme.colors.customHeader,
|
||||
},
|
||||
});
|
||||
|
@ -21,10 +21,10 @@ import {
|
||||
VaultButton,
|
||||
BlueFormLabel,
|
||||
BlueButton,
|
||||
BlueNavigationStyle,
|
||||
BlueButtonLinkHook,
|
||||
BlueButtonLink,
|
||||
BlueSpacing20,
|
||||
} from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import { HDSegwitBech32Wallet, SegwitP2SHWallet, HDSegwitP2SHWallet, LightningCustodianWallet, AppStorage } from '../../class';
|
||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||
import { useTheme, useNavigation } from '@react-navigation/native';
|
||||
@ -189,7 +189,7 @@ const WalletsAdd = () => {
|
||||
A(A.ENUM.CREATED_WALLET);
|
||||
ReactNativeHapticFeedback.trigger('notificationSuccess', { ignoreAndroidSystemSettings: false });
|
||||
navigate('PleaseBackupLNDHub', {
|
||||
wallet,
|
||||
walletID: wallet.getID(),
|
||||
});
|
||||
};
|
||||
|
||||
@ -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}
|
||||
@ -334,8 +334,8 @@ const WalletsAdd = () => {
|
||||
);
|
||||
};
|
||||
|
||||
WalletsAdd.navigationOptions = ({ navigation }) => ({
|
||||
...BlueNavigationStyle(navigation, true),
|
||||
WalletsAdd.navigationOptions = navigationStyle({
|
||||
closeButton: true,
|
||||
headerTitle: loc.wallets.add_title,
|
||||
headerLeft: null,
|
||||
});
|
||||
|
@ -5,7 +5,8 @@ import { Icon } from 'react-native-elements';
|
||||
import { useNavigation, useTheme } from '@react-navigation/native';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
|
||||
import { BlueButton, BlueListItem, BlueNavigationStyle, BlueSpacing20 } from '../../BlueComponents';
|
||||
import { BlueButton, BlueListItem, BlueSpacing20 } from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import BottomModal from '../../components/BottomModal';
|
||||
import { MultisigHDWallet } from '../../class';
|
||||
import loc from '../../loc';
|
||||
@ -344,8 +345,7 @@ WalletsAddMultisig.getCurrentFormatReadable = f => {
|
||||
}
|
||||
};
|
||||
|
||||
WalletsAddMultisig.navigationOptions = () => ({
|
||||
...BlueNavigationStyle(),
|
||||
WalletsAddMultisig.navigationOptions = navigationStyle({
|
||||
headerTitle: null,
|
||||
});
|
||||
|
||||
|
@ -13,35 +13,36 @@ import {
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import { useNavigation, useRoute, useTheme } from '@react-navigation/native';
|
||||
import { getSystemName } from 'react-native-device-info';
|
||||
import ImagePicker from 'react-native-image-picker';
|
||||
import QRCode from 'react-native-qrcode-svg';
|
||||
import Clipboard from '@react-native-community/clipboard';
|
||||
import showPopupMenu from 'react-native-popup-menu-android';
|
||||
import ToolTip from 'react-native-tooltip';
|
||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||
|
||||
import {
|
||||
BlueButton,
|
||||
BlueButtonLinkHook,
|
||||
BlueButtonLink,
|
||||
BlueFormMultiInput,
|
||||
BlueLoadingHook,
|
||||
BlueNavigationStyle,
|
||||
BlueLoading,
|
||||
BlueSpacing10,
|
||||
BlueSpacing20,
|
||||
BlueSpacing40,
|
||||
BlueTextCentered,
|
||||
} from '../../BlueComponents';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import { HDSegwitBech32Wallet, MultisigCosigner, MultisigHDWallet } from '../../class';
|
||||
import { useNavigation, useRoute, useTheme } from '@react-navigation/native';
|
||||
import loc from '../../loc';
|
||||
import { getSystemName } from 'react-native-device-info';
|
||||
import ImagePicker from 'react-native-image-picker';
|
||||
import ScanQRCode from '../send/ScanQRCode';
|
||||
import QRCode from 'react-native-qrcode-svg';
|
||||
import { SquareButton } from '../../components/SquareButton';
|
||||
import BottomModal from '../../components/BottomModal';
|
||||
import MultipleStepsListItem, {
|
||||
MultipleStepsListItemButtohType,
|
||||
MultipleStepsListItemDashType,
|
||||
} from '../../components/MultipleStepsListItem';
|
||||
import Clipboard from '@react-native-community/clipboard';
|
||||
import showPopupMenu from 'react-native-popup-menu-android';
|
||||
import ToolTip from 'react-native-tooltip';
|
||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
|
||||
const prompt = require('../../blue_modules/prompt');
|
||||
@ -290,6 +291,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 +306,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 +583,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 +631,7 @@ const WalletsAddMultisigStep2 = () => {
|
||||
);
|
||||
};
|
||||
const footer = isLoading ? (
|
||||
<BlueLoadingHook />
|
||||
<BlueLoading />
|
||||
) : (
|
||||
<View style={styles.buttonBottom}>
|
||||
<BlueButton title={loc.multisig.create} onPress={onCreate} disabled={!isOnCreateButtonEnabled} />
|
||||
@ -754,8 +765,7 @@ const styles = StyleSheet.create({
|
||||
qrCodeContainer: { borderWidth: 6, borderRadius: 8, borderColor: '#FFFFFF' },
|
||||
});
|
||||
|
||||
WalletsAddMultisigStep2.navigationOptions = () => ({
|
||||
...BlueNavigationStyle(),
|
||||
WalletsAddMultisigStep2.navigationOptions = navigationStyle({
|
||||
headerTitle: null,
|
||||
});
|
||||
|
||||
|
@ -1,13 +1,16 @@
|
||||
import React, { Component } from 'react';
|
||||
import { StyleSheet, StatusBar, Linking, Platform } from 'react-native';
|
||||
import { BlueNavigationStyle, BlueLoading, SafeBlueArea } from '../../BlueComponents';
|
||||
import PropTypes from 'prop-types';
|
||||
import { StyleSheet, StatusBar, Linking, Platform } from 'react-native';
|
||||
import { WebView } from 'react-native-webview';
|
||||
import { LightningCustodianWallet, WatchOnlyWallet } from '../../class';
|
||||
import InAppBrowser from 'react-native-inappbrowser-reborn';
|
||||
|
||||
import { BlueLoading, SafeBlueArea } from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import { LightningCustodianWallet, WatchOnlyWallet } from '../../class';
|
||||
import * as NavigationService from '../../NavigationService';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
const currency = require('../../blue_modules/currency');
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
flex: 1,
|
||||
@ -105,8 +108,8 @@ BuyBitcoin.propTypes = {
|
||||
}),
|
||||
};
|
||||
|
||||
BuyBitcoin.navigationOptions = ({ navigation }) => ({
|
||||
...BlueNavigationStyle(navigation, true),
|
||||
BuyBitcoin.navigationOptions = navigationStyle({
|
||||
closeButton: true,
|
||||
title: '',
|
||||
headerLeft: null,
|
||||
});
|
||||
|
@ -16,7 +16,8 @@ import {
|
||||
StatusBar,
|
||||
PermissionsAndroid,
|
||||
} from 'react-native';
|
||||
import { SecondButton, SafeBlueArea, BlueCard, BlueSpacing20, BlueNavigationStyle, BlueText, BlueLoadingHook } from '../../BlueComponents';
|
||||
import { SecondButton, SafeBlueArea, BlueCard, BlueSpacing20, BlueText, BlueLoading } from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
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 +369,7 @@ const WalletDetails = () => {
|
||||
|
||||
return isLoading ? (
|
||||
<View style={styles.root}>
|
||||
<BlueLoadingHook />
|
||||
<BlueLoading />
|
||||
</View>
|
||||
) : (
|
||||
<SafeBlueArea style={styles.root}>
|
||||
@ -525,8 +526,7 @@ const WalletDetails = () => {
|
||||
);
|
||||
};
|
||||
|
||||
WalletDetails.navigationOptions = () => ({
|
||||
...BlueNavigationStyle(),
|
||||
WalletDetails.navigationOptions = navigationStyle({
|
||||
headerTitle: loc.wallets.details_title,
|
||||
});
|
||||
|
||||
|
@ -1,18 +1,16 @@
|
||||
import React, { useContext, useEffect, useRef, useState } from 'react';
|
||||
import { StatusBar, View, TouchableOpacity, StyleSheet, Alert, useWindowDimensions } from 'react-native';
|
||||
import { StatusBar, View, StyleSheet, Alert, useWindowDimensions } from 'react-native';
|
||||
import { DrawerContentScrollView } from '@react-navigation/drawer';
|
||||
import { BlueNavigationStyle, BlueHeaderDefaultMain } from '../../BlueComponents';
|
||||
import WalletsCarousel from '../../components/WalletsCarousel';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||
import PropTypes from 'prop-types';
|
||||
import { PlaceholderWallet } from '../../class';
|
||||
import WalletImport from '../../class/wallet-import';
|
||||
import * as NavigationService from '../../NavigationService';
|
||||
import loc from '../../loc';
|
||||
import { BlueCurrentTheme } from '../../components/themes';
|
||||
import { useTheme } from '@react-navigation/native';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
|
||||
import { BlueHeaderDefaultMain } from '../../BlueComponents';
|
||||
import WalletsCarousel from '../../components/WalletsCarousel';
|
||||
import { PlaceholderWallet } from '../../class';
|
||||
import WalletImport from '../../class/wallet-import';
|
||||
import loc from '../../loc';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
|
||||
const DrawerList = props => {
|
||||
@ -131,6 +129,7 @@ const DrawerList = props => {
|
||||
};
|
||||
|
||||
export default DrawerList;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
contentContainerCustomStyle: {
|
||||
paddingRight: 10,
|
||||
@ -157,22 +156,3 @@ DrawerList.propTypes = {
|
||||
params: PropTypes.object,
|
||||
}),
|
||||
};
|
||||
|
||||
DrawerList.navigationOptions = ({ navigation }) => {
|
||||
return {
|
||||
...BlueNavigationStyle(navigation, true),
|
||||
title: '',
|
||||
headerStyle: {
|
||||
backgroundColor: BlueCurrentTheme.colors.customHeader,
|
||||
borderBottomWidth: 0,
|
||||
elevation: 0,
|
||||
shadowOpacity: 0,
|
||||
shadowOffset: { height: 0, width: 0 },
|
||||
},
|
||||
headerRight: () => (
|
||||
<TouchableOpacity testID="SettingsButton" style={styles.headerTouch} onPress={() => NavigationService.navigate('Settings')}>
|
||||
<Icon size={22} name="kebab-horizontal" type="octicon" color={BlueCurrentTheme.colors.foregroundColor} />
|
||||
</TouchableOpacity>
|
||||
),
|
||||
};
|
||||
};
|
||||
|
@ -1,12 +1,14 @@
|
||||
import React, { useState, useCallback, useContext } from 'react';
|
||||
import React, { useState, useCallback, useContext, useRef } from 'react';
|
||||
import { useWindowDimensions, InteractionManager, ScrollView, ActivityIndicator, StatusBar, View, StyleSheet } from 'react-native';
|
||||
import QRCode from 'react-native-qrcode-svg';
|
||||
import { BlueSpacing20, SafeBlueArea, BlueNavigationStyle, BlueText, BlueCopyTextToClipboard, BlueCard } from '../../BlueComponents';
|
||||
import { useTheme, useNavigation, useFocusEffect, useRoute } from '@react-navigation/native';
|
||||
|
||||
import { BlueSpacing20, SafeBlueArea, BlueText, BlueCopyTextToClipboard, BlueCard } from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import Privacy from '../../Privacy';
|
||||
import Biometric from '../../class/biometrics';
|
||||
import { LegacyWallet, LightningCustodianWallet, SegwitBech32Wallet, SegwitP2SHWallet, WatchOnlyWallet } from '../../class';
|
||||
import loc from '../../loc';
|
||||
import { useTheme, useNavigation, useFocusEffect, useRoute } from '@react-navigation/native';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
@ -38,7 +40,7 @@ const styles = StyleSheet.create({
|
||||
const WalletExport = () => {
|
||||
const { wallets, saveToDisk } = useContext(BlueStorageContext);
|
||||
const { walletID } = useRoute().params;
|
||||
const wallet = wallets.find(w => w.getID() === walletID);
|
||||
const wallet = useRef(wallets.find(w => w.getID() === walletID));
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const { goBack } = useNavigation();
|
||||
const { colors } = useTheme();
|
||||
@ -55,6 +57,7 @@ const WalletExport = () => {
|
||||
},
|
||||
type: { ...styles.type, color: colors.foregroundColor },
|
||||
secret: { ...styles.secret, color: colors.foregroundColor },
|
||||
warning: { ...styles.secret, color: colors.failedColor },
|
||||
};
|
||||
|
||||
useFocusEffect(
|
||||
@ -76,14 +79,14 @@ const WalletExport = () => {
|
||||
return () => {
|
||||
task.cancel();
|
||||
Privacy.disableBlur();
|
||||
wallet.setUserHasSavedExport(true);
|
||||
wallet.current.setUserHasSavedExport(true);
|
||||
saveToDisk();
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [goBack, wallet]),
|
||||
}, [goBack, walletID]),
|
||||
);
|
||||
|
||||
return isLoading ? (
|
||||
return isLoading && wallet ? (
|
||||
<View style={stylesHook.loading}>
|
||||
<ActivityIndicator />
|
||||
</View>
|
||||
@ -92,14 +95,14 @@ const WalletExport = () => {
|
||||
<StatusBar barStyle="light-content" />
|
||||
<ScrollView contentContainerStyle={styles.scrollViewContent}>
|
||||
<View>
|
||||
<BlueText style={stylesHook.type}>{wallet.typeReadable}</BlueText>
|
||||
<BlueText style={stylesHook.type}>{wallet.current.typeReadable}</BlueText>
|
||||
</View>
|
||||
|
||||
{(() => {
|
||||
if ([LegacyWallet.type, SegwitBech32Wallet.type, SegwitP2SHWallet.type].includes(wallet.type)) {
|
||||
if ([LegacyWallet.type, SegwitBech32Wallet.type, SegwitP2SHWallet.type].includes(wallet.current.type)) {
|
||||
return (
|
||||
<BlueCard>
|
||||
<BlueText>{wallet.getAddress()}</BlueText>
|
||||
<BlueText>{wallet.current.getAddress()}</BlueText>
|
||||
</BlueCard>
|
||||
);
|
||||
}
|
||||
@ -107,7 +110,7 @@ const WalletExport = () => {
|
||||
<BlueSpacing20 />
|
||||
<View style={styles.activeQrcode}>
|
||||
<QRCode
|
||||
value={wallet.getSecret()}
|
||||
value={wallet.current.getSecret()}
|
||||
logo={require('../../img/qr-code.png')}
|
||||
size={height > width ? width - 40 : width / 2}
|
||||
logoSize={70}
|
||||
@ -117,19 +120,20 @@ const WalletExport = () => {
|
||||
ecl="H"
|
||||
/>
|
||||
</View>
|
||||
{wallet.type !== WatchOnlyWallet.type && <BlueText style={stylesHook.warning}>{loc.wallets.warning_do_not_disclose}</BlueText>}
|
||||
<BlueSpacing20 />
|
||||
{wallet.type === LightningCustodianWallet.type || wallet.type === WatchOnlyWallet.type ? (
|
||||
<BlueCopyTextToClipboard text={wallet.getSecret()} />
|
||||
{wallet.current.type === LightningCustodianWallet.type || wallet.current.type === WatchOnlyWallet.type ? (
|
||||
<BlueCopyTextToClipboard text={wallet.current.getSecret()} />
|
||||
) : (
|
||||
<BlueText style={stylesHook.secret}>{wallet.getSecret()}</BlueText>
|
||||
<BlueText style={stylesHook.secret}>{wallet.current.getSecret()}</BlueText>
|
||||
)}
|
||||
</ScrollView>
|
||||
</SafeBlueArea>
|
||||
);
|
||||
};
|
||||
|
||||
WalletExport.navigationOptions = ({ navigation }) => ({
|
||||
...BlueNavigationStyle(navigation, true),
|
||||
WalletExport.navigationOptions = navigationStyle({
|
||||
closeButton: true,
|
||||
title: loc.wallets.export_title,
|
||||
headerLeft: null,
|
||||
});
|
||||
|
@ -1,11 +1,13 @@
|
||||
import React, { useCallback, useContext, useState } from 'react';
|
||||
import { ActivityIndicator, InteractionManager, ScrollView, StatusBar, StyleSheet, View } from 'react-native';
|
||||
import { BlueNavigationStyle, BlueSpacing20, BlueText, SafeBlueArea } from '../../BlueComponents';
|
||||
import { useFocusEffect, useNavigation, useRoute, useTheme } from '@react-navigation/native';
|
||||
|
||||
import { BlueSpacing20, BlueText, SafeBlueArea } from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import { DynamicQRCode } from '../../components/DynamicQRCode';
|
||||
import Privacy from '../../Privacy';
|
||||
import Biometric from '../../class/biometrics';
|
||||
import loc from '../../loc';
|
||||
import { useFocusEffect, useNavigation, useRoute, useTheme } from '@react-navigation/native';
|
||||
import { SquareButton } from '../../components/SquareButton';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
const fs = require('../../blue_modules/fs');
|
||||
@ -118,8 +120,8 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
});
|
||||
|
||||
ExportMultisigCoordinationSetup.navigationOptions = ({ navigation }) => ({
|
||||
...BlueNavigationStyle(navigation, true),
|
||||
ExportMultisigCoordinationSetup.navigationOptions = navigationStyle({
|
||||
closeButton: true,
|
||||
title: loc.multisig.export_coordination_setup,
|
||||
headerLeft: null,
|
||||
});
|
||||
|
@ -22,7 +22,8 @@ import {
|
||||
} from 'react-native';
|
||||
import Geolocation from '@react-native-community/geolocation';
|
||||
|
||||
import { BlueButtonLink, BlueNavigationStyle, SafeBlueArea } from '../../BlueComponents';
|
||||
import { BlueButtonLink, SafeBlueArea } from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import { HodlHodlApi } from '../../class/hodl-hodl-api';
|
||||
import * as NavigationService from '../../NavigationService';
|
||||
import { BlueCurrentTheme } from '../../components/themes';
|
||||
@ -881,21 +882,25 @@ HodlHodl.propTypes = {
|
||||
}),
|
||||
};
|
||||
|
||||
HodlHodl.navigationOptions = ({ navigation, route }) => ({
|
||||
...BlueNavigationStyle(navigation, true),
|
||||
title: '',
|
||||
headerStyle: {
|
||||
...BlueNavigationStyle(navigation, true).headerStyle,
|
||||
backgroundColor: BlueCurrentTheme.colors.customHeader,
|
||||
HodlHodl.navigationOptions = navigationStyle(
|
||||
{
|
||||
title: '',
|
||||
},
|
||||
headerRight: () => {
|
||||
return route.params.displayLoginButton ? (
|
||||
<BlueButtonLink title={loc.hodl.login} onPress={route.params.handleLoginPress} style={styles.marginHorizontal20} />
|
||||
) : (
|
||||
<BlueButtonLink title={loc.hodl.mycont} onPress={route.params.handleMyContractsPress} style={styles.marginHorizontal20} />
|
||||
);
|
||||
},
|
||||
});
|
||||
(options, { theme, navigation, route }) => ({
|
||||
...options,
|
||||
headerStyle: {
|
||||
...options.headerStyle,
|
||||
backgroundColor: theme.colors.customHeader,
|
||||
},
|
||||
headerRight: () => {
|
||||
return route.params.displayLoginButton ? (
|
||||
<BlueButtonLink title={loc.hodl.login} onPress={route.params.handleLoginPress} style={styles.marginHorizontal20} />
|
||||
) : (
|
||||
<BlueButtonLink title={loc.hodl.mycont} onPress={route.params.handleMyContractsPress} style={styles.marginHorizontal20} />
|
||||
);
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
grayDropdownText: {
|
||||
|
@ -1,7 +1,9 @@
|
||||
import React, { useRef } from 'react';
|
||||
import { WebView } from 'react-native-webview';
|
||||
import { BlueNavigationStyle, SafeBlueArea } from '../../BlueComponents';
|
||||
import { useRoute, useNavigation } from '@react-navigation/native';
|
||||
|
||||
import { SafeBlueArea } from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import loc from '../../loc';
|
||||
|
||||
const url = 'https://accounts.hodlhodl.com/accounts/request_access?attributes=api_key,api_signature_key';
|
||||
@ -51,8 +53,8 @@ const HodlHodlLogin = () => {
|
||||
);
|
||||
};
|
||||
|
||||
HodlHodlLogin.navigationOptions = ({ navigation }) => ({
|
||||
...BlueNavigationStyle(navigation, true),
|
||||
HodlHodlLogin.navigationOptions = navigationStyle({
|
||||
closeButton: true,
|
||||
title: loc.hodl.login,
|
||||
headerLeft: null,
|
||||
});
|
||||
|
@ -1,5 +1,6 @@
|
||||
/* global alert */
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
Alert,
|
||||
FlatList,
|
||||
@ -14,15 +15,8 @@ import {
|
||||
View,
|
||||
} from 'react-native';
|
||||
|
||||
import {
|
||||
BlueButton,
|
||||
BlueCopyTextToClipboard,
|
||||
BlueLoading,
|
||||
BlueNavigationStyle,
|
||||
BlueSpacing10,
|
||||
BlueSpacing20,
|
||||
BlueText,
|
||||
} from '../../BlueComponents';
|
||||
import { BlueButton, BlueCopyTextToClipboard, BlueLoading, BlueSpacing10, BlueSpacing20, BlueText } from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import { HodlHodlApi } from '../../class/hodl-hodl-api';
|
||||
import * as NavigationService from '../../NavigationService';
|
||||
import { BlueCurrentTheme } from '../../components/themes';
|
||||
@ -35,6 +29,7 @@ export default class HodlHodlMyContracts extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
props.navigation.setParams({ handleLogout: this.handleLogout });
|
||||
this.state = {
|
||||
contracts: [],
|
||||
isLoading: true,
|
||||
@ -45,6 +40,11 @@ export default class HodlHodlMyContracts extends Component {
|
||||
clearInterval(this.state.inverval);
|
||||
}
|
||||
|
||||
handleLogout = () => {
|
||||
this.context.setHodlHodlApiKey('', '<empty>');
|
||||
this.props.navigation.navigate('WalletsList');
|
||||
};
|
||||
|
||||
async componentDidMount() {
|
||||
const hodlApiKey = await this.context.getHodlHodlApiKey();
|
||||
const hodlApi = new HodlHodlApi(hodlApiKey);
|
||||
@ -424,35 +424,44 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
});
|
||||
|
||||
HodlHodlMyContracts.navigationOptions = ({ navigation }) => ({
|
||||
...BlueNavigationStyle(navigation, true),
|
||||
title: loc.hodl.cont_title,
|
||||
headerStyle: {
|
||||
backgroundColor: BlueCurrentTheme.colors.elevated,
|
||||
HodlHodlMyContracts.propTypes = {
|
||||
navigation: PropTypes.shape({
|
||||
navigate: PropTypes.func,
|
||||
setParams: PropTypes.func,
|
||||
}),
|
||||
};
|
||||
|
||||
HodlHodlMyContracts.navigationOptions = navigationStyle(
|
||||
{
|
||||
closeButton: true,
|
||||
title: loc.hodl.cont_title,
|
||||
},
|
||||
headerRight: () => (
|
||||
<TouchableOpacity
|
||||
style={styles.marginRight}
|
||||
onPress={() => {
|
||||
Alert.alert(
|
||||
loc.hodl.are_you_sure_you_want_to_logout,
|
||||
'',
|
||||
[
|
||||
{
|
||||
text: loc._.ok,
|
||||
onPress: () => {
|
||||
this.context.setHodlHodlApiKey('', '<empty>');
|
||||
navigation.navigate('WalletsList');
|
||||
(options, { theme, navigation, route }) => ({
|
||||
...options,
|
||||
headerStyle: {
|
||||
backgroundColor: theme.colors.elevated,
|
||||
},
|
||||
headerRight: () => (
|
||||
<TouchableOpacity
|
||||
style={styles.marginRight}
|
||||
onPress={() => {
|
||||
Alert.alert(
|
||||
loc.hodl.are_you_sure_you_want_to_logout,
|
||||
'',
|
||||
[
|
||||
{
|
||||
text: loc._.ok,
|
||||
onPress: route.params.handleLogout,
|
||||
style: 'default',
|
||||
},
|
||||
style: 'default',
|
||||
},
|
||||
{ text: loc._.cancel, onPress: () => {}, style: 'cancel' },
|
||||
],
|
||||
{ cancelable: false },
|
||||
);
|
||||
}}
|
||||
>
|
||||
<BlueText>logout</BlueText>
|
||||
</TouchableOpacity>
|
||||
),
|
||||
});
|
||||
{ text: loc._.cancel, onPress: () => {}, style: 'cancel' },
|
||||
],
|
||||
{ cancelable: false },
|
||||
);
|
||||
}}
|
||||
>
|
||||
<BlueText>{loc.hodl.logout}</BlueText>
|
||||
</TouchableOpacity>
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
@ -1,10 +1,12 @@
|
||||
/* global alert */
|
||||
import React, { Component } from 'react';
|
||||
import { Alert, FlatList, Image, KeyboardAvoidingView, Platform, ScrollView, StyleSheet, Text, View } from 'react-native';
|
||||
import { BlueButton, BlueLoading, BlueNavigationStyle, BlueSpacing10, SafeBlueArea } from '../../BlueComponents';
|
||||
import PropTypes from 'prop-types';
|
||||
import { HodlHodlApi } from '../../class/hodl-hodl-api';
|
||||
import { Icon } from 'react-native-elements';
|
||||
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import { BlueButton, BlueLoading, BlueSpacing10, SafeBlueArea } from '../../BlueComponents';
|
||||
import { HodlHodlApi } from '../../class/hodl-hodl-api';
|
||||
import * as NavigationService from '../../NavigationService';
|
||||
import { BlueCurrentTheme } from '../../components/themes';
|
||||
import loc from '../../loc';
|
||||
@ -380,11 +382,15 @@ const styles = StyleSheet.create({
|
||||
acceptOfferButtonWrapperWrapper: { marginTop: 24, alignItems: 'center' },
|
||||
});
|
||||
|
||||
HodlHodlViewOffer.navigationOptions = () => ({
|
||||
...BlueNavigationStyle(),
|
||||
title: '',
|
||||
headerStyle: {
|
||||
...BlueNavigationStyle().headerStyle,
|
||||
backgroundColor: BlueCurrentTheme.colors.customHeader,
|
||||
HodlHodlViewOffer.navigationOptions = navigationStyle(
|
||||
{
|
||||
title: '',
|
||||
},
|
||||
});
|
||||
(options, { theme }) => ({
|
||||
...options,
|
||||
headerStyle: {
|
||||
...options.headerStyle,
|
||||
backgroundColor: theme.colors.customHeader,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
@ -1,8 +1,10 @@
|
||||
import React, { Component } from 'react';
|
||||
import { WebView } from 'react-native-webview';
|
||||
import { BlueNavigationStyle, SafeBlueArea } from '../../BlueComponents';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { SafeBlueArea } from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
|
||||
export default class HodlHodlWebview extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@ -31,8 +33,8 @@ HodlHodlWebview.propTypes = {
|
||||
}),
|
||||
};
|
||||
|
||||
HodlHodlWebview.navigationOptions = ({ navigation }) => ({
|
||||
...BlueNavigationStyle(navigation, true),
|
||||
HodlHodlWebview.navigationOptions = navigationStyle({
|
||||
closeButton: true,
|
||||
title: '',
|
||||
headerLeft: null,
|
||||
});
|
||||
|
@ -1,6 +1,14 @@
|
||||
/* global alert */
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Platform, View, Keyboard, StatusBar, StyleSheet } from 'react-native';
|
||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||
import { useNavigation, useRoute, useTheme } from '@react-navigation/native';
|
||||
import Clipboard from '@react-native-community/clipboard';
|
||||
import ImagePicker from 'react-native-image-picker';
|
||||
import { getSystemName } from 'react-native-device-info';
|
||||
import RNFS from 'react-native-fs';
|
||||
import DocumentPicker from 'react-native-document-picker';
|
||||
|
||||
import {
|
||||
BlueFormMultiInput,
|
||||
BlueButtonLink,
|
||||
@ -9,19 +17,12 @@ import {
|
||||
BlueButton,
|
||||
SafeBlueArea,
|
||||
BlueSpacing20,
|
||||
BlueNavigationStyle,
|
||||
} from '../../BlueComponents';
|
||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import Privacy from '../../Privacy';
|
||||
import { useNavigation, useRoute, useTheme } from '@react-navigation/native';
|
||||
import WalletImport from '../../class/wallet-import';
|
||||
import Clipboard from '@react-native-community/clipboard';
|
||||
import ActionSheet from '../ActionSheet';
|
||||
import ImagePicker from 'react-native-image-picker';
|
||||
import loc from '../../loc';
|
||||
import { getSystemName } from 'react-native-device-info';
|
||||
import RNFS from 'react-native-fs';
|
||||
import DocumentPicker from 'react-native-document-picker';
|
||||
import { presentCameraNotAuthorizedAlert } from '../../class/camera';
|
||||
const LocalQRCode = require('@remobile/react-native-qrcode-local-image');
|
||||
const isDesktop = getSystemName() === 'Mac OS X';
|
||||
@ -267,8 +268,7 @@ const WalletsImport = () => {
|
||||
);
|
||||
};
|
||||
|
||||
WalletsImport.navigationOptions = () => ({
|
||||
...BlueNavigationStyle(),
|
||||
WalletsImport.navigationOptions = navigationStyle({
|
||||
title: loc.wallets.import_title,
|
||||
});
|
||||
export default WalletsImport;
|
||||
|
@ -208,7 +208,7 @@ const WalletsList = () => {
|
||||
{`${loc.transactions.list_title}${' '}`}
|
||||
</Text>
|
||||
{isCatalyst && (
|
||||
<TouchableOpacity style={style} onPress={refreshTransactions} disabled={isLoading}>
|
||||
<TouchableOpacity style={style} onPress={() => refreshTransactions(true)} disabled={isLoading}>
|
||||
<Icon name="refresh" type="font-awesome" color={colors.feeText} />
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
|
@ -1,8 +1,10 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { BackHandler } from 'react-native';
|
||||
import { WebView } from 'react-native-webview';
|
||||
import { BlueLoading, BlueNavigationStyle } from '../../BlueComponents';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { BlueLoading } from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
|
||||
export default class Marketplace extends Component {
|
||||
webview = React.createRef();
|
||||
@ -75,8 +77,8 @@ Marketplace.propTypes = {
|
||||
}),
|
||||
};
|
||||
|
||||
Marketplace.navigationOptions = ({ navigation }) => ({
|
||||
...BlueNavigationStyle(navigation, true),
|
||||
Marketplace.navigationOptions = navigationStyle({
|
||||
closeButton: true,
|
||||
title: 'Marketplace',
|
||||
headerLeft: null,
|
||||
});
|
||||
|
@ -1,7 +1,9 @@
|
||||
import React, { useEffect, useState, useCallback, useContext } from 'react';
|
||||
import { ActivityIndicator, View, BackHandler, Text, ScrollView, StyleSheet, StatusBar } from 'react-native';
|
||||
import { useNavigation, useRoute, useTheme } from '@react-navigation/native';
|
||||
import { BlueSpacing20, SafeBlueArea, BlueNavigationStyle, BlueText, BlueButton } from '../../BlueComponents';
|
||||
|
||||
import { BlueSpacing20, SafeBlueArea, BlueText, BlueButton } from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import Privacy from '../../Privacy';
|
||||
import loc from '../../loc';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
@ -85,14 +87,15 @@ const PleaseBackup = () => {
|
||||
);
|
||||
};
|
||||
|
||||
PleaseBackup.navigationOptions = ({ navigation }) => ({
|
||||
...BlueNavigationStyle(navigation, true),
|
||||
PleaseBackup.navigationOptions = navigationStyle({
|
||||
closeButton: true,
|
||||
title: loc.pleasebackup.title,
|
||||
headerLeft: null,
|
||||
headerRight: null,
|
||||
gestureEnabled: false,
|
||||
swipeEnabled: false,
|
||||
});
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
flex: {
|
||||
flex: 1,
|
||||
|
@ -1,21 +1,19 @@
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import React, { useCallback, useContext, useEffect } from 'react';
|
||||
import { useNavigation, useRoute, useTheme } from '@react-navigation/native';
|
||||
import { View, useWindowDimensions, StyleSheet, BackHandler, StatusBar } from 'react-native';
|
||||
import {
|
||||
SafeBlueArea,
|
||||
BlueNavigationStyle,
|
||||
BlueSpacing20,
|
||||
BlueCopyTextToClipboard,
|
||||
BlueButton,
|
||||
BlueTextCentered,
|
||||
} from '../../BlueComponents';
|
||||
import QRCode from 'react-native-qrcode-svg';
|
||||
import Privacy from '../../Privacy';
|
||||
import { ScrollView } from 'react-native-gesture-handler';
|
||||
|
||||
import { BlueButton, BlueCopyTextToClipboard, BlueSpacing20, BlueTextCentered, SafeBlueArea } from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import Privacy from '../../Privacy';
|
||||
import loc from '../../loc';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
|
||||
const PleaseBackupLNDHub = () => {
|
||||
const { wallet } = useRoute().params;
|
||||
const { wallets } = useContext(BlueStorageContext);
|
||||
const { walletID } = useRoute().params;
|
||||
const wallet = wallets.find(w => w.getID() === walletID);
|
||||
const navigation = useNavigation();
|
||||
const { colors } = useTheme();
|
||||
const { height, width } = useWindowDimensions();
|
||||
@ -77,8 +75,8 @@ const PleaseBackupLNDHub = () => {
|
||||
);
|
||||
};
|
||||
|
||||
PleaseBackupLNDHub.navigationOptions = ({ navigation }) => ({
|
||||
...BlueNavigationStyle(navigation, true),
|
||||
PleaseBackupLNDHub.navigationOptions = navigationStyle({
|
||||
closeButton: true,
|
||||
title: loc.pleasebackup.title,
|
||||
headerLeft: null,
|
||||
headerRight: null,
|
||||
|
@ -3,11 +3,12 @@ import PropTypes from 'prop-types';
|
||||
import BN from 'bignumber.js';
|
||||
import { Dimensions, PixelRatio, View, ScrollView, Text, Image, TouchableOpacity, StyleSheet, useWindowDimensions } from 'react-native';
|
||||
import { Icon } from 'react-native-elements';
|
||||
|
||||
import { useNavigation, useRoute, useTheme } from '@react-navigation/native';
|
||||
|
||||
import { BlueCurrentTheme } from '../../components/themes';
|
||||
import { FContainer, FButton } from '../../components/FloatButtons';
|
||||
import { BlueSpacing20, SafeBlueArea, BlueNavigationStyle, BlueTabs } from '../../BlueComponents';
|
||||
import { BlueSpacing20, SafeBlueArea, BlueTabs } from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import loc from '../../loc';
|
||||
|
||||
const ENTROPY_LIMIT = 256;
|
||||
@ -270,15 +271,7 @@ const Entropy = () => {
|
||||
);
|
||||
};
|
||||
|
||||
Entropy.propTypes = {
|
||||
navigation: PropTypes.shape({
|
||||
navigate: PropTypes.func,
|
||||
goBack: PropTypes.func,
|
||||
}),
|
||||
};
|
||||
|
||||
Entropy.navigationOptions = () => ({
|
||||
...BlueNavigationStyle(),
|
||||
Entropy.navigationOptions = navigationStyle({
|
||||
title: loc.entropy.title,
|
||||
});
|
||||
|
||||
|
@ -1,13 +1,14 @@
|
||||
import React, { useEffect, useState, useRef, useContext } from 'react';
|
||||
import { View, ActivityIndicator, Image, Text, StyleSheet, StatusBar, ScrollView } from 'react-native';
|
||||
import { BlueNavigationStyle } from '../../BlueComponents';
|
||||
import SortableList from 'react-native-sortable-list';
|
||||
import LinearGradient from 'react-native-linear-gradient';
|
||||
import { PlaceholderWallet, LightningCustodianWallet, MultisigHDWallet } from '../../class';
|
||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||
import { useNavigation, useTheme } from '@react-navigation/native';
|
||||
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import { PlaceholderWallet, LightningCustodianWallet, MultisigHDWallet } from '../../class';
|
||||
import WalletGradient from '../../class/wallet-gradient';
|
||||
import loc, { formatBalance, transactionTimeToReadable } from '../../loc';
|
||||
import { useNavigation, useTheme } from '@react-navigation/native';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
@ -187,13 +188,14 @@ const ReorderWallets = () => {
|
||||
);
|
||||
};
|
||||
|
||||
ReorderWallets.navigationOptions = ({ navigation, route }) => ({
|
||||
...BlueNavigationStyle(
|
||||
navigation,
|
||||
true,
|
||||
route.params && route.params.customCloseButtonFunction ? route.params.customCloseButtonFunction : undefined,
|
||||
),
|
||||
headerTitle: loc.wallets.reorder_title,
|
||||
ReorderWallets.navigationOptions = navigationStyle({
|
||||
title: loc.wallets.reorder_title,
|
||||
closeButton: true,
|
||||
closeButtonFunc: ({ navigation, route }) => {
|
||||
if (route.params && route.params.customCloseButtonFunction) {
|
||||
route.params.customCloseButtonFunction();
|
||||
}
|
||||
},
|
||||
headerLeft: null,
|
||||
});
|
||||
|
||||
|
@ -1,12 +1,14 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { View, ActivityIndicator, Image, Text, TouchableOpacity, FlatList, StyleSheet, StatusBar } from 'react-native';
|
||||
import { SafeBlueArea, BlueText, BlueSpacing20, BluePrivateBalance, BlueNavigationStyle } from '../../BlueComponents';
|
||||
import LinearGradient from 'react-native-linear-gradient';
|
||||
import { LightningCustodianWallet } from '../../class/wallets/lightning-custodian-wallet';
|
||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||
import WalletGradient from '../../class/wallet-gradient';
|
||||
import { useRoute, useTheme } from '@react-navigation/native';
|
||||
|
||||
import { SafeBlueArea, BlueText, BlueSpacing20, BluePrivateBalance } from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import { LightningCustodianWallet } from '../../class/wallets/lightning-custodian-wallet';
|
||||
import WalletGradient from '../../class/wallet-gradient';
|
||||
import loc, { formatBalance, transactionTimeToReadable } from '../../loc';
|
||||
import { MultisigHDWallet } from '../../class';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
@ -170,11 +172,8 @@ const SelectWallet = ({ navigation }) => {
|
||||
}
|
||||
};
|
||||
|
||||
SelectWallet.navigationOptions = ({ navigation }) => ({
|
||||
...BlueNavigationStyle(navigation, true),
|
||||
headerRight: null,
|
||||
headerTitle: loc.wallets.select_wallet,
|
||||
headerBackTitleVisible: false,
|
||||
SelectWallet.navigationOptions = navigationStyle({
|
||||
title: loc.wallets.select_wallet,
|
||||
});
|
||||
|
||||
export default SelectWallet;
|
||||
|
@ -23,15 +23,17 @@ import Clipboard from '@react-native-community/clipboard';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import Handoff from 'react-native-handoff';
|
||||
import { useRoute, useNavigation, useTheme, useFocusEffect } from '@react-navigation/native';
|
||||
import isCatalyst from 'react-native-is-catalyst';
|
||||
|
||||
import { Chain } from '../../models/bitcoinUnits';
|
||||
import { BlueTransactionListItem, BlueWalletNavigationHeader, BlueAlertWalletExportReminder, BlueListItem } from '../../BlueComponents';
|
||||
import WalletGradient from '../../class/wallet-gradient';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import { LightningCustodianWallet, WatchOnlyWallet } from '../../class';
|
||||
import HandoffSettings from '../../class/handoff';
|
||||
import ActionSheet from '../ActionSheet';
|
||||
import loc from '../../loc';
|
||||
import { FContainer, FButton } from '../../components/FloatButtons';
|
||||
import isCatalyst from 'react-native-is-catalyst';
|
||||
import BottomModal from '../../components/BottomModal';
|
||||
import BuyBitcoin from './buyBitcoin';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
@ -140,6 +142,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(() => {
|
||||
@ -651,7 +662,7 @@ const WalletTransactions = () => {
|
||||
text={loc.receive.header}
|
||||
onPress={() => {
|
||||
if (wallet.current.chain === Chain.OFFCHAIN) {
|
||||
navigate('LNDCreateInvoiceRoot', { screen: 'LNDCreateInvoice', params: { fromWallet: wallet.current } });
|
||||
navigate('LNDCreateInvoiceRoot', { screen: 'LNDCreateInvoice', params: { walletID: wallet.current.getID() } });
|
||||
} else {
|
||||
navigate('ReceiveDetailsRoot', { screen: 'ReceiveDetails', params: { walletID: wallet.current.getID() } });
|
||||
}
|
||||
@ -684,7 +695,7 @@ const WalletTransactions = () => {
|
||||
|
||||
export default WalletTransactions;
|
||||
|
||||
WalletTransactions.navigationOptions = ({ navigation, route }) => {
|
||||
WalletTransactions.navigationOptions = navigationStyle({}, (options, { theme, navigation, route }) => {
|
||||
return {
|
||||
headerRight: () => (
|
||||
<TouchableOpacity
|
||||
@ -699,7 +710,7 @@ WalletTransactions.navigationOptions = ({ navigation, route }) => {
|
||||
<Icon name="kebab-horizontal" type="octicon" size={22} color="#FFFFFF" />
|
||||
</TouchableOpacity>
|
||||
),
|
||||
headerTitle: '',
|
||||
title: '',
|
||||
headerStyle: {
|
||||
backgroundColor: WalletGradient.headerColorFor(route.params.walletType),
|
||||
borderBottomWidth: 0,
|
||||
@ -710,7 +721,7 @@ WalletTransactions.navigationOptions = ({ navigation, route }) => {
|
||||
headerTintColor: '#FFFFFF',
|
||||
headerBackTitleVisible: false,
|
||||
};
|
||||
};
|
||||
});
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
flex: {
|
||||
|
@ -22,15 +22,15 @@ import ImagePicker from 'react-native-image-picker';
|
||||
|
||||
import {
|
||||
BlueButton,
|
||||
BlueButtonLinkHook,
|
||||
BlueButtonLink,
|
||||
BlueFormMultiInput,
|
||||
BlueLoadingHook,
|
||||
BlueNavigationStyle,
|
||||
BlueLoading,
|
||||
BlueSpacing10,
|
||||
BlueSpacing20,
|
||||
BlueSpacing40,
|
||||
BlueTextCentered,
|
||||
} from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import SquareEnumeratedWords, { SquareEnumeratedWordsContentAlign } from '../../components/SquareEnumeratedWords';
|
||||
import BottomModal from '../../components/BottomModal';
|
||||
import { HDSegwitBech32Wallet, MultisigHDWallet } from '../../class';
|
||||
@ -168,7 +168,7 @@ const ViewEditMultisigCosigners = () => {
|
||||
</View>
|
||||
<View style={styles.vaultKeyTextWrapper}>
|
||||
<Text style={[styles.vaultKeyText, stylesHook.vaultKeyText]}>
|
||||
{loc.formatString(loc.multisig.vault_key, { number: vaultKeyData.keyIndex + 1 })}
|
||||
{loc.formatString(loc.multisig.vault_key, { number: vaultKeyData.keyIndex })}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
@ -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>
|
||||
);
|
||||
|
||||
@ -574,8 +574,8 @@ const styles = StyleSheet.create({
|
||||
squareButtonWrapper: { height: 50, width: 250 },
|
||||
});
|
||||
|
||||
ViewEditMultisigCosigners.navigationOptions = ({ navigation }) => ({
|
||||
...BlueNavigationStyle(navigation, true),
|
||||
ViewEditMultisigCosigners.navigationOptions = navigationStyle({
|
||||
closeButton: true,
|
||||
title: loc.multisig.view_edit_cosigners_title,
|
||||
headerLeft: null,
|
||||
});
|
||||
|
@ -1,11 +1,13 @@
|
||||
import React, { useCallback, useContext, useState } from 'react';
|
||||
import { InteractionManager, useWindowDimensions, ActivityIndicator, View, StatusBar, StyleSheet } from 'react-native';
|
||||
import QRCode from 'react-native-qrcode-svg';
|
||||
import { BlueSpacing20, SafeBlueArea, BlueText, BlueNavigationStyle, BlueCopyTextToClipboard } from '../../BlueComponents';
|
||||
import { useFocusEffect, useRoute, useNavigation, useTheme } from '@react-navigation/native';
|
||||
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import { BlueSpacing20, SafeBlueArea, BlueText, BlueCopyTextToClipboard } from '../../BlueComponents';
|
||||
import Privacy from '../../Privacy';
|
||||
import Biometric from '../../class/biometrics';
|
||||
import loc from '../../loc';
|
||||
import { useFocusEffect, useRoute, useNavigation, useTheme } from '@react-navigation/native';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
@ -97,8 +99,8 @@ const WalletXpub = () => {
|
||||
);
|
||||
};
|
||||
|
||||
WalletXpub.navigationOptions = ({ navigation }) => ({
|
||||
...BlueNavigationStyle(navigation, true),
|
||||
WalletXpub.navigationOptions = navigationStyle({
|
||||
closeButton: true,
|
||||
title: loc.wallets.xpub_title,
|
||||
headerLeft: null,
|
||||
});
|
||||
|
@ -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…
Reference in New Issue
Block a user