a little bit more admin and settings

This commit is contained in:
Paul Miller
2023-04-21 11:51:10 -05:00
parent 37cac92a16
commit 7c9bd21517
14 changed files with 141 additions and 92 deletions

View File

@@ -1,7 +1,7 @@
import send from '~/assets/icons/send.svg';
import receive from '~/assets/icons/receive.svg';
import { Card, Hr, LoadingSpinner, SmallAmount, SmallHeader, VStack } from './layout';
import { For, JSX, Match, Show, Suspense, Switch, createMemo, createResource, createSignal } from 'solid-js';
import { Card, LoadingSpinner, SmallAmount, SmallHeader, VStack } from './layout';
import { For, Match, ParentComponent, Suspense, Switch, createMemo, createResource, createSignal } from 'solid-js';
import { useMegaStore } from '~/state/megaStore';
import { MutinyInvoice } from '@mutinywallet/mutiny-wasm';
import { prettyPrintTime } from '~/utils/prettyPrintTime';
@@ -34,7 +34,7 @@ type Utxo = {
is_spent: boolean
}
function SubtleText(props: { children: any }) {
const SubtleText: ParentComponent = (props) => {
return <h3 class='text-xs text-gray-500 uppercase'>{props.children}</h3>
}
@@ -50,7 +50,7 @@ function OnChainItem(props: { item: OnChainTx }) {
Mempool Link
</a>
</JsonModal>
<div class={THREE_COLUMNS} onclick={() => setOpen(!open())}>
<div class={THREE_COLUMNS} onClick={() => setOpen(!open())}>
{isReceive() ? <img src={receive} alt="receive arrow" /> : <img src={send} alt="send arrow" />}
<div class={CENTER_COLUMN}>
<h2 class={MISSING_LABEL}>Label Missing</h2>
@@ -76,7 +76,7 @@ function InvoiceItem(props: { item: MutinyInvoice }) {
return (
<>
<JsonModal open={open()} data={props.item} title="Lightning Transaction" setOpen={setOpen} />
<div class={THREE_COLUMNS} onclick={() => setOpen(!open())}>
<div class={THREE_COLUMNS} onClick={() => setOpen(!open())}>
{isSend() ? <img src={send} alt="send arrow" /> : <img src={receive} alt="receive arrow" />}
<div class={CENTER_COLUMN}>
<h2 class={MISSING_LABEL}>Label Missing</h2>
@@ -101,7 +101,7 @@ function Utxo(props: { item: Utxo }) {
return (
<>
<JsonModal open={open()} data={props.item} title="Unspent Transaction Output" setOpen={setOpen} />
<div class={THREE_COLUMNS} onclick={() => setOpen(!open())}>
<div class={THREE_COLUMNS} onClick={() => setOpen(!open())}>
<img src={receive} alt="receive arrow" />
<div class={CENTER_COLUMN}>
<h2 class={MISSING_LABEL}>Label Missing</h2>
@@ -138,9 +138,9 @@ export function Activity() {
return utxos;
}
const [transactions, { refetch: refetchTransactions }] = createResource(getTransactions);
const [invoices, { refetch: refetchInvoices }] = createResource(getInvoices);
const [utxos, { refetch: refetchUtxos }] = createResource(getUtXos);
const [transactions, { refetch: _refetchTransactions }] = createResource(getTransactions);
const [invoices, { refetch: _refetchInvoices }] = createResource(getInvoices);
const [utxos, { refetch: _refetchUtxos }] = createResource(getUtXos);
return (
<VStack>

View File

@@ -1,4 +1,4 @@
import { Show, createResource } from "solid-js"
import { Show } from "solid-js"
import { useMegaStore } from "~/state/megaStore"
import { satsToUsd } from "~/utils/conversions"

View File

@@ -1,4 +1,4 @@
import { For, createMemo, createResource, createSignal } from 'solid-js';
import { For, createMemo, createSignal } from 'solid-js';
import { Button } from '~/components/layout';
import { useMegaStore } from '~/state/megaStore';
import { satsToUsd } from '~/utils/conversions';

View File

@@ -0,0 +1,50 @@
import { createSignal } from "solid-js";
import { ConfirmDialog } from "~/components/Dialog";
import { Button } from "~/components/layout";
import { showToast } from "~/components/Toaster";
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 function DeleteEverything() {
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);
}
async function confirmReset() {
setConfirmOpen(true);
}
const [confirmOpen, setConfirmOpen] = createSignal(false);
const [confirmLoading, setConfirmLoading] = createSignal(false);
return (
<>
<Button onClick={confirmReset}>Delete Everything</Button>
<ConfirmDialog loading={confirmLoading()} isOpen={confirmOpen()} onConfirm={resetNode} onCancel={() => setConfirmOpen(false)}>
This will delete your node's state. This can't be undone!
</ConfirmDialog>
</>
)
}

View File

@@ -3,11 +3,11 @@ import { Card, Hr, SmallHeader, Button, InnerCard, VStack } from "~/components/l
import PeerConnectModal from "~/components/PeerConnectModal";
import { For, Show, Suspense, createEffect, createResource, createSignal, onCleanup } from "solid-js";
import { MutinyChannel, MutinyPeer } from "@mutinywallet/mutiny-wasm";
import { Collapsible, TextField, toaster } from "@kobalte/core";
import { Collapsible, TextField } from "@kobalte/core";
import mempoolTxUrl from "~/utils/mempoolTxUrl";
import eify from "~/utils/eify";
import { ConfirmDialog } from "./Dialog";
import { ToastItem, showToast } from "./Toaster";
import { showToast } from "./Toaster";
// TODO: hopefully I don't have to maintain this type forever but I don't know how to pass it around otherwise
type RefetchPeersType = (info?: unknown) => MutinyPeer[] | Promise<MutinyPeer[] | undefined> | null | undefined

View File

@@ -0,0 +1,27 @@
import { Match, Switch, createSignal } from "solid-js"
export function SeedWords(props: { words: string }) {
const [shouldShow, setShouldShow] = createSignal(false)
function toggleShow() {
setShouldShow(!shouldShow())
}
return (<pre class="flex items-center gap-4 bg-m-red p-4 rounded-xl overflow-hidden">
<Switch>
<Match when={!shouldShow()}>
<div onClick={toggleShow} class="cursor-pointer">
<code class="text-red">TAP TO REVEAL SEED WORDS</code>
</div>
</Match>
<Match when={shouldShow()}>
<div onClick={toggleShow} class="cursor-pointer overflow-hidden">
<p class="font-mono w-full whitespace-pre-wrap">
{props.words}
</p>
</div>
</Match>
</Switch>
</pre >)
}

View File

@@ -3,7 +3,7 @@ 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 { deleteDb } from '~/components/DeleteEverything';
import { showToast } from './Toaster';
import eify from '~/utils/eify';
import { ConfirmDialog } from "~/components/Dialog";
@@ -11,12 +11,12 @@ import { useMegaStore } from '~/state/megaStore';
export function SettingsStringsEditor() {
const existingSettings = getExistingSettings();
const [settingsForm, { Form, Field, FieldArray }] = createForm<NodeManagerSettingStrings>({ initialValues: existingSettings });
const [_settingsForm, { Form, Field }] = createForm<NodeManagerSettingStrings>({ initialValues: existingSettings });
const [confirmOpen, setConfirmOpen] = createSignal(false);
const [settingsTemp, setSettingsTemp] = createSignal<NodeManagerSettingStrings>();
const [state, actions] = useMegaStore();
const [_store, actions] = useMegaStore();
async function handleSubmit(values: NodeManagerSettingStrings) {
try {
@@ -56,7 +56,10 @@ export function SettingsStringsEditor() {
}
return <Form onSubmit={handleSubmit} class="flex flex-col gap-4">
<ConfirmDialog loading={false} isOpen={confirmOpen()} onConfirm={confirmStateReset} onCancel={() => setConfirmOpen(false)} />
<ConfirmDialog loading={false} isOpen={confirmOpen()} onConfirm={confirmStateReset} onCancel={() => setConfirmOpen(false)}>
Are you sure? Changing networks will delete your node's state. This can't be undone!
</ConfirmDialog>
<h2 class="text-2xl font-light">Don't trust us! Use your own servers to back Mutiny.</h2>
<Field name="network">
{(field, props) => (
// TODO: make a cool select component

View File

@@ -1,5 +1,5 @@
import { cva, VariantProps } from "class-variance-authority";
import { children, JSX, ParentComponent, Show, splitProps, Switch } from "solid-js";
import { children, JSX, ParentComponent, Show, splitProps } from "solid-js";
import { Dynamic } from "solid-js/web";
import { A } from "solid-start";
import { LoadingSpinner } from ".";

View File

@@ -92,8 +92,8 @@ const LargeHeader: ParentComponent = (props) => {
return (<h1 class="text-4xl font-semibold uppercase border-b-2 border-b-white my-4">{props.children}</h1>)
}
const VStack: ParentComponent = (props) => {
return (<div class="flex flex-col gap-4">{props.children}</div>)
const VStack: ParentComponent<{ biggap?: boolean }> = (props) => {
return (<div class={`flex flex-col gap-${props.biggap ? "8" : "4"}`}>{props.children}</div>)
}
const SmallAmount: ParentComponent<{ amount: number | bigint }> = (props) => {

View File

@@ -73,7 +73,7 @@ export default function WaitlistForm() {
</h2>
<Form onSubmit={newHandleSubmit} class="flex flex-col gap-8">
<Field name="user_type">
{(field, props) => (
{(field, _props) => (
// TODO: there's probably a "real" way to do this with modular-forms
<StyledRadioGroup value={field.value || "nostr"} onValueChange={(newValue) => setValue(waitlistForm, "user_type", newValue as "nostr" | "email")} choices={COMMUNICATION_METHODS} />
)}

View File

@@ -1,18 +1,25 @@
import { DeleteEverything } from "~/components/DeleteEverything";
import KitchenSink from "~/components/KitchenSink";
import NavBar from "~/components/NavBar";
import { Card, DefaultMain, LargeHeader, SafeArea, VStack } from "~/components/layout";
import { Card, DefaultMain, LargeHeader, NodeManagerGuard, SafeArea, SmallHeader, VStack } from "~/components/layout";
export default function Admin() {
return (
<SafeArea>
<DefaultMain>
<LargeHeader>Admin</LargeHeader>
<VStack>
<Card><p>If you know what you're doing you're in the right place!</p></Card>
<KitchenSink />
</VStack>
</DefaultMain>
<NavBar activeTab="none" />
</SafeArea>
<NodeManagerGuard>
<SafeArea>
<DefaultMain>
<LargeHeader>Admin</LargeHeader>
<VStack>
<Card><p>If you know what you're doing you're in the right place!</p></Card>
<KitchenSink />
<div class='rounded-xl p-4 flex flex-col gap-2 bg-m-red overflow-x-hidden'>
<SmallHeader>Danger zone</SmallHeader>
<DeleteEverything />
</div>
</VStack>
</DefaultMain>
<NavBar activeTab="none" />
</SafeArea>
</NodeManagerGuard>
)
}

View File

@@ -1,7 +1,7 @@
import Reader from "~/components/Reader";
import { createEffect, createSignal, Show } from "solid-js";
import { useNavigate } from "solid-start";
import { Button, SafeArea } from "~/components/layout";
import { Button } from "~/components/layout";
export default function Scanner() {
const [scanResult, setScanResult] = createSignal<string>();

View File

@@ -1,66 +1,28 @@
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 { ButtonLink, DefaultMain, LargeHeader, NodeManagerGuard, SafeArea, VStack } from "~/components/layout";
import NavBar from "~/components/NavBar";
import { SeedWords } from "~/components/SeedWords";
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();
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);
}
async function confirmReset() {
setConfirmOpen(true);
}
const [confirmOpen, setConfirmOpen] = createSignal(false);
const [confirmLoading, setConfirmLoading] = createSignal(false);
const [store, _actions] = useMegaStore();
return (
<SafeArea>
<DefaultMain>
<LargeHeader>Settings</LargeHeader>
<SettingsStringsEditor />
<Card title="Random utilities">
{/* <Button onClick={clearWaitlistId}>Clear waitlist_id</Button> */}
{/* <Button onClick={setTestWaitlistId}>Use test waitlist_id</Button> */}
<Button onClick={confirmReset}>Delete Everything</Button>
<ConfirmDialog loading={confirmLoading()} isOpen={confirmOpen()} onConfirm={resetNode} onCancel={() => setConfirmOpen(false)} />
</Card>
</DefaultMain>
<NavBar activeTab="settings" />
</SafeArea>
<NodeManagerGuard>
<SafeArea>
<DefaultMain>
<LargeHeader>Settings</LargeHeader>
<VStack biggap>
<VStack>
<p class="text-2xl font-light">Write down these words or you'll die!</p>
<SeedWords words={store.node_manager?.show_seed() || ""} />
</VStack>
<SettingsStringsEditor />
<ButtonLink href="/admin">"I know what I'm doing"</ButtonLink>
</VStack>
</DefaultMain>
<NavBar activeTab="settings" />
</SafeArea>
</NodeManagerGuard>
)
}

View File

@@ -1,5 +1,5 @@
export function prettyPrintTime(ts: number) {
const options = {
const options: Intl.DateTimeFormatOptions = {
weekday: 'long',
year: 'numeric',
month: 'short',
@@ -8,5 +8,5 @@ export function prettyPrintTime(ts: number) {
minute: 'numeric'
};
return new Date(ts * 1000).toLocaleString('en-US', options as any);
return new Date(ts * 1000).toLocaleString('en-US', options);
}