diff --git a/BlueComponents.js b/BlueComponents.js index 847c7bd12..315743f3a 100644 --- a/BlueComponents.js +++ b/BlueComponents.js @@ -1,6 +1,6 @@ /* eslint react/prop-types: "off", react-native/no-inline-styles: "off" */ /* global alert */ -import React, { Component, useState } from 'react'; +import React, { Component, useState, useMemo, useCallback } from 'react'; import Ionicons from 'react-native-vector-icons/Ionicons'; import PropTypes from 'prop-types'; import { Icon, Input, Text, Header, ListItem, Avatar } from 'react-native-elements'; @@ -1670,15 +1670,27 @@ export const NewWalletPanel = props => { export const BlueTransactionListItem = React.memo(({ item, itemPriceUnit = BitcoinUnit.BTC, timeElapsed }) => { const [subtitleNumberOfLines, setSubtitleNumberOfLines] = useState(1); const { colors } = useTheme(); + const containerStyle = useMemo( + () => ({ + backgroundColor: 'transparent', + borderBottomColor: colors.lightBorder, + paddingTop: 16, + paddingBottom: 16, + }), + [colors.lightBorder], + ); - const txMemo = () => { - if (BlueApp.tx_metadata[item.hash] && BlueApp.tx_metadata[item.hash].memo) { - return BlueApp.tx_metadata[item.hash].memo; - } - return ''; - }; + const title = useMemo(() => transactionTimeToReadable(item.received), [item.received]); + const txMemo = BlueApp.tx_metadata[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 = () => { + const rowTitle = useMemo(() => { if (item.type === 'user_invoice' || item.type === 'payment_request') { if (isNaN(item.value)) { item.value = '0'; @@ -1699,9 +1711,9 @@ export const BlueTransactionListItem = React.memo(({ item, itemPriceUnit = Bitco } else { return formatBalanceWithoutSuffix(item.value && item.value, itemPriceUnit, true).toString(); } - }; + }, [item, itemPriceUnit]); - const rowTitleStyle = () => { + const rowTitleStyle = useMemo(() => { let color = colors.successColor; if (item.type === 'user_invoice' || item.type === 'payment_request') { @@ -1723,15 +1735,15 @@ export const BlueTransactionListItem = React.memo(({ item, itemPriceUnit = Bitco } return { - fontWeight: '600', + color, fontSize: 14, - color: color, + fontWeight: '600', textAlign: 'right', width: 96, }; - }; + }, [item, colors.foregroundColor, colors.successColor]); - const avatar = () => { + const avatar = useMemo(() => { // is it lightning refill tx? if (item.category === 'receive' && item.confirmations < 3) { return ( @@ -1797,13 +1809,9 @@ export const BlueTransactionListItem = React.memo(({ item, itemPriceUnit = Bitco ); } - }; + }, [item]); - const subtitle = () => { - return (item.confirmations < 7 ? loc.transactions.list_conf + ': ' + item.confirmations + ' ' : '') + txMemo() + (item.memo || ''); - }; - - const onPress = async () => { + const onPress = useCallback(async () => { if (item.hash) { NavigationService.navigate('TransactionStatus', { hash: item.hash }); } else if (item.type === 'user_invoice' || item.type === 'payment_request' || item.type === 'paid_invoice') { @@ -1826,7 +1834,7 @@ export const BlueTransactionListItem = React.memo(({ item, itemPriceUnit = Bitco NavigationService.navigate('ScanLndInvoiceRoot', { screen: 'LnurlPaySuccess', params: { - paymentHash: paymentHash, + paymentHash, justPaid: false, fromWalletID: lightningWallet[0].getID(), }, @@ -1841,34 +1849,31 @@ export const BlueTransactionListItem = React.memo(({ item, itemPriceUnit = Bitco }); } } - }; + }, [item]); - const onLongPress = () => { + const onLongPress = useCallback(() => { if (subtitleNumberOfLines === 1) { setSubtitleNumberOfLines(0); } - }; + }, [subtitleNumberOfLines]); + + const subtitleProps = useMemo(() => ({ numberOfLines: subtitleNumberOfLines }), [subtitleNumberOfLines]); return ( ); diff --git a/loc/en.json b/loc/en.json index 5be7915cf..ebe0880c9 100644 --- a/loc/en.json +++ b/loc/en.json @@ -293,7 +293,7 @@ "details_to": "Output", "details_transaction_details": "Transaction details", "enable_hw": "This wallet is not being used in conjunction with a hardwarde wallet. Would you like to enable hardware wallet use?", - "list_conf": "conf", + "list_conf": "conf: {number}", "list_title": "transactions", "rbf_explain": "We will replace this transaction with the one with a higher fee, so it should be mined faster. This is called RBF - Replace By Fee.", "rbf_title": "Bump fee (RBF)", diff --git a/screen/send/details.js b/screen/send/details.js index 940326a22..989dfc98d 100644 --- a/screen/send/details.js +++ b/screen/send/details.js @@ -816,6 +816,39 @@ export default class SendDetails extends Component { } }; + handleAddRecipient = () => { + const { addresses } = this.state; + addresses.push(new BitcoinTransaction()); + this.setState( + { + addresses, + isAdvancedTransactionOptionsVisible: false, + }, + () => { + this.scrollView.scrollToEnd(); + if (this.state.addresses.length > 1) this.scrollView.flashScrollIndicators(); + // after adding recipient it automatically scrolls to the last one + this.setState({ recipientsScrollIndex: this.state.addresses.length - 1 }); + }, + ); + }; + + handleRemoveRecipient = () => { + const { addresses } = this.state; + addresses.splice(this.state.recipientsScrollIndex, 1); + this.setState( + { + addresses, + isAdvancedTransactionOptionsVisible: false, + }, + () => { + if (this.state.addresses.length > 1) this.scrollView.flashScrollIndicators(); + // after deletion it automatically scrolls to the last one + this.setState({ recipientsScrollIndex: this.state.addresses.length - 1 }); + }, + ); + }; + renderAdvancedTransactionOptionsModal = () => { const isSendMaxUsed = this.state.addresses.some(element => element.amount === BitcoinUnit.MAX); return ( @@ -865,43 +898,14 @@ export default class SendDetails extends Component { title={loc.send.details_add_rec_add} hideChevron component={TouchableOpacity} - onPress={() => { - const addresses = this.state.addresses; - addresses.push(new BitcoinTransaction()); - this.setState( - { - addresses, - isAdvancedTransactionOptionsVisible: false, - }, - () => { - this.scrollView.scrollToEnd(); - if (this.state.addresses.length > 1) this.scrollView.flashScrollIndicators(); - // after adding recipient it automatically scrolls to the last one - this.setState({ recipientsScrollIndex: this.state.addresses.length - 1 }); - }, - ); - }} + onPress={this.handleAddRecipient} /> { - const addresses = this.state.addresses; - addresses.splice(this.state.recipientsScrollIndex, 1); - this.setState( - { - addresses, - isAdvancedTransactionOptionsVisible: false, - }, - () => { - if (this.state.addresses.length > 1) this.scrollView.flashScrollIndicators(); - // after deletion it automatically scrolls to the last one - this.setState({ recipientsScrollIndex: this.state.addresses.length - 1 }); - }, - ); - }} + onPress={this.handleRemoveRecipient} /> )} diff --git a/screen/wallets/transactions.js b/screen/wallets/transactions.js index 08e39879a..3fd02c491 100644 --- a/screen/wallets/transactions.js +++ b/screen/wallets/transactions.js @@ -1,5 +1,5 @@ /* global alert */ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useState, useCallback } from 'react'; import { Chain } from '../../models/bitcoinUnits'; import { Text, @@ -37,7 +37,7 @@ import Handoff from 'react-native-handoff'; import ActionSheet from '../ActionSheet'; import loc from '../../loc'; import { getSystemName } from 'react-native-device-info'; -import { useRoute, useNavigation, useTheme } from '@react-navigation/native'; +import { useRoute, useNavigation, useTheme, useFocusEffect } from '@react-navigation/native'; import BuyBitcoin from './buyBitcoin'; const BlueApp = require('../../BlueApp'); const EV = require('../../blue_modules/events'); @@ -230,7 +230,6 @@ const WalletTransactions = () => { return b.sort_ts - a.sort_ts; }); return txs.slice(0, limit); - // eslint-disable-next-line react-hooks/exhaustive-deps }; useEffect(() => { @@ -257,6 +256,13 @@ const WalletTransactions = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [wallet]); + // if description of transaction has been changed we want to show new one + useFocusEffect( + useCallback(() => { + setTimeElapsed(prev => prev + 1); + }, []), + ); + /** * Forcefully fetches TXs and balance for wallet */