import React, { useContext, useEffect, useRef, useState } from 'react'; import { View, Text, ScrollView, BackHandler, TouchableOpacity, StyleSheet, I18nManager, Image } from 'react-native'; import Share from 'react-native-share'; import { Icon } from 'react-native-elements'; import QRCodeComponent from '../../components/QRCodeComponent'; import { useNavigation, useNavigationState, useRoute } from '@react-navigation/native'; import { BlueLoading, BlueText, BlueSpacing20, BlueTextCentered } from '../../BlueComponents'; import loc from '../../loc'; import { BlueStorageContext } from '../../blue_modules/storage-context'; import { BitcoinUnit } from '../../models/bitcoinUnits'; import { SuccessView } from '../send/success'; import LNDCreateInvoice from './lndCreateInvoice'; import { useTheme } from '../../components/themes'; import Button from '../../components/Button'; import triggerHapticFeedback, { HapticFeedbackTypes } from '../../blue_modules/hapticFeedback'; import SafeArea from '../../components/SafeArea'; import CopyTextToClipboard from '../../components/CopyTextToClipboard'; const LNDViewInvoice = () => { const { invoice, walletID } = useRoute().params; const { wallets, setSelectedWalletID, fetchAndSaveWalletTransactions } = useContext(BlueStorageContext); const wallet = wallets.find(w => w.getID() === walletID); const { colors, closeImage } = useTheme(); const { goBack, navigate, setParams, setOptions, getParent } = useNavigation(); const [isLoading, setIsLoading] = useState(typeof invoice === 'string'); const [isFetchingInvoices, setIsFetchingInvoices] = useState(true); const [invoiceStatusChanged, setInvoiceStatusChanged] = useState(false); const [qrCodeSize, setQRCodeSize] = useState(90); const fetchInvoiceInterval = useRef(); const isModal = useNavigationState(state => state.routeNames[0] === LNDCreateInvoice.routeName); const stylesHook = StyleSheet.create({ root: { backgroundColor: colors.background, }, detailsText: { color: colors.alternativeTextColor, }, expired: { backgroundColor: colors.success, }, additionalInfo: { backgroundColor: colors.brandingColor, }, }); useEffect(() => { BackHandler.addEventListener('hardwareBackPress', handleBackButton); return () => { BackHandler.removeEventListener('hardwareBackPress', handleBackButton); clearInterval(fetchInvoiceInterval.current); fetchInvoiceInterval.current = undefined; }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); useEffect(() => { setOptions( isModal ? { headerStyle: { backgroundColor: colors.customHeader, }, gestureEnabled: false, headerBackVisible: false, // eslint-disable-next-line react/no-unstable-nested-components headerRight: () => ( { getParent().pop(); }} testID="NavigationCloseButton" > ), } : { headerRight: () => {}, headerStyle: { backgroundColor: colors.customHeader, }, }, ); // eslint-disable-next-line react-hooks/exhaustive-deps }, [colors, isModal]); useEffect(() => { setSelectedWalletID(walletID); console.log('LNDViewInvoice - useEffect'); if (!invoice.ispaid) { fetchInvoiceInterval.current = setInterval(async () => { if (isFetchingInvoices) { try { const userInvoices = await wallet.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(filteredInvoice => typeof invoice === 'object' ? filteredInvoice.payment_request === invoice.payment_request : filteredInvoice.payment_request === invoice, )[0]; if (typeof updatedUserInvoice !== 'undefined') { setInvoiceStatusChanged(true); setParams({ invoice: updatedUserInvoice }); setIsLoading(false); if (updatedUserInvoice.ispaid) { // we fetched the invoice, and it is paid :-) setIsFetchingInvoices(false); fetchAndSaveWalletTransactions(walletID); } else { const currentDate = new Date(); const now = (currentDate.getTime() / 1000) | 0; // eslint-disable-line no-bitwise const invoiceExpiration = updatedUserInvoice.timestamp + updatedUserInvoice.expire_time; if (invoiceExpiration < now && !updatedUserInvoice.ispaid) { // invoice expired :-( fetchAndSaveWalletTransactions(walletID); setIsFetchingInvoices(false); triggerHapticFeedback(HapticFeedbackTypes.NotificationError); clearInterval(fetchInvoiceInterval.current); fetchInvoiceInterval.current = undefined; } } } } catch (error) { console.log(error); } } }, 3000); } else { setIsFetchingInvoices(false); clearInterval(fetchInvoiceInterval.current); fetchInvoiceInterval.current = undefined; } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const handleBackButton = () => { goBack(null); return true; }; const navigateToPreImageScreen = () => { navigate('LNDViewAdditionalInvoicePreImage', { preImageData: invoice.payment_preimage && typeof invoice.payment_preimage === 'string' ? invoice.payment_preimage : 'none', }); }; const handleOnSharePressed = () => { Share.open({ message: `lightning:${invoice.payment_request}` }).catch(error => console.log(error)); }; const handleOnViewAdditionalInformationPressed = () => { navigate('LNDViewAdditionalInvoiceInformation', { walletID }); }; useEffect(() => { if (invoice.ispaid && invoiceStatusChanged) { setInvoiceStatusChanged(true); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [invoice]); useEffect(() => { if (invoiceStatusChanged) { triggerHapticFeedback(HapticFeedbackTypes.NotificationSuccess); } }, [invoiceStatusChanged]); const onLayout = e => { const { height, width } = e.nativeEvent.layout; setQRCodeSize(height > width ? width - 40 : e.nativeEvent.layout.width / 1.8); }; const render = () => { if (isLoading) { return ( ); } if (typeof invoice === 'object') { const currentDate = new Date(); const now = (currentDate.getTime() / 1000) | 0; // eslint-disable-line no-bitwise const invoiceExpiration = invoice.timestamp + invoice.expire_time; if (invoice.ispaid || invoice.type === 'paid_invoice') { let amount = 0; if (invoice.type === 'paid_invoice' && invoice.value) { amount = invoice.value; } else if (invoice.type === 'user_invoice' && invoice.amt) { amount = invoice.amt; } let description = invoice.description; if (invoice.memo && invoice.memo.length > 0) { description = invoice.memo; } return ( {invoice.payment_preimage && typeof invoice.payment_preimage === 'string' ? ( {loc.send.create_details} ) : undefined} ); } if (invoiceExpiration < now) { return ( {loc.lndViewInvoice.wasnt_paid_and_expired} ); } // Invoice has not expired, nor has it been paid for. return ( {loc.lndViewInvoice.please_pay} {invoice.amt} {loc.lndViewInvoice.sats} {'description' in invoice && invoice.description.length > 0 && ( {loc.lndViewInvoice.for} {invoice.description} )}