mirror of
https://github.com/aljazceru/mutiny-web.git
synced 2025-12-21 16:24:22 +01:00
add textbox to send screen
This commit is contained in:
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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...")
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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 >
|
||||||
|
|||||||
Reference in New Issue
Block a user