mirror of
https://github.com/aljazceru/mutiny-web.git
synced 2025-12-27 02:44:26 +01:00
203 lines
5.2 KiB
TypeScript
203 lines
5.2 KiB
TypeScript
import { LoadingSpinner, NiceP } from "./layout"
|
|
import {
|
|
For,
|
|
Match,
|
|
Switch,
|
|
createEffect,
|
|
createMemo,
|
|
createResource,
|
|
createSignal,
|
|
} from "solid-js"
|
|
import { useMegaStore } from "~/state/megaStore"
|
|
import { MutinyInvoice } from "@mutinywallet/mutiny-wasm"
|
|
import { ActivityItem } from "./ActivityItem"
|
|
import { MutinyTagItem } from "~/utils/tags"
|
|
import { Network } from "~/logic/mutinyWalletSetup"
|
|
import { DetailsModal } from "./DetailsModal"
|
|
|
|
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"
|
|
export const MISSING_LABEL =
|
|
"py-1 px-2 bg-white/10 rounded inline-block text-sm"
|
|
export const REDSHIFT_LABEL =
|
|
"py-1 px-2 bg-white text-m-red rounded inline-block text-sm"
|
|
export const RIGHT_COLUMN = "flex flex-col items-right text-right max-w-[8rem]"
|
|
|
|
export type OnChainTx = {
|
|
txid: string
|
|
received: number
|
|
sent: number
|
|
fee?: number
|
|
confirmation_time?: {
|
|
Confirmed?: {
|
|
height: number
|
|
time: number
|
|
}
|
|
}
|
|
labels: string[]
|
|
}
|
|
|
|
export type UtxoItem = {
|
|
outpoint: string
|
|
txout: {
|
|
value: number
|
|
script_pubkey: string
|
|
}
|
|
keychain: string
|
|
is_spent: boolean
|
|
redshifted?: boolean
|
|
}
|
|
|
|
function OnChainItem(props: {
|
|
item: OnChainTx
|
|
labels: MutinyTagItem[]
|
|
network: Network
|
|
}) {
|
|
const isReceive = () => props.item.received > props.item.sent
|
|
|
|
const [open, setOpen] = createSignal(false)
|
|
|
|
return (
|
|
<>
|
|
<DetailsModal open={open()} data={props.item} setOpen={setOpen} />
|
|
<ActivityItem
|
|
kind={"onchain"}
|
|
labels={props.labels}
|
|
// FIXME: is this something we can put into node logic?
|
|
amount={
|
|
isReceive()
|
|
? props.item.received - props.item.sent
|
|
: props.item.sent - props.item.received
|
|
}
|
|
date={props.item.confirmation_time?.Confirmed?.time}
|
|
positive={isReceive()}
|
|
onClick={() => setOpen(o => !o)}
|
|
/>
|
|
</>
|
|
)
|
|
}
|
|
|
|
function InvoiceItem(props: { item: MutinyInvoice; labels: MutinyTagItem[] }) {
|
|
const isSend = createMemo(() => props.item.is_send)
|
|
|
|
const [open, setOpen] = createSignal(false)
|
|
|
|
return (
|
|
<>
|
|
<DetailsModal open={open()} data={props.item} setOpen={setOpen} />
|
|
<ActivityItem
|
|
kind={"lightning"}
|
|
labels={props.labels}
|
|
amount={props.item.amount_sats || 0n}
|
|
date={props.item.last_updated}
|
|
positive={!isSend()}
|
|
onClick={() => setOpen(o => !o)}
|
|
/>
|
|
</>
|
|
)
|
|
}
|
|
|
|
type ActivityItem = {
|
|
type: "onchain" | "lightning"
|
|
item: OnChainTx | MutinyInvoice
|
|
time: number
|
|
labels: MutinyTagItem[]
|
|
}
|
|
|
|
function sortByTime(a: ActivityItem, b: ActivityItem) {
|
|
return b.time - a.time
|
|
}
|
|
|
|
export function CombinedActivity(props: { limit?: number }) {
|
|
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()
|
|
|
|
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
|
|
}
|
|
|
|
const [activity, { refetch }] = createResource(getAllActivity)
|
|
|
|
const network = state.mutiny_wallet?.get_network() as Network
|
|
|
|
createEffect(() => {
|
|
// After every sync we should refetch the activity
|
|
if (!state.is_syncing) {
|
|
refetch()
|
|
}
|
|
})
|
|
|
|
return (
|
|
<Switch>
|
|
<Match when={activity.loading}>
|
|
<LoadingSpinner wide />
|
|
</Match>
|
|
<Match when={activity.state === "ready" && activity().length === 0}>
|
|
<NiceP>No activity to show</NiceP>
|
|
</Match>
|
|
<Match when={activity.state === "ready" && activity().length >= 0}>
|
|
<For each={activity.latest}>
|
|
{(activityItem) => (
|
|
<Switch>
|
|
<Match when={activityItem.type === "onchain"}>
|
|
<OnChainItem
|
|
item={activityItem.item as OnChainTx}
|
|
labels={activityItem.labels}
|
|
network={network}
|
|
/>
|
|
</Match>
|
|
<Match when={activityItem.type === "lightning"}>
|
|
<InvoiceItem
|
|
item={activityItem.item as MutinyInvoice}
|
|
labels={activityItem.labels}
|
|
/>
|
|
</Match>
|
|
</Switch>
|
|
)}
|
|
</For>
|
|
</Match>
|
|
</Switch>
|
|
)
|
|
}
|