setOpen(!open())}
+ />
+ >
+ )
}
function Utxo(props: { item: UtxoItem }) {
- const spent = createMemo(() => props.item.is_spent);
+ const spent = createMemo(() => props.item.is_spent)
- const [open, setOpen] = createSignal(false)
+ const [open, setOpen] = createSignal(false)
- const redshifted = createMemo(() => getRedshifted(props.item.outpoint));
+ const redshifted = createMemo(() => getRedshifted(props.item.outpoint))
- return (
- <>
-
- setOpen(!open())}>
-
-

-
-
-
- Unknown}>
- Redshift
-
-
-
-
-
-
- {/* {spent() ? "SPENT" : "UNSPENT"} */}
-
-
-
- >
- )
+ return (
+ <>
+
+ setOpen(!open())}>
+
+

+
+
+
+ Unknown}
+ >
+ Redshift
+
+
+
+
+
+
+ {/* {spent() ? "SPENT" : "UNSPENT"} */}
+
+
+
+ >
+ )
}
-type ActivityItem = { type: "onchain" | "lightning", item: OnChainTx | MutinyInvoice, time: number, labels: MutinyTagItem[] }
+type ActivityItem = {
+ type: "onchain" | "lightning"
+ item: OnChainTx | MutinyInvoice
+ time: number
+ labels: MutinyTagItem[]
+}
function sortByTime(a: ActivityItem, b: ActivityItem) {
- return b.time - a.time;
+ return b.time - a.time
}
export function CombinedActivity(props: { limit?: number }) {
- const [state, actions] = useMegaStore();
+ const [state, actions] = useMegaStore()
- const getAllActivity = async () => {
- console.log("Getting all activity");
- const txs = await state.mutiny_wallet?.list_onchain() as OnChainTx[];
- const invoices = await state.mutiny_wallet?.list_invoices() as MutinyInvoice[];
- const tags = await actions.listTags();
+ const getAllActivity = async () => {
+ console.log("Getting all activity")
+ const txs = (await state.mutiny_wallet?.list_onchain()) as OnChainTx[]
+ const invoices =
+ (await state.mutiny_wallet?.list_invoices()) as MutinyInvoice[]
+ const tags = await actions.listTags()
- let activity: ActivityItem[] = [];
+ let activity: ActivityItem[] = []
- for (let i = 0; i < txs.length; i++) {
- activity.push({ type: "onchain", item: txs[i], time: txs[i].confirmation_time?.Confirmed?.time || Date.now(), labels: [] })
- }
-
- for (let i = 0; i < invoices.length; i++) {
- if (invoices[i].paid) {
- activity.push({ type: "lightning", item: invoices[i], time: Number(invoices[i].last_updated), labels: [] })
- }
- }
-
- if (props.limit) {
- activity = activity.sort(sortByTime).slice(0, props.limit);
- } else {
- activity.sort(sortByTime);
- }
-
- for (let i = 0; i < activity.length; i++) {
- // filter the tags to only include the ones that have an id matching one of the labels
- activity[i].labels = tags.filter((tag) => activity[i].item.labels.includes(tag.id));
- }
-
- return activity;
+ for (let i = 0; i < txs.length; i++) {
+ activity.push({
+ type: "onchain",
+ item: txs[i],
+ time: txs[i].confirmation_time?.Confirmed?.time || Date.now(),
+ labels: [],
+ })
}
- const [activity, { refetch }] = createResource(getAllActivity);
+ for (let i = 0; i < invoices.length; i++) {
+ if (invoices[i].paid) {
+ activity.push({
+ type: "lightning",
+ item: invoices[i],
+ time: Number(invoices[i].last_updated),
+ labels: [],
+ })
+ }
+ }
- const network = state.mutiny_wallet?.get_network() as Network;
+ if (props.limit) {
+ activity = activity.sort(sortByTime).slice(0, props.limit)
+ } else {
+ activity.sort(sortByTime)
+ }
- createEffect(() => {
- // After every sync we should refetch the activity
- if (!state.is_syncing) {
- refetch();
- }
- })
+ for (let i = 0; i < activity.length; i++) {
+ // filter the tags to only include the ones that have an id matching one of the labels
+ activity[i].labels = tags.filter((tag) =>
+ activity[i].item.labels.includes(tag.id)
+ )
+ }
- return (
-
-
-
-
-
- No activity to show
-
- = 0}>
-
- {(activityItem) =>
-
-
-
-
-
-
-
-
- }
-
-
-
+ return activity
+ }
- )
+ const [activity, { refetch }] = createResource(getAllActivity)
+ const network = state.mutiny_wallet?.get_network() as Network
-}
\ No newline at end of file
+ createEffect(() => {
+ // After every sync we should refetch the activity
+ if (!state.is_syncing) {
+ refetch()
+ }
+ })
+
+ return (
+
+
+
+
+
+ No activity to show
+
+ = 0}>
+
+ {(activityItem) => (
+
+
+
+
+
+
+
+
+ )}
+
+
+
+ )
+}
diff --git a/src/components/Amount.tsx b/src/components/Amount.tsx
index 5d21e16..289d4c6 100644
--- a/src/components/Amount.tsx
+++ b/src/components/Amount.tsx
@@ -3,27 +3,45 @@ import { useMegaStore } from "~/state/megaStore"
import { satsToUsd } from "~/utils/conversions"
function prettyPrintAmount(n?: number | bigint): string {
- if (!n || n.valueOf() === 0) {
- return "0"
- }
- return n.toLocaleString()
+ if (!n || n.valueOf() === 0) {
+ return "0"
+ }
+ return n.toLocaleString()
}
-export function Amount(props: { amountSats: bigint | number | undefined, showFiat?: boolean, loading?: boolean }) {
- const [state, _] = useMegaStore()
+export function Amount(props: {
+ amountSats: bigint | number | undefined
+ showFiat?: boolean
+ loading?: boolean
+}) {
+ 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
-
-
-
- ≈ {props.loading ? "..." : amountInUsd()} USD
-
-
-
- )
-}
\ No newline at end of file
+ return (
+
+
+ {props.loading ? "..." : prettyPrintAmount(props.amountSats)}
+ SATS
+
+
+
+ ≈ {props.loading ? "..." : amountInUsd()}
+ USD
+
+
+
+ )
+}
+
+export function AmountSmall(props: {
+ amountSats: bigint | number | undefined
+}) {
+ return (
+
+ {prettyPrintAmount(props.amountSats)}
+ SATS
+
+ )
+}
diff --git a/src/components/DetailsModal.tsx b/src/components/DetailsModal.tsx
index 4f08c09..0737893 100644
--- a/src/components/DetailsModal.tsx
+++ b/src/components/DetailsModal.tsx
@@ -1,145 +1,299 @@
-import { Dialog } from "@kobalte/core";
-import { For, JSX, ParentComponent, Show, createMemo } from "solid-js";
-import { Hr, TinyButton, VStack } from "~/components/layout";
-import { MutinyInvoice } from "@mutinywallet/mutiny-wasm";
-import { OnChainTx } from "./Activity";
+import { Dialog } from "@kobalte/core"
+import {
+ For,
+ JSX,
+ Match,
+ ParentComponent,
+ Show,
+ Switch,
+ createMemo,
+} from "solid-js"
+import { Hr, TinyButton, VStack } from "~/components/layout"
+import { MutinyInvoice } from "@mutinywallet/mutiny-wasm"
+import { OnChainTx } from "./Activity"
-import close from "~/assets/icons/close.svg";
-import bolt from "~/assets/icons/bolt-black.svg";
+import close from "~/assets/icons/close.svg"
+import bolt from "~/assets/icons/bolt-black.svg"
+import chain from "~/assets/icons/chain-black.svg"
import copyIcon from "~/assets/icons/copy.svg"
-import { ActivityAmount } from "./ActivityItem";
-import { CopyButton } from "./ShareCard";
-import { prettyPrintTime } from "~/utils/prettyPrintTime";
-import { useMegaStore } from "~/state/megaStore";
-import { tagToMutinyTag } from "~/utils/tags";
-import { useCopy } from "~/utils/useCopy";
+import { ActivityAmount } from "./ActivityItem"
+import { CopyButton } from "./ShareCard"
+import { prettyPrintTime } from "~/utils/prettyPrintTime"
+import { useMegaStore } from "~/state/megaStore"
+import { tagToMutinyTag } from "~/utils/tags"
+import { useCopy } from "~/utils/useCopy"
+import mempoolTxUrl from "~/utils/mempoolTxUrl"
+import { Network } from "~/logic/mutinyWalletSetup"
+import { AmountSmall } from "./Amount"
const OVERLAY = "fixed inset-0 z-50 bg-black/50 backdrop-blur-sm"
const DIALOG_POSITIONER = "fixed inset-0 z-50 flex items-center justify-center"
-const DIALOG_CONTENT = "max-w-[500px] w-[90vw] max-h-[100dvh] overflow-y-scroll disable-scrollbars mx-4 p-4 bg-neutral-800/80 backdrop-blur-md shadow-xl rounded-xl border border-white/10"
+const DIALOG_CONTENT =
+ "max-w-[500px] w-[90vw] max-h-[100dvh] overflow-y-scroll disable-scrollbars mx-4 p-4 bg-neutral-800/80 backdrop-blur-md shadow-xl rounded-xl border border-white/10"
function LightningHeader(props: { info: MutinyInvoice }) {
- const [state, _actions] = useMegaStore();
+ const [state, _actions] = useMegaStore()
- const tags = createMemo(() => {
- if (props.info.labels.length) {
- let contact = state.mutiny_wallet?.get_contact(props.info.labels[0]);
- if (contact) {
- return [tagToMutinyTag(contact)]
- } else {
- return []
- }
- } else {
- return []
- }
- })
+ const tags = createMemo(() => {
+ if (props.info.labels.length) {
+ let contact = state.mutiny_wallet?.get_contact(props.info.labels[0])
+ if (contact) {
+ return [tagToMutinyTag(contact)]
+ } else {
+ return []
+ }
+ } else {
+ return []
+ }
+ })
- return (
-
-
-

-
-
{props.info.is_send ? "Lightning send" : "Lightning receive"}
-
-
- {(tag) => (
- { }}>
- {tag.name}
-
- )}
-
-
- )
+ return (
+
+
+

+
+
+ {props.info.is_send ? "Lightning send" : "Lightning receive"}
+
+
+
+ {(tag) => (
+ {}}>
+ {tag.name}
+
+ )}
+
+
+ )
+}
+
+function OnchainHeader(props: { info: OnChainTx }) {
+ const [state, _actions] = useMegaStore()
+
+ const tags = createMemo(() => {
+ if (props.info.labels.length) {
+ let contact = state.mutiny_wallet?.get_contact(props.info.labels[0])
+ if (contact) {
+ return [tagToMutinyTag(contact)]
+ } else {
+ return []
+ }
+ } else {
+ return []
+ }
+ })
+
+ const isSend = () => {
+ return props.info.sent > props.info.received
+ }
+
+ const amount = () => {
+ if (isSend()) {
+ return (props.info.sent - props.info.received).toString()
+ } else {
+ return (props.info.received - props.info.sent).toString()
+ }
+ }
+
+ return (
+
+
+

+
+
+ {isSend() ? "On-chain send" : "On-chain receive"}
+
+
+
+ {(tag) => (
+ {}}>
+ {tag.name}
+
+ )}
+
+
+ )
}
const KeyValue: ParentComponent<{ key: string }> = (props) => {
- return (
-
- {props.key}
- {props.children}
-
-
- )
-
+ return (
+
+ {props.key}
+ {props.children}
+
+ )
}
function MiniStringShower(props: { text: string }) {
- const [copy, _copied] = useCopy({ copiedTimeout: 1000 });
+ const [copy, _copied] = useCopy({ copiedTimeout: 1000 })
- return (
-
-
{props.text}
-
-
- )
+ return (
+
+
{props.text}
+
+
+ )
}
function LightningDetails(props: { info: MutinyInvoice }) {
- return (
-
-
-
- {props.info.paid ? "Paid" : "Unpaid"}
-
-
- {prettyPrintTime(Number(props.info.last_updated))}
-
-
-
- {props.info.description}
-
-
-
- {props.info.fees_paid?.toLocaleString() ?? 0}
-
-
-
-
-
-
-
-
-
-
-
-
-
- )
+ return (
+
+
+
+
+ {props.info.paid ? "Paid" : "Unpaid"}
+
+
+
+
+ {prettyPrintTime(Number(props.info.last_updated))}
+
+
+
+
+
+ {props.info.description}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
}
-export function DetailsModal(props: { title: string, open: boolean, data?: MutinyInvoice | OnChainTx, setOpen: (open: boolean) => void, children?: JSX.Element }) {
- const json = createMemo(() => JSON.stringify(props.data, null, 2));
+function OnchainDetails(props: { info: OnChainTx }) {
+ const [state, _actions] = useMegaStore()
- return (
- props.setOpen(isOpen)}>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- )
+ const confirmationTime = () => {
+ return props.info.confirmation_time?.Confirmed?.time
+ }
+
+ const network = state.mutiny_wallet?.get_network() as Network
+
+ return (
+
+
+
+
+ {confirmationTime() ? "Confirmed" : "Unconfirmed"}
+
+
+
+
+
+ {confirmationTime()
+ ? prettyPrintTime(Number(confirmationTime()))
+ : "Pending"}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Mempool.space
+
+
+ )
+}
+
+export function DetailsModal(props: {
+ open: boolean
+ data: MutinyInvoice | OnChainTx
+ setOpen: (open: boolean) => void
+ children?: JSX.Element
+}) {
+ const json = createMemo(() => JSON.stringify(props.data, null, 2))
+
+ const isInvoice = () => {
+ return ("bolt11" in props.data) as boolean
+ }
+
+ return (
+ props.setOpen(isOpen)}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
}