diff --git a/src/assets/icons/bolt.svg b/src/assets/icons/bolt.svg new file mode 100644 index 0000000..92080ef --- /dev/null +++ b/src/assets/icons/bolt.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/assets/icons/chain.svg b/src/assets/icons/chain.svg new file mode 100644 index 0000000..7946243 --- /dev/null +++ b/src/assets/icons/chain.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/components/Activity.tsx b/src/components/Activity.tsx index 50d6702..438c08c 100644 --- a/src/components/Activity.tsx +++ b/src/components/Activity.tsx @@ -10,6 +10,7 @@ import mempoolTxUrl from '~/utils/mempoolTxUrl'; import wave from "~/assets/wave.gif" import utxoIcon from '~/assets/icons/coin.svg'; import { getRedshifted } from '~/utils/fakeLabels'; +import { ActivityItem } from './ActivityItem'; export const THREE_COLUMNS = 'grid grid-cols-[auto,1fr,auto] gap-4 py-2 px-2 border-b border-neutral-800 last:border-b-0' export const CENTER_COLUMN = 'min-w-0 overflow-hidden max-w-full' @@ -57,7 +58,15 @@ function OnChainItem(props: { item: OnChainTx }) { Mempool Link -
setOpen(!open())}> + setOpen(!open())} + /> + {/*
setOpen(!open())}>
{isReceive() ? receive arrow : send arrow}
@@ -71,7 +80,7 @@ function OnChainItem(props: { item: OnChainTx }) { {props.item.confirmation_time?.Confirmed ? prettyPrintTime(props.item.confirmation_time?.Confirmed?.time) : "Unconfirmed"}
-
+ */} ) } @@ -84,7 +93,8 @@ function InvoiceItem(props: { item: MutinyInvoice }) { return ( <> -
setOpen(!open())}> + setOpen(!open())} /> + {/*
setOpen(!open())}>
{isSend() ? send arrow : receive arrow}
@@ -98,7 +108,7 @@ function InvoiceItem(props: { item: MutinyInvoice }) { {prettyPrintTime(Number(props.item.expire))}
-
+ */} ) } @@ -243,7 +253,9 @@ export function CombinedActivity(props: { limit?: number }) { }) invoices.forEach((invoice) => { - activity.push({ type: "lightning", item: invoice, time: Number(invoice.expire) }) + if (invoice.paid) { + activity.push({ type: "lightning", item: invoice, time: Number(invoice.expire) }) + } }) if (props.limit) { diff --git a/src/components/ActivityItem.tsx b/src/components/ActivityItem.tsx new file mode 100644 index 0000000..915d034 --- /dev/null +++ b/src/components/ActivityItem.tsx @@ -0,0 +1,72 @@ +import { ParentComponent, createMemo } from "solid-js"; +import { InlineAmount } from "./AmountCard"; +import { satsToUsd } from "~/utils/conversions"; +import bolt from "~/assets/icons/bolt.svg" +import chain from "~/assets/icons/chain.svg" +import { timeAgo } from "~/utils/prettyPrintTime"; + +export const ActivityAmount: ParentComponent<{ amount: string, price: number, positive?: boolean }> = (props) => { + const amountInUsd = createMemo(() => { + const parsed = Number(props.amount); + if (isNaN(parsed)) { + return props.amount; + } else { + return satsToUsd(props.price, parsed, true); + } + }) + + const prettyPrint = createMemo(() => { + const parsed = Number(props.amount); + if (isNaN(parsed)) { + return props.amount; + } else { + return parsed.toLocaleString(); + } + }) + + return ( +
+
{props.positive && "+ "}{prettyPrint()} SATS +
+
≈ {amountInUsd()} USD
+
+ ) +} + +function LabelCircle(props: { name: string }) { + return ( +
+ {props.name[0] || "?"} +
+ ) +} + +export function ActivityItem(props: { kind: "lightning" | "onchain", labels: string[], amount: number | bigint, date?: number | bigint, positive?: boolean, onClick?: () => void }) { + return ( +
props.onClick && props.onClick()} + class="grid grid-cols-[auto_minmax(0,_1fr)_minmax(0,_max-content)] pb-4 gap-4 border-b border-neutral-800 last:border-b-0" + classList={{ "cursor-pointer": !!props.onClick }} + > +
+
+ {props.kind === "lightning" ? lightning : onchain} +
+
+ +
+
+
+ {props.labels.length ? props.labels[0] : "Unknown"} + +
+
+ +
+
+ ) +} \ No newline at end of file diff --git a/src/components/App.tsx b/src/components/App.tsx index 927960c..b96a600 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -21,11 +21,12 @@ export default function App() { +
{/* View All */} - View All + View All diff --git a/src/routes/Activity.tsx b/src/routes/Activity.tsx index 6234989..76ce361 100644 --- a/src/routes/Activity.tsx +++ b/src/routes/Activity.tsx @@ -58,7 +58,10 @@ export default function Activity() { {/* */} - +
+ + + diff --git a/src/routes/Storybook.tsx b/src/routes/Storybook.tsx index 1ac4995..9a6ce43 100644 --- a/src/routes/Storybook.tsx +++ b/src/routes/Storybook.tsx @@ -1,7 +1,8 @@ +import { ActivityItem } from "~/components/ActivityItem"; import { AmountCard } from "~/components/AmountCard"; import NavBar from "~/components/NavBar"; import { ShareCard } from "~/components/ShareCard"; -import { DefaultMain, LargeHeader, SafeArea, VStack } from "~/components/layout"; +import { Card, DefaultMain, LargeHeader, SafeArea, VStack } from "~/components/layout"; const SAMPLE = "bitcoin:tb1prqm8xtlgme0vmw5s30lgf0a4f5g4mkgsqundwmpu6thrg8zr6uvq2qrhzq?amount=0.001&lightning=lntbs1m1pj9n9xjsp5xgdrmvprtm67p7nq4neparalexlhlmtxx87zx6xeqthsplu842zspp546d6zd2seyaxpapaxx62m88yz3xueqtjmn9v6wj8y56np8weqsxqdqqnp4qdn2hj8tfknpuvdg6tz9yrf3e27ltrx9y58c24jh89lnm43yjwfc5xqrpwjcqpj9qrsgq5sdgh0m3ur5mu5hrmmag4mx9yvy86f83pd0x9ww80kgck6tac3thuzkj0mrtltaxwnlfea95h2re7tj4qsnwzxlvrdmyq2h9mgapnycpppz6k6" export default function Admin() { @@ -11,10 +12,13 @@ export default function Admin() { Storybook - - - + + + + + + diff --git a/src/utils/prettyPrintTime.ts b/src/utils/prettyPrintTime.ts index 254c2db..de82a2a 100644 --- a/src/utils/prettyPrintTime.ts +++ b/src/utils/prettyPrintTime.ts @@ -9,4 +9,31 @@ export function prettyPrintTime(ts: number) { }; return new Date(ts * 1000).toLocaleString('en-US', options); -} \ No newline at end of file +} + +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 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"; + } else if (elapsedMinutes < 60) { + return `${elapsedMinutes} minute${elapsedMinutes > 1 ? 's' : ''} ago`; + } else if (elapsedHours < 24) { + return `${elapsedHours} hour${elapsedHours > 1 ? 's' : ''} ago`; + } else if (elapsedDays < 7) { + return `${elapsedDays} day${elapsedDays > 1 ? 's' : ''} ago`; + } else { + const date = new Date(timestamp); + const day = String(date.getDate()).padStart(2, '0'); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const year = date.getFullYear(); + return `${month}/${day}/${year}`; + } +}