/* global alert */ import React, { Component } from 'react'; import { Chain } from '../../models/bitcoinUnits'; import { Text, Platform, View, ActivityIndicator, InteractionManager, FlatList, RefreshControl, TouchableOpacity, StatusBar, } from 'react-native'; import PropTypes from 'prop-types'; import { NavigationEvents } from 'react-navigation'; import { BlueSendButtonIcon, BlueReceiveButtonIcon, BlueTransactionListItem, BlueWalletNavigationHeader } from '../../BlueComponents'; import { Icon } from 'react-native-elements'; import { LightningCustodianWallet } from '../../class'; import Handoff from 'react-native-handoff'; import { ScrollView } from 'react-native-gesture-handler'; /** @type {AppStorage} */ let BlueApp = require('../../BlueApp'); let loc = require('../../loc'); let EV = require('../../events'); let BlueElectrum = require('../../BlueElectrum'); export default class WalletTransactions extends Component { static navigationOptions = ({ navigation }) => { return { headerRight: ( navigation.navigate('WalletDetails', { wallet: navigation.state.params.wallet, }) } > ), headerStyle: { backgroundColor: navigation.getParam('headerColor'), borderBottomWidth: 0, elevation: 0, shadowRadius: 0, }, headerTintColor: '#FFFFFF', }; }; walletBalanceText = null; constructor(props) { super(props); // here, when we receive REMOTE_TRANSACTIONS_COUNT_CHANGED we fetch TXs and balance for current wallet EV(EV.enum.REMOTE_TRANSACTIONS_COUNT_CHANGED, this.refreshTransactionsFunction.bind(this)); const wallet = props.navigation.getParam('wallet'); this.props.navigation.setParams({ wallet: wallet, isLoading: true }); this.state = { showMarketplace: Platform.OS !== 'ios', isLoading: true, showShowFlatListRefreshControl: false, wallet: wallet, dataSource: this.getTransactions(15), limit: 15, pageSize: 20, }; } componentDidMount() { // nop this.props.navigation.setParams({ isLoading: false }); } /** * Forcefully fetches TXs and balance for wallet */ refreshTransactionsFunction() { let that = this; setTimeout(function() { that.refreshTransactions(); }, 4000); // giving a chance to remote server to propagate } /** * Simple wrapper for `wallet.getTransactions()`, where `wallet` is current wallet. * Sorts. Provides limiting. * * @param limit {Integer} How many txs return, starting from the earliest. Default: all of them. * @returns {Array} */ getTransactions(limit = Infinity) { let wallet = this.props.navigation.getParam('wallet'); let txs = wallet.getTransactions(); for (let tx of txs) { tx.sort_ts = +new Date(tx.received); } txs = txs.sort(function(a, b) { return b.sort_ts - a.sort_ts; }); return txs.slice(0, limit); } redrawScreen() { InteractionManager.runAfterInteractions(async () => { console.log('wallets/transactions redrawScreen()'); this.setState({ isLoading: false, showShowFlatListRefreshControl: false, dataSource: this.getTransactions(this.state.limit), }); }); } isLightning() { let w = this.state.wallet; if (w && w.type === LightningCustodianWallet.type) { return true; } return false; } /** * Forcefully fetches TXs and balance for wallet */ refreshTransactions() { if (this.state.isLoading) return; this.setState( { showShowFlatListRefreshControl: true, isLoading: true, }, async () => { let noErr = true; let smthChanged = false; try { await BlueElectrum.ping(); await BlueElectrum.waitTillConnected(); /** @type {LegacyWallet} */ let wallet = this.state.wallet; let balanceStart = +new Date(); const oldBalance = wallet.getBalance(); await wallet.fetchBalance(); if (oldBalance !== wallet.getBalance()) smthChanged = true; let balanceEnd = +new Date(); console.log(wallet.getLabel(), 'fetch balance took', (balanceEnd - balanceStart) / 1000, 'sec'); let start = +new Date(); const oldTxLen = wallet.getTransactions().length; await wallet.fetchTransactions(); if (oldTxLen !== wallet.getTransactions().length) smthChanged = true; if (wallet.fetchPendingTransactions) { await wallet.fetchPendingTransactions(); } if (wallet.fetchUserInvoices) { await wallet.fetchUserInvoices(); } let end = +new Date(); console.log(wallet.getLabel(), 'fetch tx took', (end - start) / 1000, 'sec'); } catch (err) { noErr = false; alert(err.message); this.setState({ isLoading: false, showShowFlatListRefreshControl: false, }); } if (noErr && smthChanged) { console.log('saving to disk'); await BlueApp.saveToDisk(); // caching EV(EV.enum.TRANSACTIONS_COUNT_CHANGED); // let other components know they should redraw } this.redrawScreen(); }, ); } _keyExtractor = (_item, index) => index.toString(); renderListFooterComponent = () => { // if not all txs rendered - display indicator return (this.getTransactions(Infinity).length > this.state.limit && ) || ; }; renderListHeaderComponent = () => { return ( {loc.transactions.list.title} ); }; async onWillBlur() { StatusBar.setBarStyle('dark-content'); } componentWillUnmount() { this.onWillBlur(); } renderItem = item => { return ; }; render() { const { navigate } = this.props.navigation; return ( {this.state.wallet.chain === Chain.ONCHAIN && ( )} { StatusBar.setBarStyle('light-content'); this.redrawScreen(); }} onWillBlur={() => this.onWillBlur()} onDidFocus={() => this.props.navigation.setParams({ isLoading: false })} /> InteractionManager.runAfterInteractions(async () => { this.setState({ wallet }, () => InteractionManager.runAfterInteractions(() => BlueApp.saveToDisk())); }) } /> {this.state.showMarketplace && ( { if (this.state.wallet.type === LightningCustodianWallet.type) { navigate('LappBrowser', { fromSecret: this.state.wallet.getSecret(), fromWallet: this.state.wallet }); } else { navigate('Marketplace', { fromWallet: this.state.wallet }); } }} > marketplace )} { // pagination in works. in this block we will add more txs to flatlist // so as user scrolls closer to bottom it will render mode transactions if (this.getTransactions(Infinity).length < this.state.limit) { // all list rendered. nop return; } this.setState({ dataSource: this.getTransactions(this.state.limit + this.state.pageSize), limit: this.state.limit + this.state.pageSize, pageSize: this.state.pageSize * 2, }); }} ListHeaderComponent={this.renderListHeaderComponent} ListFooterComponent={this.renderListFooterComponent} ListEmptyComponent={ {(this.isLightning() && loc.wallets.list.empty_txs1_lightning) || loc.wallets.list.empty_txs1} {(this.isLightning() && loc.wallets.list.empty_txs2_lightning) || loc.wallets.list.empty_txs2} {!this.isLightning() && ( this.props.navigation.navigate('BuyBitcoin', { address: this.state.wallet.getAddress(), secret: this.state.wallet.getSecret(), }) } > {loc.wallets.list.tap_here_to_buy} )} } refreshControl={ this.refreshTransactions()} refreshing={this.state.showShowFlatListRefreshControl} /> } data={this.state.dataSource} keyExtractor={this._keyExtractor} renderItem={this.renderItem} contentInset={{ top: 0, left: 0, bottom: 90, right: 0 }} /> {(() => { if (this.state.wallet.allowReceive()) { return ( { if (this.state.wallet.type === LightningCustodianWallet.type) { navigate('LNDCreateInvoice', { fromWallet: this.state.wallet }); } else { navigate('ReceiveDetails', { address: this.state.wallet.getAddress(), secret: this.state.wallet.getSecret() }); } }} /> ); } })()} {(() => { if (this.state.wallet.allowSend()) { return ( { if (this.state.wallet.type === LightningCustodianWallet.type) { navigate('ScanLndInvoice', { fromSecret: this.state.wallet.getSecret() }); } else { navigate('SendDetails', { fromAddress: this.state.wallet.getAddress(), fromSecret: this.state.wallet.getSecret(), fromWallet: this.state.wallet, }); } }} /> ); } })()} ); } } WalletTransactions.propTypes = { navigation: PropTypes.shape({ navigate: PropTypes.func, goBack: PropTypes.func, getParam: PropTypes.func, setParams: PropTypes.func, }), };