BlueWallet/screen/lnd/lndViewInvoice.js

426 lines
13 KiB
JavaScript
Raw Normal View History

2020-11-23 22:55:04 -05:00
import React, { useContext, useEffect, useRef, useState } from 'react';
2021-08-16 00:43:04 -04:00
import { View, Text, StatusBar, ScrollView, BackHandler, TouchableOpacity, StyleSheet, I18nManager } from 'react-native';
2019-08-06 00:20:33 -04:00
import Share from 'react-native-share';
2020-12-25 19:09:53 +03:00
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
import { Icon } from 'react-native-elements';
import QRCode from 'react-native-qrcode-svg';
import { useNavigation, useRoute, useTheme } from '@react-navigation/native';
import {
BlueLoading,
BlueText,
SafeBlueArea,
BlueButton,
BlueCopyTextToClipboard,
BlueSpacing20,
2020-11-24 22:01:51 -05:00
BlueTextCentered,
} from '../../BlueComponents';
2020-12-25 19:09:53 +03:00
import navigationStyle from '../../components/navigationStyle';
2020-07-20 16:38:46 +03:00
import loc from '../../loc';
import { BlueStorageContext } from '../../blue_modules/storage-context';
2020-11-23 23:41:37 -05:00
import { BitcoinUnit } from '../../models/bitcoinUnits';
2020-11-24 22:01:51 -05:00
import { SuccessView } from '../send/success';
2021-08-03 11:05:16 -04:00
import ToolTipMenu from '../../components/TooltipMenu';
2020-11-23 22:54:58 -05:00
const LNDViewInvoice = () => {
2020-11-27 22:06:37 -05:00
const { invoice, walletID, isModal } = useRoute().params;
const { wallets, setSelectedWallet, fetchAndSaveWalletTransactions } = useContext(BlueStorageContext);
const wallet = wallets.find(w => w.getID() === walletID);
2020-11-23 22:54:58 -05:00
const { colors } = useTheme();
2020-11-24 22:01:51 -05:00
const { goBack, navigate, setParams, setOptions } = useNavigation();
2020-11-23 22:54:58 -05:00
const [isLoading, setIsLoading] = useState(typeof invoice === 'string');
const [isFetchingInvoices, setIsFetchingInvoices] = useState(true);
2020-11-24 22:01:51 -05:00
const [invoiceStatusChanged, setInvoiceStatusChanged] = useState(false);
2021-02-03 12:49:31 -05:00
const [qrCodeSize, setQRCodeSize] = useState(90);
2020-11-23 22:54:58 -05:00
const fetchInvoiceInterval = useRef();
2021-08-03 11:05:16 -04:00
const qrCode = useRef();
2020-11-23 22:54:58 -05:00
const stylesHook = StyleSheet.create({
root: {
backgroundColor: colors.background,
},
valueText: {
color: colors.alternativeTextColor2,
},
2020-11-23 23:41:37 -05:00
valueRoot: {
backgroundColor: colors.background,
},
2020-11-23 22:54:58 -05:00
valueSats: {
color: colors.alternativeTextColor2,
},
paidMark: {
backgroundColor: colors.success,
},
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 === true
? {
headerStyle: {
backgroundColor: colors.customHeader,
2020-11-27 22:26:38 -05:00
borderBottomWidth: 0,
elevation: 0,
shadowOpacity: 0,
shadowOffset: { height: 0, width: 0 },
2020-11-23 22:54:58 -05:00
},
gestureEnabled: false,
}
: {
headerStyle: {
backgroundColor: colors.customHeader,
2020-11-27 22:26:38 -05:00
borderBottomWidth: 0,
elevation: 0,
shadowOpacity: 0,
shadowOffset: { height: 0, width: 0 },
2020-11-23 22:54:58 -05:00
},
},
);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [colors]);
useEffect(() => {
2020-11-27 22:06:37 -05:00
setSelectedWallet(walletID);
2020-11-23 22:54:58 -05:00
console.log('LNDViewInvoice - useEffect');
2020-11-24 22:01:51 -05:00
if (!invoice.ispaid) {
fetchInvoiceInterval.current = setInterval(async () => {
if (isFetchingInvoices) {
try {
2020-11-27 22:06:37 -05:00
const userInvoices = await wallet.getUserInvoices(20);
2020-11-24 22:01:51 -05:00
// 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 :-)
2020-11-23 22:54:58 -05:00
setIsFetchingInvoices(false);
2020-11-27 22:06:37 -05:00
fetchAndSaveWalletTransactions(walletID);
2020-11-24 22:01:51 -05:00
} 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 :-(
2020-11-27 22:06:37 -05:00
fetchAndSaveWalletTransactions(walletID);
2020-11-24 22:01:51 -05:00
setIsFetchingInvoices(false);
ReactNativeHapticFeedback.trigger('notificationError', { ignoreAndroidSystemSettings: false });
clearInterval(fetchInvoiceInterval.current);
fetchInvoiceInterval.current = undefined;
}
2020-11-23 22:54:58 -05:00
}
}
2020-11-24 22:01:51 -05:00
} catch (error) {
console.log(error);
2020-11-23 22:54:58 -05:00
}
}
2020-11-24 22:01:51 -05:00
}, 3000);
} else {
setIsFetchingInvoices(false);
clearInterval(fetchInvoiceInterval.current);
fetchInvoiceInterval.current = undefined;
}
2020-11-23 22:54:58 -05:00
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const handleBackButton = () => {
goBack(null);
return true;
};
2020-11-27 22:24:20 -05:00
const navigateToPreImageScreen = () => {
navigate('LNDViewAdditionalInvoicePreImage', {
preImageData: invoice.payment_preimage && typeof invoice.payment_preimage === 'string' ? invoice.payment_preimage : 'none',
});
2020-11-23 22:54:58 -05:00
};
const handleOnSharePressed = () => {
Share.open({ message: `lightning:${invoice.payment_request}` }).catch(error => console.log(error));
};
const handleOnViewAdditionalInformationPressed = () => {
2020-11-27 22:06:37 -05:00
navigate('LNDViewAdditionalInvoiceInformation', { walletID });
2020-11-23 22:54:58 -05:00
};
2021-08-03 11:05:16 -04:00
const handleShareQRCode = () => {
qrCode.current.toDataURL(data => {
const shareImageBase64 = {
url: `data:image/png;base64,${data}`,
};
Share.open(shareImageBase64).catch(error => console.log(error));
});
};
2020-11-23 23:41:37 -05:00
useEffect(() => {
2020-11-24 22:01:51 -05:00
if (invoice.ispaid && invoiceStatusChanged) {
ReactNativeHapticFeedback.trigger('notificationSuccess', { ignoreAndroidSystemSettings: false });
setInvoiceStatusChanged(true);
2020-11-23 23:41:37 -05:00
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [invoice]);
2020-11-23 22:54:58 -05:00
2021-02-03 12:49:31 -05:00
const onLayout = e => {
2021-02-24 20:49:14 -05:00
const { height, width } = e.nativeEvent.layout;
setQRCodeSize(height > width ? width - 40 : e.nativeEvent.layout.width / 1.8);
2021-02-03 12:49:31 -05:00
};
2020-11-23 23:41:37 -05:00
const render = () => {
if (isLoading) {
2020-11-23 22:54:58 -05:00
return (
2020-11-23 23:41:37 -05:00
<View style={[styles.root, stylesHook.root]}>
<BlueLoading />
</View>
);
}
if (typeof invoice === 'object') {
const currentDate = new Date();
const now = (currentDate.getTime() / 1000) | 0;
const invoiceExpiration = invoice.timestamp + invoice.expire_time;
if (invoice.ispaid || invoice.type === 'paid_invoice') {
2020-11-24 22:01:51 -05:00
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;
}
2020-11-23 23:41:37 -05:00
return (
2020-11-27 22:06:37 -05:00
<View style={styles.root}>
2020-11-24 22:01:51 -05:00
<SuccessView
amount={amount}
amountUnit={BitcoinUnit.SATS}
invoiceDescription={description}
shouldAnimate={invoiceStatusChanged}
/>
2020-11-23 23:41:37 -05:00
<View style={styles.detailsRoot}>
{invoice.payment_preimage && typeof invoice.payment_preimage === 'string' ? (
<TouchableOpacity accessibilityRole="button" style={styles.detailsTouch} onPress={navigateToPreImageScreen}>
2020-11-23 23:41:37 -05:00
<Text style={[styles.detailsText, stylesHook.detailsText]}>{loc.send.create_details}</Text>
2021-03-19 11:12:17 -04:00
<Icon
name={I18nManager.isRTL ? 'angle-left' : 'angle-right'}
size={18}
type="font-awesome"
color={colors.alternativeTextColor}
/>
2020-11-23 23:41:37 -05:00
</TouchableOpacity>
2020-11-24 22:01:51 -05:00
) : undefined}
2020-11-23 23:41:37 -05:00
</View>
2020-11-24 22:01:51 -05:00
</View>
2020-11-23 23:41:37 -05:00
);
}
2020-11-24 22:05:36 -05:00
if (invoiceExpiration < now) {
2020-11-23 23:41:37 -05:00
return (
2020-11-27 22:06:37 -05:00
<View style={[styles.root, stylesHook.root, styles.justifyContentCenter]}>
2020-11-23 22:54:58 -05:00
<View style={[styles.expired, stylesHook.expired]}>
<Icon name="times" size={50} type="font-awesome" color={colors.successCheck} />
</View>
2020-11-24 22:01:51 -05:00
<BlueTextCentered>{loc.lndViewInvoice.wasnt_paid_and_expired}</BlueTextCentered>
2020-11-23 22:54:58 -05:00
</View>
);
}
2020-11-23 23:41:37 -05:00
// Invoice has not expired, nor has it been paid for.
return (
<ScrollView>
<View style={[styles.activeRoot, stylesHook.root]}>
2021-08-16 00:43:04 -04:00
<View style={styles.activeQrcode}>
<ToolTipMenu
actions={[
{
id: LNDViewInvoice.actionKeys.Share,
2021-08-16 00:43:04 -04:00
text: loc.receive.details_share,
icon: LNDViewInvoice.actionIcons.Share,
2021-08-16 00:43:04 -04:00
},
]}
onPress={handleShareQRCode}
>
2021-08-03 11:05:16 -04:00
<QRCode
value={invoice.payment_request}
logo={require('../../img/qr-code.png')}
size={qrCodeSize}
logoSize={90}
color="#000000"
logoBackgroundColor={colors.brandingColor}
backgroundColor="#FFFFFF"
getRef={qrCode}
/>
2021-08-16 00:43:04 -04:00
</ToolTipMenu>
</View>
<BlueSpacing20 />
2020-11-23 22:54:58 -05:00
<BlueText>
{loc.lndViewInvoice.please_pay} {invoice.amt} {loc.lndViewInvoice.sats}
2020-11-23 22:54:58 -05:00
</BlueText>
{'description' in invoice && invoice.description.length > 0 && (
<BlueText>
{loc.lndViewInvoice.for} {invoice.description}
</BlueText>
)}
<BlueCopyTextToClipboard text={invoice.payment_request} />
2020-11-23 22:54:58 -05:00
<BlueButton onPress={handleOnSharePressed} title={loc.receive.details_share} />
2020-11-23 23:41:37 -05:00
<BlueSpacing20 />
<BlueButton
style={stylesHook.additionalInfo}
onPress={handleOnViewAdditionalInformationPressed}
title={loc.lndViewInvoice.additional_info}
/>
</View>
</ScrollView>
2020-11-23 23:41:37 -05:00
);
}
};
return (
<SafeBlueArea onLayout={onLayout}>
2020-11-23 23:41:37 -05:00
<StatusBar barStyle="default" />
2021-04-16 10:05:55 -04:00
{render()}
2020-11-23 22:54:58 -05:00
</SafeBlueArea>
);
};
2018-12-25 11:34:51 -05:00
LNDViewInvoice.actionKeys = {
Share: 'share',
};
LNDViewInvoice.actionIcons = {
Share: {
iconType: 'SYSTEM',
iconValue: 'square.and.arrow.up',
},
};
const styles = StyleSheet.create({
root: {
flex: 1,
2021-04-16 10:05:55 -04:00
justifyContent: 'space-between',
},
2020-11-27 22:06:37 -05:00
justifyContentCenter: {
justifyContent: 'center',
2020-11-26 21:02:45 -05:00
},
2020-08-03 13:26:16 -04:00
qrCodeContainer: { borderWidth: 6, borderRadius: 8, borderColor: '#FFFFFF' },
valueAmount: {
flexDirection: 'row',
justifyContent: 'center',
paddingBottom: 8,
},
valueText: {
fontSize: 32,
fontWeight: '600',
},
valueSats: {
fontSize: 16,
marginHorizontal: 4,
paddingBottom: 3,
fontWeight: '600',
alignSelf: 'flex-end',
},
memo: {
color: '#9aa0aa',
fontSize: 14,
marginHorizontal: 4,
paddingBottom: 6,
fontWeight: '400',
alignSelf: 'center',
},
paid: {
flex: 3,
alignItems: 'center',
justifyContent: 'center',
},
paidMark: {
marginTop: -100,
marginBottom: 16,
},
detailsRoot: {
justifyContent: 'flex-end',
marginBottom: 24,
alignItems: 'center',
},
detailsTouch: {
flexDirection: 'row',
alignItems: 'center',
},
detailsText: {
fontSize: 14,
marginRight: 8,
},
expired: {
width: 120,
height: 120,
borderRadius: 60,
alignSelf: 'center',
justifyContent: 'center',
marginBottom: 30,
},
activeRoot: {
flex: 1,
alignItems: 'center',
justifyContent: 'space-between',
},
activeQrcode: {
alignItems: 'center',
justifyContent: 'center',
2020-08-04 20:28:37 -04:00
marginHorizontal: 16,
2020-08-03 13:26:16 -04:00
borderWidth: 6,
borderRadius: 8,
borderColor: '#FFFFFF',
},
});
2020-12-25 19:09:53 +03:00
LNDViewInvoice.navigationOptions = navigationStyle(
{
closeButton: true,
closeButtonFunc: ({ navigation }) => navigation.dangerouslyGetParent().pop(),
},
(options, { theme, navigation, route }) => {
const additionalOptions =
route.params.isModal === true
? {
headerLeft: null,
gestureEnabled: false,
}
: {
headerRight: null,
};
2020-12-04 13:39:47 +00:00
2020-12-25 19:09:53 +03:00
return {
...options,
...additionalOptions,
2021-02-15 11:03:54 +03:00
title: loc.lndViewInvoice.lightning_invoice,
2020-12-25 19:09:53 +03:00
};
},
);
export default LNDViewInvoice;