export * from ~/components

This commit is contained in:
gawlk
2023-08-18 13:18:08 +02:00
committed by Paul Miller
parent 4d5c75fc84
commit 8fa30119e1
66 changed files with 320 additions and 1593 deletions

View File

@@ -1,183 +0,0 @@
import { NiceP } from "./layout";
import {
For,
Match,
Show,
Switch,
createEffect,
createResource,
createSignal
} from "solid-js";
import { useMegaStore } from "~/state/megaStore";
import { useI18n } from "~/i18n/context";
import { Contact } from "@mutinywallet/mutiny-wasm";
import { ActivityItem, HackActivityType } from "./ActivityItem";
import { DetailsIdModal } from "./DetailsModal";
import { A } from "solid-start";
import { LoadingShimmer } from "./BalanceBox";
import { createDeepSignal } from "~/utils/deepSignal";
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;
};
export type ActivityItem = {
kind: HackActivityType;
id: string;
amount_sats: number;
inbound: boolean;
labels: string[];
contacts: Contact[];
last_updated: number;
};
function UnifiedActivityItem(props: {
item: ActivityItem;
onClick: (id: string, kind: HackActivityType) => void;
}) {
const click = () => {
props.onClick(
props.item.id,
props.item.kind as unknown as HackActivityType
);
};
return (
<ActivityItem
// This is actually the ActivityType enum but wasm is hard
kind={props.item.kind as unknown as HackActivityType}
labels={props.item.labels}
contacts={props.item.contacts}
// FIXME: is this something we can put into node logic?
amount={props.item.amount_sats || 0}
date={props.item.last_updated}
positive={props.item.inbound}
onClick={click}
/>
);
}
export function CombinedActivity(props: { limit?: number }) {
const [state, _actions] = useMegaStore();
const i18n = useI18n();
const [detailsOpen, setDetailsOpen] = createSignal(false);
const [detailsKind, setDetailsKind] = createSignal<HackActivityType>();
const [detailsId, setDetailsId] = createSignal("");
function openDetailsModal(id: string, kind: HackActivityType) {
console.log("Opening details modal: ", id, kind);
// Some old channels don't have a channel id in the activity list
if (!id) {
console.warn("No id provided to openDetailsModal");
return;
}
setDetailsId(id);
setDetailsKind(kind);
setDetailsOpen(true);
}
async function fetchActivity() {
return await state.mutiny_wallet?.get_activity();
}
const [activity, { refetch }] = createResource(fetchActivity, {
storage: createDeepSignal
});
createEffect(() => {
// Should re-run after every sync
if (!state.is_syncing) {
refetch();
}
});
return (
<Show
when={activity.state === "ready" || activity.state === "refreshing"}
fallback={<LoadingShimmer />}
>
<Show when={detailsId() && detailsKind()}>
<DetailsIdModal
open={detailsOpen()}
kind={detailsKind()}
id={detailsId()}
setOpen={setDetailsOpen}
/>
</Show>
<Switch>
<Match when={activity.latest.length === 0}>
<div class="w-full text-center pb-4">
<NiceP>
{i18n.t(
"activity.receive_some_sats_to_get_started"
)}
</NiceP>
</div>
</Match>
<Match
when={props.limit && activity.latest.length > props.limit}
>
<For each={activity.latest.slice(0, props.limit)}>
{(activityItem) => (
<UnifiedActivityItem
item={activityItem}
onClick={openDetailsModal}
/>
)}
</For>
</Match>
<Match when={activity.latest.length >= 0}>
<For each={activity.latest}>
{(activityItem) => (
<UnifiedActivityItem
item={activityItem}
onClick={openDetailsModal}
/>
)}
</For>
</Match>
</Switch>
<Show when={props.limit && activity.latest.length > 0}>
<A
href="/activity"
class="text-m-red active:text-m-red/80 font-semibold no-underline self-center"
>
{i18n.t("activity.view_all")}
</A>
</Show>
</Show>
);
}

View File

@@ -10,7 +10,7 @@ import { generateGradient } from "~/utils/gradientHash";
import { useMegaStore } from "~/state/megaStore"; import { useMegaStore } from "~/state/megaStore";
import { Contact } from "@mutinywallet/mutiny-wasm"; import { Contact } from "@mutinywallet/mutiny-wasm";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";
import { AmountFiat, AmountSats } from "~/components/Amount"; import { AmountFiat, AmountSats } from "~/components";
export const ActivityAmount: ParentComponent<{ export const ActivityAmount: ParentComponent<{
amount: string; amount: string;

View File

@@ -1,8 +1,7 @@
import { Match, ParentComponent, Show, Switch, createMemo } from "solid-js"; import { Match, ParentComponent, Show, Switch, createMemo } from "solid-js";
import { Card, VStack } from "~/components/layout"; import { Card, VStack, AmountEditable } from "~/components";
import { useMegaStore } from "~/state/megaStore"; import { useMegaStore } from "~/state/megaStore";
import { satsToUsd } from "~/utils/conversions"; import { satsToUsd } from "~/utils/conversions";
import { AmountEditable } from "./AmountEditable";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";
const noop = () => { const noop = () => {

View File

@@ -9,18 +9,15 @@ import {
Switch, Switch,
Match Match
} from "solid-js"; } from "solid-js";
import { Button } from "~/components/layout"; import { Button, InlineAmount, InfoBox, FeesModal } from "~/components";
import { useMegaStore } from "~/state/megaStore"; import { useMegaStore } from "~/state/megaStore";
import { satsToUsd, usdToSats } from "~/utils/conversions"; import { satsToUsd, usdToSats } from "~/utils/conversions";
import { Dialog } from "@kobalte/core"; import { Dialog } from "@kobalte/core";
import close from "~/assets/icons/close.svg"; import close from "~/assets/icons/close.svg";
import pencil from "~/assets/icons/pencil.svg"; import pencil from "~/assets/icons/pencil.svg";
import currencySwap from "~/assets/icons/currency-swap.svg"; import currencySwap from "~/assets/icons/currency-swap.svg";
import { InlineAmount } from "./AmountCard";
import { DIALOG_CONTENT, DIALOG_POSITIONER } from "~/styles/dialogs"; import { DIALOG_CONTENT, DIALOG_POSITIONER } from "~/styles/dialogs";
import { InfoBox } from "./InfoBox";
import { Network } from "~/logic/mutinyWalletSetup"; import { Network } from "~/logic/mutinyWalletSetup";
import { FeesModal } from "./MoreInfoModal";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";
import { useNavigate } from "solid-start"; import { useNavigate } from "solid-start";

View File

@@ -1,23 +1,29 @@
import { DefaultMain, SafeArea, VStack, Card } from "~/components/layout"; import {
import BalanceBox, { LoadingShimmer } from "~/components/BalanceBox"; DefaultMain,
import NavBar from "~/components/NavBar"; SafeArea,
import ReloadPrompt from "~/components/Reload"; VStack,
Card,
LoadingShimmer,
BalanceBox,
ReloadPrompt,
NavBar,
OnboardWarning,
CombinedActivity,
BetaWarningModal,
PendingNwc,
DecryptDialog,
LoadingIndicator
} from "~/components";
import { A } from "solid-start"; import { A } from "solid-start";
import { OnboardWarning } from "~/components/OnboardWarning";
import { CombinedActivity } from "./Activity";
import { useMegaStore } from "~/state/megaStore"; import { useMegaStore } from "~/state/megaStore";
import { Match, Show, Suspense, Switch } from "solid-js"; import { Match, Show, Suspense, Switch } from "solid-js";
import { BetaWarningModal } from "~/components/BetaWarningModal";
import settings from "~/assets/icons/settings.svg"; import settings from "~/assets/icons/settings.svg";
import pixelLogo from "~/assets/mutiny-pixel-logo.png"; import pixelLogo from "~/assets/mutiny-pixel-logo.png";
import plusLogo from "~/assets/mutiny-plus-logo.png"; import plusLogo from "~/assets/mutiny-plus-logo.png";
import { PendingNwc } from "./PendingNwc";
import { DecryptDialog } from "./DecryptDialog";
import { LoadingIndicator } from "./LoadingIndicator";
import { FeedbackLink } from "~/routes/Feedback"; import { FeedbackLink } from "~/routes/Feedback";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";
export default function App() { export function App() {
const i18n = useI18n(); const i18n = useI18n();
const [state, _actions] = useMegaStore(); const [state, _actions] = useMegaStore();

View File

@@ -1,11 +1,16 @@
import { Match, Show, Switch } from "solid-js"; import { Match, Show, Switch } from "solid-js";
import { Button, FancyCard, Indicator } from "~/components/layout"; import {
Button,
FancyCard,
Indicator,
AmountSats,
AmountFiat,
InfoBox
} from "~/components";
import { useMegaStore } from "~/state/megaStore"; import { useMegaStore } from "~/state/megaStore";
import { AmountSats, AmountFiat } from "./Amount";
import { A, useNavigate } from "solid-start"; import { A, useNavigate } from "solid-start";
import shuffle from "~/assets/icons/shuffle.svg"; import shuffle from "~/assets/icons/shuffle.svg";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";
import { InfoBox } from "./InfoBox";
export function LoadingShimmer() { export function LoadingShimmer() {
return ( return (
@@ -23,7 +28,7 @@ export function LoadingShimmer() {
const STYLE = const STYLE =
"px-2 py-1 rounded-xl text-sm flex gap-2 items-center font-semibold"; "px-2 py-1 rounded-xl text-sm flex gap-2 items-center font-semibold";
export default function BalanceBox(props: { loading?: boolean }) { export function BalanceBox(props: { loading?: boolean }) {
const [state, _actions] = useMegaStore(); const [state, _actions] = useMegaStore();
const i18n = useI18n(); const i18n = useI18n();

View File

@@ -1,7 +1,12 @@
import { Dialog } from "@kobalte/core"; import { Dialog } from "@kobalte/core";
import { ParentComponent, createSignal } from "solid-js"; import { ParentComponent, createSignal } from "solid-js";
import { DIALOG_CONTENT, DIALOG_POSITIONER, OVERLAY } from "./DetailsModal"; import {
import { ModalCloseButton, SmallHeader } from "./layout"; DIALOG_CONTENT,
DIALOG_POSITIONER,
OVERLAY,
ModalCloseButton,
SmallHeader
} from "~/components";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";
import { useMegaStore } from "~/state/megaStore"; import { useMegaStore } from "~/state/megaStore";
import { ExternalLink } from "@mutinywallet/ui"; import { ExternalLink } from "@mutinywallet/ui";

View File

@@ -1,10 +1,13 @@
import { Match, Switch, createSignal } from "solid-js"; import { Match, Switch, createSignal } from "solid-js";
import { SmallHeader, TinyButton } from "~/components/layout";
import { Dialog } from "@kobalte/core"; import { Dialog } from "@kobalte/core";
import close from "~/assets/icons/close.svg"; import close from "~/assets/icons/close.svg";
import { SubmitHandler } from "@modular-forms/solid"; import { SubmitHandler } from "@modular-forms/solid";
import { ContactForm } from "./ContactForm"; import {
import { ContactFormValues } from "./ContactViewer"; ContactFormValues,
ContactForm,
SmallHeader,
TinyButton
} from "~/components";
import { DIALOG_CONTENT, DIALOG_POSITIONER } from "~/styles/dialogs"; import { DIALOG_CONTENT, DIALOG_POSITIONER } from "~/styles/dialogs";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";

View File

@@ -1,7 +1,11 @@
import { SubmitHandler, createForm, required } from "@modular-forms/solid"; import { SubmitHandler, createForm, required } from "@modular-forms/solid";
import { Button, LargeHeader, VStack } from "~/components/layout"; import {
import { TextField } from "~/components/layout/TextField"; Button,
import { ContactFormValues } from "./ContactViewer"; LargeHeader,
VStack,
TextField,
ContactFormValues
} from "~/components";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";
export function ContactForm(props: { export function ContactForm(props: {

View File

@@ -1,10 +1,15 @@
import { Match, Switch, createSignal } from "solid-js"; import { Match, Switch, createSignal } from "solid-js";
import { Button, Card, NiceP, SmallHeader } from "~/components/layout"; import {
Button,
Card,
NiceP,
SmallHeader,
ContactForm,
showToast
} from "~/components";
import { Dialog } from "@kobalte/core"; import { Dialog } from "@kobalte/core";
import close from "~/assets/icons/close.svg"; import close from "~/assets/icons/close.svg";
import { SubmitHandler } from "@modular-forms/solid"; import { SubmitHandler } from "@modular-forms/solid";
import { ContactForm } from "./ContactForm";
import { showToast } from "./Toaster";
import { Contact } from "@mutinywallet/mutiny-wasm"; import { Contact } from "@mutinywallet/mutiny-wasm";
import { DIALOG_CONTENT, DIALOG_POSITIONER } from "~/styles/dialogs"; import { DIALOG_CONTENT, DIALOG_POSITIONER } from "~/styles/dialogs";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";

View File

@@ -1,7 +1,5 @@
import { Show, createSignal } from "solid-js"; import { Show, createSignal } from "solid-js";
import { Button, SimpleDialog } from "~/components/layout"; import { Button, SimpleDialog, TextField, InfoBox } from "~/components";
import { TextField } from "~/components/layout/TextField";
import { InfoBox } from "~/components/InfoBox";
import { useMegaStore } from "~/state/megaStore"; import { useMegaStore } from "~/state/megaStore";
import eify from "~/utils/eify"; import eify from "~/utils/eify";
import { A } from "solid-start"; import { A } from "solid-start";

View File

@@ -1,8 +1,6 @@
import initMutinyWallet, { MutinyWallet } from "@mutinywallet/mutiny-wasm"; import initMutinyWallet, { MutinyWallet } from "@mutinywallet/mutiny-wasm";
import { createSignal } from "solid-js"; import { createSignal } from "solid-js";
import { ConfirmDialog } from "~/components/Dialog"; import { ConfirmDialog, Button, showToast } from "~/components";
import { Button } from "~/components/layout";
import { showToast } from "~/components/Toaster";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";
import { useMegaStore } from "~/state/megaStore"; import { useMegaStore } from "~/state/megaStore";
import eify from "~/utils/eify"; import eify from "~/utils/eify";

View File

@@ -1,510 +0,0 @@
import { Dialog } from "@kobalte/core";
import {
For,
Match,
ParentComponent,
Show,
Suspense,
Switch,
createEffect,
createMemo,
createResource
} from "solid-js";
import { Hr, ModalCloseButton, TinyButton, VStack } from "~/components/layout";
import { MutinyChannel, MutinyInvoice } from "@mutinywallet/mutiny-wasm";
import { OnChainTx } from "./Activity";
import bolt from "~/assets/icons/bolt-black.svg";
import chain from "~/assets/icons/chain-black.svg";
import copyIcon from "~/assets/icons/copy.svg";
import shuffle from "~/assets/icons/shuffle-black.svg";
import { ActivityAmount, HackActivityType } from "./ActivityItem";
import { CopyButton, TruncateMiddle } from "./ShareCard";
import { prettyPrintTime } from "~/utils/prettyPrintTime";
import { useMegaStore } from "~/state/megaStore";
import { MutinyTagItem, tagToMutinyTag } from "~/utils/tags";
import { useCopy } from "~/utils/useCopy";
import mempoolTxUrl from "~/utils/mempoolTxUrl";
import { Network } from "~/logic/mutinyWalletSetup";
import { AmountSmall } from "./Amount";
import { ExternalLink } from "@mutinywallet/ui";
import { InfoBox } from "./InfoBox";
import { useI18n } from "~/i18n/context";
type ChannelClosure = {
channel_id: string;
node_id: string;
reason: string;
timestamp: number;
};
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-[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;
tags: MutinyTagItem[];
}) {
const i18n = useI18n();
const [state, _actions] = useMegaStore();
return (
<div class="flex flex-col items-center gap-4">
<div class="p-4 bg-neutral-100 rounded-full">
<img src={bolt} alt="lightning bolt" class="w-8 h-8" />
</div>
<h1 class="uppercase font-semibold">
{props.info.inbound
? i18n.t("modals.transaction_details.lightning_receive")
: i18n.t("modals.transaction_details.lightning_send")}
</h1>
<ActivityAmount
center
amount={props.info.amount_sats?.toString() ?? "0"}
price={state.price}
positive={props.info.inbound}
/>
<For each={props.tags}>
{(tag) => (
<TinyButton
tag={tag}
onClick={() => {
// noop
}}
>
{tag.name}
</TinyButton>
)}
</For>
</div>
);
}
function OnchainHeader(props: {
info: OnChainTx;
tags: MutinyTagItem[];
kind?: HackActivityType;
}) {
const i18n = useI18n();
const [state, _actions] = useMegaStore();
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 (
<div class="flex flex-col items-center gap-4">
<div class="p-4 bg-neutral-100 rounded-full">
<Switch>
<Match
when={
props.kind === "ChannelOpen" ||
props.kind === "ChannelClose"
}
>
<img src={shuffle} alt="swap" class="w-8 h-8" />
</Match>
<Match when={true}>
<img src={chain} alt="blockchain" class="w-8 h-8" />
</Match>
</Switch>
</div>
<h1 class="uppercase font-semibold">
{props.kind === "ChannelOpen"
? i18n.t("modals.transaction_details.channel_open")
: props.kind === "ChannelClose"
? i18n.t("modals.transaction_details.channel_close")
: isSend()
? i18n.t("modals.transaction_details.onchain_send")
: i18n.t("modals.transaction_details.onchain_receive")}
</h1>
<Show when={props.kind !== "ChannelClose"}>
<ActivityAmount
center
amount={amount() ?? "0"}
price={state.price}
positive={!isSend()}
/>
</Show>
<For each={props.tags}>
{(tag) => (
<TinyButton
tag={tag}
onClick={() => {
// noop
}}
>
{tag.name}
</TinyButton>
)}
</For>
</div>
);
}
export const KeyValue: ParentComponent<{ key: string }> = (props) => {
return (
<li class="flex justify-between items-center gap-4">
<span class="uppercase font-semibold whitespace-nowrap text-sm">
{props.key}
</span>
<span class="font-light">{props.children}</span>
</li>
);
};
export function MiniStringShower(props: { text: string }) {
const [copy, copied] = useCopy({ copiedTimeout: 1000 });
return (
<div class="w-full grid gap-1 grid-cols-[minmax(0,_1fr)_auto]">
<TruncateMiddle text={props.text} />
{/* <pre class="truncate text-neutral-300 font-light">{props.text}</pre> */}
<button
class="w-[1.5rem] p-1"
classList={{ "bg-m-green rounded": copied() }}
onClick={() => copy(props.text)}
>
<img src={copyIcon} alt="copy" class="w-4 h-4" />
</button>
</div>
);
}
function LightningDetails(props: { info: MutinyInvoice }) {
const i18n = useI18n();
return (
<VStack>
<ul class="flex flex-col gap-4">
<KeyValue key={i18n.t("modals.transaction_details.status")}>
<span class="text-neutral-300">
{props.info.paid
? i18n.t("modals.transaction_details.paid")
: i18n.t("modals.transaction_details.unpaid")}
</span>
</KeyValue>
<KeyValue key={i18n.t("modals.transaction_details.when")}>
<span class="text-neutral-300">
{prettyPrintTime(Number(props.info.last_updated))}
</span>
</KeyValue>
<Show when={props.info.description}>
<KeyValue
key={i18n.t("modals.transaction_details.description")}
>
<span class="text-neutral-300 truncate">
{props.info.description}
</span>
</KeyValue>
</Show>
<KeyValue key={i18n.t("modals.transaction_details.fees")}>
<span class="text-neutral-300">
<AmountSmall amountSats={props.info.fees_paid} />
</span>
</KeyValue>
<KeyValue key={i18n.t("modals.transaction_details.bolt11")}>
<MiniStringShower text={props.info.bolt11 ?? ""} />
</KeyValue>
<KeyValue
key={i18n.t("modals.transaction_details.payment_hash")}
>
<MiniStringShower text={props.info.payment_hash ?? ""} />
</KeyValue>
<KeyValue key={i18n.t("modals.transaction_details.preimage")}>
<MiniStringShower text={props.info.preimage ?? ""} />
</KeyValue>
</ul>
</VStack>
);
}
function OnchainDetails(props: { info: OnChainTx; kind?: HackActivityType }) {
const i18n = useI18n();
const [state, _actions] = useMegaStore();
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 (
<VStack>
{/* <pre>{JSON.stringify(channelInfo() || "", null, 2)}</pre> */}
<ul class="flex flex-col gap-4">
<KeyValue key={i18n.t("modals.transaction_details.status")}>
<span class="text-neutral-300">
{confirmationTime()
? i18n.t("modals.transaction_details.confirmed")
: i18n.t("modals.transaction_details.unconfirmed")}
</span>
</KeyValue>
<Show when={confirmationTime()}>
<KeyValue key={i18n.t("modals.transaction_details.when")}>
<span class="text-neutral-300">
{confirmationTime()
? prettyPrintTime(Number(confirmationTime()))
: "Pending"}
</span>
</KeyValue>
</Show>
<Show when={props.info.fee && props.info.fee > 0}>
<KeyValue key={i18n.t("modals.transaction_details.fee")}>
<span class="text-neutral-300">
<AmountSmall amountSats={props.info.fee} />
</span>
</KeyValue>
</Show>
<KeyValue key={i18n.t("modals.transaction_details.txid")}>
<MiniStringShower text={props.info.txid ?? ""} />
</KeyValue>
<Switch>
<Match when={props.kind === "ChannelOpen" && channelInfo()}>
<KeyValue
key={i18n.t("modals.transaction_details.balance")}
>
<span class="text-neutral-300">
<AmountSmall
amountSats={channelInfo()?.balance}
/>
</span>
</KeyValue>
<KeyValue
key={i18n.t("modals.transaction_details.reserve")}
>
<span class="text-neutral-300">
<AmountSmall
amountSats={channelInfo()?.reserve}
/>
</span>
</KeyValue>
<KeyValue
key={i18n.t("modals.transaction_details.peer")}
>
<span class="text-neutral-300">
<MiniStringShower
text={channelInfo()?.peer ?? ""}
/>
</span>
</KeyValue>
</Match>
<Match when={props.kind === "ChannelOpen"}>
<InfoBox accent="blue">
{i18n.t("modals.transaction_details.no_details")}
</InfoBox>
</Match>
</Switch>
</ul>
<div class="text-center">
<ExternalLink href={mempoolTxUrl(props.info.txid, network)}>
{i18n.t("common.view_transaction")}
</ExternalLink>
</div>
</VStack>
);
}
function ChannelCloseDetails(props: { info: ChannelClosure }) {
const i18n = useI18n();
return (
<VStack>
{/* <pre>{JSON.stringify(props.info.value, null, 2)}</pre> */}
<ul class="flex flex-col gap-4">
<KeyValue key={i18n.t("modals.transaction_details.channel_id")}>
<MiniStringShower text={props.info.channel_id ?? ""} />
</KeyValue>
<Show when={props.info.timestamp}>
<KeyValue key={i18n.t("modals.transaction_details.when")}>
<span class="text-neutral-300">
{props.info.timestamp
? prettyPrintTime(Number(props.info.timestamp))
: i18n.t("common.pending")}
</span>
</KeyValue>
</Show>
<KeyValue key={i18n.t("modals.transaction_details.reason")}>
<p class="text-neutral-300 text-right">
{props.info.reason ?? ""}
</p>
</KeyValue>
</ul>
</VStack>
);
}
export function DetailsIdModal(props: {
open: boolean;
kind?: HackActivityType;
id: string;
setOpen: (open: boolean) => void;
}) {
const i18n = useI18n();
const [state, _actions] = useMegaStore();
const id = () => props.id;
const kind = () => props.kind;
// TODO: is there a cleaner way to do refetch when id changes?
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 && data().labels.length > 0) {
try {
const contact = state.mutiny_wallet?.get_contact(
data().labels[0]
);
if (contact) {
return [tagToMutinyTag(contact)];
} else {
return [];
}
} catch (e) {
console.error(e);
return [];
}
} else {
return [];
}
});
createEffect(() => {
if (props.id && props.kind && props.open) {
refetch();
}
});
const json = createMemo(() => JSON.stringify(data() || "", null, 2));
return (
<Dialog.Root open={props.open} onOpenChange={props.setOpen}>
<Dialog.Portal>
<Dialog.Overlay class={OVERLAY} />
<div class={DIALOG_POSITIONER}>
<Dialog.Content class={DIALOG_CONTENT}>
<Suspense>
<div class="flex justify-between mb-2">
<div />
<Dialog.CloseButton>
<ModalCloseButton />
</Dialog.CloseButton>
</div>
<Dialog.Title>
<Switch>
<Match when={props.kind === "Lightning"}>
<LightningHeader
info={data() as MutinyInvoice}
tags={tags()}
/>
</Match>
<Match
when={
props.kind === "OnChain" ||
props.kind === "ChannelOpen" ||
props.kind === "ChannelClose"
}
>
<OnchainHeader
info={data() as OnChainTx}
tags={tags()}
kind={props.kind}
/>
</Match>
</Switch>
</Dialog.Title>
<Hr />
<Dialog.Description class="flex flex-col gap-4">
<Switch>
<Match when={props.kind === "Lightning"}>
<LightningDetails
info={data() as MutinyInvoice}
/>
</Match>
<Match
when={
props.kind === "OnChain" ||
props.kind === "ChannelOpen"
}
>
<OnchainDetails
info={data() as OnChainTx}
kind={props.kind}
/>
</Match>
<Match when={props.kind === "ChannelClose"}>
<ChannelCloseDetails
info={data() as ChannelClosure}
/>
</Match>
</Switch>
<Show when={props.kind !== "ChannelClose"}>
<div class="flex justify-center">
<CopyButton
title={i18n.t("common.copy")}
text={json()}
/>
</div>
</Show>
</Dialog.Description>
</Suspense>
</Dialog.Content>
</div>
</Dialog.Portal>
</Dialog.Root>
);
}

View File

@@ -1,6 +1,6 @@
import { Dialog } from "@kobalte/core"; import { Dialog } from "@kobalte/core";
import { ParentComponent } from "solid-js"; import { ParentComponent } from "solid-js";
import { Button, SmallHeader } from "./layout"; import { Button, SmallHeader } from "~/components";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";
const OVERLAY = "fixed inset-0 z-50 bg-black/50 backdrop-blur-sm"; const OVERLAY = "fixed inset-0 z-50 bg-black/50 backdrop-blur-sm";

View File

@@ -10,7 +10,7 @@ import {
import { ExternalLink } from "@mutinywallet/ui"; import { ExternalLink } from "@mutinywallet/ui";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";
export default function ErrorDisplay(props: { error: Error }) { export function ErrorDisplay(props: { error: Error }) {
const i18n = useI18n(); const i18n = useI18n();
return ( return (
<SafeArea> <SafeArea>

View File

@@ -1,6 +1,5 @@
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";
import { AmountFiat, AmountSats } from "~/components/Amount"; import { AmountFiat, AmountSats, FeesModal } from "~/components";
import { FeesModal } from "~/components/MoreInfoModal";
export function Fee(props: { amountSats?: bigint | number }) { export function Fee(props: { amountSats?: bigint | number }) {
const i18n = useI18n(); const i18n = useI18n();

View File

@@ -1,5 +1,5 @@
import { ParentComponent, Show, createResource } from "solid-js"; import { ParentComponent, Show, createResource } from "solid-js";
import { I18nContext } from "../i18n/context"; import { I18nContext } from "~/i18n/context";
import i18next from "i18next"; import i18next from "i18next";
import i18nConfig from "~/i18n/config"; import i18nConfig from "~/i18n/config";

View File

@@ -4,17 +4,17 @@ import {
InnerCard, InnerCard,
NiceP, NiceP,
SimpleDialog, SimpleDialog,
VStack VStack,
} from "~/components/layout"; ConfirmDialog,
showToast,
InfoBox,
TextField
} from "~/components";
import { Show, createSignal } from "solid-js"; import { Show, createSignal } from "solid-js";
import eify from "~/utils/eify"; import eify from "~/utils/eify";
import { showToast } from "./Toaster";
import { downloadTextFile } from "~/utils/download"; import { downloadTextFile } from "~/utils/download";
import { createFileUploader } from "@solid-primitives/upload"; import { createFileUploader } from "@solid-primitives/upload";
import { ConfirmDialog } from "./Dialog";
import initMutinyWallet, { MutinyWallet } from "@mutinywallet/mutiny-wasm"; import initMutinyWallet, { MutinyWallet } from "@mutinywallet/mutiny-wasm";
import { InfoBox } from "./InfoBox";
import { TextField } from "./layout/TextField";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";
export function ImportExport(props: { emergency?: boolean }) { export function ImportExport(props: { emergency?: boolean }) {

View File

@@ -2,8 +2,7 @@ import { Match, Show, Switch } from "solid-js";
import { QRCodeSVG } from "solid-qr-code"; import { QRCodeSVG } from "solid-qr-code";
import { ReceiveFlavor } from "~/routes/Receive"; import { ReceiveFlavor } from "~/routes/Receive";
import { useCopy } from "~/utils/useCopy"; import { useCopy } from "~/utils/useCopy";
import { AmountSats, AmountFiat } from "./Amount"; import { AmountSats, AmountFiat, TruncateMiddle } from "~/components";
import { TruncateMiddle } from "./ShareCard";
import copyBlack from "~/assets/icons/copy-black.svg"; import copyBlack from "~/assets/icons/copy-black.svg";
import shareBlack from "~/assets/icons/share-black.svg"; import shareBlack from "~/assets/icons/share-black.svg";
import chainBlack from "~/assets/icons/chain-black.svg"; import chainBlack from "~/assets/icons/chain-black.svg";

View File

@@ -1,12 +1,13 @@
import { Dialog } from "@kobalte/core"; import { Dialog } from "@kobalte/core";
import { JSX, createMemo } from "solid-js"; import { JSX, createMemo } from "solid-js";
import { ModalCloseButton, SmallHeader } from "~/components/layout";
import { import {
ModalCloseButton,
SmallHeader,
DIALOG_CONTENT, DIALOG_CONTENT,
DIALOG_POSITIONER, DIALOG_POSITIONER,
OVERLAY OVERLAY,
} from "~/components/DetailsModal"; CopyButton
import { CopyButton } from "./ShareCard"; } from "~/components";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";
export function JsonModal(props: { export function JsonModal(props: {

View File

@@ -1,5 +1,4 @@
import { useMegaStore } from "~/state/megaStore"; import { useMegaStore } from "~/state/megaStore";
import { Hr, Button, InnerCard, VStack } from "~/components/layout";
import { import {
For, For,
Match, Match,
@@ -13,14 +12,20 @@ import { MutinyChannel, MutinyPeer } from "@mutinywallet/mutiny-wasm";
import { Collapsible, TextField } from "@kobalte/core"; import { Collapsible, TextField } from "@kobalte/core";
import mempoolTxUrl from "~/utils/mempoolTxUrl"; import mempoolTxUrl from "~/utils/mempoolTxUrl";
import eify from "~/utils/eify"; import eify from "~/utils/eify";
import { ConfirmDialog } from "~/components/Dialog"; import {
import { showToast } from "~/components/Toaster"; ConfirmDialog,
Hr,
Button,
InnerCard,
VStack,
showToast,
Restart,
ResyncOnchain,
ResetRouter,
MiniStringShower
} from "~/components";
import { Network } from "~/logic/mutinyWalletSetup"; import { Network } from "~/logic/mutinyWalletSetup";
import { ExternalLink } from "@mutinywallet/ui"; import { ExternalLink } from "@mutinywallet/ui";
import { Restart } from "./Restart";
import { ResyncOnchain } from "./ResyncOnchain";
import { ResetRouter } from "./ResetRouter";
import { MiniStringShower } from "./DetailsModal";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";
// TODO: hopefully I don't have to maintain this type forever but I don't know how to pass it around otherwise // TODO: hopefully I don't have to maintain this type forever but I don't know how to pass it around otherwise
@@ -451,7 +456,7 @@ function ListNodes() {
); );
} }
export default function KitchenSink() { export function KitchenSink() {
return ( return (
<> <>
<ListNodes /> <ListNodes />

View File

@@ -1,5 +1,5 @@
import { MutinyWallet } from "@mutinywallet/mutiny-wasm"; import { MutinyWallet } from "@mutinywallet/mutiny-wasm";
import { InnerCard, NiceP, VStack, Button } from "~/components/layout"; import { Button, InnerCard, NiceP, VStack } from "~/components";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";
import { downloadTextFile } from "~/utils/download"; import { downloadTextFile } from "~/utils/download";

View File

@@ -1,7 +1,12 @@
import { Dialog } from "@kobalte/core"; import { Dialog } from "@kobalte/core";
import { ParentComponent, createSignal, JSXElement } from "solid-js"; import { ParentComponent, createSignal, JSXElement } from "solid-js";
import { DIALOG_CONTENT, DIALOG_POSITIONER, OVERLAY } from "./DetailsModal"; import {
import { ModalCloseButton, SmallHeader } from "./layout"; DIALOG_CONTENT,
DIALOG_POSITIONER,
OVERLAY,
ModalCloseButton,
SmallHeader
} from "~/components";
import { ExternalLink } from "@mutinywallet/ui"; import { ExternalLink } from "@mutinywallet/ui";
import help from "~/assets/icons/help.svg"; import help from "~/assets/icons/help.svg";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";

View File

@@ -39,7 +39,7 @@ function NavBarItem(props: {
); );
} }
export default function NavBar(props: { activeTab: ActiveTab }) { export function NavBar(props: { activeTab: ActiveTab }) {
return ( return (
<nav class="hidden md:block fixed shadow-none z-40 safe-bottom top-0 bottom-auto left-0 h-full"> <nav class="hidden md:block fixed shadow-none z-40 safe-bottom top-0 bottom-auto left-0 h-full">
<ul class="flex flex-col justify-start gap-4 px-4 mt-4"> <ul class="flex flex-col justify-start gap-4 px-4 mt-4">

View File

@@ -1,5 +1,5 @@
import { Show } from "solid-js"; import { Show } from "solid-js";
import { ButtonLink, SmallHeader } from "./layout"; import { ButtonLink, SmallHeader } from "~/components";
import { useMegaStore } from "~/state/megaStore"; import { useMegaStore } from "~/state/megaStore";
import save from "~/assets/icons/save.svg"; import save from "~/assets/icons/save.svg";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";

View File

@@ -1,6 +1,6 @@
import { NwcProfile } from "@mutinywallet/mutiny-wasm"; import { NwcProfile } from "@mutinywallet/mutiny-wasm";
import { formatExpiration } from "~/utils/prettyPrintTime"; import { formatExpiration } from "~/utils/prettyPrintTime";
import { Card, VStack } from "./layout"; import { Card, VStack, ActivityAmount, InfoBox } from "~/components";
import { LoadingSpinner } from "@mutinywallet/ui"; import { LoadingSpinner } from "@mutinywallet/ui";
import bolt from "~/assets/icons/bolt.svg"; import bolt from "~/assets/icons/bolt.svg";
import { import {
@@ -16,8 +16,6 @@ import { useMegaStore } from "~/state/megaStore";
import greenCheck from "~/assets/icons/green-check.svg"; import greenCheck from "~/assets/icons/green-check.svg";
import redClose from "~/assets/icons/red-close.svg"; import redClose from "~/assets/icons/red-close.svg";
import { ActivityAmount } from "./ActivityItem";
import { InfoBox } from "./InfoBox";
import eify from "~/utils/eify"; import eify from "~/utils/eify";
import { A } from "solid-start"; import { A } from "solid-start";
import { createDeepSignal } from "~/utils/deepSignal"; import { createDeepSignal } from "~/utils/deepSignal";

View File

@@ -8,7 +8,7 @@ import {
import QrScanner from "qr-scanner"; import QrScanner from "qr-scanner";
import { Capacitor } from "@capacitor/core"; import { Capacitor } from "@capacitor/core";
export default function Scanner(props: { onResult: (result: string) => void }) { export function Scanner(props: { onResult: (result: string) => void }) {
let container: HTMLVideoElement | undefined; let container: HTMLVideoElement | undefined;
let scanner: QrScanner | undefined; let scanner: QrScanner | undefined;

View File

@@ -1,9 +1,8 @@
import type { Component } from "solid-js";
import { Show } from "solid-js"; import { Show } from "solid-js";
// eslint-disable-next-line import/no-unresolved // eslint-disable-next-line import/no-unresolved
import { useRegisterSW } from "virtual:pwa-register/solid"; import { useRegisterSW } from "virtual:pwa-register/solid";
const ReloadPrompt: Component = () => { export function ReloadPrompt() {
const { const {
offlineReady: [offlineReady, _setOfflineReady], offlineReady: [offlineReady, _setOfflineReady],
needRefresh: [needRefresh, _setNeedRefresh], needRefresh: [needRefresh, _setNeedRefresh],
@@ -41,6 +40,4 @@ const ReloadPrompt: Component = () => {
</Card> */} </Card> */}
</Show> </Show>
); );
}; }
export default ReloadPrompt;

View File

@@ -1,4 +1,4 @@
import { Button, InnerCard, NiceP, VStack } from "~/components/layout"; import { Button, InnerCard, NiceP, VStack } from "~/components";
import { useMegaStore } from "~/state/megaStore"; import { useMegaStore } from "~/state/megaStore";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";

View File

@@ -1,5 +1,5 @@
import { createSignal } from "solid-js"; import { createSignal } from "solid-js";
import { Button, InnerCard, NiceP, VStack } from "~/components/layout"; import { Button, InnerCard, NiceP, VStack } from "~/components";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";
import { useMegaStore } from "~/state/megaStore"; import { useMegaStore } from "~/state/megaStore";

View File

@@ -1,4 +1,4 @@
import { Button, InnerCard, NiceP, VStack } from "~/components/layout"; import { Button, InnerCard, NiceP, VStack } from "~/components";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";
import { useMegaStore } from "~/state/megaStore"; import { useMegaStore } from "~/state/megaStore";

View File

@@ -4,13 +4,13 @@ import {
LargeHeader, LargeHeader,
NiceP, NiceP,
SafeArea, SafeArea,
SmallHeader SmallHeader,
} from "~/components/layout"; ImportExport,
Logs,
DeleteEverything
} from "~/components";
import { ExternalLink } from "@mutinywallet/ui"; import { ExternalLink } from "@mutinywallet/ui";
import { Match, Switch } from "solid-js"; import { Match, Switch } from "solid-js";
import { ImportExport } from "./ImportExport";
import { Logs } from "./Logs";
import { DeleteEverything } from "./DeleteEverything";
import { FeedbackLink } from "~/routes/Feedback"; import { FeedbackLink } from "~/routes/Feedback";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";
@@ -25,7 +25,7 @@ function ErrorFooter() {
); );
} }
export default function SetupErrorDisplay(props: { initialError: Error }) { export function SetupErrorDisplay(props: { initialError: Error }) {
// Error shouldn't be reactive, so we assign to it so it just gets rendered with the first value // Error shouldn't be reactive, so we assign to it so it just gets rendered with the first value
const i18n = useI18n(); const i18n = useI18n();
const error = props.initialError; const error = props.initialError;

View File

@@ -1,4 +1,4 @@
import { Card, VStack } from "~/components/layout"; import { Card, VStack, JsonModal } from "~/components";
import { useCopy } from "~/utils/useCopy"; import { useCopy } from "~/utils/useCopy";
import copyIcon from "~/assets/icons/copy.svg"; import copyIcon from "~/assets/icons/copy.svg";
import copyBlack from "~/assets/icons/copy-black.svg"; import copyBlack from "~/assets/icons/copy-black.svg";
@@ -6,7 +6,6 @@ import shareIcon from "~/assets/icons/share.svg";
import shareBlack from "~/assets/icons/share-black.svg"; import shareBlack from "~/assets/icons/share-black.svg";
import eyeIcon from "~/assets/icons/eye.svg"; import eyeIcon from "~/assets/icons/eye.svg";
import { Show, createSignal } from "solid-js"; import { Show, createSignal } from "solid-js";
import { JsonModal } from "./JsonModal";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";
const STYLE = const STYLE =

View File

@@ -1,7 +1,7 @@
import { Select, createOptions } from "@thisbeyond/solid-select"; import { Select, createOptions } from "@thisbeyond/solid-select";
import "~/styles/solid-select.css"; import "~/styles/solid-select.css";
import { For, Show, createMemo, createSignal, onMount } from "solid-js"; import { For, Show, createMemo, createSignal, onMount } from "solid-js";
import { TinyButton } from "./layout"; import { TinyButton } from "~/components";
import { MutinyTagItem, sortByLastUsed } from "~/utils/tags"; import { MutinyTagItem, sortByLastUsed } from "~/utils/tags";
import { useMegaStore } from "~/state/megaStore"; import { useMegaStore } from "~/state/megaStore";

View File

@@ -1,7 +1,7 @@
import { Toast, toaster } from "@kobalte/core"; import { Toast, toaster } from "@kobalte/core";
import { Portal } from "solid-js/web"; import { Portal } from "solid-js/web";
import close from "~/assets/icons/close.svg"; import close from "~/assets/icons/close.svg";
import { SmallHeader } from "./layout"; import { SmallHeader } from "~/components";
export function Toaster() { export function Toaster() {
return ( return (

43
src/components/index.ts Normal file
View File

@@ -0,0 +1,43 @@
export * from "./layout";
export * from "./successfail";
export * from "./Activity";
export * from "./ActivityItem";
export * from "./Amount";
export * from "./AmountCard";
export * from "./AmountEditable";
export * from "./App";
export * from "./BalanceBox";
export * from "./BetaWarningModal";
export * from "./ContactEditor";
export * from "./ContactForm";
export * from "./ContactViewer";
export * from "./CopyableQR";
export * from "./DecryptDialog";
export * from "./DeleteEverything";
export * from "./DetailsModal";
export * from "./Dialog";
export * from "./ErrorDisplay";
export * from "./Fee";
export * from "./I18nProvider";
export * from "./ImportExport";
export * from "./InfoBox";
export * from "./IntegratedQR";
export * from "./JsonModal";
export * from "./KitchenSink";
export * from "./LoadingIndicator";
export * from "./Logs";
export * from "./MoreInfoModal";
export * from "./NavBar";
export * from "./OnboardWarning";
export * from "./PendingNwc";
export * from "./Reader";
export * from "./Reload";
export * from "./ResetRouter";
export * from "./Restart";
export * from "./ResyncOnchain";
export * from "./SeedWords";
export * from "./SetupErrorDisplay";
export * from "./ShareCard";
export * from "./TagEditor";
export * from "./Toaster";

View File

@@ -1,5 +1,5 @@
import { useLocation, useNavigate } from "solid-start"; import { useLocation, useNavigate } from "solid-start";
import { BackButton } from "./BackButton"; import { BackButton } from "~/components";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";
type StateWithPrevious = { type StateWithPrevious = {

View File

@@ -4,7 +4,7 @@ interface LinkifyProps {
initialText: string; initialText: string;
} }
export default function Linkify(props: LinkifyProps): JSX.Element { export function Linkify(props: LinkifyProps): JSX.Element {
// By naming this "initialText" we can prove to eslint that the props won't change // By naming this "initialText" we can prove to eslint that the props won't change
const text = props.initialText; const text = props.initialText;
const links: (string | JSX.Element)[] = []; const links: (string | JSX.Element)[] = [];

View File

@@ -8,8 +8,6 @@ import {
createResource, createResource,
createSignal createSignal
} from "solid-js"; } from "solid-js";
import Linkify from "./Linkify";
import { Button, ButtonLink } from "./Button";
import { import {
Collapsible, Collapsible,
Checkbox as KCheckbox, Checkbox as KCheckbox,
@@ -23,13 +21,10 @@ import { generateGradient } from "~/utils/gradientHash";
import close from "~/assets/icons/close.svg"; import close from "~/assets/icons/close.svg";
import { A } from "solid-start"; import { A } from "solid-start";
import down from "~/assets/icons/down.svg"; import down from "~/assets/icons/down.svg";
import { DecryptDialog } from "../DecryptDialog"; import { LoadingIndicator, DecryptDialog } from "~/components";
import { LoadingIndicator } from "~/components/LoadingIndicator";
import { LoadingSpinner } from "@mutinywallet/ui"; import { LoadingSpinner } from "@mutinywallet/ui";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";
export { Button, ButtonLink, Linkify };
export const SmallHeader: ParentComponent<{ class?: string }> = (props) => { export const SmallHeader: ParentComponent<{ class?: string }> = (props) => {
return ( return (
<header class={`text-sm font-semibold uppercase ${props.class}`}> <header class={`text-sm font-semibold uppercase ${props.class}`}>

View File

@@ -1,8 +1,8 @@
import { Progress } from "@kobalte/core"; import { Progress } from "@kobalte/core";
import { SmallHeader } from "."; import { SmallHeader } from "~/components";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";
export default function formatNumber(num: number) { export function formatNumber(num: number) {
const map = [ const map = [
{ suffix: "T", threshold: 1e12 }, { suffix: "T", threshold: 1e12 },
{ suffix: "B", threshold: 1e9 }, { suffix: "B", threshold: 1e9 },

View File

@@ -1,6 +1,6 @@
import { TextField as KTextField } from "@kobalte/core"; import { TextField as KTextField } from "@kobalte/core";
import { type JSX, Show, splitProps } from "solid-js"; import { type JSX, Show, splitProps } from "solid-js";
import { TinyText } from "."; import { TinyText } from "~/components";
export type TextFieldProps = { export type TextFieldProps = {
name: string; name: string;

View File

@@ -0,0 +1,9 @@
export * from "./BackButton";
export * from "./BackLink";
export * from "./BackPop";
export * from "./Button";
export * from "./Linkify";
export * from "./Misc";
export * from "./ProgressBar";
export * from "./Radio";
export * from "./TextField";

View File

@@ -1,6 +1,6 @@
import { Dialog } from "@kobalte/core"; import { Dialog } from "@kobalte/core";
import { JSX } from "solid-js"; import { JSX } from "solid-js";
import { Button } from "~/components/layout"; import { Button } from "~/components";
import { DIALOG_CONTENT, DIALOG_POSITIONER } from "~/styles/dialogs"; import { DIALOG_CONTENT, DIALOG_POSITIONER } from "~/styles/dialogs";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";

View File

@@ -0,0 +1,3 @@
export * from "./MegaCheck";
export * from "./MegaEx";
export * from "./SuccessModal";

View File

@@ -15,9 +15,7 @@ import {
// eslint-disable-next-line // eslint-disable-next-line
import "@mutinywallet/ui/style.css"; import "@mutinywallet/ui/style.css";
import { Provider as MegaStoreProvider } from "~/state/megaStore"; import { Provider as MegaStoreProvider } from "~/state/megaStore";
import { Toaster } from "~/components/Toaster"; import { Toaster, ErrorDisplay, I18nProvider } from "~/components";
import ErrorDisplay from "./components/ErrorDisplay";
import { I18nProvider } from "./components/I18nProvider";
export default function Root() { export default function Root() {
return ( return (

View File

@@ -1,5 +1,4 @@
import { For, Show, Suspense, createResource } from "solid-js"; import { For, Show, Suspense, createResource } from "solid-js";
import NavBar from "~/components/NavBar";
import { import {
Button, Button,
Card, Card,
@@ -8,18 +7,20 @@ import {
NiceP, NiceP,
MutinyWalletGuard, MutinyWalletGuard,
SafeArea, SafeArea,
VStack VStack,
} from "~/components/layout"; NavBar,
import { BackLink } from "~/components/layout/BackLink"; BackLink,
import { CombinedActivity } from "~/components/Activity"; CombinedActivity,
ContactEditor,
ContactFormValues,
ContactViewer,
showToast,
LoadingShimmer
} from "~/components";
import { Tabs } from "@kobalte/core"; import { Tabs } from "@kobalte/core";
import { gradientsPerContact } from "~/utils/gradientHash"; import { gradientsPerContact } from "~/utils/gradientHash";
import { ContactEditor } from "~/components/ContactEditor";
import { ContactFormValues, ContactViewer } from "~/components/ContactViewer";
import { useMegaStore } from "~/state/megaStore"; import { useMegaStore } from "~/state/megaStore";
import { Contact } from "@mutinywallet/mutiny-wasm"; import { Contact } from "@mutinywallet/mutiny-wasm";
import { showToast } from "~/components/Toaster";
import { LoadingShimmer } from "~/components/BalanceBox";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";
function ContactRow() { function ContactRow() {

View File

@@ -8,7 +8,6 @@ import {
} from "@modular-forms/solid"; } from "@modular-forms/solid";
import { Match, Show, Switch, createSignal } from "solid-js"; import { Match, Show, Switch, createSignal } from "solid-js";
import { A, useLocation } from "solid-start"; import { A, useLocation } from "solid-start";
import NavBar from "~/components/NavBar";
import { import {
Button, Button,
ButtonLink, ButtonLink,
@@ -17,17 +16,16 @@ import {
LargeHeader, LargeHeader,
NiceP, NiceP,
SafeArea, SafeArea,
VStack VStack,
BackPop,
StyledRadioGroup,
TextField
} from "~/components/layout"; } from "~/components/layout";
import { BackPop } from "~/components/layout/BackPop";
import { ExternalLink } from "@mutinywallet/ui"; import { ExternalLink } from "@mutinywallet/ui";
import { StyledRadioGroup } from "~/components/layout/Radio";
import { TextField } from "~/components/layout/TextField";
import feedback from "~/assets/icons/feedback.svg"; import feedback from "~/assets/icons/feedback.svg";
import { InfoBox } from "~/components/InfoBox";
import eify from "~/utils/eify"; import eify from "~/utils/eify";
import { MegaCheck } from "~/components/successfail/MegaCheck";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";
import { InfoBox, MegaCheck, NavBar } from "~/components";
const FEEDBACK_API = import.meta.env.VITE_FEEDBACK; const FEEDBACK_API = import.meta.env.VITE_FEEDBACK;

View File

@@ -13,7 +13,12 @@ import {
Show, Show,
Switch Switch
} from "solid-js"; } from "solid-js";
import { useMegaStore } from "~/state/megaStore";
import { objectToSearchParams } from "~/utils/objectToSearchParams";
import mempoolTxUrl from "~/utils/mempoolTxUrl";
import { useNavigate } from "solid-start";
import { import {
Fee,
Button, Button,
Card, Card,
DefaultMain, DefaultMain,
@@ -21,33 +26,29 @@ import {
LargeHeader, LargeHeader,
MutinyWalletGuard, MutinyWalletGuard,
SafeArea, SafeArea,
SimpleDialog SimpleDialog,
} from "~/components/layout"; NavBar,
AmountSats,
AmountFiat,
BackLink,
TagEditor,
StyledRadioGroup,
showToast,
AmountCard,
BackButton,
SuccessModal,
MegaCheck,
InfoBox,
FeesModal,
IntegratedQr
} from "~/components";
import { ExternalLink } from "@mutinywallet/ui"; import { ExternalLink } from "@mutinywallet/ui";
import NavBar from "~/components/NavBar";
import { useMegaStore } from "~/state/megaStore";
import { objectToSearchParams } from "~/utils/objectToSearchParams";
import mempoolTxUrl from "~/utils/mempoolTxUrl";
import { AmountSats, AmountFiat } from "~/components/Amount";
import { BackLink } from "~/components/layout/BackLink";
import { TagEditor } from "~/components/TagEditor";
import { StyledRadioGroup } from "~/components/layout/Radio";
import { showToast } from "~/components/Toaster";
import { useNavigate } from "solid-start";
import { AmountCard } from "~/components/AmountCard";
import { BackButton } from "~/components/layout/BackButton";
import { MutinyTagItem } from "~/utils/tags"; import { MutinyTagItem } from "~/utils/tags";
import { Network } from "~/logic/mutinyWalletSetup"; import { Network } from "~/logic/mutinyWalletSetup";
import { SuccessModal } from "~/components/successfail/SuccessModal";
import { MegaCheck } from "~/components/successfail/MegaCheck";
import { InfoBox } from "~/components/InfoBox";
import { FeesModal } from "~/components/MoreInfoModal";
import { IntegratedQr } from "~/components/IntegratedQR";
import side2side from "~/assets/icons/side-to-side.svg"; import side2side from "~/assets/icons/side-to-side.svg";
import eify from "~/utils/eify"; import eify from "~/utils/eify";
import { matchError } from "~/logic/errorDispatch"; import { matchError } from "~/logic/errorDispatch";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";
import { Fee } from "~/components/Fee";
type OnChainTx = { type OnChainTx = {
transaction: { transaction: {

View File

@@ -1,644 +0,0 @@
import {
createEffect,
createMemo,
createResource,
createSignal,
For,
Match,
onMount,
ParentComponent,
Show,
Suspense,
Switch
} from "solid-js";
import {
CENTER_COLUMN,
MISSING_LABEL,
REDSHIFT_LABEL,
RIGHT_COLUMN,
THREE_COLUMNS,
UtxoItem
} from "~/components/Activity";
import {
Button,
Card,
DefaultMain,
LargeHeader,
NiceP,
MutinyWalletGuard,
SafeArea,
SmallAmount,
SmallHeader,
VStack
} from "~/components/layout";
import { LoadingSpinner } from "@mutinywallet/ui";
import { BackLink } from "~/components/layout/BackLink";
import { StyledRadioGroup } from "~/components/layout/Radio";
import NavBar from "~/components/NavBar";
import { useMegaStore } from "~/state/megaStore";
import wave from "~/assets/wave.gif";
import utxoIcon from "~/assets/icons/coin.svg";
import { ProgressBar } from "~/components/layout/ProgressBar";
import { MutinyChannel } from "@mutinywallet/mutiny-wasm";
import mempoolTxUrl from "~/utils/mempoolTxUrl";
import { AmountSats } from "~/components/Amount";
import { getRedshifted, setRedshifted } from "~/utils/fakeLabels";
import { Network } from "~/logic/mutinyWalletSetup";
import { useI18n } from "~/i18n/context";
type ShiftOption = "utxo" | "lightning";
type ShiftStage = "choose" | "observe" | "success" | "failure";
type OutPoint = string; // Replace with the actual TypeScript type for OutPoint
type RedshiftStatus = string; // Replace with the actual TypeScript type for RedshiftStatus
type RedshiftRecipient = unknown; // Replace with the actual TypeScript type for RedshiftRecipient
type PublicKey = unknown; // Replace with the actual TypeScript type for PublicKey
interface RedshiftResult {
id: string;
input_utxo: OutPoint;
status: RedshiftStatus;
recipient: RedshiftRecipient;
output_utxo?: OutPoint;
introduction_channel?: OutPoint;
output_channel?: OutPoint;
introduction_node: PublicKey;
amount_sats: bigint;
change_amt?: bigint;
fees_paid: bigint;
}
const dummyRedshift: RedshiftResult = {
id: "44036599c37d590899e8d5d920860286",
input_utxo:
"44036599c37d590899e8d5d92086028695d2c2966fdc354ce1da9a9eac610a53:1",
status: "Completed", // Replace with a dummy value for RedshiftStatus
recipient: {}, // Replace with a dummy value for RedshiftRecipient
output_utxo:
"44036599c37d590899e8d5d92086028695d2c2966fdc354ce1da9a9eac610a53:1",
introduction_channel:
"a7773e57f8595848a635e9af105927cac9ecaf292d71a76456ae0455bd3c9c64:0",
output_channel:
"a7773e57f8595848a635e9af105927cac9ecaf292d71a76456ae0455bd3c9c64:0",
introduction_node: {}, // Replace with a dummy value for PublicKey
amount_sats: BigInt(1000000),
change_amt: BigInt(12345),
fees_paid: BigInt(2500)
};
function RedshiftReport(props: { redshift: RedshiftResult; utxo: UtxoItem }) {
const i18n = useI18n();
const [state, _actions] = useMegaStore();
const getUtXos = async () => {
console.log("Getting utxos");
return (await state.mutiny_wallet?.list_utxos()) as UtxoItem[];
};
// function findUtxoByOutpoint(
// outpoint?: string,
// utxos: UtxoItem[] = []
// ): UtxoItem | undefined {
// if (!outpoint) return undefined
// return utxos.find((utxo) => utxo.outpoint === outpoint)
// }
const [_utxos, { refetch: _refetchUtxos }] = createResource(getUtXos);
// const inputUtxo = createMemo(() => {
// console.log(utxos())
// const foundUtxo = findUtxoByOutpoint(props.redshift.input_utxo, utxos())
// console.log("Found utxo:", foundUtxo)
// return foundUtxo
// })
const [redshiftResource, { refetch: _refetchRedshift }] = createResource(
async () => {
console.log("Checking redshift", props.redshift.id);
const redshift = await state.mutiny_wallet?.get_redshift(
props.redshift.id
);
console.log(redshift);
return redshift;
}
);
onMount(() => {
// const interval = setInterval(() => {
// if (redshiftResource()) refetch()
// // if (sentAmount() === 200000) {
// // clearInterval(interval)
// // props.setShiftStage("success");
// // // setSentAmount((0))
// // } else {
// // setSentAmount((sentAmount() + 50000))
// // }
// }, 1000)
});
// const outputUtxo = createMemo(() => {
// return findUtxoByOutpoint(redshiftResource()?.output_utxo, utxos())
// })
createEffect(() => {
setRedshifted(true, redshiftResource()?.output_utxo);
});
const network = state.mutiny_wallet?.get_network() as Network;
return (
<VStack biggap>
{/* <VStack>
<NiceP>We did it. Here's your new UTXO:</NiceP>
<Show when={utxos() && outputUtxo()}>
<Card>
<Utxo item={outputUtxo()!} />
</Card>
</Show>
</VStack> */}
<VStack>
<NiceP>{i18n.t("redshift.what_happened")}</NiceP>
<Show when={redshiftResource()}>
<Card>
<VStack biggap>
{/* <KV key="Input utxo">
<Show when={utxos() && inputUtxo()}>
<Utxo item={inputUtxo()!} />
</Show>
</KV> */}
<KV key={i18n.t("redshift.starting_amount")}>
<AmountSats
amountSats={redshiftResource()!.amount_sats}
/>
</KV>
<KV key={i18n.t("redshift.fees_paid")}>
<AmountSats
amountSats={redshiftResource()!.fees_paid}
/>
</KV>
<KV key={i18n.t("redshift.change")}>
<AmountSats
amountSats={redshiftResource()!.change_amt}
/>
</KV>
<KV key={i18n.t("redshift.outbound_channel")}>
<VStack>
<pre class="whitespace-pre-wrap break-all">
{
redshiftResource()!
.introduction_channel
}
</pre>
<a
class=""
href={mempoolTxUrl(
redshiftResource()!.introduction_channel?.split(
":"
)[0],
network
)}
target="_blank"
rel="noreferrer"
>
{i18n.t("common.view_transaction")}
</a>
</VStack>
</KV>
<Show when={redshiftResource()!.output_channel}>
<KV key={i18n.t("redshift.return_channel")}>
<VStack>
<pre class="whitespace-pre-wrap break-all">
{redshiftResource()!.output_channel}
</pre>
<a
class=""
href={mempoolTxUrl(
redshiftResource()!.output_channel?.split(
":"
)[0],
network
)}
target="_blank"
rel="noreferrer"
>
{i18n.t("common.view_transaction")}
</a>
</VStack>
</KV>
</Show>
</VStack>
</Card>
</Show>
</VStack>
</VStack>
);
}
export function Utxo(props: { item: UtxoItem; onClick?: () => void }) {
const i18n = useI18n();
const redshifted = createMemo(() => getRedshifted(props.item.outpoint));
return (
<>
<div
class={THREE_COLUMNS}
onClick={() => props.onClick && props.onClick()}
>
<div class="flex items-center">
<img src={utxoIcon} alt="coin" />
</div>
<div class={CENTER_COLUMN}>
<div class="flex gap-2">
<Show
when={redshifted()}
fallback={
<h2 class={MISSING_LABEL}>
{i18n.t("redshift.unknown")}
</h2>
}
>
<h2 class={REDSHIFT_LABEL}>
{i18n.t("redshift.title")}
</h2>
</Show>
</div>
<SmallAmount amount={props.item.txout.value} />
</div>
<div class={RIGHT_COLUMN}>
<SmallHeader
class={
props.item?.is_spent ? "text-m-red" : "text-m-green"
}
>
{/* {props.item?.is_spent ? "SPENT" : "UNSPENT"} */}
</SmallHeader>
</div>
</div>
</>
);
}
const FAKE_STATES = [
"Creating a new node",
"Opening a channel",
"Sending funds through",
"Closing the channel",
"Redshift complete"
];
function ShiftObserver(props: {
setShiftStage: (stage: ShiftStage) => void;
redshiftId: string;
}) {
const i18n = useI18n();
const [_state, _actions] = useMegaStore();
const [fakeStage, _setFakeStage] = createSignal(2);
const [sentAmount, setSentAmount] = createSignal(0);
onMount(() => {
const interval = setInterval(() => {
if (sentAmount() === 200000) {
clearInterval(interval);
props.setShiftStage("success");
// setSentAmount((0))
} else {
setSentAmount(sentAmount() + 50000);
}
}, 1000);
});
// async function checkRedshift(id: string) {
// console.log("Checking redshift", id)
// const redshift = await state.mutiny_wallet?.get_redshift(id)
// console.log(redshift)
// return redshift
// }
// const [redshiftResource, { refetch }] = createResource(
// props.redshiftId,
// checkRedshift
// )
// onMount(() => {
// const interval = setInterval(() => {
// if (redshiftResource()) refetch();
// // if (sentAmount() === 200000) {
// // clearInterval(interval)
// // props.setShiftStage("success");
// // // setSentAmount((0))
// // } else {
// // setSentAmount((sentAmount() + 50000))
// // }
// }, 1000)
// })
// createEffect(() => {
// const interval = setInterval(() => {
// if (chosenUtxo()) refetch();
// }, 1000); // Poll every second
// onCleanup(() => {
// clearInterval(interval);
// });
// });
return (
<>
<NiceP>{i18n.t("redshift.watch_it_go")}</NiceP>
<Card>
<VStack>
<pre class="self-center">{FAKE_STATES[fakeStage()]}</pre>
<ProgressBar value={sentAmount()} max={200000} />
<img src={wave} class="h-4 self-center" alt="sine wave" />
</VStack>
</Card>
</>
);
}
const KV: ParentComponent<{ key: string }> = (props) => {
return (
<div class="flex flex-col gap-2">
<p class="text-sm font-semibold uppercase">{props.key}</p>
{props.children}
</div>
);
};
export default function Redshift() {
const i18n = useI18n();
const [state, _actions] = useMegaStore();
const [shiftStage, setShiftStage] = createSignal<ShiftStage>("choose");
const [shiftType, setShiftType] = createSignal<ShiftOption>("utxo");
const [chosenUtxo, setChosenUtxo] = createSignal<UtxoItem>();
const SHIFT_OPTIONS = [
{
value: "utxo",
label: i18n.t("redshift.utxo_label"),
caption: i18n.t("redshift.utxo_caption")
},
{
value: "lightning",
label: i18n.t("redshift.lightning_label"),
caption: i18n.t("redshift.lightning_caption")
}
];
const getUtXos = async () => {
console.log("Getting utxos");
return (await state.mutiny_wallet?.list_utxos()) as UtxoItem[];
};
// TODO: FIXME: this is old code needs to be revisited!
const getChannels = async () => {
console.log("Getting channels");
// await state.mutiny_wallet?.sync();
const channels =
(await state.mutiny_wallet?.list_channels()) as Promise<
MutinyChannel[]
>;
console.log(channels);
return channels;
};
const [utxos, { refetch: _refetchUtxos }] = createResource(getUtXos);
const [_channels, { refetch: _refetchChannels }] =
createResource(getChannels);
const redshiftedUtxos = createMemo(() => {
return utxos()?.filter((utxo) => getRedshifted(utxo.outpoint));
});
const unredshiftedUtxos = createMemo(() => {
return utxos()?.filter((utxo) => !getRedshifted(utxo.outpoint));
});
function resetState() {
setShiftStage("choose");
setShiftType("utxo");
setChosenUtxo(undefined);
}
async function redshiftUtxo(utxo: UtxoItem) {
console.log("Redshifting utxo", utxo.outpoint);
const redshift = await state.mutiny_wallet?.init_redshift(
utxo.outpoint
);
console.log("Redshift initialized:");
console.log(redshift);
return redshift;
}
const [initializedRedshift, { refetch: _refetchRedshift }] = createResource(
chosenUtxo,
redshiftUtxo
);
createEffect(() => {
if (chosenUtxo() && initializedRedshift()) {
// window.location.href = "/"
setShiftStage("observe");
}
});
return (
<MutinyWalletGuard>
<SafeArea>
<DefaultMain>
<BackLink />
<LargeHeader>
{i18n.t("redshift.title")}{" "}
{i18n.t("common.coming_soon")}
</LargeHeader>
<div class="relative filter grayscale pointer-events-none opacity-75">
<VStack biggap>
{/* <pre>{JSON.stringify(redshiftResource(), null, 2)}</pre> */}
<Switch>
<Match when={shiftStage() === "choose"}>
<VStack>
<NiceP>
{i18n.t("redshift.where_this_goes")}
</NiceP>
<StyledRadioGroup
accent="red"
value={shiftType()}
onValueChange={(newValue) =>
setShiftType(
newValue as ShiftOption
)
}
choices={SHIFT_OPTIONS}
/>
</VStack>
<VStack>
<NiceP>
{i18n.t("redshift.choose_your")}{" "}
<span class="inline-block">
<img
class="h-4"
src={wave}
alt="sine wave"
/>
</span>{" "}
{i18n.t("redshift.utxo_to_begin")}
</NiceP>
<Suspense>
<Card
title={i18n.t(
"redshift.unshifted_utxo"
)}
>
<Switch>
<Match when={utxos.loading}>
<LoadingSpinner wide />
</Match>
<Match
when={
utxos.state ===
"ready" &&
unredshiftedUtxos()
?.length === 0
}
>
<code>
{i18n.t(
"redshift.no_utxos_empty_state"
)}
</code>
</Match>
<Match
when={
utxos.state ===
"ready" &&
unredshiftedUtxos() &&
unredshiftedUtxos()!
.length >= 0
}
>
<For
each={unredshiftedUtxos()}
>
{(utxo) => (
<Utxo
item={utxo}
onClick={() =>
setChosenUtxo(
utxo
)
}
/>
)}
</For>
</Match>
</Switch>
</Card>
</Suspense>
<Suspense>
<Card
titleElement={
<SmallHeader>
<span class="text-m-red">
{i18n.t(
"redshift.redshifted"
)}{" "}
</span>
{i18n.t(
"redshift.utxos"
)}
</SmallHeader>
}
>
<Switch>
<Match when={utxos.loading}>
<LoadingSpinner wide />
</Match>
<Match
when={
utxos.state ===
"ready" &&
redshiftedUtxos()
?.length === 0
}
>
<code>
{i18n.t(
"redshift.no_utxos_empty_state"
)}
</code>
</Match>
<Match
when={
utxos.state ===
"ready" &&
redshiftedUtxos() &&
redshiftedUtxos()!
.length >= 0
}
>
<For
each={redshiftedUtxos()}
>
{(utxo) => (
<Utxo
item={utxo}
/>
)}
</For>
</Match>
</Switch>
</Card>
</Suspense>
</VStack>
</Match>
<Match
when={
shiftStage() === "observe" &&
chosenUtxo()
}
>
<ShiftObserver
setShiftStage={setShiftStage}
redshiftId="dummy-redshift"
/>
</Match>
<Match
when={
shiftStage() === "success" &&
chosenUtxo()
}
>
<VStack biggap>
<RedshiftReport
redshift={dummyRedshift}
utxo={chosenUtxo()!}
/>
<Button
intent="red"
onClick={resetState}
>
{i18n.t("common.nice")}
</Button>
</VStack>
</Match>
<Match when={shiftStage() === "failure"}>
<NiceP>{i18n.t("redshift.oh_dear")}</NiceP>
<NiceP>
{i18n.t("redshift.here_is_error")}
</NiceP>
<Button intent="red" onClick={resetState}>
{i18n.t("common.dangit")}
</Button>
</Match>
</Switch>
</VStack>
</div>
</DefaultMain>
<NavBar activeTab="redshift" />
</SafeArea>
</MutinyWalletGuard>
);
}

View File

@@ -1,8 +1,6 @@
import Reader from "~/components/Reader";
import { createEffect, createSignal } from "solid-js"; import { createEffect, createSignal } from "solid-js";
import { useNavigate } from "solid-start"; import { useNavigate } from "solid-start";
import { Button } from "~/components/layout"; import { Button, Scanner as Reader, showToast } from "~/components";
import { showToast } from "~/components/Toaster";
import { useMegaStore } from "~/state/megaStore"; import { useMegaStore } from "~/state/megaStore";
import { toParsedParams } from "~/logic/waila"; import { toParsedParams } from "~/logic/waila";
import { Clipboard } from "@capacitor/clipboard"; import { Clipboard } from "@capacitor/clipboard";

View File

@@ -7,9 +7,10 @@ import {
createSignal, createSignal,
onMount onMount
} from "solid-js"; } from "solid-js";
import { AmountSats, AmountFiat } from "~/components/Amount";
import NavBar from "~/components/NavBar";
import { import {
NavBar,
AmountSats,
AmountFiat,
Button, Button,
ButtonLink, ButtonLink,
Card, Card,
@@ -19,35 +20,35 @@ import {
MutinyWalletGuard, MutinyWalletGuard,
SafeArea, SafeArea,
SmallHeader, SmallHeader,
VStack VStack,
} from "~/components/layout"; StyledRadioGroup,
showToast,
MegaCheck,
MegaEx,
BackLink,
TagEditor,
StringShower,
AmountCard,
BackButton,
SuccessModal,
InfoBox,
Fee
} from "~/components";
import { ExternalLink } from "@mutinywallet/ui";
import { Paste } from "~/assets/svg/Paste"; import { Paste } from "~/assets/svg/Paste";
import { Scan } from "~/assets/svg/Scan"; import { Scan } from "~/assets/svg/Scan";
import { useMegaStore } from "~/state/megaStore"; import { useMegaStore } from "~/state/megaStore";
import { Contact, MutinyInvoice } from "@mutinywallet/mutiny-wasm"; import { Contact, MutinyInvoice } from "@mutinywallet/mutiny-wasm";
import { StyledRadioGroup } from "~/components/layout/Radio";
import { showToast } from "~/components/Toaster";
import eify from "~/utils/eify"; import eify from "~/utils/eify";
import { MegaCheck } from "~/components/successfail/MegaCheck";
import { MegaEx } from "~/components/successfail/MegaEx";
import mempoolTxUrl from "~/utils/mempoolTxUrl"; import mempoolTxUrl from "~/utils/mempoolTxUrl";
import { BackLink } from "~/components/layout/BackLink";
import { useNavigate } from "solid-start"; import { useNavigate } from "solid-start";
import { TagEditor } from "~/components/TagEditor";
import { StringShower } from "~/components/ShareCard";
import { AmountCard } from "~/components/AmountCard";
import { MutinyTagItem } from "~/utils/tags"; import { MutinyTagItem } from "~/utils/tags";
import { BackButton } from "~/components/layout/BackButton";
import { Network } from "~/logic/mutinyWalletSetup"; import { Network } from "~/logic/mutinyWalletSetup";
import { SuccessModal } from "~/components/successfail/SuccessModal";
import { ExternalLink } from "@mutinywallet/ui";
import { InfoBox } from "~/components/InfoBox";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";
import { ParsedParams, toParsedParams } from "~/logic/waila"; import { ParsedParams, toParsedParams } from "~/logic/waila";
import { Clipboard } from "@capacitor/clipboard"; import { Clipboard } from "@capacitor/clipboard";
import { Capacitor } from "@capacitor/core"; import { Capacitor } from "@capacitor/core";
import { FeedbackLink } from "./Feedback"; import { FeedbackLink } from "./Feedback";
import { Fee } from "~/components/Fee";
export type SendSource = "lightning" | "onchain"; export type SendSource = "lightning" | "onchain";

View File

@@ -1,15 +1,15 @@
import { AmountCard } from "~/components/AmountCard";
import { Fee } from "~/components/Fee";
import NavBar from "~/components/NavBar";
import { OnboardWarning } from "~/components/OnboardWarning";
import { ShareCard } from "~/components/ShareCard";
import { import {
AmountCard,
Fee,
NavBar,
OnboardWarning,
Button, Button,
DefaultMain, DefaultMain,
ShareCard,
LargeHeader, LargeHeader,
SafeArea, SafeArea,
VStack VStack
} from "~/components/layout"; } from "~/components";
const SAMPLE = const SAMPLE =
"bitcoin:tb1prqm8xtlgme0vmw5s30lgf0a4f5g4mkgsqundwmpu6thrg8zr6uvq2qrhzq?amount=0.001&lightning=lntbs1m1pj9n9xjsp5xgdrmvprtm67p7nq4neparalexlhlmtxx87zx6xeqthsplu842zspp546d6zd2seyaxpapaxx62m88yz3xueqtjmn9v6wj8y56np8weqsxqdqqnp4qdn2hj8tfknpuvdg6tz9yrf3e27ltrx9y58c24jh89lnm43yjwfc5xqrpwjcqpj9qrsgq5sdgh0m3ur5mu5hrmmag4mx9yvy86f83pd0x9ww80kgck6tac3thuzkj0mrtltaxwnlfea95h2re7tj4qsnwzxlvrdmyq2h9mgapnycpppz6k6"; "bitcoin:tb1prqm8xtlgme0vmw5s30lgf0a4f5g4mkgsqundwmpu6thrg8zr6uvq2qrhzq?amount=0.001&lightning=lntbs1m1pj9n9xjsp5xgdrmvprtm67p7nq4neparalexlhlmtxx87zx6xeqthsplu842zspp546d6zd2seyaxpapaxx62m88yz3xueqtjmn9v6wj8y56np8weqsxqdqqnp4qdn2hj8tfknpuvdg6tz9yrf3e27ltrx9y58c24jh89lnm43yjwfc5xqrpwjcqpj9qrsgq5sdgh0m3ur5mu5hrmmag4mx9yvy86f83pd0x9ww80kgck6tac3thuzkj0mrtltaxwnlfea95h2re7tj4qsnwzxlvrdmyq2h9mgapnycpppz6k6";

View File

@@ -9,33 +9,33 @@ import {
createResource, createResource,
createSignal createSignal
} from "solid-js"; } from "solid-js";
import { AmountCard } from "~/components/AmountCard";
import NavBar from "~/components/NavBar";
import { showToast } from "~/components/Toaster";
import { import {
AmountCard,
NavBar,
showToast,
Button, Button,
Card, Card,
DefaultMain, DefaultMain,
LargeHeader, LargeHeader,
MutinyWalletGuard, MutinyWalletGuard,
SafeArea, SafeArea,
VStack BackLink,
} from "~/components/layout"; TextField,
import { BackLink } from "~/components/layout/BackLink"; VStack,
import { TextField } from "~/components/layout/TextField"; MegaCheck,
MegaEx,
InfoBox,
SuccessModal,
AmountFiat
} from "~/components";
import { MethodChooser, SendSource } from "~/routes/Send"; import { MethodChooser, SendSource } from "~/routes/Send";
import { useMegaStore } from "~/state/megaStore"; import { useMegaStore } from "~/state/megaStore";
import eify from "~/utils/eify"; import eify from "~/utils/eify";
import { MegaCheck } from "~/components/successfail/MegaCheck";
import { MegaEx } from "~/components/successfail/MegaEx";
import { InfoBox } from "~/components/InfoBox";
import { useNavigate } from "solid-start"; import { useNavigate } from "solid-start";
import mempoolTxUrl from "~/utils/mempoolTxUrl"; import mempoolTxUrl from "~/utils/mempoolTxUrl";
import { SuccessModal } from "~/components/successfail/SuccessModal";
import { ExternalLink } from "@mutinywallet/ui"; import { ExternalLink } from "@mutinywallet/ui";
import { Network } from "~/logic/mutinyWalletSetup"; import { Network } from "~/logic/mutinyWalletSetup";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";
import { AmountFiat } from "~/components/Amount";
const CHANNEL_FEE_ESTIMATE_ADDRESS = const CHANNEL_FEE_ESTIMATE_ADDRESS =
"bc1qf7546vg73ddsjznzq57z3e8jdn6gtw6au576j07kt6d9j7nz8mzsyn6lgf"; "bc1qf7546vg73ddsjznzq57z3e8jdn6gtw6au576j07kt6d9j7nz8mzsyn6lgf";

View File

@@ -1,11 +1,6 @@
import { Title } from "solid-start"; import { Title } from "solid-start";
import { HttpStatusCode } from "solid-start/server"; import { HttpStatusCode } from "solid-start/server";
import { import { ButtonLink, DefaultMain, LargeHeader, SafeArea } from "~/components";
ButtonLink,
DefaultMain,
LargeHeader,
SafeArea
} from "~/components/layout";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";
export default function NotFound() { export default function NotFound() {

View File

@@ -1,8 +1,6 @@
import App from "~/components/App";
import { Switch, Match } from "solid-js"; import { Switch, Match } from "solid-js";
import { useMegaStore } from "~/state/megaStore"; import { useMegaStore } from "~/state/megaStore";
import { FullscreenLoader } from "~/components/layout"; import { App, FullscreenLoader, SetupErrorDisplay } from "~/components";
import SetupErrorDisplay from "~/components/SetupErrorDisplay";
export default function Home() { export default function Home() {
const [state, _] = useMegaStore(); const [state, _] = useMegaStore();

View File

@@ -1,16 +1,16 @@
import { DeleteEverything } from "~/components/DeleteEverything";
import KitchenSink from "~/components/KitchenSink";
import NavBar from "~/components/NavBar";
import { import {
DeleteEverything,
KitchenSink,
NavBar,
DefaultMain, DefaultMain,
LargeHeader, LargeHeader,
MutinyWalletGuard, MutinyWalletGuard,
NiceP, NiceP,
SafeArea, SafeArea,
SmallHeader, SmallHeader,
VStack VStack,
} from "~/components/layout"; BackLink
import { BackLink } from "~/components/layout/BackLink"; } from "~/components";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";
export default function Admin() { export default function Admin() {

View File

@@ -6,14 +6,14 @@ import {
MutinyWalletGuard, MutinyWalletGuard,
SafeArea, SafeArea,
VStack, VStack,
Checkbox Checkbox,
} from "~/components/layout"; NavBar,
import NavBar from "~/components/NavBar"; SeedWords,
BackLink
} from "~/components";
import { useNavigate } from "solid-start"; import { useNavigate } from "solid-start";
import { SeedWords } from "~/components/SeedWords";
import { useMegaStore } from "~/state/megaStore"; import { useMegaStore } from "~/state/megaStore";
import { Show, createEffect, createSignal } from "solid-js"; import { Show, createEffect, createSignal } from "solid-js";
import { BackLink } from "~/components/layout/BackLink";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";
function Quiz(props: { setHasCheckedAll: (hasChecked: boolean) => void }) { function Quiz(props: { setHasCheckedAll: (hasChecked: boolean) => void }) {

View File

@@ -9,11 +9,11 @@ import {
SafeArea, SafeArea,
SmallHeader, SmallHeader,
TinyText, TinyText,
VStack VStack,
} from "~/components/layout"; AmountSmall,
import { AmountSmall } from "~/components/Amount"; BackLink,
import { BackLink } from "~/components/layout/BackLink"; NavBar
import NavBar from "~/components/NavBar"; } from "~/components";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";
function BalanceBar(props: { inbound: number; outbound: number }) { function BalanceBar(props: { inbound: number; outbound: number }) {

View File

@@ -1,11 +1,12 @@
import { NwcProfile } from "@mutinywallet/mutiny-wasm"; import { NwcProfile } from "@mutinywallet/mutiny-wasm";
import { For, Show, createResource, createSignal } from "solid-js"; import { For, Show, createResource, createSignal } from "solid-js";
import { QRCodeSVG } from "solid-qr-code"; import { QRCodeSVG } from "solid-qr-code";
import { KeyValue, MiniStringShower } from "~/components/DetailsModal";
import { InfoBox } from "~/components/InfoBox";
import NavBar from "~/components/NavBar";
import { ShareCard } from "~/components/ShareCard";
import { import {
KeyValue,
MiniStringShower,
InfoBox,
NavBar,
ShareCard,
Button, Button,
Collapser, Collapser,
DefaultMain, DefaultMain,
@@ -15,10 +16,10 @@ import {
SafeArea, SafeArea,
SettingsCard, SettingsCard,
SimpleDialog, SimpleDialog,
VStack VStack,
} from "~/components/layout"; BackLink,
import { BackLink } from "~/components/layout/BackLink"; TextField
import { TextField } from "~/components/layout/TextField"; } from "~/components";
import { useMegaStore } from "~/state/megaStore"; import { useMegaStore } from "~/state/megaStore";
import eify from "~/utils/eify"; import eify from "~/utils/eify";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";

View File

@@ -1,17 +1,17 @@
import { DeleteEverything } from "~/components/DeleteEverything";
import { ImportExport } from "~/components/ImportExport";
import { LoadingIndicator } from "~/components/LoadingIndicator";
import { Logs } from "~/components/Logs";
import NavBar from "~/components/NavBar";
import { import {
DeleteEverything,
ImportExport,
LoadingIndicator,
Logs,
NavBar,
DefaultMain, DefaultMain,
LargeHeader, LargeHeader,
NiceP, NiceP,
SafeArea, SafeArea,
SmallHeader, SmallHeader,
VStack VStack,
} from "~/components/layout"; BackLink
import { BackLink } from "~/components/layout/BackLink"; } from "~/components";
import { ExternalLink } from "@mutinywallet/ui"; import { ExternalLink } from "@mutinywallet/ui";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";

View File

@@ -6,17 +6,17 @@ import {
NiceP, NiceP,
MutinyWalletGuard, MutinyWalletGuard,
SafeArea, SafeArea,
VStack VStack,
} from "~/components/layout"; NavBar,
import NavBar from "~/components/NavBar"; BackLink,
TextField,
InfoBox
} from "~/components";
import { useMegaStore } from "~/state/megaStore"; import { useMegaStore } from "~/state/megaStore";
import { Show, createSignal } from "solid-js"; import { Show, createSignal } from "solid-js";
import { BackLink } from "~/components/layout/BackLink";
import { createForm } from "@modular-forms/solid"; import { createForm } from "@modular-forms/solid";
import { TextField } from "~/components/layout/TextField";
import { timeout } from "~/utils/timeout"; import { timeout } from "~/utils/timeout";
import eify from "~/utils/eify"; import eify from "~/utils/eify";
import { InfoBox } from "~/components/InfoBox";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";
type EncryptPasswordForm = { type EncryptPasswordForm = {

View File

@@ -1,15 +1,15 @@
import { TextField } from "@kobalte/core"; import { TextField } from "@kobalte/core";
import { createSignal } from "solid-js"; import { createSignal } from "solid-js";
import NavBar from "~/components/NavBar";
import { import {
Button, Button,
NavBar,
DefaultMain, DefaultMain,
InnerCard, InnerCard,
LargeHeader, LargeHeader,
MutinyWalletGuard, MutinyWalletGuard,
SafeArea SafeArea,
} from "~/components/layout"; BackLink
import { BackLink } from "~/components/layout/BackLink"; } from "~/components";
import { useMegaStore } from "~/state/megaStore"; import { useMegaStore } from "~/state/megaStore";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";

View File

@@ -7,10 +7,10 @@ import {
createSignal createSignal
} from "solid-js"; } from "solid-js";
import { A } from "solid-start"; import { A } from "solid-start";
import { ConfirmDialog } from "~/components/Dialog";
import { InfoBox } from "~/components/InfoBox";
import NavBar from "~/components/NavBar";
import { import {
ConfirmDialog,
InfoBox,
NavBar,
Button, Button,
DefaultMain, DefaultMain,
FancyCard, FancyCard,
@@ -19,13 +19,13 @@ import {
NiceP, NiceP,
SafeArea, SafeArea,
TinyText, TinyText,
VStack VStack,
} from "~/components/layout"; BackLink,
import { BackLink } from "~/components/layout/BackLink"; LoadingShimmer
} from "~/components";
import { useMegaStore } from "~/state/megaStore"; import { useMegaStore } from "~/state/megaStore";
import eify from "~/utils/eify"; import eify from "~/utils/eify";
import party from "~/assets/party.gif"; import party from "~/assets/party.gif";
import { LoadingShimmer } from "~/components/BalanceBox";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";
import { subscriptionValid } from "~/utils/subscriptions"; import { subscriptionValid } from "~/utils/subscriptions";

View File

@@ -4,10 +4,14 @@ import {
LargeHeader, LargeHeader,
NiceP, NiceP,
SafeArea, SafeArea,
VStack VStack,
} from "~/components/layout"; BackLink,
import { BackLink } from "~/components/layout/BackLink"; NavBar,
import NavBar from "~/components/NavBar"; TextFieldProps,
showToast,
ConfirmDialog,
InfoBox
} from "~/components";
import { useMegaStore } from "~/state/megaStore"; import { useMegaStore } from "~/state/megaStore";
import { For, Show, createSignal, splitProps } from "solid-js"; import { For, Show, createSignal, splitProps } from "solid-js";
import pasteIcon from "~/assets/icons/paste.svg"; import pasteIcon from "~/assets/icons/paste.svg";
@@ -20,13 +24,9 @@ import {
validate validate
} from "@modular-forms/solid"; } from "@modular-forms/solid";
import { TextField as KTextField } from "@kobalte/core"; import { TextField as KTextField } from "@kobalte/core";
import { TextFieldProps } from "~/components/layout/TextField";
import { showToast } from "~/components/Toaster";
import eify from "~/utils/eify"; import eify from "~/utils/eify";
import { ConfirmDialog } from "~/components/Dialog";
import { MutinyWallet } from "@mutinywallet/mutiny-wasm"; import { MutinyWallet } from "@mutinywallet/mutiny-wasm";
import { WORDS_EN } from "~/utils/words"; import { WORDS_EN } from "~/utils/words";
import { InfoBox } from "~/components/InfoBox";
import { Clipboard } from "@capacitor/clipboard"; import { Clipboard } from "@capacitor/clipboard";
import { Capacitor } from "@capacitor/core"; import { Capacitor } from "@capacitor/core";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";

View File

@@ -1,5 +1,4 @@
import { createForm, url } from "@modular-forms/solid"; import { createForm, url } from "@modular-forms/solid";
import { TextField } from "~/components/layout/TextField";
import { import {
MutinyWalletSettingStrings, MutinyWalletSettingStrings,
setSettings setSettings
@@ -8,16 +7,17 @@ import {
Button, Button,
Card, Card,
DefaultMain, DefaultMain,
TextField,
LargeHeader, LargeHeader,
MutinyWalletGuard, MutinyWalletGuard,
NiceP, NiceP,
SafeArea SafeArea,
} from "~/components/layout"; showToast,
import { showToast } from "~/components/Toaster"; BackLink,
NavBar
} from "~/components";
import eify from "~/utils/eify"; import eify from "~/utils/eify";
import { ExternalLink } from "@mutinywallet/ui"; import { ExternalLink } from "@mutinywallet/ui";
import { BackLink } from "~/components/layout/BackLink";
import NavBar from "~/components/NavBar";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";
import { useMegaStore } from "~/state/megaStore"; import { useMegaStore } from "~/state/megaStore";

View File

@@ -3,10 +3,10 @@ import {
LargeHeader, LargeHeader,
SafeArea, SafeArea,
SettingsCard, SettingsCard,
VStack VStack,
} from "~/components/layout"; BackLink,
import { BackLink } from "~/components/layout/BackLink"; NavBar
import NavBar from "~/components/NavBar"; } from "~/components";
import { A } from "solid-start"; import { A } from "solid-start";
import { For, Show } from "solid-js"; import { For, Show } from "solid-js";
import forward from "~/assets/icons/forward.svg"; import forward from "~/assets/icons/forward.svg";