lnurl-pay.me/src/PayFlow.svelte
2021-07-17 13:44:07 +03:00

171 lines
4.4 KiB
Svelte
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script>
import { Styles,
Container,
Row,
Col,
Card, CardBody, CardHeader, CardTitle,
Button,
Tooltip,
Icon,
InputGroup,
Input,
Nav, NavItem, NavLink,
Modal, ModalHeader, ModalBody, ModalFooter,
} from 'sveltestrap';
import InputMask from './InputMask.svelte';
import QR from './QR.svelte';
import * as pf from './payflow.js';
import CTC from './CTC.svelte';
import Tipped from './Tipped.svelte';
export let lnurl = undefined;
let isOpen;
let res1;
let res2;
let memo;
let err;
let bolt;
let confirmAmount;
let amount;
let minSendSat=0, maxSendSat=0;
$: minSendSat = res1 && (Math.round((res1.minSendable+999)/1000))
$: maxSendSat = res1 && (Math.round(res1.maxSendable/1000))
$: if (lnurl && !isOpen) {
res1 = res2 = err = memo = confirmAmount = undefined;
isOpen = true;
fetchInvoice()
}
let stage;
$: if (!res1) {
stage = err || "Connecting to server…";
} else if (confirmAmount) {
stage = "Amount confirmation";
} else if (!res2) {
stage = err || "Requesting invoice";
} else {
stage = err || "Invoice ready.";
}
async function fetchInvoice() {
try {
let url = pf.decodeLnurl(lnurl)
//await new Promise(r => setTimeout(r, 5000));
res1 = await pf.payStep1(url)
if (res1.status == "ERROR") {
throw new Error(res1.reason)
}
if (!isOpen) { return }
if (res1.minSendable != res1.maxSendable) {
amount = Math.round((res1.minSendable+999)/1000).toString();
await new Promise((resolve, reject)=>{confirmAmount = resolve})
confirmAmount = undefined;
} else {
amount = (res1.minSendable/1000).toString();
}
res2 = await pf.payStep2(url,res1,amount*1000)
if (!isOpen) { return }
if (res2.status == "ERROR") {
throw new Error(res2.reason)
}
} catch(e) {
err = e.toString()
}
}
function mdText() {
for (let md of res1.decodedMetadata) {
if (md[0]=="text/plain") {
return md[1]
}
}
}
const canShare = !!navigator.share;
</script>
<Modal {isOpen} size='lg'>
<ModalHeader>BOLT11 invoice: {stage}</ModalHeader>
<ModalBody>
{#if confirmAmount}
<div class="row">
<label class="form-label mb-3">
Amount: {minSendSat} {maxSendSat} satoshi
<div class="input-group">
{#key minSendSat}
<InputMask bind:value={amount}
autofocus
unmask
imask={{
mask: Number, scale:0,
min: minSendSat , max: maxSendSat,
normalizeZeros: true}}
inputmode={"numeric"}
class="form-control"/>
{/key}
<button class="btn btn-outline-secondary"
type="button"
on:click={()=> amount=minSendSat.toString()}>Min
</button>
<button class="btn btn-outline-secondary"
type="button"
on:click={()=> amount=maxSendSat.toString()}>Max
</button>
<button class="btn btn-secondary"
type="button"
disabled={!(amount && amount <= maxSendSat
&& amount >= minSendSat)}
on:click={()=> confirmAmount()}>OK
</button>
</div>
</label>
</div>
{/if}
{#if (res2&&res2.pr)}
<div class="row">
<CTC let:id let:action force text={res2.pr}>
<div class="form-text user-select-all mb-2 mt-2"
style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis"
on:click={action} id={id}>{res2.pr.toUpperCase()}
</div>
</CTC>
<div class="d-flex justify-content-center">
<a href="lightning:{res2.pr}">
<QR value="{res2.pr}" size="230"/>
</a>
</div>
</div>
{/if}
</ModalBody>
<ModalFooter>
<div class="btn-group flex-wrap">
{#if res2 && res2.pr}
<CTC let:id let:action text={res2.pr}>
<button type="button" class="btn btn-outline-secondary"
id={id} on:click={action}>Copy</button>
</CTC>
{#if canShare}
<button type="button" class="btn btn-outline-secondary"
on:click={()=>{ navigator.share({text:res2.pr})}}>Share
</button>
{/if}
<Tipped let:id>
<a slot="thing" role="button" class="btn btn-outline-secondary"
href="lightning:{res2.pr}" {id}>Pay</a>
<div slot="tip">open invoice in your wallet</div>
</Tipped>
{/if}
<button
class="btn btn-outline-primary"
type="button" on:click="{()=>{isOpen=false; lnurl = null}}">Cancel</button>
</div>
</ModalFooter>
</Modal>