Browse Source

feat(payments): add CreateInvoice modal component

master
jamaljsr 5 years ago
parent
commit
d9032e1506
  1. 4
      src/components/designer/NetworkDesigner.tsx
  2. 124
      src/components/designer/lightning/actions/CreateInvoiceModal.tsx
  3. 4
      src/components/designer/lightning/actions/PaymentButtons.tsx
  4. 8
      src/i18n/locales/en-US.json
  5. 8
      src/i18n/locales/es.json
  6. 15
      src/store/models/modals.ts

4
src/components/designer/NetworkDesigner.tsx

@ -7,6 +7,7 @@ import { Network } from 'types';
import { Loader } from 'components/common'; import { Loader } from 'components/common';
import { Link, NodeInner, Port, Ports } from './custom'; import { Link, NodeInner, Port, Ports } from './custom';
import { OpenChannelModal } from './lightning/actions'; import { OpenChannelModal } from './lightning/actions';
import CreateInvoiceModal from './lightning/actions/CreateInvoiceModal';
import Sidebar from './Sidebar'; import Sidebar from './Sidebar';
const Styled = { const Styled = {
@ -27,7 +28,7 @@ interface Props {
const NetworkDesigner: React.FC<Props> = ({ network, updateStateDelay = 3000 }) => { const NetworkDesigner: React.FC<Props> = ({ network, updateStateDelay = 3000 }) => {
const { setActiveId, ...callbacks } = useStoreActions(s => s.designer); const { setActiveId, ...callbacks } = useStoreActions(s => s.designer);
const { allCharts, activeId } = useStoreState(s => s.designer); const { allCharts, activeId } = useStoreState(s => s.designer);
const { openChannel } = useStoreState(s => s.modals); const { openChannel, createInvoice } = useStoreState(s => s.modals);
// update the redux store with the current network's chart // update the redux store with the current network's chart
useEffect(() => { useEffect(() => {
if (allCharts[network.id] && activeId !== network.id) setActiveId(network.id); if (allCharts[network.id] && activeId !== network.id) setActiveId(network.id);
@ -60,6 +61,7 @@ const NetworkDesigner: React.FC<Props> = ({ network, updateStateDelay = 3000 })
/> />
<Sidebar network={network} chart={chart} /> <Sidebar network={network} chart={chart} />
{openChannel.visible && <OpenChannelModal network={network} />} {openChannel.visible && <OpenChannelModal network={network} />}
{createInvoice.visible && <CreateInvoiceModal network={network} />}
</Styled.Designer> </Styled.Designer>
); );
}; };

124
src/components/designer/lightning/actions/CreateInvoiceModal.tsx

@ -0,0 +1,124 @@
import React, { ReactNode } from 'react';
import { useAsyncCallback } from 'react-async-hook';
import { Col, Form, InputNumber, Modal, Result, Row } from 'antd';
import { FormComponentProps } from 'antd/lib/form';
import { usePrefixedTranslation } from 'hooks';
import { LightningNode } from 'shared/types';
import { useStoreActions, useStoreState } from 'store';
import { Network } from 'types';
import { delay } from 'utils/async';
import { format } from 'utils/units';
import CopyableInput from 'components/common/form/CopyableInput';
import LightningNodeSelect from 'components/common/form/LightningNodeSelect';
interface FormFields {
nodeName?: string;
amount?: string;
}
interface Props extends FormComponentProps<FormFields> {
network: Network;
}
const CreateInvoiceModal: React.FC<Props> = ({ network, form }) => {
const { l } = usePrefixedTranslation(
'cmps.designer.lightning.actions.CreateInvoiceModal',
);
const { visible, nodeName, invoice, amount } = useStoreState(
s => s.modals.createInvoice,
);
const { showCreateInvoice, hideCreateInvoice } = useStoreActions(s => s.modals);
const { notify } = useStoreActions(s => s.app);
const createAsync = useAsyncCallback(async (node: LightningNode, amount: string) => {
try {
await delay(500);
showCreateInvoice({
nodeName: node.name,
amount,
invoice:
'lnbc2m1pw7k8zmpp5az7tf6matv8ntaanmsdrkaq48xwl7a8gg4q2j0f2rpvqx5cxpj6qdqqcqzpgxqyz5vqj5pp20l7uhpvx3lvqqenjud365c8nmsmnu37ffza8e4lftr5k69z38aptdcjwh6ms5hc992g5dxwckulvwm7gly88ukhsy3f0hqc2yqp2guv38',
});
} catch (error) {
notify({ message: l('submitError'), error });
}
});
const handleSubmit = () => {
form.validateFields((err, values: FormFields) => {
if (err) return;
const { lightning } = network.nodes;
const node = lightning.find(n => n.name === values.nodeName);
if (!node || !values.amount) return;
createAsync.execute(node, values.amount);
});
};
let cmp: ReactNode;
if (!invoice) {
cmp = (
<Form hideRequiredMark colon={false}>
<Row type="flex" gutter={16}>
<Col span={12}>
<LightningNodeSelect
network={network}
id="nodeName"
form={form}
label={l('nodeLabel')}
disabled={createAsync.loading}
initialValue={nodeName}
/>
</Col>
<Col span={12}>
<Form.Item label={l('amountLabel') + ' (sats)'}>
{form.getFieldDecorator('amount', {
initialValue: 50000,
rules: [{ required: true, message: l('cmps.forms.required') }],
})(
<InputNumber
min={1}
disabled={createAsync.loading}
formatter={v => `${v}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')}
parser={v => `${v}`.replace(/(undefined|,*)/g, '')}
style={{ width: '100%' }}
/>,
)}
</Form.Item>
</Col>
</Row>
</Form>
);
} else {
cmp = (
<Result
status="success"
title={l('successTitle')}
subTitle={l('successDesc', { nodeName, amount: format(`${amount}`) })}
extra={<CopyableInput label="Invoice" value={invoice} />}
/>
);
}
return (
<>
<Modal
title={l('title')}
visible={visible}
onCancel={() => hideCreateInvoice()}
destroyOnClose
footer={invoice ? null : undefined}
cancelText={l('cancelBtn')}
okText={l('okBtn')}
okButtonProps={{
loading: createAsync.loading,
}}
onOk={handleSubmit}
>
{cmp}
</Modal>
</>
);
};
export default Form.create<Props>()(CreateInvoiceModal);

4
src/components/designer/lightning/actions/PaymentButtons.tsx

@ -22,11 +22,11 @@ const PaymentButtons: React.FC<Props> = ({ node }) => {
return ( return (
<Form.Item label={l('paymentsTitle')} colon={false}> <Form.Item label={l('paymentsTitle')} colon={false}>
<Button.Group style={{ width: '100%' }}> <Button.Group style={{ width: '100%' }}>
<Styled.Button onClick={() => showCreateInvoice({ node })}> <Styled.Button onClick={() => showCreateInvoice({ nodeName: node.name })}>
<Icon type="thunderbolt" /> <Icon type="thunderbolt" />
{l('payInvoice')} {l('payInvoice')}
</Styled.Button> </Styled.Button>
<Styled.Button onClick={() => showCreateInvoice({ node })}> <Styled.Button onClick={() => showCreateInvoice({ nodeName: node.name })}>
<Icon type="file-protect" /> <Icon type="file-protect" />
{l('createInvoice')} {l('createInvoice')}
</Styled.Button> </Styled.Button>

8
src/i18n/locales/en-US.json

@ -55,6 +55,14 @@
"cmps.designer.link.Channel.closeChanError": "Unable to close the channel", "cmps.designer.link.Channel.closeChanError": "Unable to close the channel",
"cmps.designer.link.LinkDetails.invalidTitle": "Invalid Selection", "cmps.designer.link.LinkDetails.invalidTitle": "Invalid Selection",
"cmps.designer.link.LinkDetails.invalidMsg": "You've somehow managed to select an invalid link. Click the reload icon above to ensure your chart accurately represents the state in your nodes", "cmps.designer.link.LinkDetails.invalidMsg": "You've somehow managed to select an invalid link. Click the reload icon above to ensure your chart accurately represents the state in your nodes",
"cmps.designer.lightning.actions.CreateInvoiceModal.title": "Create Invoice",
"cmps.designer.lightning.actions.CreateInvoiceModal.nodeLabel": "Node",
"cmps.designer.lightning.actions.CreateInvoiceModal.amountLabel": "Amount",
"cmps.designer.lightning.actions.CreateInvoiceModal.cancelBtn": "Cancel",
"cmps.designer.lightning.actions.CreateInvoiceModal.okBtn": "Create Invoice",
"cmps.designer.lightning.actions.CreateInvoiceModal.submitError": "Unable to create the Invoice",
"cmps.designer.lightning.actions.CreateInvoiceModal.successTitle": "Successfully Created the Invoice",
"cmps.designer.lightning.actions.CreateInvoiceModal.successDesc": "Pay the invoice below to send {{amount}} sats to {{nodeName}}",
"cmps.designer.lightning.actions.Deposit.title": "Deposit Funds", "cmps.designer.lightning.actions.Deposit.title": "Deposit Funds",
"cmps.designer.lightning.actions.Deposit.depositBtn": "Deposit", "cmps.designer.lightning.actions.Deposit.depositBtn": "Deposit",
"cmps.designer.lightning.actions.Deposit.depositSuccess": "Deposited {{amount}} sats to {{node}}", "cmps.designer.lightning.actions.Deposit.depositSuccess": "Deposited {{amount}} sats to {{node}}",

8
src/i18n/locales/es.json

@ -55,6 +55,14 @@
"cmps.designer.link.Channel.closeChanError": "No se puede cerrar el canal", "cmps.designer.link.Channel.closeChanError": "No se puede cerrar el canal",
"cmps.designer.link.LinkDetails.invalidTitle": "Selección invalida", "cmps.designer.link.LinkDetails.invalidTitle": "Selección invalida",
"cmps.designer.link.LinkDetails.invalidMsg": "De alguna manera has logrado seleccionar un enlace no válido. Haga clic en el icono de recarga arriba para asegurarse de que su gráfico represente con precisión el estado en sus nodos", "cmps.designer.link.LinkDetails.invalidMsg": "De alguna manera has logrado seleccionar un enlace no válido. Haga clic en el icono de recarga arriba para asegurarse de que su gráfico represente con precisión el estado en sus nodos",
"cmps.designer.lightning.actions.CreateInvoiceModal.title": "Crear factura",
"cmps.designer.lightning.actions.CreateInvoiceModal.nodeLabel": "Nodo",
"cmps.designer.lightning.actions.CreateInvoiceModal.amountLabel": "Cantidad",
"cmps.designer.lightning.actions.CreateInvoiceModal.cancelBtn": "Cancelar",
"cmps.designer.lightning.actions.CreateInvoiceModal.okBtn": "Crear factura",
"cmps.designer.lightning.actions.CreateInvoiceModal.submitError": "No se puede crear la factura",
"cmps.designer.lightning.actions.CreateInvoiceModal.successTitle": "Creó con éxito la factura",
"cmps.designer.lightning.actions.CreateInvoiceModal.successDesc": "Pla siguiente factura para enviar sats {{amount}} a {{nodeName}}",
"cmps.designer.lightning.actions.Deposit.title": "Fondos de depósito", "cmps.designer.lightning.actions.Deposit.title": "Fondos de depósito",
"cmps.designer.lightning.actions.Deposit.depositBtn": "Depositar", "cmps.designer.lightning.actions.Deposit.depositBtn": "Depositar",
"cmps.designer.lightning.actions.Deposit.depositSuccess": "Depositó {{amount}} sats a {{node}}", "cmps.designer.lightning.actions.Deposit.depositSuccess": "Depositó {{amount}} sats a {{node}}",

15
src/store/models/modals.ts

@ -1,5 +1,4 @@
import { Action, action, Thunk, thunk } from 'easy-peasy'; import { Action, action, Thunk, thunk } from 'easy-peasy';
import { LightningNode } from 'shared/types';
import { StoreInjections } from 'types'; import { StoreInjections } from 'types';
import { RootModel } from './'; import { RootModel } from './';
@ -12,7 +11,9 @@ interface OpenChannelModel {
interface CreateInvoiceModel { interface CreateInvoiceModel {
visible: boolean; visible: boolean;
node?: LightningNode; nodeName?: string;
invoice?: string;
amount?: string;
} }
export interface ModalsModel { export interface ModalsModel {
@ -22,7 +23,7 @@ export interface ModalsModel {
showOpenChannel: Thunk<ModalsModel, Partial<OpenChannelModel>, StoreInjections>; showOpenChannel: Thunk<ModalsModel, Partial<OpenChannelModel>, StoreInjections>;
hideOpenChannel: Thunk<ModalsModel, any, StoreInjections, RootModel>; hideOpenChannel: Thunk<ModalsModel, any, StoreInjections, RootModel>;
setCreateInvoice: Action<ModalsModel, CreateInvoiceModel>; setCreateInvoice: Action<ModalsModel, CreateInvoiceModel>;
showCreateInvoice: Thunk<ModalsModel, { node: LightningNode }, StoreInjections>; showCreateInvoice: Thunk<ModalsModel, Partial<CreateInvoiceModel>, StoreInjections>;
hideCreateInvoice: Thunk<ModalsModel, any, StoreInjections, RootModel>; hideCreateInvoice: Thunk<ModalsModel, any, StoreInjections, RootModel>;
} }
@ -64,13 +65,15 @@ const modalsModel: ModalsModel = {
...payload, ...payload,
}; };
}), }),
showCreateInvoice: thunk((actions, { node }) => { showCreateInvoice: thunk((actions, { nodeName, invoice, amount }) => {
actions.setCreateInvoice({ visible: true, node }); actions.setCreateInvoice({ visible: true, nodeName, invoice, amount });
}), }),
hideCreateInvoice: thunk(actions => { hideCreateInvoice: thunk(actions => {
actions.setCreateInvoice({ actions.setCreateInvoice({
visible: false, visible: false,
node: undefined, nodeName: undefined,
invoice: undefined,
amount: undefined,
}); });
}), }),
}; };

Loading…
Cancel
Save