front-end for lightning-to-fiat exchange at https://lnurl-pay.me
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
170 lines
4.3 KiB
170 lines
4.3 KiB
<script> |
|
import { |
|
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; |
|
export let hasAmount; |
|
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; |
|
|
|
const dismiss = () => { |
|
lnurl=undefined; |
|
isOpen=false |
|
} |
|
|
|
let cancelButton; |
|
|
|
</script> |
|
|
|
<Modal {isOpen} toggle={dismiss} |
|
on:open="{()=>{if (hasAmount) cancelButton.focus()}}" |
|
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} |
|
focusOnMount={true} |
|
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" |
|
bind:this={cancelButton} |
|
type="button" on:click="{dismiss}">Cancel</button> |
|
</div> |
|
</ModalFooter> |
|
</Modal>
|
|
|