* Pin sharp via pnpm override, add wallet receive * Revert toast success styling * Route receive through wallet connect * Simplify receive invoice validation * Polish receive modal layout * Clarify NWC client config * Adjust wallet action layout on mobile
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
import ModalTitle from "@lib/components/ModalTitle.svelte"
|
||||
import ModalSubtitle from "@lib/components/ModalSubtitle.svelte"
|
||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||
import {errorMessage} from "@lib/util"
|
||||
import {payInvoice} from "@app/core/commands"
|
||||
import {pushToast} from "@app/util/toast"
|
||||
import {clearModals} from "@app/util/modal"
|
||||
@@ -29,14 +30,18 @@
|
||||
loading = true
|
||||
|
||||
try {
|
||||
await payInvoice(invoice!.paymentRequest, sats * 1000)
|
||||
if (hasAmount) {
|
||||
await payInvoice(invoice!.paymentRequest)
|
||||
} else {
|
||||
await payInvoice(invoice!.paymentRequest, sats * 1000)
|
||||
}
|
||||
|
||||
pushToast({message: `Payment sent!`})
|
||||
clearModals()
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
|
||||
const message = String(e).replace(/^.*Error: /, "")
|
||||
const message = errorMessage(e)
|
||||
|
||||
pushToast({
|
||||
theme: "error",
|
||||
@@ -50,6 +55,7 @@
|
||||
let loading = $state(false)
|
||||
let invoice: Invoice | undefined = $state()
|
||||
let sats = $state(0)
|
||||
const hasAmount = $derived((invoice?.satoshi ?? 0) > 0)
|
||||
</script>
|
||||
|
||||
<Modal>
|
||||
@@ -60,7 +66,7 @@
|
||||
</ModalHeader>
|
||||
{#if invoice}
|
||||
<div class="card2 bg-alt flex flex-col gap-2">
|
||||
{#if $session?.wallet?.type === "webln" && invoice.satoshi === 0}
|
||||
{#if $session?.wallet?.type === "webln" && !hasAmount}
|
||||
<p class="text-sm opacity-75">
|
||||
Uh oh! It looks like your current wallet doesn't support invoices without an amount. See
|
||||
if you can get a lightning invoice with a pre-set amount.
|
||||
@@ -101,7 +107,13 @@
|
||||
<Icon icon={AltArrowLeft} />
|
||||
Go back
|
||||
</Button>
|
||||
<Button class="btn btn-primary" onclick={confirm} disabled={!invoice || sats === 0 || loading}>
|
||||
<Button
|
||||
class="btn btn-primary"
|
||||
onclick={confirm}
|
||||
disabled={!invoice ||
|
||||
sats === 0 ||
|
||||
loading ||
|
||||
($session?.wallet?.type === "webln" && !hasAmount)}>
|
||||
{#if loading}
|
||||
<span class="loading loading-spinner loading-sm"></span>
|
||||
{:else}
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
<script lang="ts">
|
||||
import {Invoice} from "@getalby/lightning-tools/bolt11"
|
||||
import AltArrowLeft from "@assets/icons/alt-arrow-left.svg?dataurl"
|
||||
import Bolt from "@assets/icons/bolt.svg?dataurl"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import FieldInline from "@lib/components/FieldInline.svelte"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Modal from "@lib/components/Modal.svelte"
|
||||
import ModalBody from "@lib/components/ModalBody.svelte"
|
||||
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||
import ModalTitle from "@lib/components/ModalTitle.svelte"
|
||||
import ModalSubtitle from "@lib/components/ModalSubtitle.svelte"
|
||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||
import {errorMessage} from "@lib/util"
|
||||
import QRCode from "@app/components/QRCode.svelte"
|
||||
import {createInvoice} from "@app/core/commands"
|
||||
import {pushToast} from "@app/util/toast"
|
||||
|
||||
const back = () => history.back()
|
||||
|
||||
const create = async () => {
|
||||
const satAmount = Math.floor(Number(sats))
|
||||
|
||||
if (!Number.isFinite(satAmount) || satAmount <= 0) {
|
||||
pushToast({theme: "error", message: "Enter an amount greater than 0."})
|
||||
return
|
||||
}
|
||||
|
||||
loading = true
|
||||
|
||||
try {
|
||||
const paymentRequest = await createInvoice({sats: satAmount})
|
||||
|
||||
invoice = new Invoice({pr: paymentRequest})
|
||||
sats = invoice.satoshi && invoice.satoshi > 0 ? invoice.satoshi : satAmount
|
||||
|
||||
pushToast({message: "Invoice created"})
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
|
||||
const message = errorMessage(e)
|
||||
|
||||
pushToast({
|
||||
theme: "error",
|
||||
message: `Failed to create invoice: ${message}`,
|
||||
})
|
||||
} finally {
|
||||
loading = false
|
||||
}
|
||||
}
|
||||
|
||||
const reset = () => {
|
||||
invoice = undefined
|
||||
sats = 10
|
||||
}
|
||||
|
||||
let loading = $state(false)
|
||||
let invoice: Invoice | undefined = $state()
|
||||
let sats = $state(10)
|
||||
|
||||
const invoiceHasAmount = $derived((invoice?.satoshi ?? 0) > 0)
|
||||
</script>
|
||||
|
||||
<Modal>
|
||||
<ModalBody>
|
||||
<ModalHeader>
|
||||
<ModalTitle>Receive with Lightning</ModalTitle>
|
||||
<ModalSubtitle>Use your wallet to receive Bitcoin payments over lightning.</ModalSubtitle>
|
||||
</ModalHeader>
|
||||
{#if invoice}
|
||||
<div class="card2 bg-alt flex flex-col gap-2">
|
||||
<div class="flex flex-col items-center gap-6 pt-4">
|
||||
<QRCode code={invoice.paymentRequest} class="w-full max-w-64" />
|
||||
<p class="text-center text-sm opacity-75">Scan with your wallet, or click to copy.</p>
|
||||
</div>
|
||||
<p class="text-sm opacity-75">
|
||||
Share this invoice to receive <strong>{sats}</strong> satoshis
|
||||
{invoiceHasAmount ? "" : " (payer chooses the exact amount)"}.
|
||||
</p>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="card2 bg-alt flex flex-col gap-2">
|
||||
<FieldInline>
|
||||
{#snippet label()}
|
||||
Amount (satoshis)
|
||||
{/snippet}
|
||||
{#snippet input()}
|
||||
<div class="flex flex-grow justify-end">
|
||||
<label class="input input-bordered flex items-center gap-2">
|
||||
<Icon icon={Bolt} />
|
||||
<input bind:value={sats} type="number" class="w-14" placeholder="0" />
|
||||
</label>
|
||||
</div>
|
||||
{/snippet}
|
||||
</FieldInline>
|
||||
<p class="text-sm opacity-75">Enter an amount to generate an invoice with your wallet.</p>
|
||||
</div>
|
||||
{/if}
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button class="btn btn-link" onclick={back}>
|
||||
<Icon icon={AltArrowLeft} />
|
||||
Go back
|
||||
</Button>
|
||||
{#if invoice}
|
||||
<Button class="btn btn-neutral" onclick={reset} disabled={loading}>Clear Invoice</Button>
|
||||
{:else}
|
||||
<Button class="btn btn-primary" onclick={create} disabled={loading}>
|
||||
{#if loading}
|
||||
<span class="loading loading-spinner loading-sm"></span>
|
||||
{:else}
|
||||
<Icon icon={Bolt} />
|
||||
{/if}
|
||||
Create Invoice
|
||||
</Button>
|
||||
{/if}
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
@@ -17,6 +17,7 @@
|
||||
import ModalSubtitle from "@lib/components/ModalSubtitle.svelte"
|
||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||
import EmojiButton from "@lib/components/EmojiButton.svelte"
|
||||
import {errorMessage} from "@lib/util"
|
||||
import ProfileLink from "@app/components/ProfileLink.svelte"
|
||||
import {payInvoice} from "@app/core/commands"
|
||||
import {pushToast} from "@app/util/toast"
|
||||
@@ -85,7 +86,7 @@
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
|
||||
const message = String(e).replace(/^.*Error: /, "")
|
||||
const message = errorMessage(e)
|
||||
|
||||
pushToast({
|
||||
theme: "error",
|
||||
|
||||
Reference in New Issue
Block a user