diff --git a/package.json b/package.json
index d91a0e5..40e559a 100644
--- a/package.json
+++ b/package.json
@@ -31,6 +31,7 @@
"dependencies": {
"@kobalte/core": "^0.8.2",
"@kobalte/tailwindcss": "^0.5.0",
+ "@modular-forms/solid": "^0.12.0",
"@motionone/solid": "^10.16.0",
"@mutinywallet/mutiny-wasm": "^0.2.5",
"@nostr-dev-kit/ndk": "^0.0.13",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 412f984..c8b220b 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -7,6 +7,9 @@ dependencies:
'@kobalte/tailwindcss':
specifier: ^0.5.0
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':
specifier: ^10.16.0
version: 10.16.0(solid-js@1.7.3)
@@ -1561,6 +1564,14 @@ packages:
solid-js: 1.7.3
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:
resolution: {integrity: sha512-mZcJxLjHor+bhcPuIFErMDNyrdb2vJur8lSfMCsuCB4UyV8ILZLvK+t+pg56erv8ud9xQGK/1OGPt10agPrCyQ==}
dependencies:
diff --git a/src/assets/check-spinner.gif b/src/assets/check-spinner.gif
new file mode 100644
index 0000000..97e3f3a
Binary files /dev/null and b/src/assets/check-spinner.gif differ
diff --git a/src/components/BalanceBox.tsx b/src/components/BalanceBox.tsx
index 6bf5b33..c1190eb 100644
--- a/src/components/BalanceBox.tsx
+++ b/src/components/BalanceBox.tsx
@@ -49,10 +49,7 @@ export default function BalanceBox() {
-
- Send
- Receive
-
+
}>
}>
@@ -72,6 +69,10 @@ export default function BalanceBox() {
+
+ Send
+ Receive
+
>
)
}
diff --git a/src/components/Dialog.tsx b/src/components/Dialog.tsx
index e206deb..4ec624a 100644
--- a/src/components/Dialog.tsx
+++ b/src/components/Dialog.tsx
@@ -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_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) => {
return (
diff --git a/src/components/SettingsStringsEditor.tsx b/src/components/SettingsStringsEditor.tsx
new file mode 100644
index 0000000..11c19b4
--- /dev/null
+++ b/src/components/SettingsStringsEditor.tsx
@@ -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({ initialValues: existingSettings });
+ const [confirmOpen, setConfirmOpen] = createSignal(false);
+
+ const [settingsTemp, setSettingsTemp] = createSignal();
+
+ 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
+
+}
\ No newline at end of file
diff --git a/src/components/layout/TextField.tsx b/src/components/layout/TextField.tsx
new file mode 100644
index 0000000..51ac702
--- /dev/null
+++ b/src/components/layout/TextField.tsx
@@ -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;
+ onChange: JSX.EventHandler;
+ onBlur: JSX.EventHandler;
+};
+
+export function TextField(props: TextFieldProps) {
+ const [fieldProps] = splitProps(props, [
+ 'placeholder',
+ 'ref',
+ 'onInput',
+ 'onChange',
+ 'onBlur',
+ ]);
+ return (
+
+
+
+ {props.label}
+
+
+ }
+ >
+
+
+ {props.error}
+
+ );
+}
\ No newline at end of file
diff --git a/src/logic/nodeManagerSetup.ts b/src/logic/nodeManagerSetup.ts
index db1d9d9..607d637 100644
--- a/src/logic/nodeManagerSetup.ts
+++ b/src/logic/nodeManagerSetup.ts
@@ -1,8 +1,13 @@
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 = {
- network?: string, proxy?: string, esplora?: string, rgs?: string, lsp?: string,
+ network?: Network, proxy?: string, esplora?: string, rgs?: string, lsp?: string,
}
export function getExistingSettings(): NodeManagerSettingStrings {
@@ -64,12 +69,12 @@ export async function checkForWasm() {
}
}
-export async function setupNodeManager(): Promise {
+export async function setupNodeManager(settings?: NodeManagerSettingStrings): Promise {
const _ = await init();
console.time("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("Using network", network);
console.log("Using proxy", proxy);
diff --git a/src/routes/Receive.tsx b/src/routes/Receive.tsx
index ee7c9bc..17ceb67 100644
--- a/src/routes/Receive.tsx
+++ b/src/routes/Receive.tsx
@@ -9,11 +9,10 @@ import { useMegaStore } from "~/state/megaStore";
import { satsToUsd } from "~/utils/conversions";
import { objectToSearchParams } from "~/utils/objectToSearchParams";
import { useCopy } from "~/utils/useCopy";
-import { JsonModal } from '~/components/JsonModal';
import mempoolTxUrl from "~/utils/mempoolTxUrl";
import { ReceiveSuccessModal } from "~/components/ReceiveSuccessModal";
-import party from '~/assets/party.gif';
+import party from '~/assets/check-spinner.gif';
import { Amount } from "~/components/Amount";
type OnChainTx = {
diff --git a/src/routes/Settings.tsx b/src/routes/Settings.tsx
index 38d53f5..7e106c1 100644
--- a/src/routes/Settings.tsx
+++ b/src/routes/Settings.tsx
@@ -1,46 +1,64 @@
+import { createSignal } from "solid-js";
import { useNavigate } from "solid-start";
+import { ConfirmDialog } from "~/components/Dialog";
import KitchenSink from "~/components/KitchenSink";
import { Button, Card, DefaultMain, Hr, LargeHeader, SafeArea } from "~/components/layout";
import NavBar from "~/components/NavBar";
+import { SettingsStringsEditor } from "~/components/SettingsStringsEditor";
+import { showToast } from "~/components/Toaster";
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() {
const navigate = useNavigate();
const [_, actions] = useMegaStore();
- function clearWaitlistId() {
- actions.setWaitlistId('');
- navigate("/")
+ async function resetNode() {
+ setConfirmLoading(true);
+ deleteDb("gossip")
+ localStorage.clear();
+ showToast({ title: "Deleted", description: `Deleted all data` })
+ setConfirmOpen(false);
+ setConfirmLoading(false);
+ setTimeout(() => {
+ window.location.reload();
+ }, 1000);
}
- function setTestWaitlistId() {
- actions.setWaitlistId('npub17zcnktw7svnechf5g666t33d7slw36sz8el3ep4c7kmyfwjhxn9qjvavs6');
- navigate("/")
+ async function confirmReset() {
+ setConfirmOpen(true);
}
- function resetNode() {
- Object.keys(localStorage).forEach(function (key) {
- 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("/")
- }
+ const [confirmOpen, setConfirmOpen] = createSignal(false);
+ const [confirmLoading, setConfirmLoading] = createSignal(false);
return (
Settings
+
-
-
-
+ {/* */}
+ {/* */}
+
+ setConfirmOpen(false)} />
-
-
diff --git a/src/state/megaStore.tsx b/src/state/megaStore.tsx
index d0d60e8..20dd68e 100644
--- a/src/state/megaStore.tsx
+++ b/src/state/megaStore.tsx
@@ -2,7 +2,7 @@
import { ParentComponent, createContext, createEffect, onCleanup, onMount, useContext } from "solid-js";
import { createStore } from "solid-js/store";
-import { setupNodeManager } from "~/logic/nodeManagerSetup";
+import { NodeManagerSettingStrings, setupNodeManager } from "~/logic/nodeManagerSetup";
import { MutinyBalance, NodeManager } from "@mutinywallet/mutiny-wasm";
const MegaStoreContext = createContext();
@@ -19,7 +19,7 @@ export type MegaStore = [{
price: number
}, {
fetchUserStatus(): Promise;
- setupNodeManager(): Promise;
+ setupNodeManager(settings?: NodeManagerSettingStrings): Promise;
setWaitlistId(waitlist_id: string): void;
sync(): Promise;
}];
@@ -52,9 +52,9 @@ export const Provider: ParentComponent = (props) => {
return "new_here"
}
},
- async setupNodeManager(): Promise {
+ async setupNodeManager(settings?: NodeManagerSettingStrings): Promise {
try {
- const nodeManager = await setupNodeManager()
+ const nodeManager = await setupNodeManager(settings)
setState({ node_manager: nodeManager })
} catch (e) {
console.error(e)