Skip to content
This repository was archived by the owner on Apr 21, 2025. It is now read-only.

Commit 722f190

Browse files
committed
Optional node manager
1 parent 0da0dce commit 722f190

File tree

5 files changed

+196
-78
lines changed

5 files changed

+196
-78
lines changed

src/components/BalanceBox.tsx

Lines changed: 159 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
import { A, useNavigate } from "@solidjs/router";
2-
import { Shuffle, Users } from "lucide-solid";
3-
import { createMemo, Match, Show, Suspense, Switch } from "solid-js";
2+
import { Plus, Shuffle, Trash, Users } from "lucide-solid";
3+
import {
4+
createMemo,
5+
createResource,
6+
createSignal,
7+
Match,
8+
Show,
9+
Suspense,
10+
Switch
11+
} from "solid-js";
412

513
import {
614
AmountFiat,
@@ -11,6 +19,7 @@ import {
1119
InfoBox,
1220
MediumHeader,
1321
NiceP,
22+
SubtleButton,
1423
VStack
1524
} from "~/components";
1625
import { useI18n } from "~/i18n/context";
@@ -47,10 +56,14 @@ const STYLE =
4756
"px-2 py-1 rounded-xl text-sm flex gap-2 items-center font-semibold";
4857

4958
export function BalanceBox(props: { loading?: boolean; small?: boolean }) {
50-
const [state, _actions] = useMegaStore();
59+
const [state, _actions, sw] = useMegaStore();
5160
const navigate = useNavigate();
5261
const i18n = useI18n();
5362

63+
const [nodeManagerLoading, setNodeManagerLoading] = createSignal(false);
64+
65+
const lightningBalance = () => state.balance?.lightning || 0n;
66+
5467
const totalOnchain = createMemo(
5568
() =>
5669
(state.balance?.confirmed || 0n) +
@@ -64,6 +77,35 @@ export function BalanceBox(props: { loading?: boolean; small?: boolean }) {
6477
(state.balance?.unconfirmed || 0n)
6578
);
6679

80+
const [hasSelfCustody, { refetch }] = createResource(async () => {
81+
// short circuit if we have a balance
82+
if (totalOnchain() > 0 || state.balance?.lightning || 0n > 0n) {
83+
return true;
84+
}
85+
86+
// otherwise check if we have created a node
87+
const nodes: string[] = await sw.list_nodes();
88+
return nodes.length > 0;
89+
});
90+
91+
const createNodeManager = async () => {
92+
if (confirm("Pass this test:")) {
93+
setNodeManagerLoading(true);
94+
await sw.create_node_manager_if_needed();
95+
await refetch();
96+
setNodeManagerLoading(false);
97+
}
98+
};
99+
100+
const removeNodeManager = async () => {
101+
if (confirm("Are you sure:")) {
102+
setNodeManagerLoading(true);
103+
await sw.remove_node_manager();
104+
await refetch();
105+
setNodeManagerLoading(false);
106+
}
107+
};
108+
67109
return (
68110
<VStack>
69111
<Switch>
@@ -131,81 +173,123 @@ export function BalanceBox(props: { loading?: boolean; small?: boolean }) {
131173
</Match>
132174
</Switch>
133175
<MediumHeader>{i18n.t("profile.self_custody")}</MediumHeader>
134-
<FancyCard>
135-
<Show when={!props.loading} fallback={<LoadingShimmer />}>
136-
<Switch>
137-
<Match when={state.safe_mode}>
138-
<div class="flex flex-col gap-1">
139-
<InfoBox accent="red">
140-
{i18n.t("common.error_safe_mode")}
141-
</InfoBox>
142-
</div>
143-
</Match>
144-
<Match when={true}>
145-
<div class="flex flex-col gap-1">
146-
<div class="text-2xl">
147-
<AmountSats
148-
amountSats={
149-
state.balance?.lightning || 0
150-
}
151-
icon="lightning"
152-
denominationSize="lg"
153-
/>
154-
</div>
155-
<div class="text-lg text-white/70">
156-
<Suspense>
157-
<AmountFiat
158-
amountSats={
159-
state.balance?.lightning || 0
160-
}
161-
denominationSize="sm"
162-
/>
163-
</Suspense>
164-
</div>
165-
</div>
166-
</Match>
167-
</Switch>
168-
</Show>
169-
<hr class="my-2 border-m-grey-750" />
170-
<Show when={!props.loading} fallback={<LoadingShimmer />}>
171-
<div class="flex justify-between">
172-
<div class="flex flex-col gap-1">
173-
<div class="text-2xl">
174-
<AmountSats
175-
amountSats={totalOnchain()}
176-
icon="chain"
177-
denominationSize="lg"
178-
/>
179-
</div>
180-
<div class="text-lg text-white/70">
181-
<Suspense>
182-
<AmountFiat
183-
amountSats={totalOnchain()}
184-
denominationSize="sm"
185-
/>
186-
</Suspense>
187-
</div>
188-
</div>
189-
<div class="flex flex-col items-end justify-between gap-1">
190-
<Show when={state.balance?.unconfirmed != 0n}>
191-
<Indicator>
192-
{i18n.t("common.pending")}
193-
</Indicator>
194-
</Show>
195-
<Show when={state.balance?.unconfirmed === 0n}>
196-
<div />
176+
<Suspense>
177+
<Switch>
178+
<Match when={hasSelfCustody()}>
179+
<FancyCard>
180+
<Show
181+
when={!props.loading}
182+
fallback={<LoadingShimmer />}
183+
>
184+
<Switch>
185+
<Match when={state.safe_mode}>
186+
<div class="flex flex-col gap-1">
187+
<InfoBox accent="red">
188+
{i18n.t(
189+
"common.error_safe_mode"
190+
)}
191+
</InfoBox>
192+
</div>
193+
</Match>
194+
<Match when={true}>
195+
<div class="flex flex-col gap-1">
196+
<div class="text-2xl">
197+
<AmountSats
198+
amountSats={lightningBalance()}
199+
icon="lightning"
200+
denominationSize="lg"
201+
/>
202+
</div>
203+
<div class="text-lg text-white/70">
204+
<Suspense>
205+
<AmountFiat
206+
amountSats={lightningBalance()}
207+
denominationSize="sm"
208+
/>
209+
</Suspense>
210+
</div>
211+
</div>
212+
</Match>
213+
</Switch>
197214
</Show>
198-
<Show when={usableOnchain() > 0n}>
199-
<div class="self-end justify-self-end">
200-
<A href="/swap" class={STYLE}>
201-
<Shuffle class="h-6 w-6" />
202-
</A>
215+
<hr class="my-2 border-m-grey-750" />
216+
<Show
217+
when={!props.loading}
218+
fallback={<LoadingShimmer />}
219+
>
220+
<div class="flex justify-between">
221+
<div class="flex flex-col gap-1">
222+
<div class="text-2xl">
223+
<AmountSats
224+
amountSats={totalOnchain()}
225+
icon="chain"
226+
denominationSize="lg"
227+
/>
228+
</div>
229+
<div class="text-lg text-white/70">
230+
<Suspense>
231+
<AmountFiat
232+
amountSats={totalOnchain()}
233+
denominationSize="sm"
234+
/>
235+
</Suspense>
236+
</div>
237+
</div>
238+
<div class="flex flex-col items-end justify-between gap-1">
239+
<Show
240+
when={
241+
state.balance?.unconfirmed != 0n
242+
}
243+
>
244+
<Indicator>
245+
{i18n.t("common.pending")}
246+
</Indicator>
247+
</Show>
248+
<Show
249+
when={
250+
state.balance?.unconfirmed ===
251+
0n
252+
}
253+
>
254+
<div />
255+
</Show>
256+
<Show when={usableOnchain() > 0n}>
257+
<div class="self-end justify-self-end">
258+
<A href="/swap" class={STYLE}>
259+
<Shuffle class="h-6 w-6" />
260+
</A>
261+
</div>
262+
</Show>
263+
</div>
203264
</div>
265+
<Show
266+
when={
267+
totalOnchain() === 0n &&
268+
lightningBalance() === 0n &&
269+
state.federations &&
270+
state.federations.length
271+
}
272+
>
273+
<SubtleButton
274+
onClick={removeNodeManager}
275+
loading={nodeManagerLoading()}
276+
>
277+
<Trash class="h-4 w-4" />
278+
</SubtleButton>
279+
</Show>
204280
</Show>
205-
</div>
206-
</div>
207-
</Show>
208-
</FancyCard>
281+
</FancyCard>
282+
</Match>
283+
<Match when={true}>
284+
<SubtleButton
285+
onClick={createNodeManager}
286+
loading={nodeManagerLoading()}
287+
>
288+
<Plus class="h-4 w-4" />
289+
</SubtleButton>
290+
</Match>
291+
</Switch>
292+
</Suspense>
209293
</VStack>
210294
);
211295
}

src/components/HomePrompt.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ export function HomePrompt() {
6868
lsps_token: params.token
6969
};
7070
try {
71+
// If we're setting an LSPS config, we want a node manager
72+
await sw.create_node_manager_if_needed();
73+
7174
await sw.change_lsp(
7275
values.lsp ? values.lsp : undefined,
7376
values.lsps_connection_string

src/routes/setup/AddFederation.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,19 @@ import {
99
MutinyWalletGuard
1010
} from "~/components";
1111
import { useI18n } from "~/i18n/context";
12+
import { useMegaStore } from "~/state/megaStore";
1213

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

1516
export function AddFederation() {
1617
const i18n = useI18n();
1718
const navigate = useNavigate();
19+
const [_state, _actions, sw] = useMegaStore();
1820

1921
const [confirmOpen, setConfirmOpen] = createSignal(false);
2022

2123
async function handleSkip() {
24+
await sw.create_node_manager_if_needed();
2225
navigate("/");
2326
}
2427

src/state/megaStore.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ export const makeMegaStoreContext = () => {
149149
await sw.initializeWasm();
150150

151151
setState({ load_stage: "checking_for_existing_wallet" });
152-
const existing = await sw.has_node_manager();
152+
const existing = await sw.is_wallet_present();
153153

154154
if (!existing && !searchParams.skip_setup) {
155155
navigate("/setup");
@@ -337,6 +337,17 @@ export const makeMegaStoreContext = () => {
337337
},
338338
60 * 1000 * state.price_sync_backoff_multiple
339339
); // Poll every minute * backoff multiple
340+
341+
// handle if it is an empty wallet (we have no federations or nodes), take them to the add federation page.
342+
// This will either force them to pick a federation or create a node manager.
343+
const nodes: string[] = await sw.list_nodes();
344+
const numFederations = state.federations
345+
? state.federations.length
346+
: 0;
347+
348+
if (nodes.length === 0 && numFederations === 0) {
349+
navigate("/addfederation");
350+
}
340351
},
341352
async deleteMutinyWallet(): Promise<void> {
342353
try {

src/workers/walletWorker.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,14 @@ export async function stop(): Promise<void> {
227227
await wallet!.stop();
228228
}
229229

230+
/**
231+
* Removes the node manager from the wallet. If the node manager has any balances, this function will fail.
232+
* @returns {Promise<void>}
233+
*/
234+
export async function remove_node_manager(): Promise<void> {
235+
await wallet!.remove_node_manager();
236+
}
237+
230238
/**
231239
* Clears storage and deletes all data.
232240
*
@@ -546,6 +554,7 @@ export async function estimate_tx_fee(
546554
const fee = await wallet!.estimate_tx_fee(address, amount, feeRate);
547555
return fee;
548556
}
557+
549558
/**
550559
* Calls upon a LNURL to get the parameters for it.
551560
* This contains what kind of LNURL it is (pay, withdrawal, auth, etc).
@@ -1198,6 +1207,14 @@ export async function start(): Promise<void> {
11981207
await wallet!.start();
11991208
}
12001209

1210+
/**
1211+
* Creates a node manager if we don't have one already.
1212+
* @returns {Promise<void>}
1213+
*/
1214+
export async function create_node_manager_if_needed(): Promise<void> {
1215+
await wallet!.create_node_manager_if_needed();
1216+
}
1217+
12011218
/**
12021219
* Authenticates with a LNURL-auth for the given profile.
12031220
* @param {string} lnurl
@@ -1416,8 +1433,8 @@ export async function convert_btc_to_sats(btc: number): Promise<bigint> {
14161433
* This is checked by seeing if a mnemonic seed exists in storage.
14171434
* @returns {Promise<boolean>}
14181435
*/
1419-
export async function has_node_manager(): Promise<boolean> {
1420-
return await MutinyWallet.has_node_manager();
1436+
export async function is_wallet_present(): Promise<boolean> {
1437+
return await MutinyWallet.is_wallet_present();
14211438
}
14221439

14231440
/**

0 commit comments

Comments
 (0)