Skip to content
This repository has been archived by the owner on Feb 23, 2021. It is now read-only.

Redirect navigation flow for 0 value invocies. Fixes #991 #1327

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/action/nav-mobile.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ class NavAction {
this._navigate('Pay');
}

goPayLightningSupplyAmount() {
this._navigate('PayLightningSupplyAmount');
}

goPayLightningConfirm() {
this._navigate('PayLightningConfirm');
}
Expand Down
4 changes: 4 additions & 0 deletions src/action/nav.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ class NavAction {
this._store.route = 'Pay';
}

goPayLightningSupplyAmount() {
this._store.route = 'PayLightningSupplyAmount';
}

goPayLightningConfirm() {
this._store.route = 'PayLightningConfirm';
}
Expand Down
46 changes: 43 additions & 3 deletions src/action/payment.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ class PaymentAction {
*/
init() {
this._store.payment.address = '';
this._store.payment.destination = '';
this._store.payment.amount = '';
this._store.payment.targetConf = MED_TARGET_CONF;
this._store.payment.fee = '';
Expand Down Expand Up @@ -165,14 +166,44 @@ class PaymentAction {
return this._notification.display({ msg: 'Enter an invoice or address' });
}
if (await this.decodeInvoice({ invoice: this._store.payment.address })) {
this._nav.goPayLightningConfirm();
if (this._store.payment.amount === '0') {
this._store.payment.amount = '';
this._nav.goPayLightningSupplyAmount();
} else {
this._nav.goPayLightningConfirm();
}
} else if (isAddress(this._store.payment.address)) {
this._nav.goPayBitcoin();
} else {
this._notification.display({ msg: 'Invalid invoice or address' });
}
}

/**
* Check If payment amount was supplied and destination is set. Estimate
* the routing fee and go to the confirmation view for lightning payments.
*
* Note: This function is dependent on that an invoice has already been decoded.
* If payment amount is 0 the function will display a message and return.
*/
async checkAmountSuppliedAndGoPayLightningConfirm() {
if (
this._store.payment.amount === '0' ||
this._store.payment.amount === ''
) {
this._notification.display({ msg: 'Enter amount to pay.' });
} else if (this._store.payment.destination === '') {
this._notification.display({ msg: 'Internal Error, try again.' });
this._nav.goHome();
} else {
this.estimateLightningFee({
destination: this._store.payment.destination,
satAmt: toSatoshis(this._store.payment.amount, this._store.settings),
});
this._nav.goPayLightningConfirm();
}
}

/**
* Attempt to decode a lightning invoice using the lnd grpc api. If it is
* an invoice the amount and note store values will be set and the lightning
Expand All @@ -188,6 +219,7 @@ class PaymentAction {
});
payment.amount = toAmount(request.numSatoshis, settings);
payment.note = request.description;
payment.destination = request.destination;
this.estimateLightningFee({
destination: request.destination,
satAmt: request.numSatoshis,
Expand Down Expand Up @@ -321,7 +353,7 @@ class PaymentAction {
}

/**
* Send the amount specified in the invoice as a lightning transaction and
* Send the amount specified in payment.amount as a lightning transaction and
* display the wait screen while the payment confirms.
* This action can be called from a view event handler as does all
* the necessary error handling and notification display.
Expand All @@ -336,6 +368,8 @@ class PaymentAction {
try {
this._nav.goWait();
const invoice = this._store.payment.address;
const { settings } = this._store;
const satAmt = toSatoshis(this._store.payment.amount, settings);
const stream = this._grpc.sendStreamCommand('sendPayment');
await new Promise((resolve, reject) => {
stream.on('data', data => {
Expand All @@ -346,7 +380,13 @@ class PaymentAction {
}
});
stream.on('error', reject);
stream.write(JSON.stringify({ paymentRequest: invoice }), 'utf8');
stream.write(
JSON.stringify({
paymentRequest: invoice,
amt: satAmt,
}),
'utf8'
);
});
if (failed) return;
this._nav.goPayLightningDone();
Expand Down
6 changes: 6 additions & 0 deletions src/view/main-mobile.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import SettingUnitView from './setting-unit';
import SettingFiatView from './setting-fiat';
import CLIView from './cli';
import PaymentView from './payment-mobile';
import PayLightningSupplyAmountView from './pay-lightning-supply-amount-mobile';
import PayLightningConfirmView from './pay-lightning-confirm-mobile';
import PayLightningDoneView from './pay-lightning-done-mobile';
import PaymentFailedView from './payment-failed-mobile';
Expand Down Expand Up @@ -171,6 +172,10 @@ const InvoiceQR = () => (

const Pay = () => <PaymentView store={store} payment={payment} nav={nav} />;

const PayLightningSupplyAmount = () => (
<PayLightningSupplyAmountView store={store} payment={payment} nav={nav} />
);

const PayLightningConfirm = () => (
<PayLightningConfirmView store={store} payment={payment} nav={nav} />
);
Expand Down Expand Up @@ -236,6 +241,7 @@ const InvoiceStack = createStackNavigator(
const PayStack = createStackNavigator(
{
Pay,
PayLightningSupplyAmount,
PayLightningConfirm,
Wait,
PayLightningDone,
Expand Down
4 changes: 4 additions & 0 deletions src/view/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import LoaderSyncing from './loader-syncing';
import Wait from './wait';
import Home from './home';
import Payment from './payment';
import PayLightningSupplyAmount from './pay-lightning-supply-amount';
import PayLightningConfirm from './pay-lightning-confirm';
import PayLightningDone from './pay-lightning-done';
import PaymentFailed from './payment-failed';
Expand Down Expand Up @@ -130,6 +131,9 @@ class MainView extends Component {
{route === 'Pay' && (
<Payment store={store} payment={payment} nav={nav} />
)}
{route === 'PayLightningSupplyAmount' && (
<PayLightningSupplyAmount store={store} payment={payment} nav={nav} />
)}
{route === 'PayLightningConfirm' && (
<PayLightningConfirm store={store} payment={payment} nav={nav} />
)}
Expand Down
84 changes: 84 additions & 0 deletions src/view/pay-lightning-supply-amount-mobile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import React from 'react';
import { StyleSheet } from 'react-native';
import { observer } from 'mobx-react';
import PropTypes from 'prop-types';
import Background from '../component/background';
import MainContent from '../component/main-content';
import { NamedField, AmountInputField } from '../component/field';
import { Header, Title } from '../component/header';
import { CancelButton, Button, SmallGlasButton } from '../component/button';
import Card from '../component/card';
import LightningBoltIcon from '../asset/icon/lightning-bolt';
import { FormStretcher, FormSubText } from '../component/form';
import { BalanceLabel, BalanceLabelUnit } from '../component/label';
import { color } from '../component/style';

const styles = StyleSheet.create({
balance: {
marginTop: 15,
},
unit: {
color: color.blackText,
},
form: {
paddingTop: 10,
paddingBottom: 10,
},
subText: {
paddingTop: 40,
paddingBottom: 40,
},
});

const PayLightningSupplyAmountView = ({ store, nav, payment }) => (
<Background color={color.purple}>
<Header color={color.purple}>
<Button disabled onPress={() => {}} />
<Title title="Payment Request For Any Amount">
<LightningBoltIcon height={12} width={6.1} />
</Title>
<CancelButton onPress={() => nav.goHome()} />
</Header>
<MainContent>
<Card style={styles.card}>
<BalanceLabel style={styles.balance}>
<AmountInputField
autoFocus={true}
value={store.invoice.amount}
onChangeText={amount => payment.setAmount({ amount })}
onSubmitEditing={() =>
payment.checkAmountSuppliedAndGoPayLightningConfirm()
}
/>
<BalanceLabelUnit style={styles.unit}>
{store.unitFiatLabel}
</BalanceLabelUnit>
</BalanceLabel>
<FormStretcher style={styles.form}>
{store.payment.note ? (
<NamedField name="Note" style={styles.note}>
{store.payment.note}
</NamedField>
) : null}
</FormStretcher>
<FormSubText style={styles.subText}>
Payment Request did not specify an amount. This is often used for
tips/donations.
</FormSubText>
</Card>
</MainContent>
<SmallGlasButton
onPress={() => payment.checkAmountSuppliedAndGoPayLightningConfirm()}
>
Pay
</SmallGlasButton>
</Background>
);

PayLightningSupplyAmountView.propTypes = {
store: PropTypes.object.isRequired,
nav: PropTypes.object.isRequired,
payment: PropTypes.object.isRequired,
};

export default observer(PayLightningSupplyAmountView);
92 changes: 92 additions & 0 deletions src/view/pay-lightning-supply-amount.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import React from 'react';
import { StyleSheet } from 'react-native';
import { observer } from 'mobx-react';
import PropTypes from 'prop-types';
import Background from '../component/background';
import MainContent from '../component/main-content';
import { NamedField, AmountInputField } from '../component/field';
import { Header, Title } from '../component/header';
import { CancelButton, BackButton, PillButton } from '../component/button';
import Card from '../component/card';
import LightningBoltIcon from '../asset/icon/lightning-bolt';
import { FormStretcher, FormSubText } from '../component/form';
import { BalanceLabel, BalanceLabelUnit } from '../component/label';
import { color } from '../component/style';

const styles = StyleSheet.create({
description: {
paddingLeft: 20,
paddingRight: 20,
},
unit: {
color: color.blackText,
},
maxBtn: {
marginTop: 10,
marginBottom: 20,
},
nextBtn: {
marginTop: 20,
backgroundColor: color.purple,
},
subText: {
paddingTop: 20,
paddingBottom: 40,
paddingLeft: 40,
paddingRight: 40,
},
});

const PayLightningSupplyAmountView = ({ store, nav, payment }) => (
<Background image="purple-gradient-bg">
<Header color={color.purple}>
<BackButton onPress={() => nav.goHome()} />
<Title title="Payment Request For Any Amount">
<LightningBoltIcon height={12} width={6.1} />
</Title>
<CancelButton onPress={() => nav.goHome()} />
</Header>
<MainContent>
<Card>
<BalanceLabel style={styles.balance}>
<AmountInputField
autoFocus={true}
value={store.payment.amount}
onChangeText={amount => payment.setAmount({ amount })}
onSubmitEditing={() =>
payment.checkAmountSuppliedAndGoPayLightningConfirm()
}
/>
<BalanceLabelUnit style={styles.unit}>
{store.unitFiatLabel}
</BalanceLabelUnit>
</BalanceLabel>
<FormStretcher>
{store.payment.note ? (
<NamedField name="Note" style={styles.note}>
{store.payment.note}
</NamedField>
) : null}
<FormSubText style={styles.subText}>
Payment Request did not specify an amount. This is often used for
tips/donations.
</FormSubText>
</FormStretcher>
<PillButton
style={styles.nextBtn}
onPress={() => payment.checkAmountSuppliedAndGoPayLightningConfirm()}
>
Pay
</PillButton>
</Card>
</MainContent>
</Background>
);

PayLightningSupplyAmountView.propTypes = {
store: PropTypes.object.isRequired,
nav: PropTypes.object.isRequired,
payment: PropTypes.object.isRequired,
};

export default observer(PayLightningSupplyAmountView);
7 changes: 7 additions & 0 deletions test/unit/action/nav.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,13 @@ describe('Action Nav Unit Tests', () => {
});
});

describe('goPayLightningSupplyAmount()', () => {
it('should set correct route', () => {
nav.goPayLightningSupplyAmount();
expect(store.route, 'to equal', 'PayLightningSupplyAmount');
});
});

describe('goPayLightningConfirm()', () => {
it('should set correct route', () => {
nav.goPayLightningConfirm();
Expand Down
Loading