import React, { Component } from 'react'; import { View, Dimensions, ScrollView, BackHandler, InteractionManager } from 'react-native'; import Share from 'react-native-share'; import { BlueLoading, BlueText, SafeBlueArea, BlueButton, BlueCopyTextToClipboard, BlueNavigationStyle, BlueSpacing20, } from '../../BlueComponents'; import PropTypes from 'prop-types'; import ReactNativeHapticFeedback from 'react-native-haptic-feedback'; import { Icon } from 'react-native-elements'; import QRCode from 'react-native-qrcode-svg'; import SystemSetting from 'react-native-system-setting'; /** @type {AppStorage} */ let BlueApp = require('../../BlueApp'); const loc = require('../../loc'); const EV = require('../../events'); const { width, height } = Dimensions.get('window'); export default class LNDViewInvoice extends Component { static navigationOptions = ({ navigation }) => navigation.getParam('isModal') === true ? { ...BlueNavigationStyle(navigation, true, () => navigation.dismiss()), title: 'Lightning Invoice', headerLeft: null, } : { ...BlueNavigationStyle(), title: 'Lightning Invoice' }; constructor(props) { super(props); const invoice = props.navigation.getParam('invoice'); const fromWallet = props.navigation.getParam('fromWallet'); this.state = { invoice, fromWallet, isLoading: typeof invoice === 'string', addressText: typeof invoice === 'object' && invoice.hasOwnProperty('payment_request') ? invoice.payment_request : invoice, isFetchingInvoices: true, qrCodeHeight: height > width ? width - 20 : width / 2, }; this.fetchInvoiceInterval = undefined; BackHandler.addEventListener('hardwareBackPress', this.handleBackButton); } async componentDidMount() { this.fetchInvoiceInterval = setInterval(async () => { if (this.state.isFetchingInvoices) { try { const userInvoices = await this.state.fromWallet.getUserInvoices(20); // fetching only last 20 invoices // for invoice that was created just now - that should be enough (it is basically the last one, so limit=1 would be sufficient) // but that might not work as intended IF user creates 21 invoices, and then tries to check the status of invoice #0, it just wont be updated const updatedUserInvoice = userInvoices.filter(invoice => typeof this.state.invoice === 'object' ? invoice.payment_request === this.state.invoice.payment_request : invoice.payment_request === this.state.invoice, )[0]; if (typeof updatedUserInvoice !== 'undefined') { this.setState({ invoice: updatedUserInvoice, isLoading: false, addressText: updatedUserInvoice.payment_request }); await SystemSetting.saveBrightness(); await SystemSetting.setAppBrightness(1.0); if (updatedUserInvoice.ispaid) { // we fetched the invoice, and it is paid :-) this.setState({ isFetchingInvoices: false }); ReactNativeHapticFeedback.trigger('notificationSuccess', { ignoreAndroidSystemSettings: false }); clearInterval(this.fetchInvoiceInterval); this.fetchInvoiceInterval = undefined; EV(EV.enum.REMOTE_TRANSACTIONS_COUNT_CHANGED); // remote because we want to refetch from server tx list and balance } else { const currentDate = new Date(); const now = (currentDate.getTime() / 1000) | 0; const invoiceExpiration = updatedUserInvoice.timestamp + updatedUserInvoice.expire_time; if (invoiceExpiration < now && !updatedUserInvoice.ispaid) { // invoice expired :-( this.setState({ isFetchingInvoices: false }); ReactNativeHapticFeedback.trigger('notificationError', { ignoreAndroidSystemSettings: false }); clearInterval(this.fetchInvoiceInterval); this.fetchInvoiceInterval = undefined; EV(EV.enum.TRANSACTIONS_COUNT_CHANGED); } } } } catch (error) { console.log(error); } } }, 3000); } async componentWillUnmount() { clearInterval(this.fetchInvoiceInterval); this.fetchInvoiceInterval = undefined; BackHandler.removeEventListener('hardwareBackPress', this.handleBackButton); await SystemSetting.restoreBrightness(); } handleBackButton = () => { this.props.navigation.goBack(null); return true; }; onLayout = () => { const { height } = Dimensions.get('window'); this.setState({ qrCodeHeight: height > width ? width - 20 : width / 2 }); }; render() { if (this.state.isLoading) { return ; } const { invoice } = this.state; if (typeof invoice === 'object') { const currentDate = new Date(); const now = (currentDate.getTime() / 1000) | 0; const invoiceExpiration = invoice.timestamp + invoice.expire_time; if (this.state.showPreimageQr) { return ( Preimage: (this.qrCodeSVG = c)} color={BlueApp.settings.foregroundColor} logoBackgroundColor={BlueApp.settings.brandingColor} /> ); } if (invoice.ispaid || invoice.type === 'paid_invoice') { return ( {loc.lndViewInvoice.has_been_paid} {invoice.payment_preimage && typeof invoice.payment_preimage === 'string' && ( this.setState({ showPreimageQr: true })} title=" " /> )} ); } if (invoiceExpiration < now && !invoice.ispaid) { return ( {loc.lndViewInvoice.wasnt_paid_and_expired} ); } else if (invoiceExpiration > now && invoice.ispaid) { if (invoice.ispaid) { return ( {loc.lndViewInvoice.has_been_paid} ); } } } // Invoice has not expired, nor has it been paid for. return ( (this.qrCodeSVG = c)} color={BlueApp.settings.foregroundColor} logoBackgroundColor={BlueApp.settings.brandingColor} /> {invoice && invoice.amt && ( {loc.lndViewInvoice.please_pay} {invoice.amt} {loc.lndViewInvoice.sats} )} {invoice && invoice.hasOwnProperty('description') && invoice.description.length > 0 && ( {loc.lndViewInvoice.for} {invoice.description} )} { if (this.qrCodeSVG === undefined) { Share.open({ message: `lightning:${invoice.payment_request}` }).catch(error => console.log(error)); } else { InteractionManager.runAfterInteractions(async () => { this.qrCodeSVG.toDataURL(data => { let shareImageBase64 = { message: `lightning:${invoice.payment_request}`, url: `data:image/png;base64,${data}`, }; Share.open(shareImageBase64).catch(error => console.log(error)); }); }); } }} title={loc.receive.details.share} /> this.props.navigation.navigate('LNDViewAdditionalInvoiceInformation', { fromWallet: this.state.fromWallet })} title={loc.lndViewInvoice.additional_info} /> ); } } LNDViewInvoice.propTypes = { navigation: PropTypes.shape({ goBack: PropTypes.func, navigate: PropTypes.func, getParam: PropTypes.func, popToTop: PropTypes.func, }), };