REF: Components isolated from BlueComponents

This commit is contained in:
Marcos Rodriguez Vélez 2021-08-16 12:10:04 -04:00
parent 812867755d
commit 47e02aa84c
13 changed files with 750 additions and 681 deletions

View file

@ -1,5 +1,5 @@
/* eslint react/prop-types: "off", react-native/no-inline-styles: "off" */
import React, { Component, useState, useMemo, useCallback, useContext, useEffect, forwardRef } from 'react';
import React, { Component, useContext, forwardRef } from 'react';
import PropTypes from 'prop-types';
import { Icon, Input, Text, Header, ListItem, Avatar } from 'react-native-elements';
import {
@ -11,7 +11,6 @@ import {
InputAccessoryView,
Keyboard,
KeyboardAvoidingView,
Linking,
PixelRatio,
Platform,
PlatformColor,
@ -21,27 +20,18 @@ import {
TextInput,
TouchableOpacity,
View,
InteractionManager,
I18nManager,
} from 'react-native';
import Clipboard from '@react-native-clipboard/clipboard';
import LinearGradient from 'react-native-linear-gradient';
import { LightningCustodianWallet, MultisigHDWallet } from './class';
import { BitcoinUnit } from './models/bitcoinUnits';
import * as NavigationService from './NavigationService';
import WalletGradient from './class/wallet-gradient';
import { BlurView } from '@react-native-community/blur';
import NetworkTransactionFees, { NetworkTransactionFee, NetworkTransactionFeeType } from './models/networkTransactionFees';
import Biometric from './class/biometrics';
import { encodeUR } from './blue_modules/ur';
import QRCode from 'react-native-qrcode-svg';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { useNavigation, useTheme } from '@react-navigation/native';
import { useTheme } from '@react-navigation/native';
import { BlueCurrentTheme } from './components/themes';
import loc, { formatBalance, formatStringAddTwoWhiteSpaces, formatBalanceWithoutSuffix, transactionTimeToReadable } from './loc';
import Lnurl from './class/lnurl';
import loc, { formatStringAddTwoWhiteSpaces } from './loc';
import { BlueStorageContext } from './blue_modules/storage-context';
import ToolTipMenu from './components/TooltipMenu';
const { height, width } = Dimensions.get('window');
const aspectRatio = height / width;
@ -259,264 +249,6 @@ export const LightningButton = props => {
);
};
export class BlueWalletNavigationHeader extends Component {
static propTypes = {
wallet: PropTypes.shape().isRequired,
onWalletUnitChange: PropTypes.func,
};
static getDerivedStateFromProps(props) {
return { wallet: props.wallet, onWalletUnitChange: props.onWalletUnitChange };
}
static contextType = BlueStorageContext;
constructor(props) {
super(props);
this.state = {
wallet: props.wallet,
walletPreviousPreferredUnit: props.wallet.getPreferredBalanceUnit(),
allowOnchainAddress: false,
};
}
handleCopyPress = _item => {
Clipboard.setString(formatBalance(this.state.wallet.getBalance(), this.state.wallet.getPreferredBalanceUnit()).toString());
};
componentDidUpdate(prevState) {
InteractionManager.runAfterInteractions(() => {
if (prevState.wallet.getID() !== this.state.wallet.getID() && this.state.wallet.type === LightningCustodianWallet.type) {
this.verifyIfWalletAllowsOnchainAddress();
}
});
}
verifyIfWalletAllowsOnchainAddress = () => {
if (this.state.wallet.type === LightningCustodianWallet.type) {
this.state.wallet
.allowOnchainAddress()
.then(value => this.setState({ allowOnchainAddress: value }))
.catch(e => {
console.log('This Lndhub wallet does not have an onchain address API.');
this.setState({ allowOnchainAddress: false });
});
}
};
componentDidMount() {
this.verifyIfWalletAllowsOnchainAddress();
}
handleBalanceVisibility = async _item => {
const wallet = this.state.wallet;
const isBiometricsEnabled = await Biometric.isBiometricUseCapableAndEnabled();
if (isBiometricsEnabled && wallet.hideBalance) {
if (!(await Biometric.unlockWithBiometrics())) {
return this.props.navigation.goBack();
}
}
wallet.hideBalance = !wallet.hideBalance;
this.setState({ wallet });
await this.context.saveToDisk();
};
changeWalletBalanceUnit = () => {
let walletPreviousPreferredUnit = this.state.wallet.getPreferredBalanceUnit();
const wallet = this.state.wallet;
if (walletPreviousPreferredUnit === BitcoinUnit.BTC) {
wallet.preferredBalanceUnit = BitcoinUnit.SATS;
walletPreviousPreferredUnit = BitcoinUnit.BTC;
} else if (walletPreviousPreferredUnit === BitcoinUnit.SATS) {
wallet.preferredBalanceUnit = BitcoinUnit.LOCAL_CURRENCY;
walletPreviousPreferredUnit = BitcoinUnit.SATS;
} else if (walletPreviousPreferredUnit === BitcoinUnit.LOCAL_CURRENCY) {
wallet.preferredBalanceUnit = BitcoinUnit.BTC;
walletPreviousPreferredUnit = BitcoinUnit.BTC;
} else {
wallet.preferredBalanceUnit = BitcoinUnit.BTC;
walletPreviousPreferredUnit = BitcoinUnit.BTC;
}
this.setState({ wallet, walletPreviousPreferredUnit: walletPreviousPreferredUnit }, () => {
this.props.onWalletUnitChange(wallet);
});
};
manageFundsPressed = () => {
this.props.onManageFundsPressed();
};
onPress = id => {
if (id === 'walletBalanceVisibility') {
this.handleBalanceVisibility();
} else if (id === 'copyToClipboard') {
this.handleCopyPress();
}
};
render() {
const balance =
!this.state.wallet.hideBalance &&
formatBalance(this.state.wallet.getBalance(), this.state.wallet.getPreferredBalanceUnit(), true).toString();
return (
<LinearGradient
colors={WalletGradient.gradientsFor(this.state.wallet.type)}
style={{ padding: 15, minHeight: 140, justifyContent: 'center' }}
{...WalletGradient.linearGradientProps(this.state.wallet.type)}
>
<Image
source={(() => {
switch (this.state.wallet.type) {
case LightningCustodianWallet.type:
return I18nManager.isRTL ? require('./img/lnd-shape-rtl.png') : require('./img/lnd-shape.png');
case MultisigHDWallet.type:
return I18nManager.isRTL ? require('./img/vault-shape-rtl.png') : require('./img/vault-shape.png');
default:
return I18nManager.isRTL ? require('./img/btc-shape-rtl.png') : require('./img/btc-shape.png');
}
})()}
style={{
width: 99,
height: 94,
position: 'absolute',
bottom: 0,
right: 0,
}}
/>
<Text
testID="WalletLabel"
numberOfLines={1}
style={{
backgroundColor: 'transparent',
fontSize: 19,
color: '#fff',
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
}}
>
{this.state.wallet.getLabel()}
</Text>
<ToolTipMenu
title={loc.wallets.balance}
onPress={this.onPress}
actions={
this.state.wallet.hideBalance
? [
{
id: 'walletBalanceVisibility',
text: loc.transactions.details_balance_show,
icon: {
iconType: 'SYSTEM',
iconValue: 'eye',
},
},
]
: [
{
id: 'walletBalanceVisibility',
text: loc.transactions.details_balance_hide,
icon: {
iconType: 'SYSTEM',
iconValue: 'eye.slash',
},
},
{
id: 'copyToClipboard',
text: loc.transactions.details_copy,
icon: {
iconType: 'SYSTEM',
iconValue: 'arrow.right.doc.on.clipboard',
},
},
]
}
>
<TouchableOpacity accessibilityRole="button" style={styles.balance} onPress={this.changeWalletBalanceUnit}>
{this.state.wallet.hideBalance ? (
<BluePrivateBalance />
) : (
<Text
testID="WalletBalance"
key={balance} // force component recreation on balance change. To fix right-to-left languages, like Farsi
numberOfLines={1}
adjustsFontSizeToFit
style={{
backgroundColor: 'transparent',
fontWeight: 'bold',
fontSize: 36,
color: '#fff',
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
}}
>
{balance}
</Text>
)}
</TouchableOpacity>
</ToolTipMenu>
{this.state.wallet.type === LightningCustodianWallet.type && this.state.allowOnchainAddress && (
<TouchableOpacity accessibilityRole="button" onPress={this.manageFundsPressed}>
<View
style={{
marginTop: 14,
marginBottom: 10,
backgroundColor: 'rgba(255,255,255,0.2)',
borderRadius: 9,
minHeight: 39,
alignSelf: 'flex-start',
paddingHorizontal: 12,
height: 39,
justifyContent: 'center',
alignItems: 'center',
}}
>
<Text
style={{
fontWeight: '500',
fontSize: 14,
color: '#FFFFFF',
}}
>
{loc.lnd.title}
</Text>
</View>
</TouchableOpacity>
)}
{this.state.wallet.type === MultisigHDWallet.type && (
<TouchableOpacity accessibilityRole="button" onPress={this.manageFundsPressed}>
<View
style={{
marginTop: 14,
marginBottom: 10,
backgroundColor: 'rgba(255,255,255,0.2)',
borderRadius: 9,
minHeight: 39,
alignSelf: 'flex-start',
paddingHorizontal: 12,
height: 39,
justifyContent: 'center',
alignItems: 'center',
}}
>
<Text
style={{
fontWeight: '500',
fontSize: 14,
color: '#FFFFFF',
}}
>
{loc.multisig.manage_keys}
</Text>
</View>
</TouchableOpacity>
)}
</LinearGradient>
);
}
}
/**
* TODO: remove this comment once this file gets properly converted to typescript.
*
@ -1254,334 +986,6 @@ export const BlueReceiveButtonIcon = props => {
);
};
export const BlueTransactionListItem = React.memo(({ item, itemPriceUnit = BitcoinUnit.BTC, timeElapsed }) => {
const [subtitleNumberOfLines, setSubtitleNumberOfLines] = useState(1);
const { colors } = useTheme();
const { navigate } = useNavigation();
const { txMetadata, wallets, preferredFiatCurrency, language } = useContext(BlueStorageContext);
const containerStyle = useMemo(
() => ({
backgroundColor: 'transparent',
borderBottomColor: colors.lightBorder,
paddingTop: 16,
paddingBottom: 16,
paddingRight: 0,
}),
[colors.lightBorder],
);
const title = useMemo(() => {
if (item.confirmations === 0) {
return loc.transactions.pending;
} else {
return transactionTimeToReadable(item.received);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [item.confirmations, item.received, language]);
const txMemo = txMetadata[item.hash]?.memo ?? '';
const subtitle = useMemo(() => {
let sub = item.confirmations < 7 ? loc.formatString(loc.transactions.list_conf, { number: item.confirmations }) : '';
if (sub !== '') sub += ' ';
sub += txMemo;
if (item.memo) sub += item.memo;
return sub || null;
}, [txMemo, item.confirmations, item.memo]);
const rowTitle = useMemo(() => {
if (item.type === 'user_invoice' || item.type === 'payment_request') {
if (isNaN(item.value)) {
item.value = '0';
}
const currentDate = new Date();
const now = (currentDate.getTime() / 1000) | 0;
const invoiceExpiration = item.timestamp + item.expire_time;
if (invoiceExpiration > now) {
return formatBalanceWithoutSuffix(item.value && item.value, itemPriceUnit, true).toString();
} else if (invoiceExpiration < now) {
if (item.ispaid) {
return formatBalanceWithoutSuffix(item.value && item.value, itemPriceUnit, true).toString();
} else {
return loc.lnd.expired;
}
}
} else {
return formatBalanceWithoutSuffix(item.value && item.value, itemPriceUnit, true).toString();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [item, itemPriceUnit, preferredFiatCurrency]);
const rowTitleStyle = useMemo(() => {
let color = colors.successColor;
if (item.type === 'user_invoice' || item.type === 'payment_request') {
const currentDate = new Date();
const now = (currentDate.getTime() / 1000) | 0;
const invoiceExpiration = item.timestamp + item.expire_time;
if (invoiceExpiration > now) {
color = colors.successColor;
} else if (invoiceExpiration < now) {
if (item.ispaid) {
color = colors.successColor;
} else {
color = '#9AA0AA';
}
}
} else if (item.value / 100000000 < 0) {
color = colors.foregroundColor;
}
return {
color,
fontSize: 14,
fontWeight: '600',
textAlign: 'right',
width: 96,
};
}, [item, colors.foregroundColor, colors.successColor]);
const avatar = useMemo(() => {
// is it lightning refill tx?
if (item.category === 'receive' && item.confirmations < 3) {
return (
<View style={{ width: 25 }}>
<BlueTransactionPendingIcon />
</View>
);
}
if (item.type && item.type === 'bitcoind_tx') {
return (
<View style={{ width: 25 }}>
<BlueTransactionOnchainIcon />
</View>
);
}
if (item.type === 'paid_invoice') {
// is it lightning offchain payment?
return (
<View style={{ width: 25 }}>
<BlueTransactionOffchainIcon />
</View>
);
}
if (item.type === 'user_invoice' || item.type === 'payment_request') {
if (!item.ispaid) {
const currentDate = new Date();
const now = (currentDate.getTime() / 1000) | 0;
const invoiceExpiration = item.timestamp + item.expire_time;
if (invoiceExpiration < now) {
return (
<View style={{ width: 25 }}>
<BlueTransactionExpiredIcon />
</View>
);
}
} else {
return (
<View style={{ width: 25 }}>
<BlueTransactionOffchainIncomingIcon />
</View>
);
}
}
if (!item.confirmations) {
return (
<View style={{ width: 25 }}>
<BlueTransactionPendingIcon />
</View>
);
} else if (item.value < 0) {
return (
<View style={{ width: 25 }}>
<BlueTransactionOutgoingIcon />
</View>
);
} else {
return (
<View style={{ width: 25 }}>
<BlueTransactionIncomingIcon />
</View>
);
}
}, [item]);
useEffect(() => {
setSubtitleNumberOfLines(1);
}, [subtitle]);
const onPress = useCallback(async () => {
if (item.hash) {
navigate('TransactionStatus', { hash: item.hash });
} else if (item.type === 'user_invoice' || item.type === 'payment_request' || item.type === 'paid_invoice') {
const lightningWallet = wallets.filter(wallet => wallet?.getID() === item.walletID);
if (lightningWallet.length === 1) {
try {
// is it a successful lnurl-pay?
const LN = new Lnurl(false, AsyncStorage);
let paymentHash = item.payment_hash;
if (typeof paymentHash === 'object') {
paymentHash = Buffer.from(paymentHash.data).toString('hex');
}
const loaded = await LN.loadSuccessfulPayment(paymentHash);
if (loaded) {
NavigationService.navigate('ScanLndInvoiceRoot', {
screen: 'LnurlPaySuccess',
params: {
paymentHash,
justPaid: false,
fromWalletID: lightningWallet[0].getID(),
},
});
return;
}
} catch (e) {
console.log(e);
}
navigate('LNDViewInvoice', {
invoice: item,
walletID: lightningWallet[0].getID(),
isModal: false,
});
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [item, wallets]);
const handleOnExpandNote = useCallback(() => {
setSubtitleNumberOfLines(0);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [subtitle]);
const subtitleProps = useMemo(() => ({ numberOfLines: subtitleNumberOfLines }), [subtitleNumberOfLines]);
const handleOnCopyAmountTap = useCallback(() => Clipboard.setString(rowTitle.replace(/[\s\\-]/g, '')), [rowTitle]);
const handleOnCopyTransactionID = useCallback(() => Clipboard.setString(item.hash), [item.hash]);
const handleOnCopyNote = useCallback(() => Clipboard.setString(subtitle), [subtitle]);
const handleOnViewOnBlockExplorer = useCallback(() => {
const url = `https://mempool.space/tx/${item.hash}`;
Linking.canOpenURL(url).then(supported => {
if (supported) {
Linking.openURL(url);
}
});
}, [item.hash]);
const handleCopyOpenInBlockExplorerPress = useCallback(() => {
Clipboard.setString(`https://mempool.space/tx/${item.hash}`);
}, [item.hash]);
const onToolTipPress = useCallback(id => {
if (id === 'copyAmount') {
handleOnCopyAmountTap();
} else if (id === 'copyNote') {
handleOnCopyNote();
} else if (id === 'open_in_blockExplorer') {
handleOnViewOnBlockExplorer();
} else if (id === 'expandNote') {
handleOnExpandNote();
} else if (id === 'copy_blockExplorer') {
handleCopyOpenInBlockExplorerPress();
} else if (id === 'copyTX_ID') {
handleOnCopyTransactionID();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const toolTipActions = useMemo(() => {
const actions = [];
if (rowTitle !== loc.lnd.expired) {
actions.push({
id: 'copyAmount',
text: `${loc.transactions.details_copy} ${loc.send.create_amount}`,
icon: {
iconType: 'SYSTEM',
iconValue: 'arrow.right.doc.on.clipboard',
},
});
}
if (subtitle) {
actions.push({
id: 'copyNote',
text: `${loc.transactions.details_copy} ${loc.transactions.note}`,
icon: {
iconType: 'SYSTEM',
iconValue: 'arrow.right.doc.on.clipboard',
},
});
}
if (item.hash) {
actions.push(
{
id: 'copyTX_ID',
text: `${loc.transactions.details_copy} ${loc.transactions.txid}`,
icon: {
iconType: 'SYSTEM',
iconValue: 'arrow.right.doc.on.clipboard',
},
},
{
id: 'copy_blockExplorer',
text: `${loc.transactions.details_copy} ${loc.transactions.block_explorer_link}`,
icon: {
iconType: 'SYSTEM',
iconValue: 'link',
},
},
);
}
return actions;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [item.hash, subtitle, rowTitle, subtitleNumberOfLines, txMetadata]);
const toolTipSubMenu = useMemo(() => {
const submenu = {
menuOptions: ['displayInline'], // <- set the `menuOptions` property
menuItems: [],
menuTitle: '',
};
if (item.hash) {
submenu.menuItems.push({
actionKey: 'open_in_blockExplorer',
actionTitle: loc.transactions.details_show_in_block_explorer,
});
}
if (subtitle && subtitleNumberOfLines === 1) {
submenu.menuItems.push({
actionKey: 'expandNote',
actionTitle: loc.transactions.expand_note,
});
}
return submenu;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<View style={{ marginHorizontal: 4 }}>
<ToolTipMenu actions={toolTipActions} submenu={toolTipSubMenu} onPress={onToolTipPress}>
<BlueListItem
leftAvatar={avatar}
title={title}
subtitleNumberOfLines={subtitleNumberOfLines}
subtitle={subtitle}
subtitleProps={subtitleProps}
onPress={onPress}
chevron={false}
Component={TouchableOpacity}
rightTitle={rowTitle}
rightTitleStyle={rowTitleStyle}
containerStyle={containerStyle}
/>
</ToolTipMenu>
</View>
);
});
export class BlueReplaceFeeSuggestions extends Component {
static propTypes = {
onFeeSelected: PropTypes.func.isRequired,

View file

@ -0,0 +1,377 @@
/* eslint react/prop-types: "off" */
import React, { useState, useMemo, useCallback, useContext, useEffect } from 'react';
import { Linking, StyleSheet, TouchableOpacity, View } from 'react-native';
import Clipboard from '@react-native-clipboard/clipboard';
import { BitcoinUnit } from '../models/bitcoinUnits';
import * as NavigationService from '../NavigationService';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { useNavigation, useTheme } from '@react-navigation/native';
import loc, { formatBalanceWithoutSuffix, transactionTimeToReadable } from '../loc';
import Lnurl from '../class/lnurl';
import { BlueStorageContext } from '../blue_modules/storage-context';
import ToolTipMenu from './TooltipMenu';
import {
BlueListItem,
BlueTransactionExpiredIcon,
BlueTransactionIncomingIcon,
BlueTransactionOffchainIcon,
BlueTransactionOffchainIncomingIcon,
BlueTransactionOnchainIcon,
BlueTransactionOutgoingIcon,
BlueTransactionPendingIcon,
} from '../BlueComponents';
export const TransactionListItem = React.memo(({ item, itemPriceUnit = BitcoinUnit.BTC }) => {
const [subtitleNumberOfLines, setSubtitleNumberOfLines] = useState(1);
const { colors } = useTheme();
const { navigate } = useNavigation();
const { txMetadata, wallets, preferredFiatCurrency, language } = useContext(BlueStorageContext);
const containerStyle = useMemo(
() => ({
backgroundColor: 'transparent',
borderBottomColor: colors.lightBorder,
paddingTop: 16,
paddingBottom: 16,
paddingRight: 0,
}),
[colors.lightBorder],
);
const title = useMemo(() => {
if (item.confirmations === 0) {
return loc.transactions.pending;
} else {
return transactionTimeToReadable(item.received);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [item.confirmations, item.received, language]);
const txMemo = txMetadata[item.hash]?.memo ?? '';
const subtitle = useMemo(() => {
let sub = item.confirmations < 7 ? loc.formatString(loc.transactions.list_conf, { number: item.confirmations }) : '';
if (sub !== '') sub += ' ';
sub += txMemo;
if (item.memo) sub += item.memo;
return sub || null;
}, [txMemo, item.confirmations, item.memo]);
const rowTitle = useMemo(() => {
if (item.type === 'user_invoice' || item.type === 'payment_request') {
if (isNaN(item.value)) {
item.value = '0';
}
const currentDate = new Date();
const now = (currentDate.getTime() / 1000) | 0;
const invoiceExpiration = item.timestamp + item.expire_time;
if (invoiceExpiration > now) {
return formatBalanceWithoutSuffix(item.value && item.value, itemPriceUnit, true).toString();
} else if (invoiceExpiration < now) {
if (item.ispaid) {
return formatBalanceWithoutSuffix(item.value && item.value, itemPriceUnit, true).toString();
} else {
return loc.lnd.expired;
}
}
} else {
return formatBalanceWithoutSuffix(item.value && item.value, itemPriceUnit, true).toString();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [item, itemPriceUnit, preferredFiatCurrency]);
const rowTitleStyle = useMemo(() => {
let color = colors.successColor;
if (item.type === 'user_invoice' || item.type === 'payment_request') {
const currentDate = new Date();
const now = (currentDate.getTime() / 1000) | 0;
const invoiceExpiration = item.timestamp + item.expire_time;
if (invoiceExpiration > now) {
color = colors.successColor;
} else if (invoiceExpiration < now) {
if (item.ispaid) {
color = colors.successColor;
} else {
color = '#9AA0AA';
}
}
} else if (item.value / 100000000 < 0) {
color = colors.foregroundColor;
}
return {
color,
fontSize: 14,
fontWeight: '600',
textAlign: 'right',
width: 96,
};
}, [item, colors.foregroundColor, colors.successColor]);
const avatar = useMemo(() => {
// is it lightning refill tx?
if (item.category === 'receive' && item.confirmations < 3) {
return (
<View style={styles.iconWidth}>
<BlueTransactionPendingIcon />
</View>
);
}
if (item.type && item.type === 'bitcoind_tx') {
return (
<View style={styles.iconWidth}>
<BlueTransactionOnchainIcon />
</View>
);
}
if (item.type === 'paid_invoice') {
// is it lightning offchain payment?
return (
<View style={styles.iconWidth}>
<BlueTransactionOffchainIcon />
</View>
);
}
if (item.type === 'user_invoice' || item.type === 'payment_request') {
if (!item.ispaid) {
const currentDate = new Date();
const now = (currentDate.getTime() / 1000) | 0;
const invoiceExpiration = item.timestamp + item.expire_time;
if (invoiceExpiration < now) {
return (
<View style={styles.iconWidth}>
<BlueTransactionExpiredIcon />
</View>
);
}
} else {
return (
<View style={styles.iconWidth}>
<BlueTransactionOffchainIncomingIcon />
</View>
);
}
}
if (!item.confirmations) {
return (
<View style={styles.iconWidth}>
<BlueTransactionPendingIcon />
</View>
);
} else if (item.value < 0) {
return (
<View style={styles.iconWidth}>
<BlueTransactionOutgoingIcon />
</View>
);
} else {
return (
<View style={styles.iconWidth}>
<BlueTransactionIncomingIcon />
</View>
);
}
}, [item]);
useEffect(() => {
setSubtitleNumberOfLines(1);
}, [subtitle]);
const onPress = useCallback(async () => {
if (item.hash) {
navigate('TransactionStatus', { hash: item.hash });
} else if (item.type === 'user_invoice' || item.type === 'payment_request' || item.type === 'paid_invoice') {
const lightningWallet = wallets.filter(wallet => wallet?.getID() === item.walletID);
if (lightningWallet.length === 1) {
try {
// is it a successful lnurl-pay?
const LN = new Lnurl(false, AsyncStorage);
let paymentHash = item.payment_hash;
if (typeof paymentHash === 'object') {
paymentHash = Buffer.from(paymentHash.data).toString('hex');
}
const loaded = await LN.loadSuccessfulPayment(paymentHash);
if (loaded) {
NavigationService.navigate('ScanLndInvoiceRoot', {
screen: 'LnurlPaySuccess',
params: {
paymentHash,
justPaid: false,
fromWalletID: lightningWallet[0].getID(),
},
});
return;
}
} catch (e) {
console.log(e);
}
navigate('LNDViewInvoice', {
invoice: item,
walletID: lightningWallet[0].getID(),
isModal: false,
});
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [item, wallets]);
const handleOnExpandNote = useCallback(() => {
setSubtitleNumberOfLines(0);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [subtitle]);
const subtitleProps = useMemo(() => ({ numberOfLines: subtitleNumberOfLines }), [subtitleNumberOfLines]);
const handleOnCopyAmountTap = useCallback(() => Clipboard.setString(rowTitle.replace(/[\s\\-]/g, '')), [rowTitle]);
const handleOnCopyTransactionID = useCallback(() => Clipboard.setString(item.hash), [item.hash]);
const handleOnCopyNote = useCallback(() => Clipboard.setString(subtitle), [subtitle]);
const handleOnViewOnBlockExplorer = useCallback(() => {
const url = `https://mempool.space/tx/${item.hash}`;
Linking.canOpenURL(url).then(supported => {
if (supported) {
Linking.openURL(url);
}
});
}, [item.hash]);
const handleCopyOpenInBlockExplorerPress = useCallback(() => {
Clipboard.setString(`https://mempool.space/tx/${item.hash}`);
}, [item.hash]);
const onToolTipPress = useCallback(id => {
if (id === TransactionListItem.actionKeys.CopyAmount) {
handleOnCopyAmountTap();
} else if (id === TransactionListItem.actionKeys.CopyNote) {
handleOnCopyNote();
} else if (id === TransactionListItem.actionKeys.OpenInBlockExplorer) {
handleOnViewOnBlockExplorer();
} else if (id === TransactionListItem.actionKeys.ExpandNote) {
handleOnExpandNote();
} else if (id === TransactionListItem.actionKeys.CopyBlockExplorerLink) {
handleCopyOpenInBlockExplorerPress();
} else if (id === TransactionListItem.actionKeys.CopyTXID) {
handleOnCopyTransactionID();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const toolTipActions = useMemo(() => {
const actions = [];
if (rowTitle !== loc.lnd.expired) {
actions.push({
id: TransactionListItem.actionKeys.CopyAmount,
text: `${loc.transactions.details_copy} ${loc.send.create_amount}`,
icon: TransactionListItem.actionIcons.Clipboard,
});
}
if (subtitle) {
actions.push({
id: TransactionListItem.actionKeys.CopyNote,
text: `${loc.transactions.details_copy} ${loc.transactions.note}`,
icon: TransactionListItem.actionIcons.Clipboard,
});
}
if (item.hash) {
actions.push(
{
id: TransactionListItem.actionKeys.CopyTXID,
text: `${loc.transactions.details_copy} ${loc.transactions.txid}`,
icon: TransactionListItem.actionIcons.Clipboard,
},
{
id: TransactionListItem.actionKeys.CopyBlockExplorerLink,
text: `${loc.transactions.details_copy} ${loc.transactions.block_explorer_link}`,
icon: TransactionListItem.actionIcons.Link,
},
);
}
return actions;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [item.hash, subtitle, rowTitle, subtitleNumberOfLines, txMetadata]);
const toolTipSubMenu = useMemo(() => {
const submenu = {
menuOptions: ['displayInline'], // <- set the `menuOptions` property
menuItems: [],
menuTitle: '',
};
if (item.hash) {
submenu.menuItems.push({
actionKey: TransactionListItem.actionKeys.OpenInBlockExplorer,
actionTitle: loc.transactions.details_show_in_block_explorer,
icon: TransactionListItem.actionIcons.Link,
});
}
if (subtitle && subtitleNumberOfLines === 1) {
submenu.menuItems.push({
actionKey: TransactionListItem.actionKeys.ExpandNote,
actionTitle: loc.transactions.expand_note,
icon: TransactionListItem.actionIcons.Note,
});
}
return submenu;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<View style={styles.container}>
<ToolTipMenu actions={toolTipActions} submenu={toolTipSubMenu} onPress={onToolTipPress}>
<BlueListItem
leftAvatar={avatar}
title={title}
subtitleNumberOfLines={subtitleNumberOfLines}
subtitle={subtitle}
subtitleProps={subtitleProps}
onPress={onPress}
chevron={false}
Component={TouchableOpacity}
rightTitle={rowTitle}
rightTitleStyle={rowTitleStyle}
containerStyle={containerStyle}
/>
</ToolTipMenu>
</View>
);
});
TransactionListItem.actionKeys = {
CopyTXID: 'copyTX_ID',
CopyBlockExplorerLink: 'copy_blockExplorer',
ExpandNote: 'expandNote',
OpenInBlockExplorer: 'open_in_blockExplorer',
CopyAmount: 'copyAmount',
CopyNote: 'copyNote',
};
TransactionListItem.actionIcons = {
Eye: {
iconType: 'SYSTEM',
iconValue: 'eye',
},
EyeSlash: {
iconType: 'SYSTEM',
iconValue: 'eye.slash',
},
Clipboard: {
iconType: 'SYSTEM',
iconValue: 'arrow.right.doc.on.clipboard',
},
Link: {
iconType: 'SYSTEM',
iconValue: 'link',
},
Note: {
iconType: 'SYSTEM',
iconValue: 'note.text',
},
};
const styles = StyleSheet.create({
iconWidth: { width: 25 },
container: { marginHorizontal: 4 },
});

View file

@ -0,0 +1,267 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Image, Text, TouchableOpacity, View, InteractionManager, I18nManager, StyleSheet } from 'react-native';
import Clipboard from '@react-native-clipboard/clipboard';
import LinearGradient from 'react-native-linear-gradient';
import { LightningCustodianWallet, MultisigHDWallet } from '../class';
import { BitcoinUnit } from '../models/bitcoinUnits';
import WalletGradient from '../class/wallet-gradient';
import Biometric from '../class/biometrics';
import loc, { formatBalance } from '../loc';
import { BlueStorageContext } from '../blue_modules/storage-context';
import ToolTipMenu from './TooltipMenu';
import { BluePrivateBalance } from '../BlueComponents';
export default class TransactionsNavigationHeader extends Component {
static propTypes = {
wallet: PropTypes.shape().isRequired,
onWalletUnitChange: PropTypes.func,
navigation: PropTypes.shape(),
onManageFundsPressed: PropTypes.func,
};
static actionKeys = {
CopyToClipboard: 'copyToClipboard',
WalletBalanceVisibility: 'walletBalanceVisibility',
};
static actionIcons = {
Eye: {
iconType: 'SYSTEM',
iconValue: 'eye',
},
EyeSlash: {
iconType: 'SYSTEM',
iconValue: 'eye.slash',
},
Clipboard: {
iconType: 'SYSTEM',
iconValue: 'arrow.right.doc.on.clipboard',
},
};
static getDerivedStateFromProps(props) {
return { wallet: props.wallet, onWalletUnitChange: props.onWalletUnitChange };
}
static contextType = BlueStorageContext;
constructor(props) {
super(props);
this.state = {
wallet: props.wallet,
walletPreviousPreferredUnit: props.wallet.getPreferredBalanceUnit(),
allowOnchainAddress: false,
};
}
handleCopyPress = _item => {
Clipboard.setString(formatBalance(this.state.wallet.getBalance(), this.state.wallet.getPreferredBalanceUnit()).toString());
};
componentDidUpdate(prevState) {
InteractionManager.runAfterInteractions(() => {
if (prevState.wallet.getID() !== this.state.wallet.getID() && this.state.wallet.type === LightningCustodianWallet.type) {
this.verifyIfWalletAllowsOnchainAddress();
}
});
}
verifyIfWalletAllowsOnchainAddress = () => {
if (this.state.wallet.type === LightningCustodianWallet.type) {
this.state.wallet
.allowOnchainAddress()
.then(value => this.setState({ allowOnchainAddress: value }))
.catch(e => {
console.log('This Lndhub wallet does not have an onchain address API.');
this.setState({ allowOnchainAddress: false });
});
}
};
componentDidMount() {
this.verifyIfWalletAllowsOnchainAddress();
}
handleBalanceVisibility = async _item => {
const wallet = this.state.wallet;
const isBiometricsEnabled = await Biometric.isBiometricUseCapableAndEnabled();
if (isBiometricsEnabled && wallet.hideBalance) {
if (!(await Biometric.unlockWithBiometrics())) {
return this.props.navigation.goBack();
}
}
wallet.hideBalance = !wallet.hideBalance;
this.setState({ wallet });
await this.context.saveToDisk();
};
changeWalletBalanceUnit = () => {
let walletPreviousPreferredUnit = this.state.wallet.getPreferredBalanceUnit();
const wallet = this.state.wallet;
if (walletPreviousPreferredUnit === BitcoinUnit.BTC) {
wallet.preferredBalanceUnit = BitcoinUnit.SATS;
walletPreviousPreferredUnit = BitcoinUnit.BTC;
} else if (walletPreviousPreferredUnit === BitcoinUnit.SATS) {
wallet.preferredBalanceUnit = BitcoinUnit.LOCAL_CURRENCY;
walletPreviousPreferredUnit = BitcoinUnit.SATS;
} else if (walletPreviousPreferredUnit === BitcoinUnit.LOCAL_CURRENCY) {
wallet.preferredBalanceUnit = BitcoinUnit.BTC;
walletPreviousPreferredUnit = BitcoinUnit.BTC;
} else {
wallet.preferredBalanceUnit = BitcoinUnit.BTC;
walletPreviousPreferredUnit = BitcoinUnit.BTC;
}
this.setState({ wallet, walletPreviousPreferredUnit: walletPreviousPreferredUnit }, () => {
this.props.onWalletUnitChange(wallet);
});
};
manageFundsPressed = () => {
this.props.onManageFundsPressed();
};
onPress = id => {
if (id === TransactionsNavigationHeader.actionKeys.WalletBalanceVisibility) {
this.handleBalanceVisibility();
} else if (id === TransactionsNavigationHeader.actionKeys.CopyToClipboard) {
this.handleCopyPress();
}
};
render() {
const balance =
!this.state.wallet.hideBalance &&
formatBalance(this.state.wallet.getBalance(), this.state.wallet.getPreferredBalanceUnit(), true).toString();
return (
<LinearGradient
colors={WalletGradient.gradientsFor(this.state.wallet.type)}
style={styles.lineaderGradient}
{...WalletGradient.linearGradientProps(this.state.wallet.type)}
>
<Image
source={(() => {
switch (this.state.wallet.type) {
case LightningCustodianWallet.type:
return I18nManager.isRTL ? require('../img/lnd-shape-rtl.png') : require('../img/lnd-shape.png');
case MultisigHDWallet.type:
return I18nManager.isRTL ? require('../img/vault-shape-rtl.png') : require('../img/vault-shape.png');
default:
return I18nManager.isRTL ? require('../img/btc-shape-rtl.png') : require('../img/btc-shape.png');
}
})()}
style={styles.chainIcon}
/>
<Text testID="WalletLabel" numberOfLines={1} style={styles.walletLabel}>
{this.state.wallet.getLabel()}
</Text>
<ToolTipMenu
title={loc.wallets.balance}
onPress={this.onPress}
actions={
this.state.wallet.hideBalance
? [
{
id: TransactionsNavigationHeader.actionKeys.WalletBalanceVisibility,
text: loc.transactions.details_balance_show,
icon: TransactionsNavigationHeader.actionIcons.Eye,
},
]
: [
{
id: TransactionsNavigationHeader.actionKeys.WalletBalanceVisibility,
text: loc.transactions.details_balance_hide,
icon: TransactionsNavigationHeader.actionIcons.EyeSlash,
},
{
id: TransactionsNavigationHeader.actionKeys.CopyToClipboard,
text: loc.transactions.details_copy,
icon: TransactionsNavigationHeader.actionIcons.Clipboard,
},
]
}
>
<TouchableOpacity accessibilityRole="button" style={styles.balance} onPress={this.changeWalletBalanceUnit}>
{this.state.wallet.hideBalance ? (
<BluePrivateBalance />
) : (
<Text
testID="WalletBalance"
key={balance} // force component recreation on balance change. To fix right-to-left languages, like Farsi
numberOfLines={1}
adjustsFontSizeToFit
style={styles.walletBalance}
>
{balance}
</Text>
)}
</TouchableOpacity>
</ToolTipMenu>
{this.state.wallet.type === LightningCustodianWallet.type && this.state.allowOnchainAddress && (
<TouchableOpacity accessibilityRole="button" onPress={this.manageFundsPressed}>
<View style={styles.manageFundsButton}>
<Text style={styles.manageFundsButtonText}>{loc.lnd.title}</Text>
</View>
</TouchableOpacity>
)}
{this.state.wallet.type === MultisigHDWallet.type && (
<TouchableOpacity accessibilityRole="button" onPress={this.manageFundsPressed}>
<View style={styles.manageFundsButton}>
<Text style={styles.manageFundsButtonText}>{loc.multisig.manage_keys}</Text>
</View>
</TouchableOpacity>
)}
</LinearGradient>
);
}
}
const styles = StyleSheet.create({
lineaderGradient: {
padding: 15,
minHeight: 140,
justifyContent: 'center',
},
chainIcon: {
width: 99,
height: 94,
position: 'absolute',
bottom: 0,
right: 0,
},
walletLabel: {
backgroundColor: 'transparent',
fontSize: 19,
color: '#fff',
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
},
walletBalance: {
backgroundColor: 'transparent',
fontWeight: 'bold',
fontSize: 36,
color: '#fff',
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
},
manageFundsButton: {
marginTop: 14,
marginBottom: 10,
backgroundColor: 'rgba(255,255,255,0.2)',
borderRadius: 9,
minHeight: 39,
alignSelf: 'flex-start',
paddingHorizontal: 12,
height: 39,
justifyContent: 'center',
alignItems: 'center',
},
manageFundsButtonText: {
fontWeight: '500',
fontSize: 14,
color: '#FFFFFF',
},
});

View file

@ -66,11 +66,11 @@ const AddressItem = ({ item, balanceUnit, walletID, allowSignVerifyMessage }) =>
};
const onToolTipPress = id => {
if (id === 'copyToClipboard') {
if (id === AddressItem.actionKeys.CopyToClipboard) {
handleCopyPress();
} else if (id === 'share') {
} else if (id === AddressItem.actionKeys.Share) {
handleSharePress();
} else if (id === 'signVerify') {
} else if (id === AddressItem.actionKeys.SignVerify) {
navigateToSignVerify();
}
};
@ -78,31 +78,22 @@ const AddressItem = ({ item, balanceUnit, walletID, allowSignVerifyMessage }) =>
const getAvailableActions = () => {
const actions = [
{
id: 'copyToClipboard',
id: AddressItem.actionKeys.CopyToClipboard,
text: loc.transactions.details_copy,
icon: {
iconType: 'SYSTEM',
iconValue: 'arrow.right.doc.on.clipboard',
},
icon: AddressItem.actionIcons.Clipboard,
},
{
id: 'share',
id: AddressItem.actionKeys.Share,
text: loc.receive.details_share,
icon: {
iconType: 'SYSTEM',
iconValue: 'square.and.arrow.up',
},
icon: AddressItem.actionIcons.Share,
},
];
if (allowSignVerifyMessage) {
actions.push({
id: 'signVerify',
id: AddressItem.actionKeys.SignVerify,
text: loc.addresses.sign_title,
icon: {
iconType: 'SYSTEM',
iconValue: 'signature',
},
icon: AddressItem.actionIcons.Signature,
});
}
@ -136,6 +127,27 @@ const AddressItem = ({ item, balanceUnit, walletID, allowSignVerifyMessage }) =>
return render();
};
AddressItem.actionKeys = {
Share: 'share',
CopyToClipboard: 'copyToClipboard',
SignVerify: 'signVerify',
};
AddressItem.actionIcons = {
Signature: {
iconType: 'SYSTEM',
iconValue: 'signature',
},
Share: {
iconType: 'SYSTEM',
iconValue: 'square.and.arrow.up',
},
Clipboard: {
iconType: 'SYSTEM',
iconValue: 'arrow.right.doc.on.clipboard',
},
};
const styles = StyleSheet.create({
address: {
fontWeight: 'bold',

View file

@ -65,10 +65,8 @@
allowLocationSimulation = "YES"
launchAutomaticallySubstyle = "8"
notificationPayloadFile = "BlueWalletWatch Extension/PushNotificationPayload.apns">
<RemoteRunnable
runnableDebuggingMode = "2"
BundleIdentifier = "com.apple.Carousel"
RemotePath = "/BlueWallet">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B40D4E2F225841EC00428FCC"
@ -76,7 +74,7 @@
BlueprintName = "BlueWalletWatch"
ReferencedContainer = "container:BlueWallet.xcodeproj">
</BuildableReference>
</RemoteRunnable>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
@ -86,10 +84,8 @@
debugDocumentVersioning = "YES"
launchAutomaticallySubstyle = "8"
notificationPayloadFile = "BlueWalletWatch Extension/PushNotificationPayload.apns">
<RemoteRunnable
runnableDebuggingMode = "2"
BundleIdentifier = "com.apple.Carousel"
RemotePath = "/BlueWallet">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B40D4E2F225841EC00428FCC"
@ -97,16 +93,7 @@
BlueprintName = "BlueWalletWatch"
ReferencedContainer = "container:BlueWallet.xcodeproj">
</BuildableReference>
</RemoteRunnable>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B40D4E2F225841EC00428FCC"
BuildableName = "BlueWalletWatch.app"
BlueprintName = "BlueWalletWatch"
ReferencedContainer = "container:BlueWallet.xcodeproj">
</BuildableReference>
</MacroExpansion>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">

View file

@ -64,10 +64,8 @@
debugServiceExtension = "internal"
allowLocationSimulation = "YES"
notificationPayloadFile = "BlueWalletWatch Extension/PushNotificationPayload.apns">
<RemoteRunnable
runnableDebuggingMode = "2"
BundleIdentifier = "com.apple.Carousel"
RemotePath = "/BlueWallet">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B40D4E2F225841EC00428FCC"
@ -75,7 +73,7 @@
BlueprintName = "BlueWalletWatch"
ReferencedContainer = "container:BlueWallet.xcodeproj">
</BuildableReference>
</RemoteRunnable>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
@ -83,10 +81,8 @@
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<RemoteRunnable
runnableDebuggingMode = "2"
BundleIdentifier = "com.apple.Carousel"
RemotePath = "/BlueWallet">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B40D4E2F225841EC00428FCC"
@ -94,16 +90,7 @@
BlueprintName = "BlueWalletWatch"
ReferencedContainer = "container:BlueWallet.xcodeproj">
</BuildableReference>
</RemoteRunnable>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B40D4E2F225841EC00428FCC"
BuildableName = "BlueWalletWatch.app"
BlueprintName = "BlueWalletWatch"
ReferencedContainer = "container:BlueWallet.xcodeproj">
</BuildableReference>
</MacroExpansion>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">

2
package-lock.json generated
View file

@ -17798,7 +17798,7 @@
},
"react-native-blue-crypto": {
"version": "git+https://github.com/BlueWallet/react-native-blue-crypto.git#841cd9e5abdc468888c76682c95a944ec34c1be7",
"from": "git+https://github.com/BlueWallet/react-native-blue-crypto.git"
"from": "git+https://github.com/BlueWallet/react-native-blue-crypto.git#841cd9e5abdc468888c76682c95a944ec34c1be7"
},
"react-native-camera": {
"version": "4.0.1",

View file

@ -257,12 +257,9 @@ const LNDViewInvoice = () => {
<ToolTipMenu
actions={[
{
id: 'shareQRCode',
id: LNDViewInvoice.actionKeys.Share,
text: loc.receive.details_share,
icon: {
iconType: 'SYSTEM',
iconValue: 'square.and.arrow.up',
},
icon: LNDViewInvoice.actionIcons.Share,
},
]}
onPress={handleShareQRCode}
@ -312,6 +309,17 @@ const LNDViewInvoice = () => {
);
};
LNDViewInvoice.actionKeys = {
Share: 'share',
};
LNDViewInvoice.actionIcons = {
Share: {
iconType: 'SYSTEM',
iconValue: 'square.and.arrow.up',
},
};
const styles = StyleSheet.create({
root: {
flex: 1,

View file

@ -162,12 +162,9 @@ const ReceiveDetails = () => {
<ToolTipMenu
actions={[
{
id: 'shareQRCode',
id: ReceiveDetails.actionKeys.Share,
text: loc.receive.details_share,
icon: {
iconType: 'SYSTEM',
iconValue: 'square.and.arrow.up',
},
icon: ReceiveDetails.actionIcons.Share,
},
]}
onPress={handleShareQRCode}
@ -383,6 +380,17 @@ const ReceiveDetails = () => {
);
};
ReceiveDetails.actionKeys = {
Share: 'share',
};
ReceiveDetails.actionIcons = {
Share: {
iconType: 'SYSTEM',
iconValue: 'square.and.arrow.up',
},
};
ReceiveDetails.navigationOptions = navigationStyle(
{
closeButton: true,

View file

@ -227,12 +227,9 @@ const TransactionsDetails = () => {
<ToolTipMenu
actions={[
{
id: 'copyToClipboard',
id: TransactionsDetails.actionKeys.Clipboard,
text: loc.transactions.details_copy,
icon: {
iconType: 'SYSTEM',
iconValue: 'arrow.right.doc.on.clipboard',
},
icon: TransactionsDetails.actionIcons.Clipboard,
},
]}
onPress={handleCopyPress}
@ -251,6 +248,17 @@ const TransactionsDetails = () => {
);
};
TransactionsDetails.actionKeys = {
CopyToClipboard: 'copyToClipboard',
};
TransactionsDetails.actionIcons = {
Clipboard: {
iconType: 'SYSTEM',
iconValue: 'arrow.right.doc.on.clipboard',
},
};
const styles = StyleSheet.create({
scroll: {
flex: 1,

View file

@ -15,7 +15,7 @@ import {
useColorScheme,
I18nManager,
} from 'react-native';
import { BlueHeaderDefaultMain, BlueTransactionListItem } from '../../BlueComponents';
import { BlueHeaderDefaultMain } from '../../BlueComponents';
import WalletsCarousel from '../../components/WalletsCarousel';
import { Icon } from 'react-native-elements';
import DeeplinkSchemaMatch from '../../class/deeplink-schema-match';
@ -28,6 +28,7 @@ import { BlueStorageContext } from '../../blue_modules/storage-context';
import { isDesktop, isMacCatalina, isTablet } from '../../blue_modules/environment';
import BlueClipboard from '../../blue_modules/clipboard';
import navigationStyle from '../../components/navigationStyle';
import { TransactionListItem } from '../../components/TransactionListItem';
const BlueElectrum = require('../../blue_modules/BlueElectrum');
const scanqrHelper = require('../../helpers/scan-qr');
@ -213,7 +214,7 @@ const WalletsList = () => {
const renderTransactionListsRow = data => {
return (
<View style={styles.transaction}>
<BlueTransactionListItem item={data.item} itemPriceUnit={data.item.walletPreferredBalanceUnit} />
<TransactionListItem item={data.item} itemPriceUnit={data.item.walletPreferredBalanceUnit} />
</View>
);
};

View file

@ -24,7 +24,7 @@ import { launchImageLibrary } from 'react-native-image-picker';
import { Icon } from 'react-native-elements';
import { useRoute, useNavigation, useTheme, useFocusEffect } from '@react-navigation/native';
import { Chain } from '../../models/bitcoinUnits';
import { BlueTransactionListItem, BlueWalletNavigationHeader, BlueAlertWalletExportReminder, BlueListItem } from '../../BlueComponents';
import { BlueAlertWalletExportReminder, BlueListItem } from '../../BlueComponents';
import WalletGradient from '../../class/wallet-gradient';
import navigationStyle from '../../components/navigationStyle';
import { LightningCustodianWallet, MultisigHDWallet, WatchOnlyWallet } from '../../class';
@ -37,6 +37,8 @@ import BuyBitcoin from './buyBitcoin';
import { BlueStorageContext } from '../../blue_modules/storage-context';
import { isDesktop, isMacCatalina } from '../../blue_modules/environment';
import BlueClipboard from '../../blue_modules/clipboard';
import TransactionsNavigationHeader from '../../components/TransactionsNavigationHeader';
import { TransactionListItem } from '../../components/TransactionListItem';
const fs = require('../../blue_modules/fs');
const BlueElectrum = require('../../blue_modules/BlueElectrum');
@ -449,7 +451,7 @@ const WalletTransactions = () => {
});
};
const renderItem = item => <BlueTransactionListItem item={item.item} itemPriceUnit={itemPriceUnit} timeElapsed={timeElapsed} />;
const renderItem = item => <TransactionListItem item={item.item} itemPriceUnit={itemPriceUnit} timeElapsed={timeElapsed} />;
const onBarCodeRead = ret => {
if (!isLoading) {
@ -611,7 +613,7 @@ const WalletTransactions = () => {
url={`https://blockpath.com/search/addr?q=${wallet.getXpub()}`}
/>
)}
<BlueWalletNavigationHeader
<TransactionsNavigationHeader
wallet={wallet}
onWalletUnitChange={passedWallet =>
InteractionManager.runAfterInteractions(async () => {

View file

@ -87,12 +87,9 @@ const WalletXpub = () => {
<ToolTipMenu
actions={[
{
id: 'shareQRCode',
id: WalletXpub.actionKeys.Share,
text: loc.receive.details_share,
icon: {
iconType: 'SYSTEM',
iconValue: 'square.and.arrow.up',
},
icon: WalletXpub.actionIcons.Share,
},
]}
onPress={handleShareQRCode}
@ -118,6 +115,17 @@ const WalletXpub = () => {
);
};
WalletXpub.actionKeys = {
Share: 'share',
};
WalletXpub.actionIcons = {
Share: {
iconType: 'SYSTEM',
iconValue: 'square.and.arrow.up',
},
};
WalletXpub.navigationOptions = navigationStyle(
{
closeButton: true,