mirror of
https://github.com/aljazceru/mutiny-web.git
synced 2025-12-19 07:14:22 +01:00
settings save kind of works
This commit is contained in:
@@ -31,6 +31,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@kobalte/core": "^0.8.2",
|
"@kobalte/core": "^0.8.2",
|
||||||
"@kobalte/tailwindcss": "^0.5.0",
|
"@kobalte/tailwindcss": "^0.5.0",
|
||||||
|
"@modular-forms/solid": "^0.12.0",
|
||||||
"@motionone/solid": "^10.16.0",
|
"@motionone/solid": "^10.16.0",
|
||||||
"@mutinywallet/mutiny-wasm": "^0.2.5",
|
"@mutinywallet/mutiny-wasm": "^0.2.5",
|
||||||
"@nostr-dev-kit/ndk": "^0.0.13",
|
"@nostr-dev-kit/ndk": "^0.0.13",
|
||||||
|
|||||||
11
pnpm-lock.yaml
generated
11
pnpm-lock.yaml
generated
@@ -7,6 +7,9 @@ dependencies:
|
|||||||
'@kobalte/tailwindcss':
|
'@kobalte/tailwindcss':
|
||||||
specifier: ^0.5.0
|
specifier: ^0.5.0
|
||||||
version: 0.5.0(tailwindcss@3.3.1)
|
version: 0.5.0(tailwindcss@3.3.1)
|
||||||
|
'@modular-forms/solid':
|
||||||
|
specifier: ^0.12.0
|
||||||
|
version: 0.12.0(solid-js@1.7.3)
|
||||||
'@motionone/solid':
|
'@motionone/solid':
|
||||||
specifier: ^10.16.0
|
specifier: ^10.16.0
|
||||||
version: 10.16.0(solid-js@1.7.3)
|
version: 10.16.0(solid-js@1.7.3)
|
||||||
@@ -1561,6 +1564,14 @@ packages:
|
|||||||
solid-js: 1.7.3
|
solid-js: 1.7.3
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@modular-forms/solid@0.12.0(solid-js@1.7.3):
|
||||||
|
resolution: {integrity: sha512-LJF+jvuJ0oG2vyxFu7MQ2bfIEaBeWFqtw6nc/PifDlufWyW/5mnhUyMxtlyTdAJAAc7A7NX02XvPA+yDCpcTVw==}
|
||||||
|
peerDependencies:
|
||||||
|
solid-js: ^1.3.1
|
||||||
|
dependencies:
|
||||||
|
solid-js: 1.7.3
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@motionone/animation@10.15.1:
|
/@motionone/animation@10.15.1:
|
||||||
resolution: {integrity: sha512-mZcJxLjHor+bhcPuIFErMDNyrdb2vJur8lSfMCsuCB4UyV8ILZLvK+t+pg56erv8ud9xQGK/1OGPt10agPrCyQ==}
|
resolution: {integrity: sha512-mZcJxLjHor+bhcPuIFErMDNyrdb2vJur8lSfMCsuCB4UyV8ILZLvK+t+pg56erv8ud9xQGK/1OGPt10agPrCyQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|||||||
BIN
src/assets/check-spinner.gif
Normal file
BIN
src/assets/check-spinner.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 526 KiB |
@@ -49,10 +49,7 @@ export default function BalanceBox() {
|
|||||||
</Show>
|
</Show>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</FancyCard>
|
</FancyCard>
|
||||||
<div class="flex gap-2 py-4">
|
|
||||||
<ButtonLink href="/send" intent="green">Send</ButtonLink>
|
|
||||||
<ButtonLink href="/receive" intent="blue">Receive</ButtonLink>
|
|
||||||
</div>
|
|
||||||
<FancyCard title="On-Chain" tag={onChainBalance.loading && <SyncingIndicator />}>
|
<FancyCard title="On-Chain" tag={onChainBalance.loading && <SyncingIndicator />}>
|
||||||
<Suspense fallback={<Amount amountSats={0} showFiat loading={true} />}>
|
<Suspense fallback={<Amount amountSats={0} showFiat loading={true} />}>
|
||||||
<div onClick={refetchBalance}>
|
<div onClick={refetchBalance}>
|
||||||
@@ -72,6 +69,10 @@ export default function BalanceBox() {
|
|||||||
</Show>
|
</Show>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</FancyCard>
|
</FancyCard>
|
||||||
|
<div class="flex gap-2 py-4">
|
||||||
|
<ButtonLink href="/send" intent="green">Send</ButtonLink>
|
||||||
|
<ButtonLink href="/receive" intent="blue">Receive</ButtonLink>
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ const OVERLAY = "fixed inset-0 z-50 bg-black/50 backdrop-blur-sm"
|
|||||||
const DIALOG_POSITIONER = "fixed inset-0 z-50 flex items-center justify-center"
|
const DIALOG_POSITIONER = "fixed inset-0 z-50 flex items-center justify-center"
|
||||||
const DIALOG_CONTENT = "w-[80vw] max-w-[400px] p-4 bg-gray/50 backdrop-blur-md shadow-xl rounded-xl border border-white/10"
|
const DIALOG_CONTENT = "w-[80vw] max-w-[400px] p-4 bg-gray/50 backdrop-blur-md shadow-xl rounded-xl border border-white/10"
|
||||||
|
|
||||||
|
// TODO: implement this like toast so it's just one global confirm and I can call it with `confirm({ title: "Are you sure?", description: "This will delete your node" })`
|
||||||
export const ConfirmDialog: ParentComponent<{ isOpen: boolean; loading: boolean; onCancel: () => void, onConfirm: () => void }> = (props) => {
|
export const ConfirmDialog: ParentComponent<{ isOpen: boolean; loading: boolean; onCancel: () => void, onConfirm: () => void }> = (props) => {
|
||||||
return (
|
return (
|
||||||
<Dialog.Root isOpen={props.isOpen} onOpenChange={props.onCancel}>
|
<Dialog.Root isOpen={props.isOpen} onOpenChange={props.onCancel}>
|
||||||
|
|||||||
97
src/components/SettingsStringsEditor.tsx
Normal file
97
src/components/SettingsStringsEditor.tsx
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
import { createForm, url } from '@modular-forms/solid';
|
||||||
|
import { TextField } from '~/components/layout/TextField';
|
||||||
|
import { NodeManagerSettingStrings, getExistingSettings } from '~/logic/nodeManagerSetup';
|
||||||
|
import { Button } from '~/components/layout';
|
||||||
|
import { createSignal } from 'solid-js';
|
||||||
|
import { deleteDb } from '~/routes/Settings';
|
||||||
|
import { showToast } from './Toaster';
|
||||||
|
import eify from '~/utils/eify';
|
||||||
|
import { ConfirmDialog } from "~/components/Dialog";
|
||||||
|
import { useMegaStore } from '~/state/megaStore';
|
||||||
|
|
||||||
|
export function SettingsStringsEditor() {
|
||||||
|
const existingSettings = getExistingSettings();
|
||||||
|
const [settingsForm, { Form, Field, FieldArray }] = createForm<NodeManagerSettingStrings>({ initialValues: existingSettings });
|
||||||
|
const [confirmOpen, setConfirmOpen] = createSignal(false);
|
||||||
|
|
||||||
|
const [settingsTemp, setSettingsTemp] = createSignal<NodeManagerSettingStrings>();
|
||||||
|
|
||||||
|
const [state, actions] = useMegaStore();
|
||||||
|
|
||||||
|
async function handleSubmit(values: NodeManagerSettingStrings) {
|
||||||
|
try {
|
||||||
|
const existing = getExistingSettings();
|
||||||
|
const newSettings = { ...existing, ...values }
|
||||||
|
if (existing.network !== values.network) {
|
||||||
|
// If the network changes we need to confirm the wipe
|
||||||
|
// Save the settings so we can get them later
|
||||||
|
setSettingsTemp(newSettings);
|
||||||
|
setConfirmOpen(true);
|
||||||
|
} else {
|
||||||
|
await actions.setupNodeManager(newSettings);
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
showToast(eify(e))
|
||||||
|
}
|
||||||
|
console.log(values)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function confirmStateReset() {
|
||||||
|
try {
|
||||||
|
deleteDb("gossip")
|
||||||
|
localStorage.clear();
|
||||||
|
showToast({ title: "Deleted", description: `Deleted all data` })
|
||||||
|
const loadedValues = settingsTemp();
|
||||||
|
|
||||||
|
await actions.setupNodeManager(loadedValues);
|
||||||
|
window.location.reload();
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
showToast(eify(e))
|
||||||
|
}
|
||||||
|
|
||||||
|
setConfirmOpen(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Form onSubmit={handleSubmit} class="flex flex-col gap-4">
|
||||||
|
<ConfirmDialog loading={false} isOpen={confirmOpen()} onConfirm={confirmStateReset} onCancel={() => setConfirmOpen(false)} />
|
||||||
|
<Field name="network">
|
||||||
|
{(field, props) => (
|
||||||
|
// TODO: make a cool select component
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<label class="text-sm font-semibold uppercase">Network</label>
|
||||||
|
<select {...field} {...props} class="bg-black rounded-xl border border-white px-4 py-2">
|
||||||
|
<option value="mainnet">Mainnet</option>
|
||||||
|
<option value="testnet">Testnet</option>
|
||||||
|
<option value="regtest">Regtest</option>
|
||||||
|
<option value="signet">Signet</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Field>
|
||||||
|
<Field name="proxy" validate={[url("Should be a url starting with wss://")]}>
|
||||||
|
{(field, props) => (
|
||||||
|
<TextField {...props} value={field.value} error={field.error} label="Websockets Proxy" />
|
||||||
|
)}
|
||||||
|
</Field>
|
||||||
|
<Field name="esplora" validate={[url("That doesn't look like a URL")]}>
|
||||||
|
{(field, props) => (
|
||||||
|
<TextField {...props} value={field.value} error={field.error} label="Esplora" />
|
||||||
|
)}
|
||||||
|
</Field>
|
||||||
|
<Field name="rgs" validate={[url("That doesn't look like a URL")]}>
|
||||||
|
{(field, props) => (
|
||||||
|
<TextField {...props} value={field.value} error={field.error} label="RGS" />
|
||||||
|
)}
|
||||||
|
</Field>
|
||||||
|
<Field name="lsp" validate={[url("That doesn't look like a URL")]}>
|
||||||
|
{(field, props) => (
|
||||||
|
<TextField {...props} value={field.value} error={field.error} label="LSP" />
|
||||||
|
)}
|
||||||
|
</Field>
|
||||||
|
<Button type="submit">Save</Button>
|
||||||
|
</Form>
|
||||||
|
|
||||||
|
}
|
||||||
49
src/components/layout/TextField.tsx
Normal file
49
src/components/layout/TextField.tsx
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import { TextField as KTextField } from '@kobalte/core';
|
||||||
|
import { type JSX, Show, splitProps } from 'solid-js';
|
||||||
|
|
||||||
|
type TextFieldProps = {
|
||||||
|
name: string;
|
||||||
|
type?: 'text' | 'email' | 'tel' | 'password' | 'url' | 'date';
|
||||||
|
label?: string;
|
||||||
|
placeholder?: string;
|
||||||
|
value: string | undefined;
|
||||||
|
error: string;
|
||||||
|
required?: boolean;
|
||||||
|
multiline?: boolean;
|
||||||
|
ref: (element: HTMLInputElement | HTMLTextAreaElement) => void;
|
||||||
|
onInput: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, InputEvent>;
|
||||||
|
onChange: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, Event>;
|
||||||
|
onBlur: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, FocusEvent>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function TextField(props: TextFieldProps) {
|
||||||
|
const [fieldProps] = splitProps(props, [
|
||||||
|
'placeholder',
|
||||||
|
'ref',
|
||||||
|
'onInput',
|
||||||
|
'onChange',
|
||||||
|
'onBlur',
|
||||||
|
]);
|
||||||
|
return (
|
||||||
|
<KTextField.Root
|
||||||
|
class="flex flex-col gap-2"
|
||||||
|
name={props.name}
|
||||||
|
value={props.value}
|
||||||
|
validationState={props.error ? 'invalid' : 'valid'}
|
||||||
|
isRequired={props.required}
|
||||||
|
>
|
||||||
|
<Show when={props.label}>
|
||||||
|
<KTextField.Label class="text-sm uppercase font-semibold">
|
||||||
|
{props.label}
|
||||||
|
</KTextField.Label>
|
||||||
|
</Show>
|
||||||
|
<Show
|
||||||
|
when={props.multiline}
|
||||||
|
fallback={<KTextField.Input {...fieldProps} type={props.type} class="w-full p-2 rounded-lg bg-white/10" />}
|
||||||
|
>
|
||||||
|
<KTextField.TextArea {...fieldProps} autoResize class="w-full p-2 rounded-lg bg-white/10" />
|
||||||
|
</Show>
|
||||||
|
<KTextField.ErrorMessage>{props.error}</KTextField.ErrorMessage>
|
||||||
|
</KTextField.Root>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,8 +1,13 @@
|
|||||||
|
|
||||||
import init, { NodeManager } from '@mutinywallet/mutiny-wasm';
|
import init, { NodeManager } from '@mutinywallet/mutiny-wasm';
|
||||||
|
|
||||||
|
// export type NodeManagerSettingStrings = {
|
||||||
|
// network?: string, proxy?: string, esplora?: string, rgs?: string, lsp?: string,
|
||||||
|
// }
|
||||||
|
|
||||||
|
type Network = "mainnet" | "testnet" | "regtest" | "signet";
|
||||||
export type NodeManagerSettingStrings = {
|
export type NodeManagerSettingStrings = {
|
||||||
network?: string, proxy?: string, esplora?: string, rgs?: string, lsp?: string,
|
network?: Network, proxy?: string, esplora?: string, rgs?: string, lsp?: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getExistingSettings(): NodeManagerSettingStrings {
|
export function getExistingSettings(): NodeManagerSettingStrings {
|
||||||
@@ -64,12 +69,12 @@ export async function checkForWasm() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setupNodeManager(): Promise<NodeManager> {
|
export async function setupNodeManager(settings?: NodeManagerSettingStrings): Promise<NodeManager> {
|
||||||
const _ = await init();
|
const _ = await init();
|
||||||
|
|
||||||
console.time("Setup");
|
console.time("Setup");
|
||||||
console.log("Starting setup...")
|
console.log("Starting setup...")
|
||||||
const { network, proxy, esplora, rgs, lsp } = await setAndGetMutinySettings()
|
const { network, proxy, esplora, rgs, lsp } = await setAndGetMutinySettings(settings)
|
||||||
console.log("Initializing Node Manager")
|
console.log("Initializing Node Manager")
|
||||||
console.log("Using network", network);
|
console.log("Using network", network);
|
||||||
console.log("Using proxy", proxy);
|
console.log("Using proxy", proxy);
|
||||||
|
|||||||
@@ -9,11 +9,10 @@ import { useMegaStore } from "~/state/megaStore";
|
|||||||
import { satsToUsd } from "~/utils/conversions";
|
import { satsToUsd } from "~/utils/conversions";
|
||||||
import { objectToSearchParams } from "~/utils/objectToSearchParams";
|
import { objectToSearchParams } from "~/utils/objectToSearchParams";
|
||||||
import { useCopy } from "~/utils/useCopy";
|
import { useCopy } from "~/utils/useCopy";
|
||||||
import { JsonModal } from '~/components/JsonModal';
|
|
||||||
import mempoolTxUrl from "~/utils/mempoolTxUrl";
|
import mempoolTxUrl from "~/utils/mempoolTxUrl";
|
||||||
import { ReceiveSuccessModal } from "~/components/ReceiveSuccessModal";
|
import { ReceiveSuccessModal } from "~/components/ReceiveSuccessModal";
|
||||||
|
|
||||||
import party from '~/assets/party.gif';
|
import party from '~/assets/check-spinner.gif';
|
||||||
import { Amount } from "~/components/Amount";
|
import { Amount } from "~/components/Amount";
|
||||||
|
|
||||||
type OnChainTx = {
|
type OnChainTx = {
|
||||||
|
|||||||
@@ -1,46 +1,64 @@
|
|||||||
|
import { createSignal } from "solid-js";
|
||||||
import { useNavigate } from "solid-start";
|
import { useNavigate } from "solid-start";
|
||||||
|
import { ConfirmDialog } from "~/components/Dialog";
|
||||||
import KitchenSink from "~/components/KitchenSink";
|
import KitchenSink from "~/components/KitchenSink";
|
||||||
import { Button, Card, DefaultMain, Hr, LargeHeader, SafeArea } from "~/components/layout";
|
import { Button, Card, DefaultMain, Hr, LargeHeader, SafeArea } from "~/components/layout";
|
||||||
import NavBar from "~/components/NavBar";
|
import NavBar from "~/components/NavBar";
|
||||||
|
import { SettingsStringsEditor } from "~/components/SettingsStringsEditor";
|
||||||
|
import { showToast } from "~/components/Toaster";
|
||||||
import { useMegaStore } from "~/state/megaStore";
|
import { useMegaStore } from "~/state/megaStore";
|
||||||
|
|
||||||
|
export function deleteDb(name: string) {
|
||||||
|
const req = indexedDB.deleteDatabase(name);
|
||||||
|
req.onsuccess = function () {
|
||||||
|
console.log("Deleted database successfully");
|
||||||
|
showToast({ title: "Deleted", description: `Deleted "${name}" database successfully` })
|
||||||
|
};
|
||||||
|
req.onerror = function () {
|
||||||
|
console.error("Couldn't delete database");
|
||||||
|
showToast(new Error("Couldn't delete database"))
|
||||||
|
};
|
||||||
|
req.onblocked = function () {
|
||||||
|
console.error("Couldn't delete database due to the operation being blocked");
|
||||||
|
showToast(new Error("Couldn't delete database due to the operation being blocked"))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export default function Settings() {
|
export default function Settings() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const [_, actions] = useMegaStore();
|
const [_, actions] = useMegaStore();
|
||||||
|
|
||||||
function clearWaitlistId() {
|
async function resetNode() {
|
||||||
actions.setWaitlistId('');
|
setConfirmLoading(true);
|
||||||
navigate("/")
|
deleteDb("gossip")
|
||||||
|
localStorage.clear();
|
||||||
|
showToast({ title: "Deleted", description: `Deleted all data` })
|
||||||
|
setConfirmOpen(false);
|
||||||
|
setConfirmLoading(false);
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.reload();
|
||||||
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setTestWaitlistId() {
|
async function confirmReset() {
|
||||||
actions.setWaitlistId('npub17zcnktw7svnechf5g666t33d7slw36sz8el3ep4c7kmyfwjhxn9qjvavs6');
|
setConfirmOpen(true);
|
||||||
navigate("/")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetNode() {
|
const [confirmOpen, setConfirmOpen] = createSignal(false);
|
||||||
Object.keys(localStorage).forEach(function (key) {
|
const [confirmLoading, setConfirmLoading] = createSignal(false);
|
||||||
if (key.startsWith('waitlist_id')) {
|
|
||||||
// Don't do anything because it's annoying to set my waitlist_id every time
|
|
||||||
} else {
|
|
||||||
localStorage.removeItem(key);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
navigate("/")
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeArea>
|
<SafeArea>
|
||||||
<DefaultMain>
|
<DefaultMain>
|
||||||
<LargeHeader>Settings</LargeHeader>
|
<LargeHeader>Settings</LargeHeader>
|
||||||
|
<SettingsStringsEditor />
|
||||||
<Card title="Random utilities">
|
<Card title="Random utilities">
|
||||||
<Button onClick={clearWaitlistId}>Clear waitlist_id</Button>
|
{/* <Button onClick={clearWaitlistId}>Clear waitlist_id</Button> */}
|
||||||
<Button onClick={setTestWaitlistId}>Use test waitlist_id</Button>
|
{/* <Button onClick={setTestWaitlistId}>Use test waitlist_id</Button> */}
|
||||||
<Button onClick={resetNode}>Reset node</Button>
|
<Button onClick={confirmReset}>Delete Everything</Button>
|
||||||
|
<ConfirmDialog loading={confirmLoading()} isOpen={confirmOpen()} onConfirm={resetNode} onCancel={() => setConfirmOpen(false)} />
|
||||||
</Card>
|
</Card>
|
||||||
<Hr />
|
|
||||||
<KitchenSink />
|
|
||||||
</DefaultMain>
|
</DefaultMain>
|
||||||
<NavBar activeTab="settings" />
|
<NavBar activeTab="settings" />
|
||||||
</SafeArea>
|
</SafeArea>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { ParentComponent, createContext, createEffect, onCleanup, onMount, useContext } from "solid-js";
|
import { ParentComponent, createContext, createEffect, onCleanup, onMount, useContext } from "solid-js";
|
||||||
import { createStore } from "solid-js/store";
|
import { createStore } from "solid-js/store";
|
||||||
import { setupNodeManager } from "~/logic/nodeManagerSetup";
|
import { NodeManagerSettingStrings, setupNodeManager } from "~/logic/nodeManagerSetup";
|
||||||
import { MutinyBalance, NodeManager } from "@mutinywallet/mutiny-wasm";
|
import { MutinyBalance, NodeManager } from "@mutinywallet/mutiny-wasm";
|
||||||
|
|
||||||
const MegaStoreContext = createContext<MegaStore>();
|
const MegaStoreContext = createContext<MegaStore>();
|
||||||
@@ -19,7 +19,7 @@ export type MegaStore = [{
|
|||||||
price: number
|
price: number
|
||||||
}, {
|
}, {
|
||||||
fetchUserStatus(): Promise<UserStatus>;
|
fetchUserStatus(): Promise<UserStatus>;
|
||||||
setupNodeManager(): Promise<void>;
|
setupNodeManager(settings?: NodeManagerSettingStrings): Promise<void>;
|
||||||
setWaitlistId(waitlist_id: string): void;
|
setWaitlistId(waitlist_id: string): void;
|
||||||
sync(): Promise<void>;
|
sync(): Promise<void>;
|
||||||
}];
|
}];
|
||||||
@@ -52,9 +52,9 @@ export const Provider: ParentComponent = (props) => {
|
|||||||
return "new_here"
|
return "new_here"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async setupNodeManager(): Promise<void> {
|
async setupNodeManager(settings?: NodeManagerSettingStrings): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const nodeManager = await setupNodeManager()
|
const nodeManager = await setupNodeManager(settings)
|
||||||
setState({ node_manager: nodeManager })
|
setState({ node_manager: nodeManager })
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
|
|||||||
Reference in New Issue
Block a user