Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optional node manager #1068

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading