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

Commit 8d1b9ff

Browse files
committed
Optional node manager
1 parent 44b86e6 commit 8d1b9ff

File tree

5 files changed

+181
-69
lines changed

5 files changed

+181
-69
lines changed

src/components/BalanceBox.tsx

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

512
import {
613
AmountFiat,
@@ -11,6 +18,7 @@ import {
1118
InfoBox,
1219
MediumHeader,
1320
NiceP,
21+
SubtleButton,
1422
VStack
1523
} from "~/components";
1624
import { useI18n } from "~/i18n/context";
@@ -48,11 +56,44 @@ export function BalanceBox(props: { loading?: boolean; small?: boolean }) {
4856
const navigate = useNavigate();
4957
const i18n = useI18n();
5058

59+
const [nodeManagerLoading, setNodeManagerLoading] = createSignal(false);
60+
61+
const lightningBalance = () => state.balance?.lightning || 0n;
62+
5163
const totalOnchain = () =>
5264
(state.balance?.confirmed || 0n) +
5365
(state.balance?.unconfirmed || 0n) +
5466
(state.balance?.force_close || 0n);
5567

68+
const [hasSelfCustody, { refetch }] = createResource(async () => {
69+
// short circuit if we have a balance
70+
if (totalOnchain() > 0 || state.balance?.lightning || 0n > 0n) {
71+
return true;
72+
}
73+
74+
// otherwise check if we have created a node
75+
const nodes: string[] = await state.mutiny_wallet?.list_nodes();
76+
return nodes.length > 0;
77+
});
78+
79+
const createNodeManager = async () => {
80+
if (confirm("Pass this test:")) {
81+
setNodeManagerLoading(true);
82+
await state.mutiny_wallet?.create_node_manager_if_needed();
83+
await refetch();
84+
setNodeManagerLoading(false);
85+
}
86+
};
87+
88+
const removeNodeManager = async () => {
89+
if (confirm("Are you sure:")) {
90+
setNodeManagerLoading(true);
91+
await state.mutiny_wallet?.remove_node_manager();
92+
await refetch();
93+
setNodeManagerLoading(false);
94+
}
95+
};
96+
5697
return (
5798
<VStack>
5899
<Switch>
@@ -108,70 +149,115 @@ export function BalanceBox(props: { loading?: boolean; small?: boolean }) {
108149
</Match>
109150
</Switch>
110151
<MediumHeader>{i18n.t("profile.self_custody")}</MediumHeader>
111-
<FancyCard>
112-
<Show when={!props.loading} fallback={<LoadingShimmer />}>
113-
<Switch>
114-
<Match when={state.safe_mode}>
115-
<div class="flex flex-col gap-1">
116-
<InfoBox accent="red">
117-
{i18n.t("common.error_safe_mode")}
118-
</InfoBox>
119-
</div>
120-
</Match>
121-
<Match when={true}>
122-
<div class="flex flex-col gap-1">
123-
<div class="text-2xl">
124-
<AmountSats
125-
amountSats={
126-
state.balance?.lightning || 0
127-
}
128-
icon="lightning"
129-
denominationSize="lg"
130-
/>
131-
</div>
132-
<div class="text-lg text-white/70">
133-
<AmountFiat
134-
amountSats={
135-
state.balance?.lightning || 0
136-
}
137-
denominationSize="sm"
138-
/>
139-
</div>
140-
</div>
141-
</Match>
142-
</Switch>
143-
</Show>
144-
<hr class="my-2 border-m-grey-750" />
145-
<Show when={!props.loading} fallback={<LoadingShimmer />}>
146-
<div class="flex justify-between">
147-
<div class="flex flex-col gap-1">
148-
<div class="text-2xl">
149-
<AmountSats
150-
amountSats={totalOnchain()}
151-
icon="chain"
152-
denominationSize="lg"
153-
/>
154-
</div>
155-
<div class="text-lg text-white/70">
156-
<AmountFiat
157-
amountSats={totalOnchain()}
158-
denominationSize="sm"
159-
/>
160-
</div>
161-
</div>
162-
<div class="flex flex-col items-end justify-between gap-1">
163-
<Show when={state.balance?.unconfirmed != 0n}>
164-
<Indicator>
165-
{i18n.t("common.pending")}
166-
</Indicator>
152+
<Suspense>
153+
<Switch>
154+
<Match when={hasSelfCustody()}>
155+
<FancyCard>
156+
<Show
157+
when={!props.loading}
158+
fallback={<LoadingShimmer />}
159+
>
160+
<Switch>
161+
<Match when={state.safe_mode}>
162+
<div class="flex flex-col gap-1">
163+
<InfoBox accent="red">
164+
{i18n.t(
165+
"common.error_safe_mode"
166+
)}
167+
</InfoBox>
168+
</div>
169+
</Match>
170+
<Match when={true}>
171+
<div class="flex flex-col gap-1">
172+
<div class="text-2xl">
173+
<AmountSats
174+
amountSats={lightningBalance()}
175+
icon="lightning"
176+
denominationSize="lg"
177+
/>
178+
</div>
179+
<div class="text-lg text-white/70">
180+
<AmountFiat
181+
amountSats={
182+
state.balance
183+
?.lightning || 0
184+
}
185+
denominationSize="sm"
186+
/>
187+
</div>
188+
</div>
189+
</Match>
190+
</Switch>
167191
</Show>
168-
<Show when={state.balance?.unconfirmed === 0n}>
169-
<div />
192+
<hr class="my-2 border-m-grey-750" />
193+
<Show
194+
when={!props.loading}
195+
fallback={<LoadingShimmer />}
196+
>
197+
<div class="flex justify-between">
198+
<div class="flex flex-col gap-1">
199+
<div class="text-2xl">
200+
<AmountSats
201+
amountSats={totalOnchain()}
202+
icon="chain"
203+
denominationSize="lg"
204+
/>
205+
</div>
206+
<div class="text-lg text-white/70">
207+
<AmountFiat
208+
amountSats={totalOnchain()}
209+
denominationSize="sm"
210+
/>
211+
</div>
212+
</div>
213+
<div class="flex flex-col items-end justify-between gap-1">
214+
<Show
215+
when={
216+
state.balance?.unconfirmed != 0n
217+
}
218+
>
219+
<Indicator>
220+
{i18n.t("common.pending")}
221+
</Indicator>
222+
</Show>
223+
<Show
224+
when={
225+
state.balance?.unconfirmed ===
226+
0n
227+
}
228+
>
229+
<div />
230+
</Show>
231+
</div>
232+
</div>
233+
<Show
234+
when={
235+
totalOnchain() === 0n &&
236+
lightningBalance() === 0n &&
237+
state.federations &&
238+
state.federations.length
239+
}
240+
>
241+
<SubtleButton
242+
onClick={removeNodeManager}
243+
loading={nodeManagerLoading()}
244+
>
245+
<Trash class="h-4 w-4" />
246+
</SubtleButton>
247+
</Show>
170248
</Show>
171-
</div>
172-
</div>
173-
</Show>
174-
</FancyCard>
249+
</FancyCard>
250+
</Match>
251+
<Match when={true}>
252+
<SubtleButton
253+
onClick={createNodeManager}
254+
loading={nodeManagerLoading()}
255+
>
256+
<Plus class="h-4 w-4" />
257+
</SubtleButton>
258+
</Match>
259+
</Switch>
260+
</Suspense>
175261
</VStack>
176262
);
177263
}

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 state.mutiny_wallet?.create_node_manager_if_needed();
73+
7174
await state.mutiny_wallet?.change_lsp(
7275
values.lsp ? values.lsp : undefined,
7376
values.lsps_connection_string

src/routes/Send.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { MutinyInvoice, TagItem } from "@mutinywallet/mutiny-wasm";
2-
import { useLocation, useNavigate, useSearchParams } from "@solidjs/router";
2+
import {
3+
createAsync,
4+
useLocation,
5+
useNavigate,
6+
useSearchParams
7+
} from "@solidjs/router";
38
import { Eye, EyeOff, Link, X, Zap } from "lucide-solid";
49
import {
510
createEffect,
@@ -300,7 +305,7 @@ export function Send() {
300305
});
301306

302307
// Rerun every time the amount changes if we're onchain
303-
const feeEstimate = createMemo(() => {
308+
const feeEstimate = createAsync(async () => {
304309
if (
305310
source() === "onchain" &&
306311
amountSats() &&
@@ -310,12 +315,12 @@ export function Send() {
310315
try {
311316
// If max we want to use the sweep fee estimator
312317
if (isMax()) {
313-
return state.mutiny_wallet?.estimate_sweep_tx_fee(
318+
return await state.mutiny_wallet?.estimate_sweep_tx_fee(
314319
address()!
315320
);
316321
}
317322

318-
return state.mutiny_wallet?.estimate_tx_fee(
323+
return await state.mutiny_wallet?.estimate_tx_fee(
319324
address()!,
320325
amountSats(),
321326
undefined

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] = useMegaStore();
1820

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

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

src/state/megaStore.tsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,21 @@ export const Provider: ParentComponent = (props) => {
312312
},
313313
60 * 1000 * state.price_sync_backoff_multiple
314314
); // Poll every minute * backoff multiple
315+
316+
// handle if it is an empty wallet (we have no federations or nodes), take them to the add federation page.
317+
// This will either force them to pick a federation or create a node manager.
318+
const nodes: string[] = await state.mutiny_wallet.list_nodes();
319+
const numFederations = state.federations
320+
? state.federations.length
321+
: 0;
322+
323+
if (nodes.length === 0 && numFederations === 0) {
324+
navigate("/addfederation");
325+
} else {
326+
console.log("nodes", nodes);
327+
console.log("federations", numFederations);
328+
navigate("/");
329+
}
315330
},
316331
async deleteMutinyWallet(): Promise<void> {
317332
try {
@@ -571,7 +586,7 @@ export const Provider: ParentComponent = (props) => {
571586
await actions.preSetup();
572587

573588
setState({ load_stage: "checking_for_existing_wallet" });
574-
const existing = await MutinyWallet.has_node_manager();
589+
const existing = await MutinyWallet.is_wallet_present();
575590

576591
if (!existing && !params.skip_setup) {
577592
navigate("/setup");

0 commit comments

Comments
 (0)