import { Dialog } from "@kobalte/core";
import {
MutinyChannel,
MutinyInvoice,
TagItem
} from "@mutinywallet/mutiny-wasm";
import {
createEffect,
createMemo,
createResource,
Match,
Show,
Suspense,
Switch
} from "solid-js";
import bolt from "~/assets/icons/bolt.svg";
import chain from "~/assets/icons/chain.svg";
import copyIcon from "~/assets/icons/copy.svg";
import shuffle from "~/assets/icons/shuffle.svg";
import {
ActivityAmount,
AmountFiat,
AmountSats,
FancyCard,
HackActivityType,
Hr,
InfoBox,
KeyValue,
ModalCloseButton,
TinyButton,
TruncateMiddle,
VStack
} from "~/components";
import { useI18n } from "~/i18n/context";
import { Network } from "~/logic/mutinyWalletSetup";
import { BalanceBar } from "~/routes/settings/Channels";
import { useMegaStore } from "~/state/megaStore";
import { mempoolTxUrl, prettyPrintTime, useCopy } from "~/utils";
interface ChannelClosure {
channel_id: string;
node_id: string;
reason: string;
timestamp: number;
}
interface OnChainTx {
txid: string;
received: number;
sent: number;
fee?: number;
confirmation_time?: {
Confirmed?: {
height: number;
time: number;
};
};
labels: string[];
}
export const OVERLAY = "fixed inset-0 z-50 bg-black/50 backdrop-blur-sm";
export const DIALOG_POSITIONER =
"fixed inset-0 z-50 flex items-center justify-center";
export const DIALOG_CONTENT =
"max-w-[500px] w-[90vw] max-h-device overflow-y-scroll disable-scrollbars bg-neutral-900/80 backdrop-blur-md shadow-xl rounded-xl border border-white/10";
function LightningHeader(props: { info: MutinyInvoice }) {
const i18n = useI18n();
return (
{props.info.inbound
? i18n.t("activity.transaction_details.lightning_receive")
: i18n.t("activity.transaction_details.lightning_send")}
);
}
function OnchainHeader(props: { info: OnChainTx; kind?: HackActivityType }) {
const i18n = useI18n();
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 (
{props.kind === "ChannelOpen"
? i18n.t("activity.transaction_details.channel_open")
: props.kind === "ChannelClose"
? i18n.t("activity.transaction_details.channel_close")
: isSend()
? i18n.t("activity.transaction_details.onchain_send")
: i18n.t("activity.transaction_details.onchain_receive")}
);
}
export function MiniStringShower(props: { text: string }) {
const [copy, copied] = useCopy({ copiedTimeout: 1000 });
return (
);
}
export function FormatPrettyPrint(props: { ts: number }) {
return (
{prettyPrintTime(props.ts).split(",", 2).join(",")}
{prettyPrintTime(props.ts).split(", ")[2]}
);
}
function LightningDetails(props: { info: MutinyInvoice; tags?: TagItem }) {
const i18n = useI18n();
const [state, _actions] = useMegaStore();
return (
{
// noop
}}
>
{props.tags?.name || props.info.labels[0]}
{props.info.paid
? i18n.t("activity.transaction_details.paid")
: i18n.t("activity.transaction_details.unpaid")}
{props.info.description}
);
}
function OnchainDetails(props: {
info: OnChainTx;
kind?: HackActivityType;
tags?: TagItem;
}) {
const i18n = useI18n();
const [state, _actions] = useMegaStore();
const [copy, copied] = useCopy({ copiedTimeout: 1000 });
const confirmationTime = () => {
return props.info.confirmation_time?.Confirmed?.time;
};
const network = state.mutiny_wallet?.get_network() as Network;
// Can return nothing if the channel is already closed
const [channelInfo] = createResource(async () => {
if (props.kind === "ChannelOpen") {
try {
const channels =
await (state.mutiny_wallet?.list_channels() as Promise<
MutinyChannel[]
>);
const channel = channels.find(
(channel) => channel.outpoint?.startsWith(props.info.txid)
);
return channel;
} catch (e) {
console.error(e);
}
} else {
return undefined;
}
});
return (
{/* {JSON.stringify(channelInfo() || "", null, 2)} */}
{i18n.t("activity.transaction_details.no_details")}
0
}
>
{
// noop
}}
>
{props.tags?.name || props.info.labels[0]}
{confirmationTime()
? i18n.t("activity.transaction_details.confirmed")
: i18n.t("activity.transaction_details.unconfirmed")}
{confirmationTime() ? (
) : (
"Pending"
)}
{/* Have to do all these shenanigans because css / html is hard */}
);
}
function ChannelCloseDetails(props: { info: ChannelClosure }) {
const i18n = useI18n();
return (
{/* {JSON.stringify(props.info.value, null, 2)} */}
);
}
export function ActivityDetailsModal(props: {
open: boolean;
kind?: HackActivityType;
id: string;
setOpen: (open: boolean) => void;
}) {
const [state, _actions] = useMegaStore();
const id = () => props.id;
const kind = () => props.kind;
const [data, { refetch }] = createResource(async () => {
try {
if (kind() === "Lightning") {
console.debug("reading invoice: ", id());
const invoice = await state.mutiny_wallet?.get_invoice_by_hash(
id()
);
return invoice;
} else if (kind() === "ChannelClose") {
console.debug("reading channel close: ", id());
const closeItem =
await state.mutiny_wallet?.get_channel_closure(id());
return closeItem;
} else {
console.debug("reading tx: ", id());
const tx = await state.mutiny_wallet?.get_transaction(id());
return tx;
}
} catch (e) {
console.error(e);
return undefined;
}
});
const tags = createMemo(() => {
if (
!!data() &&
data()?.labels !== undefined &&
typeof data()?.labels[0] === "string"
) {
try {
// find if there's just one for now
const contacts = state.mutiny_wallet?.get_contact(
data().labels[0]
);
if (contacts) {
return contacts;
} else {
return;
}
} catch (e) {
console.error(e);
}
} else {
return;
}
});
createEffect(() => {
if (props.id && props.kind && props.open) {
refetch();
}
});
return (
);
}