mirror of
https://github.com/aljazceru/mutiny-web.git
synced 2026-01-08 16:54:21 +01:00
Merge pull request #123 from MutinyWallet/tag-editor-fixes
simplified tagging
This commit is contained in:
@@ -6,6 +6,7 @@ import { Dialog } from '@kobalte/core';
|
||||
import close from "~/assets/icons/close.svg";
|
||||
import pencil from "~/assets/icons/pencil.svg";
|
||||
import { InlineAmount } from './AmountCard';
|
||||
import { DIALOG_CONTENT, DIALOG_POSITIONER } from '~/styles/dialogs';
|
||||
|
||||
const CHARACTERS = ["1", "2", "3", "4", "5", "6", "7", "8", "9", ".", "0", "DEL"];
|
||||
|
||||
@@ -131,9 +132,6 @@ export const AmountEditable: ParentComponent<{ initialAmountSats: string, initia
|
||||
setIsOpen(false);
|
||||
}
|
||||
|
||||
const DIALOG_POSITIONER = "fixed inset-0 safe-top safe-bottom z-50"
|
||||
const DIALOG_CONTENT = "h-full safe-bottom flex flex-col justify-between p-4 backdrop-blur-xl bg-neutral-800/70"
|
||||
|
||||
return (
|
||||
<Dialog.Root open={isOpen()}>
|
||||
<button onClick={() => setIsOpen(true)} class="px-4 py-2 rounded-xl border-2 border-m-blue flex gap-2 items-center">
|
||||
|
||||
@@ -5,6 +5,7 @@ import close from "~/assets/icons/close.svg";
|
||||
import { SubmitHandler } from '@modular-forms/solid';
|
||||
import { ContactForm } from './ContactForm';
|
||||
import { ContactFormValues } from './ContactViewer';
|
||||
import { DIALOG_CONTENT, DIALOG_POSITIONER } from '~/styles/dialogs';
|
||||
|
||||
export function ContactEditor(props: { createContact: (contact: ContactFormValues) => void, list?: boolean }) {
|
||||
const [isOpen, setIsOpen] = createSignal(false);
|
||||
@@ -15,9 +16,6 @@ export function ContactEditor(props: { createContact: (contact: ContactFormValue
|
||||
setIsOpen(false);
|
||||
}
|
||||
|
||||
const DIALOG_POSITIONER = "fixed inset-0 safe-top safe-bottom z-50"
|
||||
const DIALOG_CONTENT = "h-full safe-bottom flex flex-col justify-between p-4 backdrop-blur-xl bg-neutral-800/70"
|
||||
|
||||
return (
|
||||
<Dialog.Root open={isOpen()}>
|
||||
<Switch>
|
||||
|
||||
@@ -6,6 +6,7 @@ import { SubmitHandler } from '@modular-forms/solid';
|
||||
import { ContactForm } from './ContactForm';
|
||||
import { showToast } from './Toaster';
|
||||
import { Contact } from '@mutinywallet/mutiny-wasm';
|
||||
import { DIALOG_CONTENT, DIALOG_POSITIONER } from '~/styles/dialogs';
|
||||
|
||||
export type ContactFormValues = {
|
||||
name: string,
|
||||
@@ -24,9 +25,6 @@ export function ContactViewer(props: { contact: Contact, gradient: string, saveC
|
||||
setIsEditing(false)
|
||||
}
|
||||
|
||||
const DIALOG_POSITIONER = "fixed inset-0 safe-top safe-bottom z-50"
|
||||
const DIALOG_CONTENT = "h-full safe-bottom flex flex-col justify-between p-4 backdrop-blur-xl bg-neutral-800/70"
|
||||
|
||||
return (
|
||||
<Dialog.Root open={isOpen()}>
|
||||
<button onClick={() => setIsOpen(true)} class="flex flex-col items-center gap-2 w-16 flex-shrink-0 overflow-x-hidden">
|
||||
|
||||
@@ -1,31 +1,37 @@
|
||||
import { Select, createOptions } from "@thisbeyond/solid-select";
|
||||
import "~/styles/solid-select.css"
|
||||
import { For } from "solid-js";
|
||||
import { ContactEditor } from "./ContactEditor";
|
||||
import { For, Show, createMemo, createSignal, onMount } from "solid-js";
|
||||
import { TinyButton } from "./layout";
|
||||
import { ContactFormValues } from "./ContactViewer";
|
||||
import { MutinyTagItem } from "~/utils/tags";
|
||||
import { Contact } from "@mutinywallet/mutiny-wasm";
|
||||
import { MutinyTagItem, sortByLastUsed } from "~/utils/tags";
|
||||
import { useMegaStore } from "~/state/megaStore";
|
||||
|
||||
// take two arrays, subtract the second from the first, then return the first
|
||||
function subtract<T>(a: T[], b: T[]) {
|
||||
const set = new Set(b);
|
||||
return a.filter(x => !set.has(x));
|
||||
}
|
||||
|
||||
const createLabelValue = (label: string): Partial<MutinyTagItem> => {
|
||||
return { id: label, name: label, kind: "Label" };
|
||||
return { name: label, kind: "Contact" };
|
||||
};
|
||||
|
||||
export function TagEditor(props: {
|
||||
values: MutinyTagItem[],
|
||||
setValues: (values: MutinyTagItem[]) => void,
|
||||
selectedValues: MutinyTagItem[],
|
||||
setSelectedValues: (values: MutinyTagItem[]) => void,
|
||||
selectedValues: Partial<MutinyTagItem>[],
|
||||
setSelectedValues: (value: Partial<MutinyTagItem>[]) => void,
|
||||
placeholder: string
|
||||
}) {
|
||||
const [state, actions] = useMegaStore();
|
||||
const [_state, actions] = useMegaStore();
|
||||
const [availableTags, setAvailableTags] = createSignal<MutinyTagItem[]>([]);
|
||||
|
||||
onMount(async () => {
|
||||
const tags = await actions.listTags()
|
||||
if (tags) {
|
||||
setAvailableTags(tags.filter((tag) => tag.kind === "Contact").sort(sortByLastUsed))
|
||||
}
|
||||
})
|
||||
|
||||
const selectProps = createMemo(() => {
|
||||
return createOptions(availableTags() || [], {
|
||||
key: "name",
|
||||
filterable: true, // Default
|
||||
createable: createLabelValue,
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
const onChange = (selected: MutinyTagItem[]) => {
|
||||
props.setSelectedValues(selected);
|
||||
@@ -33,54 +39,30 @@ export function TagEditor(props: {
|
||||
console.log(selected)
|
||||
|
||||
const lastValue = selected[selected.length - 1];
|
||||
if (lastValue && !props.values.includes(lastValue)) {
|
||||
props.setValues([...props.values, lastValue]);
|
||||
if (lastValue && availableTags() && !availableTags()!.includes(lastValue)) {
|
||||
setAvailableTags([...availableTags(), lastValue]);
|
||||
}
|
||||
};
|
||||
|
||||
const selectProps = createOptions(props.values, {
|
||||
key: "name",
|
||||
disable: (value) => props.selectedValues.includes(value),
|
||||
filterable: true, // Default
|
||||
createable: createLabelValue,
|
||||
});
|
||||
|
||||
async function createContact(contact: ContactFormValues) {
|
||||
// FIXME: undefineds
|
||||
// FIXME: npub not valid? other undefineds
|
||||
const c = new Contact(contact.name, undefined, undefined, undefined);
|
||||
const newContactId = await state.mutiny_wallet?.create_new_contact(c);
|
||||
const contactItem = await state.mutiny_wallet?.get_contact(newContactId ?? "");
|
||||
const mutinyContactItem: MutinyTagItem = { id: contactItem?.id || "", name: contactItem?.name || "", kind: "Contact", last_used_time: 0n };
|
||||
if (contactItem) {
|
||||
// @ts-ignore
|
||||
// FIXME: make typescript less mad about this
|
||||
onChange([...props.selectedValues, mutinyContactItem])
|
||||
} else {
|
||||
console.error("Failed to create contact")
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div class="flex flex-col gap-2 flex-grow flex-shrink flex-1" >
|
||||
{/* FIXME this is causing overflow scroll for now good reason */}
|
||||
<div class="flex flex-col gap-2 flex-shrink flex-1" >
|
||||
<Select
|
||||
multiple
|
||||
initialValue={props.selectedValues}
|
||||
onChange={onChange}
|
||||
placeholder={props.placeholder}
|
||||
{...selectProps}
|
||||
onChange={onChange}
|
||||
{...selectProps()}
|
||||
/>
|
||||
<div class="flex gap-2 flex-wrap">
|
||||
<For each={subtract(props.values, props.selectedValues).slice(0, 3)}>
|
||||
{(tag) => (
|
||||
<TinyButton tag={tag} onClick={() => onChange([...props.selectedValues, tag])}
|
||||
>
|
||||
{tag.name}
|
||||
</TinyButton>
|
||||
)}
|
||||
</For>
|
||||
<ContactEditor createContact={createContact} />
|
||||
<Show when={availableTags() && availableTags()!.length > 0}>
|
||||
<For each={availableTags()!.slice(0, 3)}>
|
||||
{(tag) => (
|
||||
<TinyButton tag={tag} onClick={() => props.setSelectedValues([...props.selectedValues!, tag])}>
|
||||
{tag.name}
|
||||
</TinyButton>
|
||||
)}
|
||||
</For>
|
||||
</Show>
|
||||
</div>
|
||||
</div >
|
||||
)
|
||||
|
||||
@@ -3,9 +3,7 @@ import { Dialog } from "@kobalte/core";
|
||||
import { JSX } from "solid-js";
|
||||
import { Button, LargeHeader } from "~/components/layout";
|
||||
import close from "~/assets/icons/close.svg";
|
||||
|
||||
const DIALOG_POSITIONER = "fixed inset-0 safe-top safe-bottom z-50"
|
||||
const DIALOG_CONTENT = "h-full flex flex-col justify-between p-4 backdrop-blur-md bg-neutral-900/50"
|
||||
import { DIALOG_CONTENT, DIALOG_POSITIONER } from "~/styles/dialogs";
|
||||
|
||||
type FullscreenModalProps = {
|
||||
title: string,
|
||||
|
||||
@@ -19,7 +19,7 @@ export const SmallHeader: ParentComponent<{ class?: string }> = (props) => {
|
||||
|
||||
export const Card: ParentComponent<{ title?: string, titleElement?: JSX.Element }> = (props) => {
|
||||
return (
|
||||
<div class='rounded-xl p-4 flex flex-col gap-2 bg-neutral-950/50 overflow-x-hidden w-full'>
|
||||
<div class='rounded-xl p-4 flex flex-col gap-2 bg-neutral-950/50 w-full'>
|
||||
{props.title && <SmallHeader>{props.title}</SmallHeader>}
|
||||
{props.titleElement && props.titleElement}
|
||||
{props.children}
|
||||
@@ -50,7 +50,7 @@ export const FancyCard: ParentComponent<{ title?: string, tag?: JSX.Element }> =
|
||||
|
||||
export const SafeArea: ParentComponent = (props) => {
|
||||
return (
|
||||
<div class="safe-top safe-left safe-right safe-bottom">
|
||||
<div class="h-[100dvh] safe-left safe-right">
|
||||
{/* <div class="flex-1 disable-scrollbars overflow-y-scroll md:pl-[8rem] md:pr-[6rem]"> */}
|
||||
{props.children}
|
||||
{/* </div> */}
|
||||
@@ -60,15 +60,17 @@ export const SafeArea: ParentComponent = (props) => {
|
||||
|
||||
export const DefaultMain: ParentComponent = (props) => {
|
||||
return (
|
||||
<main class="w-full max-w-[600px] flex flex-col gap-4 mx-auto p-4">
|
||||
<main class="w-full max-w-[600px] flex flex-col gap-4 mx-auto p-4 h-full">
|
||||
{props.children}
|
||||
{/* CSS is hard sometimes */}
|
||||
<div class="py-4" />
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
export const FullscreenLoader = () => {
|
||||
return (
|
||||
<div class="w-screen h-screen flex justify-center items-center">
|
||||
<div class="w-full h-[100dvh] flex justify-center items-center">
|
||||
<LoadingSpinner />
|
||||
</div>
|
||||
);
|
||||
@@ -132,8 +134,6 @@ export const TinyButton: ParentComponent<{ onClick: () => void, tag?: MutinyTagI
|
||||
|
||||
const bg = () => (props.tag?.name && props.tag?.kind === "Contact") ? gradient() : "rgb(255 255 255 / 0.1)"
|
||||
|
||||
console.log("tiny tag", props.tag?.name, gradient())
|
||||
|
||||
return (
|
||||
<button class="py-1 px-2 rounded-lg bg-white/10" onClick={() => props.onClick()}
|
||||
style={{ background: bg() }}
|
||||
|
||||
@@ -14,7 +14,7 @@ import { Contact } from "@mutinywallet/mutiny-wasm";
|
||||
import { showToast } from "~/components/Toaster";
|
||||
|
||||
function ContactRow() {
|
||||
const [state, actions] = useMegaStore();
|
||||
const [state, _actions] = useMegaStore();
|
||||
const [contacts, { refetch }] = createResource(async () => {
|
||||
const contacts = state.mutiny_wallet?.get_contacts();
|
||||
console.log(contacts)
|
||||
@@ -44,14 +44,16 @@ function ContactRow() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div class="w-full overflow-x-scroll flex gap-4 disable-scrollbars">
|
||||
<div class="flex gap-4">
|
||||
<ContactEditor list createContact={createContact} />
|
||||
<Show when={contacts() && gradients()}>
|
||||
<For each={contacts()}>
|
||||
{(contact) => (
|
||||
<ContactViewer contact={contact} gradient={gradients()?.get(contact.name)} saveContact={saveContact} />
|
||||
)}
|
||||
</For>
|
||||
<Show when={contacts()}>
|
||||
<div class="flex gap-4 flex-1 overflow-x-scroll disable-scrollbars">
|
||||
<For each={contacts()}>
|
||||
{(contact) => (
|
||||
<ContactViewer contact={contact} gradient={gradients()?.get(contact.name)} saveContact={saveContact} />
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
)
|
||||
@@ -67,7 +69,6 @@ export default function Activity() {
|
||||
<BackLink />
|
||||
<LargeHeader action={<A class="md:hidden p-2 hover:bg-white/5 rounded-lg active:bg-m-blue" href="/settings"><img src={settings} alt="Settings" /></A>}>Activity</LargeHeader>
|
||||
<ContactRow />
|
||||
|
||||
<Tabs.Root defaultValue="mutiny">
|
||||
<Tabs.List class="relative flex justify-around mt-4 mb-8 gap-1 bg-neutral-950 p-1 rounded-xl">
|
||||
<Tabs.Trigger value="mutiny" class={TAB}>Mutiny</Tabs.Trigger>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { MutinyBip21RawMaterials, MutinyInvoice } from "@mutinywallet/mutiny-wasm";
|
||||
import { Contact, MutinyBip21RawMaterials, MutinyInvoice } from "@mutinywallet/mutiny-wasm";
|
||||
import { createEffect, createMemo, createResource, createSignal, Match, onCleanup, onMount, Show, Switch } from "solid-js";
|
||||
import { QRCodeSVG } from "solid-qr-code";
|
||||
import { Button, Card, Indicator, LargeHeader, MutinyWalletGuard, SafeArea } from "~/components/layout";
|
||||
import { Button, Card, DefaultMain, Indicator, LargeHeader, MutinyWalletGuard, SafeArea } from "~/components/layout";
|
||||
import NavBar from "~/components/NavBar";
|
||||
import { useMegaStore } from "~/state/megaStore";
|
||||
import { objectToSearchParams } from "~/utils/objectToSearchParams";
|
||||
@@ -17,7 +17,7 @@ import megacheck from "~/assets/icons/megacheck.png";
|
||||
import { AmountCard } from "~/components/AmountCard";
|
||||
import { ShareCard } from "~/components/ShareCard";
|
||||
import { BackButton } from "~/components/layout/BackButton";
|
||||
import { MutinyTagItem, UNKNOWN_TAG, sortByLastUsed, tagsToIds } from "~/utils/tags";
|
||||
import { MutinyTagItem } from "~/utils/tags";
|
||||
|
||||
type OnChainTx = {
|
||||
transaction: {
|
||||
@@ -61,7 +61,6 @@ export default function Receive() {
|
||||
|
||||
// Tagging stuff
|
||||
const [selectedValues, setSelectedValues] = createSignal<MutinyTagItem[]>([]);
|
||||
const [values, setValues] = createSignal<MutinyTagItem[]>([UNKNOWN_TAG]);
|
||||
|
||||
// The data we get after a payment
|
||||
const [paymentTx, setPaymentTx] = createSignal<OnChainTx>();
|
||||
@@ -83,12 +82,6 @@ export default function Receive() {
|
||||
}
|
||||
})
|
||||
|
||||
onMount(() => {
|
||||
actions.listTags().then((tags) => {
|
||||
setValues(prev => [...prev, ...tags.sort(sortByLastUsed) || []])
|
||||
});
|
||||
})
|
||||
|
||||
function clearAll() {
|
||||
setAmount("")
|
||||
setReceiveState("edit")
|
||||
@@ -99,10 +92,43 @@ export default function Receive() {
|
||||
setSelectedValues([])
|
||||
}
|
||||
|
||||
async function processContacts(contacts: Partial<MutinyTagItem>[]): Promise<string[]> {
|
||||
console.log("Processing contacts", contacts)
|
||||
|
||||
if (contacts.length) {
|
||||
const first = contacts![0];
|
||||
|
||||
if (!first.name) {
|
||||
console.error("Something went wrong with contact creation, proceeding anyway")
|
||||
return []
|
||||
}
|
||||
|
||||
if (!first.id && first.name) {
|
||||
console.error("Creating new contact", first.name)
|
||||
const c = new Contact(first.name, undefined, undefined, undefined);
|
||||
const newContactId = await state.mutiny_wallet?.create_new_contact(c);
|
||||
if (newContactId) {
|
||||
return [newContactId];
|
||||
}
|
||||
}
|
||||
|
||||
if (first.id) {
|
||||
console.error("Using existing contact", first.name, first.id)
|
||||
return [first.id];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
console.error("Something went wrong with contact creation, proceeding anyway")
|
||||
return []
|
||||
|
||||
}
|
||||
|
||||
async function getUnifiedQr(amount: string) {
|
||||
const bigAmount = BigInt(amount);
|
||||
try {
|
||||
const raw = await state.mutiny_wallet?.create_bip21(bigAmount, tagsToIds(selectedValues()));
|
||||
const tags = await processContacts(selectedValues());
|
||||
const raw = await state.mutiny_wallet?.create_bip21(bigAmount, tags);
|
||||
// Save the raw info so we can watch the address and invoice
|
||||
setBip21Raw(raw);
|
||||
|
||||
@@ -167,7 +193,7 @@ export default function Receive() {
|
||||
return (
|
||||
<MutinyWalletGuard>
|
||||
<SafeArea>
|
||||
<main class="max-w-[600px] flex flex-col flex-1 gap-4 mx-auto p-4 overflow-y-auto">
|
||||
<DefaultMain>
|
||||
<Show when={receiveState() === "show"} fallback={<BackLink />}>
|
||||
<BackButton onClick={() => setReceiveState("edit")} title="Edit" />
|
||||
</Show>
|
||||
@@ -177,12 +203,12 @@ export default function Receive() {
|
||||
<div class="flex flex-col flex-1 gap-8">
|
||||
<AmountCard initialOpen={shouldShowAmountEditor()} amountSats={amount() || "0"} setAmountSats={setAmount} isAmountEditable />
|
||||
|
||||
<Card title="Tag the origin">
|
||||
<TagEditor values={values()} setValues={setValues} selectedValues={selectedValues()} setSelectedValues={setSelectedValues} placeholder="Where's it coming from?" />
|
||||
<Card title="Private tags">
|
||||
<TagEditor selectedValues={selectedValues()} setSelectedValues={setSelectedValues} placeholder="Add the sender for your records" />
|
||||
</Card>
|
||||
|
||||
<div class="flex-1" />
|
||||
<Button class="w-full flex-grow-0 mb-4" disabled={!amount() || !selectedValues().length} intent="green" onClick={onSubmit}>Create Request</Button>
|
||||
<Button class="w-full flex-grow-0" disabled={!amount()} intent="green" onClick={onSubmit}>Create Request</Button>
|
||||
</div>
|
||||
</Match>
|
||||
<Match when={unified() && receiveState() === "show"}>
|
||||
@@ -223,7 +249,7 @@ export default function Receive() {
|
||||
</FullscreenModal>
|
||||
</Match>
|
||||
</Switch>
|
||||
</main>
|
||||
</DefaultMain>
|
||||
<NavBar activeTab="receive" />
|
||||
</SafeArea >
|
||||
</MutinyWalletGuard>
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Button, ButtonLink, Card, DefaultMain, HStack, LargeHeader, MutinyWalle
|
||||
import { Paste } from "~/assets/svg/Paste";
|
||||
import { Scan } from "~/assets/svg/Scan";
|
||||
import { useMegaStore } from "~/state/megaStore";
|
||||
import { MutinyInvoice } from "@mutinywallet/mutiny-wasm";
|
||||
import { Contact, MutinyInvoice } from "@mutinywallet/mutiny-wasm";
|
||||
import { StyledRadioGroup } from "~/components/layout/Radio";
|
||||
import { ParsedParams, toParsedParams } from "./Scanner";
|
||||
import { showToast } from "~/components/Toaster";
|
||||
@@ -121,8 +121,7 @@ export default function Send() {
|
||||
const [sentDetails, setSentDetails] = createSignal<SentDetails>();
|
||||
|
||||
// Tagging stuff
|
||||
const [selectedValues, setSelectedValues] = createSignal<MutinyTagItem[]>([]);
|
||||
const [values, setValues] = createSignal<MutinyTagItem[]>([UNKNOWN_TAG]);
|
||||
const [selectedContacts, setSelectedContacts] = createSignal<Partial<MutinyTagItem>[]>([]);
|
||||
|
||||
function clearAll() {
|
||||
setDestination(undefined);
|
||||
@@ -146,10 +145,6 @@ export default function Send() {
|
||||
setDestination(state.scan_result);
|
||||
actions.setScanResult(undefined);
|
||||
}
|
||||
|
||||
actions.listTags().then((tags) => {
|
||||
setValues(prev => [...prev, ...tags.sort(sortByLastUsed) || []])
|
||||
});
|
||||
})
|
||||
|
||||
// Rerun every time the destination changes
|
||||
@@ -216,27 +211,62 @@ export default function Send() {
|
||||
});
|
||||
}
|
||||
|
||||
async function processContacts(contacts: Partial<MutinyTagItem>[]): Promise<string[]> {
|
||||
console.log("Processing contacts", contacts)
|
||||
|
||||
if (contacts.length) {
|
||||
const first = contacts![0];
|
||||
|
||||
if (!first.name) {
|
||||
console.error("Something went wrong with contact creation, proceeding anyway")
|
||||
return []
|
||||
}
|
||||
|
||||
if (!first.id && first.name) {
|
||||
console.error("Creating new contact", first.name)
|
||||
const c = new Contact(first.name, undefined, undefined, undefined);
|
||||
const newContactId = await state.mutiny_wallet?.create_new_contact(c);
|
||||
if (newContactId) {
|
||||
return [newContactId];
|
||||
}
|
||||
}
|
||||
|
||||
if (first.id) {
|
||||
console.error("Using existing contact", first.name, first.id)
|
||||
return [first.id];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
console.error("Something went wrong with contact creation, proceeding anyway")
|
||||
return []
|
||||
|
||||
}
|
||||
|
||||
async function handleSend() {
|
||||
try {
|
||||
setSending(true);
|
||||
const bolt11 = invoice()?.bolt11;
|
||||
const sentDetails: Partial<SentDetails> = {};
|
||||
|
||||
const tags = await processContacts(selectedContacts());
|
||||
|
||||
if (source() === "lightning" && invoice() && bolt11) {
|
||||
const nodes = await state.mutiny_wallet?.list_nodes();
|
||||
const firstNode = nodes[0] as string || ""
|
||||
sentDetails.destination = bolt11;
|
||||
// If the invoice has sats use that, otherwise we pass the user-defined amount
|
||||
if (invoice()?.amount_sats) {
|
||||
await state.mutiny_wallet?.pay_invoice(firstNode, bolt11, undefined, tagsToIds(selectedValues()));
|
||||
await state.mutiny_wallet?.pay_invoice(firstNode, bolt11, undefined, tags);
|
||||
sentDetails.amount = invoice()?.amount_sats;
|
||||
} else {
|
||||
await state.mutiny_wallet?.pay_invoice(firstNode, bolt11, amountSats(), tagsToIds(selectedValues()));
|
||||
await state.mutiny_wallet?.pay_invoice(firstNode, bolt11, amountSats(), tags);
|
||||
sentDetails.amount = amountSats();
|
||||
}
|
||||
} else if (source() === "lightning" && nodePubkey()) {
|
||||
const nodes = await state.mutiny_wallet?.list_nodes();
|
||||
const firstNode = nodes[0] as string || ""
|
||||
const payment = await state.mutiny_wallet?.keysend(firstNode, nodePubkey()!, amountSats(), tagsToIds(selectedValues()));
|
||||
const payment = await state.mutiny_wallet?.keysend(firstNode, nodePubkey()!, amountSats(), tags);
|
||||
|
||||
// TODO: handle timeouts
|
||||
if (!payment?.paid) {
|
||||
@@ -246,7 +276,7 @@ export default function Send() {
|
||||
}
|
||||
} else if (source() === "onchain" && address()) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const txid = await state.mutiny_wallet?.send_to_address(address()!, amountSats(), tagsToIds(selectedValues()));
|
||||
const txid = await state.mutiny_wallet?.send_to_address(address()!, amountSats(), tags);
|
||||
sentDetails.amount = amountSats();
|
||||
sentDetails.destination = address();
|
||||
// TODO: figure out if this is necessary, it takes forever
|
||||
@@ -312,9 +342,11 @@ export default function Send() {
|
||||
<Card>
|
||||
<VStack>
|
||||
<DestinationShower source={source()} description={description()} invoice={invoice()} address={address()} nodePubkey={nodePubkey()} clearAll={clearAll} />
|
||||
<TagEditor values={values()} setValues={setValues} selectedValues={selectedValues()} setSelectedValues={setSelectedValues} placeholder="Where's it going to?" />
|
||||
<SmallHeader>Private tags</SmallHeader>
|
||||
<TagEditor selectedValues={selectedContacts()} setSelectedValues={setSelectedContacts} placeholder="Add the receiver for your records" />
|
||||
</VStack>
|
||||
</Card>
|
||||
|
||||
<AmountCard amountSats={amountSats().toString()} setAmountSats={setAmountSats} fee={fakeFee().toString()} isAmountEditable={!(invoice()?.amount_sats)} />
|
||||
</Match>
|
||||
<Match when={true}>
|
||||
@@ -322,7 +354,7 @@ export default function Send() {
|
||||
</Match>
|
||||
</Switch>
|
||||
<Show when={destination()}>
|
||||
<Button disabled={sendButtonDisabled()} intent="blue" onClick={handleSend} loading={sending()}>{sending() ? "Sending..." : "Confirm Send"}</Button>
|
||||
<Button class="w-full flex-grow-0" disabled={sendButtonDisabled()} intent="blue" onClick={handleSend} loading={sending()}>{sending() ? "Sending..." : "Confirm Send"}</Button>
|
||||
</Show>
|
||||
</VStack>
|
||||
</DefaultMain>
|
||||
|
||||
2
src/styles/dialogs.ts
Normal file
2
src/styles/dialogs.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export const DIALOG_POSITIONER = "fixed inset-0 h-[100dvh] z-50"
|
||||
export const DIALOG_CONTENT = "h-[100dvh] flex flex-col justify-between px-4 pt-4 pb-8 bg-neutral-800/80 backdrop-blur-xl"
|
||||
@@ -1,4 +1,4 @@
|
||||
import { TagItem } from "@mutinywallet/mutiny-wasm"
|
||||
import { Contact, TagItem } from "@mutinywallet/mutiny-wasm"
|
||||
|
||||
export type MutinyTagItem = {
|
||||
id: string,
|
||||
@@ -12,7 +12,10 @@ export type MutinyTagItem = {
|
||||
|
||||
export const UNKNOWN_TAG: MutinyTagItem = { id: "Unknown", kind: "Label", name: "Unknown", last_used_time: 0n }
|
||||
|
||||
export function tagsToIds(tags: MutinyTagItem[]): string[] {
|
||||
export function tagsToIds(tags?: MutinyTagItem[]): string[] {
|
||||
if (!tags) {
|
||||
return []
|
||||
}
|
||||
return tags.filter((tag) => tag.id !== "Unknown").map((tag) => tag.id)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user