mirror of
https://github.com/aljazceru/mutiny-web.git
synced 2026-01-03 22:34:25 +01:00
option for address / invoice
This commit is contained in:
@@ -18,9 +18,12 @@ function SingleDigitButton(props: { character: string, onClick: (c: string) => v
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AmountEditable(props: { initialAmountSats: string, setAmountSats: (s: bigint) => void, onSave?: () => void }) {
|
export function AmountEditable(props: { initialAmountSats: string, setAmountSats: (s: bigint) => void }) {
|
||||||
|
const [isOpen, setIsOpen] = createSignal(false);
|
||||||
|
|
||||||
const [displayAmount, setDisplayAmount] = createSignal(props.initialAmountSats || "0");
|
const [displayAmount, setDisplayAmount] = createSignal(props.initialAmountSats || "0");
|
||||||
|
|
||||||
|
|
||||||
let inputRef!: HTMLInputElement;
|
let inputRef!: HTMLInputElement;
|
||||||
|
|
||||||
function handleCharacterInput(character: string) {
|
function handleCharacterInput(character: string) {
|
||||||
@@ -101,9 +104,12 @@ export function AmountEditable(props: { initialAmountSats: string, setAmountSats
|
|||||||
const amountInUsd = () => satsToUsd(state.price, Number(displayAmount()) || 0, true)
|
const amountInUsd = () => satsToUsd(state.price, Number(displayAmount()) || 0, true)
|
||||||
|
|
||||||
// What we're all here for in the first place: returning a value
|
// What we're all here for in the first place: returning a value
|
||||||
function handleSubmit() {
|
function handleSubmit(e: SubmitEvent | MouseEvent) {
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
// validate it's a number
|
// validate it's a number
|
||||||
console.log("handling submit...");
|
console.log("handling submit...");
|
||||||
|
console.log(displayAmount());
|
||||||
const number = Number(displayAmount());
|
const number = Number(displayAmount());
|
||||||
if (isNaN(number) || number < 0) {
|
if (isNaN(number) || number < 0) {
|
||||||
setDisplayAmount("0");
|
setDisplayAmount("0");
|
||||||
@@ -112,36 +118,36 @@ export function AmountEditable(props: { initialAmountSats: string, setAmountSats
|
|||||||
} else {
|
} else {
|
||||||
const bign = BigInt(displayAmount());
|
const bign = BigInt(displayAmount());
|
||||||
props.setAmountSats(bign);
|
props.setAmountSats(bign);
|
||||||
// This is so the parent can focus the next input if it wants to
|
|
||||||
if (props.onSave) {
|
|
||||||
props.onSave();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setIsOpen(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
const DIALOG_POSITIONER = "fixed inset-0 safe-top safe-bottom z-50"
|
const DIALOG_POSITIONER = "fixed inset-0 safe-top safe-bottom z-50"
|
||||||
const DIALOG_CONTENT = "h-screen-safe p-4 bg-gray/50 backdrop-blur-md bg-black/80"
|
const DIALOG_CONTENT = "h-screen-safe p-4 bg-gray/50 backdrop-blur-md bg-black/80"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog.Root>
|
<Dialog.Root isOpen={isOpen()}>
|
||||||
<Dialog.Trigger>
|
<button onClick={() => setIsOpen(true)} class="p-4 rounded-xl border-2 border-m-blue">
|
||||||
<div class="p-4 rounded-xl border-2 border-m-blue">
|
<Amount amountSats={Number(displayAmount())} showFiat />
|
||||||
<Amount amountSats={Number(displayAmount())} showFiat />
|
</button>
|
||||||
</div>
|
|
||||||
</Dialog.Trigger>
|
|
||||||
<Dialog.Portal>
|
<Dialog.Portal>
|
||||||
{/* <Dialog.Overlay class={OVERLAY} /> */}
|
{/* <Dialog.Overlay class={OVERLAY} /> */}
|
||||||
<div class={DIALOG_POSITIONER}>
|
<div class={DIALOG_POSITIONER}>
|
||||||
<Dialog.Content class={DIALOG_CONTENT}>
|
<Dialog.Content class={DIALOG_CONTENT}>
|
||||||
{/* TODO: figure out how to submit on enter */}
|
{/* TODO: figure out how to submit on enter */}
|
||||||
<input ref={el => inputRef = el}
|
<form onSubmit={handleSubmit}>
|
||||||
autofocus
|
<input ref={el => inputRef = el}
|
||||||
inputmode='none'
|
autofocus
|
||||||
type="text"
|
inputmode='none'
|
||||||
class="opacity-0 absolute -z-10"
|
type="text"
|
||||||
value={displayAmount()}
|
class="opacity-0 absolute -z-10"
|
||||||
onInput={(e) => handleHiddenInput(e)}
|
value={displayAmount()}
|
||||||
/>
|
onInput={(e) => handleHiddenInput(e)}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
<div class="flex flex-col gap-4 max-w-[400px] mx-auto">
|
<div class="flex flex-col gap-4 max-w-[400px] mx-auto">
|
||||||
<div class="p-4 bg-black rounded-xl flex flex-col gap-4 items-center justify-center">
|
<div class="p-4 bg-black rounded-xl flex flex-col gap-4 items-center justify-center">
|
||||||
<h1 class={`font-light text-center transition-transform ease-out duration-300 text-6xl ${scale()}`}>
|
<h1 class={`font-light text-center transition-transform ease-out duration-300 text-6xl ${scale()}`}>
|
||||||
@@ -159,13 +165,9 @@ export function AmountEditable(props: { initialAmountSats: string, setAmountSats
|
|||||||
</For>
|
</For>
|
||||||
</div>
|
</div>
|
||||||
{/* TODO: this feels wrong */}
|
{/* TODO: this feels wrong */}
|
||||||
<Dialog.CloseButton>
|
<Button intent="inactive" class="w-full flex-none" onClick={handleSubmit}>
|
||||||
<Button intent="inactive" class="w-full flex-none"
|
Set Amount
|
||||||
onClick={handleSubmit}
|
</Button>
|
||||||
>
|
|
||||||
Set Amount
|
|
||||||
</Button>
|
|
||||||
</Dialog.CloseButton>
|
|
||||||
</div>
|
</div>
|
||||||
</Dialog.Content>
|
</Dialog.Content>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
import { Dialog } from "@kobalte/core";
|
import { Dialog } from "@kobalte/core";
|
||||||
import { JSX } from "solid-js";
|
import { JSX } from "solid-js";
|
||||||
import { Button, SmallHeader } from "~/components/layout";
|
import { Button, LargeHeader, SmallHeader } from "~/components/layout";
|
||||||
import close from "~/assets/icons/close.svg";
|
import close from "~/assets/icons/close.svg";
|
||||||
|
|
||||||
const DIALOG_POSITIONER = "fixed inset-0 safe-top safe-bottom z-50"
|
const DIALOG_POSITIONER = "fixed inset-0 safe-top safe-bottom z-50"
|
||||||
@@ -24,9 +24,9 @@ export function FullscreenModal(props: FullscreenModalProps) {
|
|||||||
<Dialog.Content class={DIALOG_CONTENT}>
|
<Dialog.Content class={DIALOG_CONTENT}>
|
||||||
<div class="flex justify-between items-center mb-2">
|
<div class="flex justify-between items-center mb-2">
|
||||||
<Dialog.Title>
|
<Dialog.Title>
|
||||||
<SmallHeader>
|
<LargeHeader>
|
||||||
{props.title}
|
{props.title}
|
||||||
</SmallHeader>
|
</LargeHeader>
|
||||||
</Dialog.Title>
|
</Dialog.Title>
|
||||||
<Dialog.CloseButton class="p-2 hover:bg-white/10 rounded-lg active:bg-m-blue">
|
<Dialog.CloseButton class="p-2 hover:bg-white/10 rounded-lg active:bg-m-blue">
|
||||||
<img src={close} alt="Close" />
|
<img src={close} alt="Close" />
|
||||||
|
|||||||
@@ -1,24 +1,26 @@
|
|||||||
import { RadioGroup } from "@kobalte/core";
|
import { RadioGroup } from "@kobalte/core";
|
||||||
import { For } from "solid-js";
|
import { For, Show } from "solid-js";
|
||||||
|
|
||||||
type Choices = { value: string, label: string, caption: string }[]
|
type Choices = { value: string, label: string, caption: string }[]
|
||||||
|
|
||||||
// TODO: how could would it be if we could just pass the estimated fees in here?
|
// TODO: how could would it be if we could just pass the estimated fees in here?
|
||||||
export function StyledRadioGroup(props: { value: string, choices: Choices, onValueChange: (value: string) => void }) {
|
export function StyledRadioGroup(props: { value: string, choices: Choices, onValueChange: (value: string) => void, small?: boolean, }) {
|
||||||
return (
|
return (
|
||||||
<RadioGroup.Root value={props.value} onValueChange={(e) => props.onValueChange(e)} class="grid w-full gap-4 grid-cols-2">
|
<RadioGroup.Root value={props.value} onValueChange={(e) => props.onValueChange(e)} class={`grid w-full gap-${props.small ? 2 : 4} grid-cols-${props.choices.length}`}>
|
||||||
<For each={props.choices}>
|
<For each={props.choices}>
|
||||||
{choice =>
|
{choice =>
|
||||||
<RadioGroup.Item value={choice.value} class="ui-checked:bg-neutral-950 bg-white/10 rounded outline outline-black/50 ui-checked:outline-m-blue ui-checked:outline-2">
|
<RadioGroup.Item value={choice.value} class="ui-checked:bg-neutral-950 bg-white/10 rounded outline outline-black/50 ui-checked:outline-m-blue ui-checked:outline-2">
|
||||||
<div class="py-3 px-4">
|
<div class={props.small ? "py-2 px-2" : "py-3 px-4"}>
|
||||||
<RadioGroup.ItemInput />
|
<RadioGroup.ItemInput />
|
||||||
<RadioGroup.ItemControl >
|
<RadioGroup.ItemControl >
|
||||||
<RadioGroup.ItemIndicator />
|
<RadioGroup.ItemIndicator />
|
||||||
</RadioGroup.ItemControl>
|
</RadioGroup.ItemControl>
|
||||||
<RadioGroup.ItemLabel class="ui-checked:text-white text-neutral-400">
|
<RadioGroup.ItemLabel class="ui-checked:text-white text-neutral-400">
|
||||||
<div class="block">
|
<div class="block">
|
||||||
<div class="text-lg font-semibold">{choice.label}</div>
|
<div class={`text-${props.small ? "base" : "lg"} font-semibold`}>{choice.label}</div>
|
||||||
<div class="text-sm font-light">{choice.caption}</div>
|
<Show when={!props.small}>
|
||||||
|
<div class="text-sm font-light">{choice.caption}</div>
|
||||||
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
</RadioGroup.ItemLabel>
|
</RadioGroup.ItemLabel>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
import { TextField } from "@kobalte/core";
|
|
||||||
import { MutinyBip21RawMaterials, MutinyInvoice } from "@mutinywallet/mutiny-wasm";
|
import { MutinyBip21RawMaterials, MutinyInvoice } from "@mutinywallet/mutiny-wasm";
|
||||||
import { createEffect, createMemo, createResource, createSignal, Match, onCleanup, Switch } from "solid-js";
|
import { createEffect, createResource, createSignal, For, Match, onCleanup, Switch } from "solid-js";
|
||||||
import { QRCodeSVG } from "solid-qr-code";
|
import { QRCodeSVG } from "solid-qr-code";
|
||||||
import { AmountEditable } from "~/components/AmountEditable";
|
import { AmountEditable } from "~/components/AmountEditable";
|
||||||
import { Button, Card, DefaultMain, LargeHeader, NodeManagerGuard, SafeArea, SmallHeader } from "~/components/layout";
|
import { Button, Card, LargeHeader, NodeManagerGuard, SafeArea, SmallHeader } from "~/components/layout";
|
||||||
import NavBar from "~/components/NavBar";
|
import NavBar from "~/components/NavBar";
|
||||||
import { useMegaStore } from "~/state/megaStore";
|
import { useMegaStore } from "~/state/megaStore";
|
||||||
import { satsToUsd } from "~/utils/conversions";
|
|
||||||
import { objectToSearchParams } from "~/utils/objectToSearchParams";
|
import { objectToSearchParams } from "~/utils/objectToSearchParams";
|
||||||
import { useCopy } from "~/utils/useCopy";
|
import { useCopy } from "~/utils/useCopy";
|
||||||
import mempoolTxUrl from "~/utils/mempoolTxUrl";
|
import mempoolTxUrl from "~/utils/mempoolTxUrl";
|
||||||
@@ -15,7 +13,8 @@ import party from '~/assets/party.gif';
|
|||||||
import { Amount } from "~/components/Amount";
|
import { Amount } from "~/components/Amount";
|
||||||
import { FullscreenModal } from "~/components/layout/FullscreenModal";
|
import { FullscreenModal } from "~/components/layout/FullscreenModal";
|
||||||
import { BackButton } from "~/components/layout/BackButton";
|
import { BackButton } from "~/components/layout/BackButton";
|
||||||
import { TagEditor } from "~/components/TagEditor";
|
import { TagEditor, TagItem } from "~/components/TagEditor";
|
||||||
|
import { StyledRadioGroup } from "~/components/layout/Radio";
|
||||||
|
|
||||||
type OnChainTx = {
|
type OnChainTx = {
|
||||||
transaction: {
|
transaction: {
|
||||||
@@ -41,6 +40,19 @@ type OnChainTx = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const createUniqueId = () => Math.random().toString(36).substr(2, 9);
|
||||||
|
|
||||||
|
const fakeContacts: TagItem[] = [
|
||||||
|
{ id: createUniqueId(), name: "Unknown", kind: "text" },
|
||||||
|
{ id: createUniqueId(), name: "Alice", kind: "contact" },
|
||||||
|
{ id: createUniqueId(), name: "Bob", kind: "contact" },
|
||||||
|
{ id: createUniqueId(), name: "Carol", kind: "contact" },
|
||||||
|
]
|
||||||
|
|
||||||
|
const RECEIVE_FLAVORS = [{ value: "unified", label: "Unified", caption: "Sender decides" }, { value: "lightning", label: "Lightning", caption: "Fast and cool" }, { value: "onchain", label: "On-chain", caption: "Just like Satoshi did it" }]
|
||||||
|
|
||||||
|
type ReceiveFlavor = "unified" | "lightning" | "onchain"
|
||||||
|
|
||||||
function ShareButton(props: { receiveString: string }) {
|
function ShareButton(props: { receiveString: string }) {
|
||||||
async function share(receiveString: string) {
|
async function share(receiveString: string) {
|
||||||
// If the browser doesn't support share we can just copy the address
|
// If the browser doesn't support share we can just copy the address
|
||||||
@@ -70,26 +82,29 @@ export default function Receive() {
|
|||||||
const [state, _] = useMegaStore()
|
const [state, _] = useMegaStore()
|
||||||
|
|
||||||
const [amount, setAmount] = createSignal("")
|
const [amount, setAmount] = createSignal("")
|
||||||
const [label, setLabel] = createSignal("")
|
|
||||||
|
|
||||||
const [receiveState, setReceiveState] = createSignal<ReceiveState>("edit")
|
const [receiveState, setReceiveState] = createSignal<ReceiveState>("edit")
|
||||||
|
|
||||||
const [bip21Raw, setBip21Raw] = createSignal<MutinyBip21RawMaterials>();
|
const [bip21Raw, setBip21Raw] = createSignal<MutinyBip21RawMaterials>();
|
||||||
|
|
||||||
const [unified, setUnified] = createSignal("")
|
const [unified, setUnified] = createSignal("")
|
||||||
|
|
||||||
|
// Tagging stuff
|
||||||
|
const [selectedValues, setSelectedValues] = createSignal<TagItem[]>([]);
|
||||||
|
const [values, setValues] = createSignal([...fakeContacts]);
|
||||||
|
|
||||||
// The data we get after a payment
|
// The data we get after a payment
|
||||||
const [paymentTx, setPaymentTx] = createSignal<OnChainTx>();
|
const [paymentTx, setPaymentTx] = createSignal<OnChainTx>();
|
||||||
const [paymentInvoice, setPaymentInvoice] = createSignal<MutinyInvoice>();
|
const [paymentInvoice, setPaymentInvoice] = createSignal<MutinyInvoice>();
|
||||||
|
|
||||||
|
// The flavor of the receive
|
||||||
|
const [flavor, setFlavor] = createSignal<ReceiveFlavor>("unified");
|
||||||
|
|
||||||
function clearAll() {
|
function clearAll() {
|
||||||
setAmount("")
|
setAmount("")
|
||||||
setLabel("")
|
|
||||||
setReceiveState("edit")
|
setReceiveState("edit")
|
||||||
setBip21Raw(undefined)
|
setBip21Raw(undefined)
|
||||||
setUnified("")
|
setUnified("")
|
||||||
setPaymentTx(undefined)
|
setPaymentTx(undefined)
|
||||||
setPaymentInvoice(undefined)
|
setPaymentInvoice(undefined)
|
||||||
|
setSelectedValues([])
|
||||||
}
|
}
|
||||||
|
|
||||||
let amountInput!: HTMLInputElement;
|
let amountInput!: HTMLInputElement;
|
||||||
@@ -107,13 +122,21 @@ export default function Receive() {
|
|||||||
labelInput.focus();
|
labelInput.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const [copy, copied] = useCopy({ copiedTimeout: 1000 });
|
const [copy, copied] = useCopy({ copiedTimeout: 1000 });
|
||||||
|
|
||||||
async function getUnifiedQr(amount: string, label: string) {
|
function handleCopy() {
|
||||||
|
if (flavor() === "unified") {
|
||||||
|
copy(unified() ?? "")
|
||||||
|
} else if (flavor() === "lightning") {
|
||||||
|
copy(bip21Raw()?.invoice ?? "")
|
||||||
|
} else if (flavor() === "onchain") {
|
||||||
|
copy(bip21Raw()?.address ?? "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getUnifiedQr(amount: string) {
|
||||||
const bigAmount = BigInt(amount);
|
const bigAmount = BigInt(amount);
|
||||||
const raw = await state.node_manager?.create_bip21(bigAmount, label);
|
const raw = await state.node_manager?.create_bip21(bigAmount, "TODO DELETE ME");
|
||||||
|
|
||||||
// Save the raw info so we can watch the address and invoice
|
// Save the raw info so we can watch the address and invoice
|
||||||
setBip21Raw(raw);
|
setBip21Raw(raw);
|
||||||
@@ -130,20 +153,12 @@ export default function Receive() {
|
|||||||
async function onSubmit(e: Event) {
|
async function onSubmit(e: Event) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
const unifiedQr = await getUnifiedQr(amount(), label())
|
const unifiedQr = await getUnifiedQr(amount())
|
||||||
|
|
||||||
setUnified(unifiedQr)
|
setUnified(unifiedQr)
|
||||||
setReceiveState("show")
|
setReceiveState("show")
|
||||||
}
|
}
|
||||||
|
|
||||||
const amountInUsd = createMemo(() => satsToUsd(state.price, parseInt(amount()) || 0, true))
|
|
||||||
|
|
||||||
function handleAmountSave() {
|
|
||||||
console.error("focusing label input...")
|
|
||||||
console.error(labelInput)
|
|
||||||
labelInput.focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function checkIfPaid(bip21?: MutinyBip21RawMaterials): Promise<PaidState | undefined> {
|
async function checkIfPaid(bip21?: MutinyBip21RawMaterials): Promise<PaidState | undefined> {
|
||||||
if (bip21) {
|
if (bip21) {
|
||||||
console.log("checking if paid...")
|
console.log("checking if paid...")
|
||||||
@@ -179,6 +194,9 @@ export default function Receive() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NodeManagerGuard>
|
<NodeManagerGuard>
|
||||||
<SafeArea>
|
<SafeArea>
|
||||||
@@ -189,32 +207,52 @@ export default function Receive() {
|
|||||||
<Match when={!unified() || receiveState() === "edit"}>
|
<Match when={!unified() || receiveState() === "edit"}>
|
||||||
<dl>
|
<dl>
|
||||||
<dd>
|
<dd>
|
||||||
|
<AmountEditable initialAmountSats={amount() || "0"} setAmountSats={setAmount} />
|
||||||
<AmountEditable initialAmountSats={amount() || "0"} setAmountSats={setAmount} onSave={handleAmountSave} />
|
|
||||||
</dd>
|
</dd>
|
||||||
<dd>
|
<dd>
|
||||||
<TagEditor />
|
<TagEditor title="Tag the origin" values={values()} setValues={setValues} selectedValues={selectedValues()} setSelectedValues={setSelectedValues} />
|
||||||
</dd>
|
</dd>
|
||||||
|
|
||||||
</dl>
|
</dl>
|
||||||
<Button class="w-full" disabled={!amount() || !label()} intent="green" onClick={onSubmit}>Create Invoice</Button>
|
<Button class="w-full" disabled={!amount() || !selectedValues().length} intent="green" onClick={onSubmit}>Create Invoice</Button>
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={unified() && receiveState() === "show"}>
|
<Match when={unified() && receiveState() === "show"}>
|
||||||
|
<StyledRadioGroup small value={flavor()} onValueChange={setFlavor} choices={RECEIVE_FLAVORS} />
|
||||||
<div class="w-full bg-white rounded-xl">
|
<div class="w-full bg-white rounded-xl">
|
||||||
<QRCodeSVG value={unified() ?? ""} class="w-full h-full p-8 max-h-[400px]" />
|
<Switch>
|
||||||
|
<Match when={flavor() === "unified"}>
|
||||||
|
<QRCodeSVG value={unified() ?? ""} class="w-full h-full p-8 max-h-[400px]" />
|
||||||
|
</Match>
|
||||||
|
<Match when={flavor() === "lightning"}>
|
||||||
|
<QRCodeSVG value={bip21Raw()?.invoice ?? ""} class="w-full h-full p-8 max-h-[400px]" />
|
||||||
|
</Match>
|
||||||
|
<Match when={flavor() === "onchain"}>
|
||||||
|
<QRCodeSVG value={bip21Raw()?.address ?? ""} class="w-full h-full p-8 max-h-[400px]" />
|
||||||
|
</Match>
|
||||||
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-2 w-full">
|
<div class="flex gap-2 w-full">
|
||||||
<Button onClick={(_) => copy(unified() ?? "")}>{copied() ? "Copied" : "Copy"}</Button>
|
<Button onClick={handleCopy}>{copied() ? "Copied" : "Copy"}</Button>
|
||||||
<ShareButton receiveString={unified() ?? ""} />
|
<ShareButton receiveString={unified() ?? ""} />
|
||||||
</div>
|
</div>
|
||||||
<Card>
|
<Card>
|
||||||
<SmallHeader>Amount</SmallHeader>
|
<SmallHeader>Amount</SmallHeader>
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
<p>{amount()} sats</p><button onClick={editAmount}>✏️</button>
|
<Amount amountSats={parseInt(amount()) || 0} showFiat={true} />
|
||||||
|
<button onClick={editAmount}>✏️</button>
|
||||||
</div>
|
</div>
|
||||||
<pre>({amountInUsd()})</pre>
|
<SmallHeader>Tags</SmallHeader>
|
||||||
<SmallHeader>Private Label</SmallHeader>
|
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
<p>{label()} </p><button onClick={editLabel}>✏️</button>
|
<div class="flex flex-wrap">
|
||||||
|
<For each={selectedValues()}>
|
||||||
|
{(tag) => (
|
||||||
|
<div class=" bg-white/20 rounded px-1">
|
||||||
|
{tag.name}
|
||||||
|
</div>)}
|
||||||
|
</For>
|
||||||
|
</div>
|
||||||
|
{/* <pre>{JSON.stringify(selectedValues(), null, 2)}</pre> */}
|
||||||
|
<button onClick={editLabel}>✏️</button>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
<Card title="Bip21">
|
<Card title="Bip21">
|
||||||
@@ -222,7 +260,7 @@ export default function Receive() {
|
|||||||
</Card>
|
</Card>
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={receiveState() === "paid" && paidState() === "lightning_paid"}>
|
<Match when={receiveState() === "paid" && paidState() === "lightning_paid"}>
|
||||||
<FullscreenModal title="Payment Received!" open={!!paidState()} setOpen={(open: boolean) => { if (!open) clearAll() }}>
|
<FullscreenModal title="Payment Received" open={!!paidState()} setOpen={(open: boolean) => { if (!open) clearAll() }}>
|
||||||
<div class="flex flex-col items-center gap-8">
|
<div class="flex flex-col items-center gap-8">
|
||||||
<img src={party} alt="party" class="w-1/2 mx-auto max-w-[50vh] aspect-square" />
|
<img src={party} alt="party" class="w-1/2 mx-auto max-w-[50vh] aspect-square" />
|
||||||
<Amount amountSats={paymentInvoice()?.amount_sats} showFiat />
|
<Amount amountSats={paymentInvoice()?.amount_sats} showFiat />
|
||||||
@@ -230,7 +268,7 @@ export default function Receive() {
|
|||||||
</FullscreenModal>
|
</FullscreenModal>
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={receiveState() === "paid" && paidState() === "onchain_paid"}>
|
<Match when={receiveState() === "paid" && paidState() === "onchain_paid"}>
|
||||||
<FullscreenModal title="Payment Received!" open={!!paidState()} setOpen={(open: boolean) => { if (!open) clearAll() }}>
|
<FullscreenModal title="Payment Received" open={!!paidState()} setOpen={(open: boolean) => { if (!open) clearAll() }}>
|
||||||
<div class="flex flex-col items-center gap-8">
|
<div class="flex flex-col items-center gap-8">
|
||||||
<img src={party} alt="party" class="w-1/2 mx-auto max-w-[50vh] aspect-square" />
|
<img src={party} alt="party" class="w-1/2 mx-auto max-w-[50vh] aspect-square" />
|
||||||
<Amount amountSats={paymentTx()?.received} showFiat />
|
<Amount amountSats={paymentTx()?.received} showFiat />
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ export default function Send() {
|
|||||||
<DefaultMain>
|
<DefaultMain>
|
||||||
<BackButton />
|
<BackButton />
|
||||||
<LargeHeader>Send Bitcoin</LargeHeader>
|
<LargeHeader>Send Bitcoin</LargeHeader>
|
||||||
<FullscreenModal title="Sent!" open={!!sentDetails()} setOpen={(open: boolean) => { if (!open) setSentDetails(undefined) }} onConfirm={() => setSentDetails(undefined)}>
|
<FullscreenModal title="Sent" open={!!sentDetails()} setOpen={(open: boolean) => { if (!open) setSentDetails(undefined) }} onConfirm={() => setSentDetails(undefined)}>
|
||||||
<div class="flex flex-col items-center gap-8">
|
<div class="flex flex-col items-center gap-8">
|
||||||
<img src={handshake} alt="party" class="w-1/2 mx-auto max-w-[50vh] zoom-image" />
|
<img src={handshake} alt="party" class="w-1/2 mx-auto max-w-[50vh] zoom-image" />
|
||||||
<Amount amountSats={sentDetails()?.amount} showFiat />
|
<Amount amountSats={sentDetails()?.amount} showFiat />
|
||||||
@@ -247,8 +247,6 @@ export default function Send() {
|
|||||||
</div>
|
</div>
|
||||||
</ButtonLink>
|
</ButtonLink>
|
||||||
</HStack>
|
</HStack>
|
||||||
|
|
||||||
|
|
||||||
</VStack>
|
</VStack>
|
||||||
</Match>
|
</Match>
|
||||||
</Switch>
|
</Switch>
|
||||||
|
|||||||
Reference in New Issue
Block a user