diff --git a/src/assets/icons/tinyArrow.svg b/src/assets/icons/tinyArrow.svg
new file mode 100644
index 0000000..4e0a5a0
--- /dev/null
+++ b/src/assets/icons/tinyArrow.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/src/components/Activity.tsx b/src/components/Activity.tsx
index 94b7622..91c8dc1 100644
--- a/src/components/Activity.tsx
+++ b/src/components/Activity.tsx
@@ -174,7 +174,9 @@ export function CombinedActivity(props: { limit?: number }) {
- No activity to show
+
+ Receive some sats get started
+
= 0}>
@@ -198,5 +200,5 @@ export function CombinedActivity(props: { limit?: number }) {
- )
+ );
}
diff --git a/src/components/Amount.tsx b/src/components/Amount.tsx
index 289d4c6..af76752 100644
--- a/src/components/Amount.tsx
+++ b/src/components/Amount.tsx
@@ -10,17 +10,17 @@ function prettyPrintAmount(n?: number | bigint): string {
}
export function Amount(props: {
- amountSats: bigint | number | undefined
- showFiat?: boolean
- loading?: boolean
+ amountSats: bigint | number | undefined;
+ showFiat?: boolean;
+ loading?: boolean;
+ centered?: boolean;
}) {
- const [state, _] = useMegaStore()
+ const [state, _] = useMegaStore();
- const amountInUsd = () =>
- satsToUsd(state.price, Number(props.amountSats) || 0, true)
+ const amountInUsd = () => satsToUsd(state.price, Number(props.amountSats) || 0, true);
return (
-
+
{props.loading ? "..." : prettyPrintAmount(props.amountSats)}
SATS
@@ -32,7 +32,7 @@ export function Amount(props: {
- )
+ );
}
export function AmountSmall(props: {
diff --git a/src/components/App.tsx b/src/components/App.tsx
index 7dcc14c..b5b54d6 100644
--- a/src/components/App.tsx
+++ b/src/components/App.tsx
@@ -9,6 +9,7 @@ import { CombinedActivity } from './Activity';
import userClock from '~/assets/icons/user-clock.svg';
import { useMegaStore } from '~/state/megaStore';
import { Show } from 'solid-js';
+import { ExternalLink } from "./layout/ExternalLink";
export default function App() {
const [state, _actions] = useMegaStore();
@@ -44,13 +45,11 @@ export default function App() {
Bugs? Feedback?{" "}
-
- Create an issue
-
+
+
+ Create an issue
+
+
diff --git a/src/components/BalanceBox.tsx b/src/components/BalanceBox.tsx
index 28b17c6..9cf09e6 100644
--- a/src/components/BalanceBox.tsx
+++ b/src/components/BalanceBox.tsx
@@ -1,73 +1,76 @@
-import { Show, Suspense } from "solid-js";
+import { Show } from "solid-js";
import { Button, FancyCard, Indicator } from "~/components/layout";
import { useMegaStore } from "~/state/megaStore";
import { Amount } from "./Amount";
import { A, useNavigate } from "solid-start";
-import shuffle from "~/assets/icons/shuffle.svg"
-
-function prettyPrintAmount(n?: number | bigint): string {
- if (!n || n.valueOf() === 0) {
- return "0"
- }
- return n.toLocaleString()
-}
+import shuffle from "~/assets/icons/shuffle.svg";
export function LoadingShimmer() {
- return (
)
+ return (
+
+ );
}
-const STYLE = "px-2 py-1 rounded-xl border border-neutral-400 text-sm flex gap-2 items-center font-semibold"
+const STYLE =
+ "px-2 py-1 rounded-xl border border-neutral-400 text-sm flex gap-2 items-center font-semibold";
export default function BalanceBox(props: { loading?: boolean }) {
- const [state, _actions] = useMegaStore();
+ const [state, _actions] = useMegaStore();
- const emptyBalance = () => (state.balance?.confirmed || 0n) === 0n && (state.balance?.lightning || 0n) === 0n
+ const emptyBalance = () =>
+ (state.balance?.confirmed || 0n) === 0n &&
+ (state.balance?.lightning || 0n) === 0n &&
+ (state.balance?.unconfirmed || 0n) === 0n;
- const navigate = useNavigate()
+ const navigate = useNavigate();
- return (
- <>
-
- }>
-
-
-
+ const totalOnchain = () => (state.balance?.confirmed || 0n) + (state.balance?.unconfirmed || 0n);
-
Syncing}>
- }>
-
-
-
-
-
-
-
- {prettyPrintAmount(state.balance?.unconfirmed)} SATS
-
-
-
-
-
-
- navigate("/send")} disabled={emptyBalance() || props.loading} intent="green">Send
- navigate("/receive")} disabled={props.loading} intent="blue">Receive
-
- >
- )
+ return (
+ <>
+
+ }>
+
+
+
+
+
Syncing}
+ >
+ }>
+
+
+
+
+ navigate("/send")}
+ disabled={emptyBalance() || props.loading}
+ intent="green"
+ >
+ Send
+
+ navigate("/receive")} disabled={props.loading} intent="blue">
+ Receive
+
+
+ >
+ );
}
diff --git a/src/components/KitchenSink.tsx b/src/components/KitchenSink.tsx
index bfa9cef..7b90dfd 100644
--- a/src/components/KitchenSink.tsx
+++ b/src/components/KitchenSink.tsx
@@ -11,170 +11,187 @@ import { ConfirmDialog } from "~/components/Dialog";
import { showToast } from "~/components/Toaster";
import { ImportExport } from "~/components/ImportExport";
import { Network } from "~/logic/mutinyWalletSetup";
+import { ExternalLink } from "./layout/ExternalLink";
// TODO: hopefully I don't have to maintain this type forever but I don't know how to pass it around otherwise
-type RefetchPeersType = (info?: unknown) => MutinyPeer[] | Promise
| null | undefined
+type RefetchPeersType = (
+ info?: unknown
+) => MutinyPeer[] | Promise | null | undefined;
function PeerItem(props: { peer: MutinyPeer }) {
- const [state, _] = useMegaStore()
+ const [state, _] = useMegaStore();
- const handleDisconnectPeer = async () => {
- const nodes = await state.mutiny_wallet?.list_nodes();
- const firstNode = nodes[0] as string || ""
+ const handleDisconnectPeer = async () => {
+ const nodes = await state.mutiny_wallet?.list_nodes();
+ const firstNode = (nodes[0] as string) || "";
- if (props.peer.is_connected) {
- await state.mutiny_wallet?.disconnect_peer(firstNode, props.peer.pubkey);
- } else {
- await state.mutiny_wallet?.delete_peer(firstNode, props.peer.pubkey);
- }
- };
+ if (props.peer.is_connected) {
+ await state.mutiny_wallet?.disconnect_peer(firstNode, props.peer.pubkey);
+ } else {
+ await state.mutiny_wallet?.delete_peer(firstNode, props.peer.pubkey);
+ }
+ };
- return (
-
-
-
- {">"} {props.peer.alias ? props.peer.alias : props.peer.pubkey}
-
-
-
-
-
- {JSON.stringify(props.peer, null, 2)}
-
- Disconnect
-
-
-
- )
+ return (
+
+
+
+ {">"} {props.peer.alias ? props.peer.alias : props.peer.pubkey}
+
+
+
+
+
+ {JSON.stringify(props.peer, null, 2)}
+
+
+ Disconnect
+
+
+
+
+ );
}
function PeersList() {
- const [state, _] = useMegaStore()
+ const [state, _] = useMegaStore();
- const getPeers = async () => {
- return await state.mutiny_wallet?.list_peers() as Promise
- };
+ const getPeers = async () => {
+ return (await state.mutiny_wallet?.list_peers()) as Promise;
+ };
- const [peers, { refetch }] = createResource(getPeers);
+ const [peers, { refetch }] = createResource(getPeers);
- createEffect(() => {
- // refetch peers every 5 seconds
- const interval = setTimeout(() => {
- refetch();
- }, 5000);
- onCleanup(() => {
- clearInterval(interval);
- });
- })
+ createEffect(() => {
+ // refetch peers every 5 seconds
+ const interval = setTimeout(() => {
+ refetch();
+ }, 5000);
+ onCleanup(() => {
+ clearInterval(interval);
+ });
+ });
- return (
- <>
-
- Peers
-
- {/* By wrapping this in a suspense I don't cause the page to jump to the top */}
-
-
- No peers}>
- {(peer) => (
-
- )}
-
-
-
- Refresh Peers
-
- >
- )
+ return (
+ <>
+ Peers
+ {/* By wrapping this in a suspense I don't cause the page to jump to the top */}
+
+
+ No peers}>
+ {(peer) => }
+
+
+
+
+ Refresh Peers
+
+
+ >
+ );
}
function ConnectPeer(props: { refetchPeers: RefetchPeersType }) {
- const [state, _] = useMegaStore()
+ const [state, _] = useMegaStore();
- const [value, setValue] = createSignal("");
+ const [value, setValue] = createSignal("");
- const onSubmit = async (e: SubmitEvent) => {
- e.preventDefault();
+ const onSubmit = async (e: SubmitEvent) => {
+ e.preventDefault();
- const peerConnectString = value().trim();
- const nodes = await state.mutiny_wallet?.list_nodes();
- const firstNode = nodes[0] as string || ""
+ const peerConnectString = value().trim();
+ const nodes = await state.mutiny_wallet?.list_nodes();
+ const firstNode = (nodes[0] as string) || "";
- await state.mutiny_wallet?.connect_to_peer(firstNode, peerConnectString)
+ await state.mutiny_wallet?.connect_to_peer(firstNode, peerConnectString);
- await props.refetchPeers()
+ await props.refetchPeers();
- setValue("");
- };
+ setValue("");
+ };
- return (
-
-
-
- )
+ return (
+
+
+
+ );
}
+type RefetchChannelsListType = (
+ info?: unknown
+) => MutinyChannel[] | Promise | null | undefined;
-type RefetchChannelsListType = (info?: unknown) => MutinyChannel[] | Promise | null | undefined
+function ChannelItem(props: { channel: MutinyChannel; network?: Network }) {
+ const [state, _] = useMegaStore();
-function ChannelItem(props: { channel: MutinyChannel, network?: Network }) {
- const [state, _] = useMegaStore()
+ const [confirmOpen, setConfirmOpen] = createSignal(false);
+ const [confirmLoading, setConfirmLoading] = createSignal(false);
- const [confirmOpen, setConfirmOpen] = createSignal(false);
- const [confirmLoading, setConfirmLoading] = createSignal(false);
+ function handleCloseChannel() {
+ setConfirmOpen(true);
+ }
- function handleCloseChannel() {
- setConfirmOpen(true);
+ async function confirmCloseChannel() {
+ setConfirmLoading(true);
+ try {
+ await state.mutiny_wallet?.close_channel(props.channel.outpoint as string);
+ } catch (e) {
+ console.error(e);
+ showToast(eify(e));
}
+ setConfirmLoading(false);
+ setConfirmOpen(false);
+ }
- async function confirmCloseChannel() {
- setConfirmLoading(true);
- try {
- await state.mutiny_wallet?.close_channel(props.channel.outpoint as string)
- } catch (e) {
- console.error(e);
- showToast(eify(e));
- }
- setConfirmLoading(false);
- setConfirmOpen(false);
- }
-
- return (
-
-
-
- {">"} {props.channel.peer}
-
-
-
-
-
- {JSON.stringify(props.channel, null, 2)}
-
-
- Mempool Link
-
- Close Channel
-
-
- setConfirmOpen(false)} loading={confirmLoading()}>
- Are you sure you want to close this channel?
-
-
-
- )
+ return (
+
+
+
+ {">"} {props.channel.peer}
+
+
+
+
+
+ {JSON.stringify(props.channel, null, 2)}
+
+
+ View Transaction
+
+
+ Close Channel
+
+
+ setConfirmOpen(false)}
+ loading={confirmLoading()}
+ >
+ Are you sure you want to close this channel?
+
+
+
+ );
}
function ChannelsList() {
@@ -258,44 +275,40 @@ function OpenChannel(props: { refetchChannels: RefetchChannelsListType }) {
const network = state.mutiny_wallet?.get_network() as Network;
return (
- <>
-
-
-
-
-
- {JSON.stringify(newChannel()?.outpoint, null, 2)}
-
- {newChannel()?.outpoint}
-
- Mempool Link
-
-
-
- {creationError()?.message}
-
- >
- )
+ <>
+
+
+
+
+
+ {JSON.stringify(newChannel()?.outpoint, null, 2)}
+
+ {newChannel()?.outpoint}
+
+ View Transaction
+
+
+
+ {creationError()?.message}
+
+ >
+ );
}
function LnUrlAuth() {
diff --git a/src/components/layout/Button.tsx b/src/components/layout/Button.tsx
index 30b60d3..f92e999 100644
--- a/src/components/layout/Button.tsx
+++ b/src/components/layout/Button.tsx
@@ -4,29 +4,33 @@ import { Dynamic } from "solid-js/web";
import { A } from "solid-start";
import { LoadingSpinner } from ".";
-const button = cva("p-3 rounded-xl font-semibold disabled:opacity-50 disabled:grayscale transition", {
+const button = cva(
+ "p-3 rounded-xl font-semibold disabled:opacity-20 disabled:grayscale transition",
+ {
variants: {
- // TODO: button hover has to work different than buttonlinks (like disabled state)
- intent: {
- active: "bg-white text-black border border-white hover:text-[#3B6CCC]",
- inactive: "bg-black text-white border border-white hover:text-[#3B6CCC]",
- glowy: "bg-black/10 shadow-xl text-white border border-m-blue hover:m-blue-dark hover:text-m-blue",
- blue: "bg-m-blue text-white shadow-inner-button hover:bg-m-blue-dark text-shadow-button",
- red: "bg-m-red text-white shadow-inner-button hover:bg-m-red-dark text-shadow-button",
- green: "bg-m-green text-white shadow-inner-button hover:bg-m-green-dark text-shadow-button",
- },
- layout: {
- flex: "flex-1 text-xl",
- pad: "px-8 text-xl",
- small: "px-4 py-2 w-auto text-lg",
- xs: "px-4 py-2 w-auto rounded-lg text-base"
- },
+ // TODO: button hover has to work different than buttonlinks (like disabled state)
+ intent: {
+ active: "bg-white text-black border border-white hover:text-[#3B6CCC]",
+ inactive: "bg-black text-white border border-white hover:text-[#3B6CCC]",
+ glowy:
+ "bg-black/10 shadow-xl text-white border border-m-blue hover:m-blue-dark hover:text-m-blue",
+ blue: "bg-m-blue text-white shadow-inner-button hover:bg-m-blue-dark text-shadow-button",
+ red: "bg-m-red text-white shadow-inner-button hover:bg-m-red-dark text-shadow-button",
+ green: "bg-m-green text-white shadow-inner-button hover:bg-m-green-dark text-shadow-button"
+ },
+ layout: {
+ flex: "flex-1 text-xl",
+ pad: "px-8 text-xl",
+ small: "px-4 py-2 w-auto text-lg",
+ xs: "px-4 py-2 w-auto rounded-lg text-base"
+ }
},
defaultVariants: {
- intent: "inactive",
- layout: "flex"
- },
-});
+ intent: "inactive",
+ layout: "flex"
+ }
+ }
+);
// Help from https://github.com/arpadgabor/credee/blob/main/packages/www/src/components/ui/button.tsx
diff --git a/src/components/layout/ExternalLink.tsx b/src/components/layout/ExternalLink.tsx
new file mode 100644
index 0000000..7d0e7ab
--- /dev/null
+++ b/src/components/layout/ExternalLink.tsx
@@ -0,0 +1,21 @@
+import { ParentComponent } from "solid-js";
+
+export const ExternalLink: ParentComponent<{ href: string }> = (props) => {
+ return (
+
+ {props.children}{" "}
+
+
+
+
+ );
+};
diff --git a/src/components/layout/FullscreenModal.tsx b/src/components/layout/FullscreenModal.tsx
deleted file mode 100644
index 0c50348..0000000
--- a/src/components/layout/FullscreenModal.tsx
+++ /dev/null
@@ -1,49 +0,0 @@
-
-import { Dialog } from "@kobalte/core";
-import { JSX } from "solid-js";
-import { Button, LargeHeader } from "~/components/layout";
-import close from "~/assets/icons/close.svg";
-import { DIALOG_CONTENT, DIALOG_POSITIONER } from "~/styles/dialogs";
-
-type FullscreenModalProps = {
- title: string,
- open: boolean,
- setOpen: (open: boolean) => void,
- children?: JSX.Element,
- onConfirm?: () => void
- confirmText?: string
-}
-
-export function FullscreenModal(props: FullscreenModalProps) {
-
- const onNice = () => {
- props.onConfirm ? props.onConfirm() : props.setOpen(false)
- }
-
- return (
-
-
-
-
-
-
-
- {props.title}
-
-
-
-
-
-
-
- {props.children}
-
-
- {props.confirmText ?? "Nice"}
-
-
-
-
-
- )
-}
\ No newline at end of file
diff --git a/src/components/layout/Radio.tsx b/src/components/layout/Radio.tsx
index cf837f9..5a78c8a 100644
--- a/src/components/layout/Radio.tsx
+++ b/src/components/layout/Radio.tsx
@@ -6,34 +6,49 @@ type Choices = { value: string, label: string, caption: string }[]
// 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, small?: boolean, accent?: "red" | "white" }) {
return (
- // TODO: rewrite this with CVA, props are bad for tailwind
-
-
- {choice =>
-
+
+ {(choice) => (
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
{choice.label}
-
- {choice.caption}
-
-
-
-
-
- }
-
-
- )
+ {choice.label}
+
+
+ {choice.caption}
+
+
+
+
+
+ )}
+
+
+ );
}
\ No newline at end of file
diff --git a/src/components/layout/index.tsx b/src/components/layout/index.tsx
index d7cd3f8..92c4da8 100644
--- a/src/components/layout/index.tsx
+++ b/src/components/layout/index.tsx
@@ -37,17 +37,24 @@ export const InnerCard: ParentComponent<{ title?: string }> = (props) => {
)
}
-export const FancyCard: ParentComponent<{ title?: string, tag?: JSX.Element }> = (props) => {
- return (
-
-
- {props.title && {props.title} }
- {props.tag && props.tag}
-
- {props.children}
+export const FancyCard: ParentComponent<{
+ title?: string;
+ subtitle?: string;
+ tag?: JSX.Element;
+}> = (props) => {
+ return (
+
+
+
+ {props.title && {props.title} }
+ {props.subtitle && {props.subtitle} }
- )
-}
+ {props.tag && props.tag}
+
+ {props.children}
+
+ );
+};
export const SafeArea: ParentComponent = (props) => {
return (
diff --git a/src/components/successfail/MegaCheck.tsx b/src/components/successfail/MegaCheck.tsx
new file mode 100644
index 0000000..a681a62
--- /dev/null
+++ b/src/components/successfail/MegaCheck.tsx
@@ -0,0 +1,5 @@
+import megacheck from "~/assets/icons/megacheck.png";
+
+export function MegaCheck() {
+ return
;
+}
diff --git a/src/components/successfail/MegaEx.tsx b/src/components/successfail/MegaEx.tsx
new file mode 100644
index 0000000..7468d15
--- /dev/null
+++ b/src/components/successfail/MegaEx.tsx
@@ -0,0 +1,5 @@
+import megaex from "~/assets/icons/megaex.png";
+
+export function MegaEx() {
+ return
;
+}
diff --git a/src/components/successfail/SuccessModal.tsx b/src/components/successfail/SuccessModal.tsx
new file mode 100644
index 0000000..1c3e13f
--- /dev/null
+++ b/src/components/successfail/SuccessModal.tsx
@@ -0,0 +1,43 @@
+import { Dialog } from "@kobalte/core";
+import { JSX } from "solid-js";
+import { Button, LargeHeader } from "~/components/layout";
+import { DIALOG_CONTENT, DIALOG_POSITIONER } from "~/styles/dialogs";
+
+type SuccessModalProps = {
+ title: string;
+ open: boolean;
+ setOpen: (open: boolean) => void;
+ children?: JSX.Element;
+ onConfirm?: () => void;
+ confirmText?: string;
+};
+
+export function SuccessModal(props: SuccessModalProps) {
+ const onNice = () => {
+ props.onConfirm ? props.onConfirm() : props.setOpen(false);
+ };
+
+ //
+ return (
+
+
+
+
+
+
+ {props.children}
+
+
+ {props.confirmText ?? "Nice"}
+
+
+
+
+
+ );
+}
diff --git a/src/root.css b/src/root.css
index a69d595..f5540cc 100644
--- a/src/root.css
+++ b/src/root.css
@@ -50,4 +50,4 @@ select {
background-position: right 0.75rem center;
background-size: 20px 20px;
background-repeat: no-repeat;
-}
+}
\ No newline at end of file
diff --git a/src/routes/Receive.tsx b/src/routes/Receive.tsx
index 17129c6..0a56d0d 100644
--- a/src/routes/Receive.tsx
+++ b/src/routes/Receive.tsx
@@ -7,254 +7,285 @@ import { useMegaStore } from "~/state/megaStore";
import { objectToSearchParams } from "~/utils/objectToSearchParams";
import mempoolTxUrl from "~/utils/mempoolTxUrl";
import { Amount } from "~/components/Amount";
-import { FullscreenModal } from "~/components/layout/FullscreenModal";
import { BackLink } from "~/components/layout/BackLink";
import { TagEditor } from "~/components/TagEditor";
import { StyledRadioGroup } from "~/components/layout/Radio";
import { showToast } from "~/components/Toaster";
import { useNavigate } from "solid-start";
-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 } from "~/utils/tags";
import { Network } from "~/logic/mutinyWalletSetup";
+import { SuccessModal } from "~/components/successfail/SuccessModal";
+import { MegaCheck } from "~/components/successfail/MegaCheck";
+import { ExternalLink } from "~/components/layout/ExternalLink";
type OnChainTx = {
- transaction: {
- version: number
- lock_time: number
- input: Array<{
- previous_output: string
- script_sig: string
- sequence: number
- witness: Array
- }>
- output: Array<{
- value: number
- script_pubkey: string
- }>
- }
- txid: string
- received: number
- sent: number
- confirmation_time: {
- height: number
- timestamp: number
- }
-}
+ transaction: {
+ version: number;
+ lock_time: number;
+ input: Array<{
+ previous_output: string;
+ script_sig: string;
+ sequence: number;
+ witness: Array;
+ }>;
+ output: Array<{
+ value: number;
+ script_pubkey: string;
+ }>;
+ };
+ txid: string;
+ received: number;
+ sent: number;
+ confirmation_time: {
+ height: number;
+ timestamp: number;
+ };
+};
-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" }]
+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"
-type ReceiveState = "edit" | "show" | "paid"
+type ReceiveFlavor = "unified" | "lightning" | "onchain";
+type ReceiveState = "edit" | "show" | "paid";
type PaidState = "lightning_paid" | "onchain_paid";
export default function Receive() {
- const [state, _actions] = useMegaStore()
- const navigate = useNavigate();
+ const [state, _actions] = useMegaStore();
+ const navigate = useNavigate();
- const [amount, setAmount] = createSignal("")
- const [receiveState, setReceiveState] = createSignal("edit")
- const [bip21Raw, setBip21Raw] = createSignal();
- const [unified, setUnified] = createSignal("")
- const [shouldShowAmountEditor, setShouldShowAmountEditor] = createSignal(true)
+ const [amount, setAmount] = createSignal("");
+ const [receiveState, setReceiveState] = createSignal("edit");
+ const [bip21Raw, setBip21Raw] = createSignal();
+ const [unified, setUnified] = createSignal("");
+ const [shouldShowAmountEditor, setShouldShowAmountEditor] = createSignal(true);
- // Tagging stuff
- const [selectedValues, setSelectedValues] = createSignal([]);
+ // Tagging stuff
+ const [selectedValues, setSelectedValues] = createSignal([]);
- // The data we get after a payment
- const [paymentTx, setPaymentTx] = createSignal();
- const [paymentInvoice, setPaymentInvoice] = createSignal();
+ // The data we get after a payment
+ const [paymentTx, setPaymentTx] = createSignal();
+ const [paymentInvoice, setPaymentInvoice] = createSignal();
- // The flavor of the receive
- const [flavor, setFlavor] = createSignal("unified");
+ // The flavor of the receive
+ const [flavor, setFlavor] = createSignal("unified");
- const receiveString = createMemo(() => {
- if (unified() && receiveState() === "show") {
- if (flavor() === "unified") {
- return unified();
- } else if (flavor() === "lightning") {
- return bip21Raw()?.invoice ?? "";
- } else if (flavor() === "onchain") {
- return bip21Raw()?.address ?? "";
- }
+ const receiveString = createMemo(() => {
+ if (unified() && receiveState() === "show") {
+ if (flavor() === "unified") {
+ return unified();
+ } else if (flavor() === "lightning") {
+ return bip21Raw()?.invoice ?? "";
+ } else if (flavor() === "onchain") {
+ return bip21Raw()?.address ?? "";
+ }
+ }
+ });
+ function clearAll() {
+ setAmount("");
+ setReceiveState("edit");
+ setBip21Raw(undefined);
+ setUnified("");
+ setPaymentTx(undefined);
+ setPaymentInvoice(undefined);
+ setSelectedValues([]);
+ }
+
+ async function processContacts(contacts: Partial[]): Promise {
+ 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];
}
- })
+ }
- function clearAll() {
- setAmount("")
- setReceiveState("edit")
- setBip21Raw(undefined)
- setUnified("")
- setPaymentTx(undefined)
- setPaymentInvoice(undefined)
- setSelectedValues([])
+ if (first.id) {
+ console.error("Using existing contact", first.name, first.id);
+ return [first.id];
+ }
}
- async function processContacts(contacts: Partial[]): Promise {
- console.log("Processing contacts", contacts)
+ console.error("Something went wrong with contact creation, proceeding anyway");
+ return [];
+ }
- if (contacts.length) {
- const first = contacts![0];
+ async function getUnifiedQr(amount: string) {
+ const bigAmount = BigInt(amount);
+ try {
+ 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);
- 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 []
+ const params = objectToSearchParams({
+ amount: raw?.btc_amount,
+ lightning: raw?.invoice
+ });
+ return `bitcoin:${raw?.address}?${params}`;
+ } catch (e) {
+ showToast(new Error("Couldn't create invoice. Are you asking for enough?"));
+ console.error(e);
}
+ }
- async function getUnifiedQr(amount: string) {
- const bigAmount = BigInt(amount);
- try {
- 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);
+ async function onSubmit(e: Event) {
+ e.preventDefault();
- const params = objectToSearchParams({
- amount: raw?.btc_amount,
- lightning: raw?.invoice
- })
+ const unifiedQr = await getUnifiedQr(amount());
- return `bitcoin:${raw?.address}?${params}`
+ setUnified(unifiedQr || "");
+ setReceiveState("show");
+ setShouldShowAmountEditor(false);
+ }
- } catch (e) {
- showToast(new Error("Couldn't create invoice. Are you asking for enough?"))
- console.error(e)
- }
+ async function checkIfPaid(bip21?: MutinyBip21RawMaterials): Promise {
+ if (bip21) {
+ console.log("checking if paid...");
+ const lightning = bip21.invoice;
+ const address = bip21.address;
+
+ const invoice = await state.mutiny_wallet?.get_invoice(lightning);
+
+ if (invoice && invoice.paid) {
+ setReceiveState("paid");
+ setPaymentInvoice(invoice);
+ return "lightning_paid";
+ }
+
+ const tx = (await state.mutiny_wallet?.check_address(address)) as OnChainTx | undefined;
+
+ if (tx) {
+ setReceiveState("paid");
+ setPaymentTx(tx);
+ return "onchain_paid";
+ }
}
+ }
- async function onSubmit(e: Event) {
- e.preventDefault();
+ const [paidState, { refetch }] = createResource(bip21Raw, checkIfPaid);
- const unifiedQr = await getUnifiedQr(amount())
+ const network = state.mutiny_wallet?.get_network() as Network;
- setUnified(unifiedQr || "")
- setReceiveState("show")
- setShouldShowAmountEditor(false)
- }
-
- async function checkIfPaid(bip21?: MutinyBip21RawMaterials): Promise {
- if (bip21) {
- console.log("checking if paid...")
- const lightning = bip21.invoice
- const address = bip21.address
-
- const invoice = await state.mutiny_wallet?.get_invoice(lightning)
-
- if (invoice && invoice.paid) {
- setReceiveState("paid")
- setPaymentInvoice(invoice)
- return "lightning_paid"
- }
-
- const tx = await state.mutiny_wallet?.check_address(address) as OnChainTx | undefined;
-
- if (tx) {
- setReceiveState("paid")
- setPaymentTx(tx)
- return "onchain_paid"
- }
- }
- }
-
- const [paidState, { refetch }] = createResource(bip21Raw, checkIfPaid);
-
- const network = state.mutiny_wallet?.get_network() as Network;
-
- createEffect(() => {
- const interval = setInterval(() => {
- if (receiveState() === "show") refetch();
- }, 1000); // Poll every second
- onCleanup(() => {
- clearInterval(interval);
- });
+ createEffect(() => {
+ const interval = setInterval(() => {
+ if (receiveState() === "show") refetch();
+ }, 1000); // Poll every second
+ onCleanup(() => {
+ clearInterval(interval);
});
+ });
- return (
-
-
-
- }>
- setReceiveState("edit")} title="Edit" />
-
- Checking}>Receive Bitcoin
-
-
-
-
+ return (
+
+
+
+ }>
+ setReceiveState("edit")} title="Edit" />
+
+ Checking}>
+ Receive Bitcoin
+
+
+
+
+
-
-
-
+
+
+
-
-
Create Request
-
-
-
-
-
-
-
- Show or share this code with the sender
-
-
-
- { if (!open) clearAll() }}
- onConfirm={() => { clearAll(); navigate("/"); }}
- >
-
-
-
-
-
-
-
- { if (!open) clearAll() }}
- onConfirm={() => { clearAll(); navigate("/"); }}
- >
-
-
-
-
-
-
-
-
- )
+
+
+ Continue
+
+
+
+
+
+
+
+
+ Show or share this code with the sender
+
+
+
+ {
+ if (!open) clearAll();
+ }}
+ onConfirm={() => {
+ clearAll();
+ navigate("/");
+ }}
+ >
+
+
+
+
+
+ {
+ if (!open) clearAll();
+ }}
+ onConfirm={() => {
+ clearAll();
+ navigate("/");
+ }}
+ >
+
+
+
+ View Transaction
+
+
+
+
+
+
+
+
+ );
}
\ No newline at end of file
diff --git a/src/routes/Send.tsx b/src/routes/Send.tsx
index 36c06f2..b4c8f3f 100644
--- a/src/routes/Send.tsx
+++ b/src/routes/Send.tsx
@@ -21,7 +21,6 @@ import { StyledRadioGroup } from "~/components/layout/Radio";
import { ParsedParams, toParsedParams } from "./Scanner";
import { showToast } from "~/components/Toaster";
import eify from "~/utils/eify";
-import { FullscreenModal } from "~/components/layout/FullscreenModal";
import megacheck from "~/assets/icons/megacheck.png";
import megaex from "~/assets/icons/megaex.png";
import mempoolTxUrl from "~/utils/mempoolTxUrl";
@@ -33,6 +32,8 @@ import { AmountCard } from "~/components/AmountCard";
import { MutinyTagItem } from "~/utils/tags";
import { BackButton } from "~/components/layout/BackButton";
import { Network } from "~/logic/mutinyWalletSetup";
+import { SuccessModal } from "~/components/successfail/SuccessModal";
+import { ExternalLink } from "~/components/layout/ExternalLink";
export type SendSource = "lightning" | "onchain";
@@ -121,7 +122,7 @@ function DestinationInput(props: {
class="p-2 rounded-lg bg-white/10 placeholder-neutral-400"
/>
- Decode
+ Continue
@@ -408,8 +409,8 @@ export default function Send() {
const sendButtonDisabled = createMemo(() => {
return !destination() || sending() || amountSats() === 0n;
});
-
- const network = state.mutiny_wallet?.get_network() as Network
+
+ const network = state.mutiny_wallet?.get_network() as Network;
return (
@@ -419,7 +420,7 @@ export default function Send() {
clearAll()} title="Start Over" />
Send Bitcoin
-
-
-
-
-
-
- {sentDetails()?.failure_reason}
-
-
-
-
-
-
-
- Mempool Link
-
-
-
-
-
-
+
+
+
+
+ {sentDetails()?.failure_reason}
+
+
+
+
+
+
+
+ View Transaction
+
+
+
+
+
@@ -463,7 +458,7 @@ export default function Send() {
setSource={setSource}
both={!!address() && !!invoice()}
/>
-
+
("onchain");
const [amountSats, setAmountSats] = createSignal(0n);
- const [useLsp, setUseLsp] = createSignal(true);
const [isConnecting, setIsConnecting] = createSignal(false);
const [selectedPeer, setSelectedPeer] = createSignal("");
@@ -121,7 +121,7 @@ export default function Swap() {
const nodes = await state.mutiny_wallet?.list_nodes();
const firstNode = (nodes[0] as string) || "";
- if (useLsp()) {
+ if (hasLsp()) {
const new_channel = await state.mutiny_wallet?.open_channel(
firstNode,
undefined,
@@ -147,7 +147,7 @@ export default function Swap() {
const canSwap = () => {
const balance = (state.balance?.confirmed || 0n) + (state.balance?.unconfirmed || 0n);
- return (!!selectedPeer() || !!useLsp()) && amountSats() >= 10000n && amountSats() <= balance;
+ return (!!selectedPeer() || !!hasLsp()) && amountSats() >= 10000n && amountSats() <= balance;
};
const amountWarning = () => {
@@ -165,14 +165,16 @@ export default function Swap() {
return undefined;
};
+ const network = state.mutiny_wallet?.get_network() as Network;
+
return (
Swap to Lightning
- {
@@ -183,50 +185,38 @@ export default function Swap() {
navigate("/");
}}
>
-
-
-
-
+
+
+
-
- {channelOpenResult()?.failure_reason?.message}
-
-
-
-
-
-
-
- Mempool Link
-
-
- {/* {JSON.stringify(channelOpenResult()?.channel?.value, null, 2)} */}
-
-
-
-
+
+ {channelOpenResult()?.failure_reason?.message}
+
+
+
+
+
+
+
+ View Transaction
+
+
+ {/* {JSON.stringify(channelOpenResult()?.channel?.value, null, 2)} */}
+
+
+
-
-
-
-
+