Skip to content

Commit

Permalink
Optional node manager
Browse files Browse the repository at this point in the history
  • Loading branch information
benthecarman committed Jun 13, 2024
1 parent f5c0a83 commit 24195ba
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 81 deletions.
234 changes: 159 additions & 75 deletions src/components/BalanceBox.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import { A, useNavigate } from "@solidjs/router";
import { Shuffle, Users } from "lucide-solid";
import { createMemo, Match, Show, Suspense, Switch } from "solid-js";
import { Plus, Shuffle, Trash, Users } from "lucide-solid";
import {
createMemo,
createResource,
createSignal,
Match,
Show,
Suspense,
Switch
} from "solid-js";

import {
AmountFiat,
Expand All @@ -11,6 +19,7 @@ import {
InfoBox,
MediumHeader,
NiceP,
SubtleButton,
VStack
} from "~/components";
import { useI18n } from "~/i18n/context";
Expand Down Expand Up @@ -47,10 +56,14 @@ const STYLE =
"px-2 py-1 rounded-xl text-sm flex gap-2 items-center font-semibold";

export function BalanceBox(props: { loading?: boolean; small?: boolean }) {
const [state, _actions] = useMegaStore();
const [state, _actions, sw] = useMegaStore();
const navigate = useNavigate();
const i18n = useI18n();

const [nodeManagerLoading, setNodeManagerLoading] = createSignal(false);

const lightningBalance = () => state.balance?.lightning || 0n;

const totalOnchain = createMemo(
() =>
(state.balance?.confirmed || 0n) +
Expand All @@ -64,6 +77,35 @@ export function BalanceBox(props: { loading?: boolean; small?: boolean }) {
(state.balance?.unconfirmed || 0n)
);

const [hasSelfCustody, { refetch }] = createResource(async () => {
// short circuit if we have a balance
if (totalOnchain() > 0 || state.balance?.lightning || 0n > 0n) {
return true;
}

// otherwise check if we have created a node
const nodes: string[] = await sw.list_nodes();
return nodes.length > 0;
});

const createNodeManager = async () => {
if (confirm("Pass this test:")) {
setNodeManagerLoading(true);
await sw.create_node_manager_if_needed();
await refetch();
setNodeManagerLoading(false);
}
};

const removeNodeManager = async () => {
if (confirm("Are you sure:")) {
setNodeManagerLoading(true);
await sw.remove_node_manager();
await refetch();
setNodeManagerLoading(false);
}
};

return (
<VStack>
<Switch>
Expand Down Expand Up @@ -131,81 +173,123 @@ export function BalanceBox(props: { loading?: boolean; small?: boolean }) {
</Match>
</Switch>
<MediumHeader>{i18n.t("profile.self_custody")}</MediumHeader>
<FancyCard>
<Show when={!props.loading} fallback={<LoadingShimmer />}>
<Switch>
<Match when={state.safe_mode}>
<div class="flex flex-col gap-1">
<InfoBox accent="red">
{i18n.t("common.error_safe_mode")}
</InfoBox>
</div>
</Match>
<Match when={true}>
<div class="flex flex-col gap-1">
<div class="text-2xl">
<AmountSats
amountSats={
state.balance?.lightning || 0
}
icon="lightning"
denominationSize="lg"
/>
</div>
<div class="text-lg text-white/70">
<Suspense>
<AmountFiat
amountSats={
state.balance?.lightning || 0
}
denominationSize="sm"
/>
</Suspense>
</div>
</div>
</Match>
</Switch>
</Show>
<hr class="my-2 border-m-grey-750" />
<Show when={!props.loading} fallback={<LoadingShimmer />}>
<div class="flex justify-between">
<div class="flex flex-col gap-1">
<div class="text-2xl">
<AmountSats
amountSats={totalOnchain()}
icon="chain"
denominationSize="lg"
/>
</div>
<div class="text-lg text-white/70">
<Suspense>
<AmountFiat
amountSats={totalOnchain()}
denominationSize="sm"
/>
</Suspense>
</div>
</div>
<div class="flex flex-col items-end justify-between gap-1">
<Show when={state.balance?.unconfirmed != 0n}>
<Indicator>
{i18n.t("common.pending")}
</Indicator>
</Show>
<Show when={state.balance?.unconfirmed === 0n}>
<div />
<Suspense>
<Switch>
<Match when={hasSelfCustody()}>
<FancyCard>
<Show
when={!props.loading}
fallback={<LoadingShimmer />}
>
<Switch>
<Match when={state.safe_mode}>
<div class="flex flex-col gap-1">
<InfoBox accent="red">
{i18n.t(
"common.error_safe_mode"
)}
</InfoBox>
</div>
</Match>
<Match when={true}>
<div class="flex flex-col gap-1">
<div class="text-2xl">
<AmountSats
amountSats={lightningBalance()}
icon="lightning"
denominationSize="lg"
/>
</div>
<div class="text-lg text-white/70">
<Suspense>
<AmountFiat
amountSats={lightningBalance()}
denominationSize="sm"
/>
</Suspense>
</div>
</div>
</Match>
</Switch>
</Show>
<Show when={usableOnchain() > 0n}>
<div class="self-end justify-self-end">
<A href="/swap" class={STYLE}>
<Shuffle class="h-6 w-6" />
</A>
<hr class="my-2 border-m-grey-750" />
<Show
when={!props.loading}
fallback={<LoadingShimmer />}
>
<div class="flex justify-between">
<div class="flex flex-col gap-1">
<div class="text-2xl">
<AmountSats
amountSats={totalOnchain()}
icon="chain"
denominationSize="lg"
/>
</div>
<div class="text-lg text-white/70">
<Suspense>
<AmountFiat
amountSats={totalOnchain()}
denominationSize="sm"
/>
</Suspense>
</div>
</div>
<div class="flex flex-col items-end justify-between gap-1">
<Show
when={
state.balance?.unconfirmed != 0n
}
>
<Indicator>
{i18n.t("common.pending")}
</Indicator>
</Show>
<Show
when={
state.balance?.unconfirmed ===
0n
}
>
<div />
</Show>
<Show when={usableOnchain() > 0n}>
<div class="self-end justify-self-end">
<A href="/swap" class={STYLE}>
<Shuffle class="h-6 w-6" />
</A>
</div>
</Show>
</div>
</div>
<Show
when={
totalOnchain() === 0n &&
lightningBalance() === 0n &&
state.federations &&
state.federations.length
}
>
<SubtleButton
onClick={removeNodeManager}
loading={nodeManagerLoading()}
>
<Trash class="h-4 w-4" />
</SubtleButton>
</Show>
</Show>
</div>
</div>
</Show>
</FancyCard>
</FancyCard>
</Match>
<Match when={true}>
<SubtleButton
onClick={createNodeManager}
loading={nodeManagerLoading()}
>
<Plus class="h-4 w-4" />
</SubtleButton>
</Match>
</Switch>
</Suspense>
</VStack>
);
}
3 changes: 3 additions & 0 deletions src/components/HomePrompt.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ export function HomePrompt() {
lsps_token: params.token
};
try {
// If we're setting an LSPS config, we want a node manager
await sw.create_node_manager_if_needed();

await sw.change_lsp(
values.lsp ? values.lsp : undefined,
values.lsps_connection_string
Expand Down
2 changes: 2 additions & 0 deletions src/routes/SwapLightning.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ export function SwapLightning() {
setLoading(true);
setFeeEstimateWarning(undefined);

await sw.create_node_manager_if_needed();

const mutinyInvoice = await sw.create_sweep_federation_invoice(
isMax() ? undefined : amountSats()
);
Expand Down
19 changes: 16 additions & 3 deletions src/routes/setup/AddFederation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,33 @@ import {
ConfirmDialog,
DefaultMain,
ExternalLink,
MutinyWalletGuard
MutinyWalletGuard,
showToast
} from "~/components";
import { useI18n } from "~/i18n/context";
import { useMegaStore } from "~/state/megaStore";
import { eify } from "~/utils";

import { AddFederationForm } from "../settings";

export function AddFederation() {
const i18n = useI18n();
const navigate = useNavigate();
const [_state, _actions, sw] = useMegaStore();

const [confirmOpen, setConfirmOpen] = createSignal(false);
const [confirmLoading, setConfirmLoading] = createSignal(false);

async function handleSkip() {
navigate("/");
setConfirmLoading(true);
try {
await sw.create_node_manager_if_needed();
navigate("/");
} catch (e) {
console.error(e);
setConfirmLoading(false);
showToast(eify(e));
}
}

return (
Expand All @@ -41,7 +54,7 @@ export function AddFederation() {
</Button>
<ConfirmDialog
open={confirmOpen()}
loading={false}
loading={confirmLoading()}
onConfirm={handleSkip}
onCancel={() => setConfirmOpen(false)}
>
Expand Down
13 changes: 12 additions & 1 deletion src/state/megaStore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ export const makeMegaStoreContext = () => {
await sw.initializeWasm();

setState({ load_stage: "checking_for_existing_wallet" });
const existing = await sw.has_node_manager();
const existing = await sw.is_wallet_present();

if (!existing && !searchParams.skip_setup) {
navigate("/setup");
Expand Down Expand Up @@ -379,6 +379,17 @@ export const makeMegaStoreContext = () => {
},
60 * 1000 * state.price_sync_backoff_multiple
); // Poll every minute * backoff multiple

// handle if it is an empty wallet (we have no federations or nodes), take them to the add federation page.
// This will either force them to pick a federation or create a node manager.
const nodes: string[] = await sw.list_nodes();
const numFederations = state.federations
? state.federations.length
: 0;

if (nodes.length === 0 && numFederations === 0) {
navigate("/addfederation");
}
},
async deleteMutinyWallet(): Promise<void> {
try {
Expand Down
Loading

0 comments on commit 24195ba

Please sign in to comment.