diff --git a/src/assets/icons/green-check.svg b/src/assets/icons/green-check.svg new file mode 100644 index 0000000..cc6ba87 --- /dev/null +++ b/src/assets/icons/green-check.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/assets/icons/red-close.svg b/src/assets/icons/red-close.svg new file mode 100644 index 0000000..33c2e80 --- /dev/null +++ b/src/assets/icons/red-close.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/App.tsx b/src/components/App.tsx index 42de8d5..f649034 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -11,6 +11,7 @@ import { ExternalLink } from "./layout/ExternalLink"; import { BetaWarningModal } from "~/components/BetaWarningModal"; import settings from "~/assets/icons/settings.svg"; import pixelLogo from "~/assets/mutiny-pixel-logo.png"; +import { PendingNwc } from "./PendingNwc"; export default function App() { const [state, _actions] = useMegaStore(); @@ -49,6 +50,9 @@ export default function App() { + + +
@@ -58,12 +62,11 @@ export default function App() { > - {/* View All */} 0}> View All diff --git a/src/components/PendingNwc.tsx b/src/components/PendingNwc.tsx new file mode 100644 index 0000000..4b97211 --- /dev/null +++ b/src/components/PendingNwc.tsx @@ -0,0 +1,187 @@ +import { NwcProfile } from "@mutinywallet/mutiny-wasm"; +import { timeAgo } from "~/utils/prettyPrintTime"; +import { Card, LoadingSpinner, VStack } from "./layout"; +import bolt from "~/assets/icons/bolt.svg"; +import { + For, + Match, + Show, + Suspense, + Switch, + createEffect, + createResource, + createSignal +} from "solid-js"; +import { useMegaStore } from "~/state/megaStore"; + +import greenCheck from "~/assets/icons/green-check.svg"; +import redClose from "~/assets/icons/red-close.svg"; +import { ActivityAmount } from "./ActivityItem"; +import { InfoBox } from "./InfoBox"; +import eify from "~/utils/eify"; +import { A } from "solid-start"; + +type PendingItem = { + id: string; + name_of_connection: string; + date?: bigint; + amount_sats?: bigint; +}; + +export function PendingNwc() { + const [state, _actions] = useMegaStore(); + + const [error, setError] = createSignal(); + + const [pendingRequests, { refetch }] = createResource(async () => { + const profiles: NwcProfile[] = + await state.mutiny_wallet?.get_nwc_profiles(); + + const pending = await state.mutiny_wallet?.get_pending_nwc_invoices(); + + const pendingItems: PendingItem[] = []; + + for (const p of pending) { + const profile = profiles.find((pro) => pro.index === p.index); + + if (profile) { + pendingItems.push({ + id: p.id, + name_of_connection: profile.name, + date: p.expiry, + amount_sats: p.amount_sats + }); + } + } + + console.log(pendingItems); + + return pendingItems; + }); + + const [paying, setPaying] = createSignal(""); + + async function payItem(item: PendingItem) { + try { + setPaying(item.id); + const nodes = await state.mutiny_wallet?.list_nodes(); + await state.mutiny_wallet?.approve_invoice(item.id, nodes[0]); + } catch (e) { + setError(eify(e)); + console.error(e); + } finally { + setPaying(""); + refetch(); + } + } + + async function rejectItem(item: PendingItem) { + try { + setPaying(item.id); + await state.mutiny_wallet?.deny_invoice(item.id); + } catch (e) { + setError(eify(e)); + console.error(e); + } finally { + setPaying(""); + refetch(); + } + } + + createEffect(() => { + // When there's an error wait five seconds and then clear it + if (error()) { + setTimeout(() => { + setError(undefined); + }, 5000); + } + }); + + return ( + + 0}> + +
+ + + {error()?.message} + + + {(pendingItem) => ( +
+ onchain +
+ + {pendingItem.name_of_connection} + + +
+
+ +
+
+ + + + + + + + + +
+
+ )} +
+
+ + Configure + + + + + ); +} diff --git a/src/utils/prettyPrintTime.ts b/src/utils/prettyPrintTime.ts index 3fdf71b..ff5c427 100644 --- a/src/utils/prettyPrintTime.ts +++ b/src/utils/prettyPrintTime.ts @@ -14,20 +14,24 @@ export function timeAgo(ts?: number | bigint): string { if (!ts || ts === 0) return "Pending"; const timestamp = Number(ts) * 1000; const now = Date.now(); - const elapsedMilliseconds = now - timestamp; + const negative = now - timestamp < 0; + const nowOrAgo = negative ? "from now" : "ago"; + const elapsedMilliseconds = Math.abs(now - timestamp); const elapsedSeconds = Math.floor(elapsedMilliseconds / 1000); const elapsedMinutes = Math.floor(elapsedSeconds / 60); const elapsedHours = Math.floor(elapsedMinutes / 60); const elapsedDays = Math.floor(elapsedHours / 24); if (elapsedSeconds < 60) { - return "Just now"; + return negative ? "seconds from now" : "Just now"; } else if (elapsedMinutes < 60) { - return `${elapsedMinutes} minute${elapsedMinutes > 1 ? "s" : ""} ago`; + return `${elapsedMinutes} minute${ + elapsedMinutes > 1 ? "s" : "" + } ${nowOrAgo}`; } else if (elapsedHours < 24) { - return `${elapsedHours} hour${elapsedHours > 1 ? "s" : ""} ago`; + return `${elapsedHours} hour${elapsedHours > 1 ? "s" : ""} ${nowOrAgo}`; } else if (elapsedDays < 7) { - return `${elapsedDays} day${elapsedDays > 1 ? "s" : ""} ago`; + return `${elapsedDays} day${elapsedDays > 1 ? "s" : ""} ${nowOrAgo}`; } else { const date = new Date(timestamp); const day = String(date.getDate()).padStart(2, "0");