add textbox to send screen

This commit is contained in:
Paul Miller
2023-04-24 14:36:20 -05:00
parent 22d09b412b
commit bc7dcb6e12
4 changed files with 72 additions and 71 deletions

View File

@@ -96,6 +96,10 @@ const VStack: ParentComponent<{ biggap?: boolean }> = (props) => {
return (<div class={`flex flex-col gap-${props.biggap ? "8" : "4"}`}>{props.children}</div>) return (<div class={`flex flex-col gap-${props.biggap ? "8" : "4"}`}>{props.children}</div>)
} }
const HStack: ParentComponent<{ biggap?: boolean }> = (props) => {
return (<div class={`flex gap-${props.biggap ? "8" : "4"}`}>{props.children}</div>)
}
const SmallAmount: ParentComponent<{ amount: number | bigint }> = (props) => { const SmallAmount: ParentComponent<{ amount: number | bigint }> = (props) => {
return (<h2 class="font-light text-lg">{props.amount.toLocaleString()} <span class="text-sm">SATS</span></h2>) return (<h2 class="font-light text-lg">{props.amount.toLocaleString()} <span class="text-sm">SATS</span></h2>)
} }
@@ -116,5 +120,6 @@ export {
DefaultMain, DefaultMain,
LargeHeader, LargeHeader,
VStack, VStack,
HStack,
SmallAmount SmallAmount
} }

View File

@@ -1,5 +1,6 @@
import init, { NodeManager } from '@mutinywallet/mutiny-wasm'; import initNodeManager, { NodeManager } from '@mutinywallet/mutiny-wasm';
import initWaila from '@mutinywallet/waila-wasm'
// export type NodeManagerSettingStrings = { // export type NodeManagerSettingStrings = {
// network?: string, proxy?: string, esplora?: string, rgs?: string, lsp?: string, // network?: string, proxy?: string, esplora?: string, rgs?: string, lsp?: string,
@@ -70,7 +71,9 @@ export async function checkForWasm() {
} }
export async function setupNodeManager(settings?: NodeManagerSettingStrings): Promise<NodeManager> { export async function setupNodeManager(settings?: NodeManagerSettingStrings): Promise<NodeManager> {
const _ = await init(); await initNodeManager();
// Might as well init waila while we're at it
await initWaila();
console.time("Setup"); console.time("Setup");
console.log("Starting setup...") console.log("Starting setup...")

View File

@@ -16,7 +16,13 @@ export type ParsedParams = {
} }
export function toParsedParams(str: string, ourNetwork: string): Result<ParsedParams> { export function toParsedParams(str: string, ourNetwork: string): Result<ParsedParams> {
const params = new PaymentParams(str || "") let params;
try {
params = new PaymentParams(str || "")
} catch (e) {
console.error(e)
return { ok: false, error: new Error("Invalid payment request") }
}
console.log("params network:", params.network) console.log("params network:", params.network)
console.log("our network:", ourNetwork) console.log("our network:", ourNetwork)

View File

@@ -1,8 +1,7 @@
import { TextField } from "@kobalte/core";
import { Match, Show, Switch, createEffect, createMemo, createResource, createSignal, onCleanup, onMount } from "solid-js"; import { Match, Show, Switch, createEffect, createMemo, createResource, createSignal, onCleanup, onMount } from "solid-js";
import { Amount } from "~/components/Amount"; import { Amount } from "~/components/Amount";
import NavBar from "~/components/NavBar"; import NavBar from "~/components/NavBar";
import { Button, ButtonLink, DefaultMain, LargeHeader, NodeManagerGuard, SafeArea, SmallHeader } from "~/components/layout"; import { Button, ButtonLink, DefaultMain, HStack, LargeHeader, NodeManagerGuard, SafeArea, SmallHeader, VStack } from "~/components/layout";
import { Paste } from "~/assets/svg/Paste"; import { Paste } from "~/assets/svg/Paste";
import { Scan } from "~/assets/svg/Scan"; import { Scan } from "~/assets/svg/Scan";
import { useMegaStore } from "~/state/megaStore"; import { useMegaStore } from "~/state/megaStore";
@@ -10,7 +9,6 @@ import { MutinyInvoice } from "@mutinywallet/mutiny-wasm";
import { AmountEditable } from "~/components/AmountEditable"; import { AmountEditable } from "~/components/AmountEditable";
import { StyledRadioGroup } from "~/components/layout/Radio"; import { StyledRadioGroup } from "~/components/layout/Radio";
import { ParsedParams, toParsedParams } from "./Scanner"; import { ParsedParams, toParsedParams } from "./Scanner";
import init from "@mutinywallet/waila-wasm";
import { showToast } from "~/components/Toaster"; import { showToast } from "~/components/Toaster";
import eify from "~/utils/eify"; import eify from "~/utils/eify";
import { FullscreenModal } from "~/components/layout/FullscreenModal"; import { FullscreenModal } from "~/components/layout/FullscreenModal";
@@ -28,23 +26,10 @@ const PAYMENT_METHODS = [{ value: "lightning", label: "Lightning", caption: "Fas
type SentDetails = { amount: bigint, destination: string, txid?: string } type SentDetails = { amount: bigint, destination: string, txid?: string }
export default function Send() { export default function Send() {
let waila;
onMount(() => {
init().then((w) => {
waila = w;
});
})
// TODO: Is this just implied?
onCleanup(() => {
waila = undefined;
})
const [state, actions] = useMegaStore(); const [state, actions] = useMegaStore();
// These can only be set by the user // These can only be set by the user
const [fieldDestination, setFieldDestination] = createSignal("");
const [destination, setDestination] = createSignal<ParsedParams>(); const [destination, setDestination] = createSignal<ParsedParams>();
const [privateLabel, setPrivateLabel] = createSignal(""); const [privateLabel, setPrivateLabel] = createSignal("");
@@ -69,6 +54,7 @@ export default function Send() {
setInvoice(undefined); setInvoice(undefined);
setAddress(undefined); setAddress(undefined);
setDescription(undefined); setDescription(undefined);
setFieldDestination("");
} }
const fakeFee = createMemo(() => { const fakeFee = createMemo(() => {
@@ -110,8 +96,7 @@ export default function Send() {
} }
}) })
function handlePaste() { function parsePaste(text: string) {
navigator.clipboard.readText().then(text => {
if (text) { if (text) {
const network = state.node_manager?.get_network() || "signet"; const network = state.node_manager?.get_network() || "signet";
const result = toParsedParams(text || "", network); const result = toParsedParams(text || "", network);
@@ -126,6 +111,21 @@ export default function Send() {
} }
} }
} }
}
function handleDecode() {
const text = fieldDestination();
parsePaste(text);
}
function handlePaste() {
if (!navigator.clipboard.readText) return showToast(new Error("Clipboard not supported"));
navigator.clipboard.readText().then(text => {
setFieldDestination(text);
parsePaste(text);
}).catch((e) => {
showToast(new Error("Failed to read clipboard: " + e.message))
}); });
} }
@@ -171,7 +171,6 @@ export default function Send() {
<DefaultMain> <DefaultMain>
<BackButton /> <BackButton />
<LargeHeader>Send Bitcoin</LargeHeader> <LargeHeader>Send Bitcoin</LargeHeader>
{/* <SentModal details={sentDetails()} /> */}
<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" />
@@ -231,7 +230,10 @@ export default function Send() {
</div> </div>
</Match> </Match>
<Match when={true}> <Match when={true}>
<div class="flex flex-row gap-4"> <VStack>
<textarea value={fieldDestination()} onInput={(e) => setFieldDestination(e.currentTarget.value)} placeholder="bitcoin:..." class="p-2 rounded-lg bg-white/10 placeholder-neutral-400" />
<Button disabled={!fieldDestination()} intent="blue" onClick={handleDecode}>Decode</Button>
<HStack>
<Button onClick={handlePaste}> <Button onClick={handlePaste}>
<div class="flex flex-col gap-2 items-center"> <div class="flex flex-col gap-2 items-center">
<Paste /> <Paste />
@@ -244,7 +246,10 @@ export default function Send() {
<span>Scan QR</span> <span>Scan QR</span>
</div> </div>
</ButtonLink> </ButtonLink>
</div> </HStack>
</VStack>
</Match> </Match>
</Switch> </Switch>
</dd> </dd>
@@ -258,28 +263,10 @@ export default function Send() {
<StyledRadioGroup value={source()} onValueChange={setSource} choices={PAYMENT_METHODS} /> <StyledRadioGroup value={source()} onValueChange={setSource} choices={PAYMENT_METHODS} />
</dd> </dd>
</Show> </Show>
<Show when={destination()}>
<TextField.Root
value={privateLabel()}
onValueChange={setPrivateLabel}
class="flex flex-col gap-2"
>
<dt>
<SmallHeader>
<TextField.Label>Label (private)</TextField.Label>
</SmallHeader>
</dt>
<dd>
<TextField.Input
autofocus
class="w-full p-2 rounded-lg bg-white/10"
placeholder="A helpful reminder of why you spent bitcoin"
/>
</dd>
</TextField.Root>
</Show>
</dl> </dl>
<Show when={destination()}>
<Button disabled={!destination() || sending()} intent="blue" onClick={handleSend} loading={sending()}>{sending() ? "Sending..." : "Confirm Send"}</Button> <Button disabled={!destination() || sending()} intent="blue" onClick={handleSend} loading={sending()}>{sending() ? "Sending..." : "Confirm Send"}</Button>
</Show>
</DefaultMain> </DefaultMain>
<NavBar activeTab="send" /> <NavBar activeTab="send" />
</SafeArea > </SafeArea >