mirror of
https://github.com/aljazceru/mutiny-web.git
synced 2025-12-18 06:44:27 +01:00
a little bit more admin and settings
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import send from '~/assets/icons/send.svg';
|
import send from '~/assets/icons/send.svg';
|
||||||
import receive from '~/assets/icons/receive.svg';
|
import receive from '~/assets/icons/receive.svg';
|
||||||
import { Card, Hr, LoadingSpinner, SmallAmount, SmallHeader, VStack } from './layout';
|
import { Card, LoadingSpinner, SmallAmount, SmallHeader, VStack } from './layout';
|
||||||
import { For, JSX, Match, Show, Suspense, Switch, createMemo, createResource, createSignal } from 'solid-js';
|
import { For, Match, ParentComponent, Suspense, Switch, createMemo, createResource, createSignal } from 'solid-js';
|
||||||
import { useMegaStore } from '~/state/megaStore';
|
import { useMegaStore } from '~/state/megaStore';
|
||||||
import { MutinyInvoice } from '@mutinywallet/mutiny-wasm';
|
import { MutinyInvoice } from '@mutinywallet/mutiny-wasm';
|
||||||
import { prettyPrintTime } from '~/utils/prettyPrintTime';
|
import { prettyPrintTime } from '~/utils/prettyPrintTime';
|
||||||
@@ -34,7 +34,7 @@ type Utxo = {
|
|||||||
is_spent: boolean
|
is_spent: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
function SubtleText(props: { children: any }) {
|
const SubtleText: ParentComponent = (props) => {
|
||||||
return <h3 class='text-xs text-gray-500 uppercase'>{props.children}</h3>
|
return <h3 class='text-xs text-gray-500 uppercase'>{props.children}</h3>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ function OnChainItem(props: { item: OnChainTx }) {
|
|||||||
Mempool Link
|
Mempool Link
|
||||||
</a>
|
</a>
|
||||||
</JsonModal>
|
</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" />}
|
{isReceive() ? <img src={receive} alt="receive arrow" /> : <img src={send} alt="send arrow" />}
|
||||||
<div class={CENTER_COLUMN}>
|
<div class={CENTER_COLUMN}>
|
||||||
<h2 class={MISSING_LABEL}>Label Missing</h2>
|
<h2 class={MISSING_LABEL}>Label Missing</h2>
|
||||||
@@ -76,7 +76,7 @@ function InvoiceItem(props: { item: MutinyInvoice }) {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<JsonModal open={open()} data={props.item} title="Lightning Transaction" setOpen={setOpen} />
|
<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" />}
|
{isSend() ? <img src={send} alt="send arrow" /> : <img src={receive} alt="receive arrow" />}
|
||||||
<div class={CENTER_COLUMN}>
|
<div class={CENTER_COLUMN}>
|
||||||
<h2 class={MISSING_LABEL}>Label Missing</h2>
|
<h2 class={MISSING_LABEL}>Label Missing</h2>
|
||||||
@@ -101,7 +101,7 @@ function Utxo(props: { item: Utxo }) {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<JsonModal open={open()} data={props.item} title="Unspent Transaction Output" setOpen={setOpen} />
|
<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" />
|
<img src={receive} alt="receive arrow" />
|
||||||
<div class={CENTER_COLUMN}>
|
<div class={CENTER_COLUMN}>
|
||||||
<h2 class={MISSING_LABEL}>Label Missing</h2>
|
<h2 class={MISSING_LABEL}>Label Missing</h2>
|
||||||
@@ -138,9 +138,9 @@ export function Activity() {
|
|||||||
return utxos;
|
return utxos;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [transactions, { refetch: refetchTransactions }] = createResource(getTransactions);
|
const [transactions, { refetch: _refetchTransactions }] = createResource(getTransactions);
|
||||||
const [invoices, { refetch: refetchInvoices }] = createResource(getInvoices);
|
const [invoices, { refetch: _refetchInvoices }] = createResource(getInvoices);
|
||||||
const [utxos, { refetch: refetchUtxos }] = createResource(getUtXos);
|
const [utxos, { refetch: _refetchUtxos }] = createResource(getUtXos);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<VStack>
|
<VStack>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Show, createResource } from "solid-js"
|
import { Show } from "solid-js"
|
||||||
import { useMegaStore } from "~/state/megaStore"
|
import { useMegaStore } from "~/state/megaStore"
|
||||||
import { satsToUsd } from "~/utils/conversions"
|
import { satsToUsd } from "~/utils/conversions"
|
||||||
|
|
||||||
|
|||||||
@@ -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 { Button } from '~/components/layout';
|
||||||
import { useMegaStore } from '~/state/megaStore';
|
import { useMegaStore } from '~/state/megaStore';
|
||||||
import { satsToUsd } from '~/utils/conversions';
|
import { satsToUsd } from '~/utils/conversions';
|
||||||
|
|||||||
50
src/components/DeleteEverything.tsx
Normal file
50
src/components/DeleteEverything.tsx
Normal 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>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -3,11 +3,11 @@ import { Card, Hr, SmallHeader, Button, InnerCard, VStack } from "~/components/l
|
|||||||
import PeerConnectModal from "~/components/PeerConnectModal";
|
import PeerConnectModal from "~/components/PeerConnectModal";
|
||||||
import { For, Show, Suspense, createEffect, createResource, createSignal, onCleanup } from "solid-js";
|
import { For, Show, Suspense, createEffect, createResource, createSignal, onCleanup } from "solid-js";
|
||||||
import { MutinyChannel, MutinyPeer } from "@mutinywallet/mutiny-wasm";
|
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 mempoolTxUrl from "~/utils/mempoolTxUrl";
|
||||||
import eify from "~/utils/eify";
|
import eify from "~/utils/eify";
|
||||||
import { ConfirmDialog } from "./Dialog";
|
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
|
// 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
|
type RefetchPeersType = (info?: unknown) => MutinyPeer[] | Promise<MutinyPeer[] | undefined> | null | undefined
|
||||||
|
|||||||
27
src/components/SeedWords.tsx
Normal file
27
src/components/SeedWords.tsx
Normal 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 >)
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ import { TextField } from '~/components/layout/TextField';
|
|||||||
import { NodeManagerSettingStrings, getExistingSettings } from '~/logic/nodeManagerSetup';
|
import { NodeManagerSettingStrings, getExistingSettings } from '~/logic/nodeManagerSetup';
|
||||||
import { Button } from '~/components/layout';
|
import { Button } from '~/components/layout';
|
||||||
import { createSignal } from 'solid-js';
|
import { createSignal } from 'solid-js';
|
||||||
import { deleteDb } from '~/routes/Settings';
|
import { deleteDb } from '~/components/DeleteEverything';
|
||||||
import { showToast } from './Toaster';
|
import { showToast } from './Toaster';
|
||||||
import eify from '~/utils/eify';
|
import eify from '~/utils/eify';
|
||||||
import { ConfirmDialog } from "~/components/Dialog";
|
import { ConfirmDialog } from "~/components/Dialog";
|
||||||
@@ -11,12 +11,12 @@ import { useMegaStore } from '~/state/megaStore';
|
|||||||
|
|
||||||
export function SettingsStringsEditor() {
|
export function SettingsStringsEditor() {
|
||||||
const existingSettings = getExistingSettings();
|
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 [confirmOpen, setConfirmOpen] = createSignal(false);
|
||||||
|
|
||||||
const [settingsTemp, setSettingsTemp] = createSignal<NodeManagerSettingStrings>();
|
const [settingsTemp, setSettingsTemp] = createSignal<NodeManagerSettingStrings>();
|
||||||
|
|
||||||
const [state, actions] = useMegaStore();
|
const [_store, actions] = useMegaStore();
|
||||||
|
|
||||||
async function handleSubmit(values: NodeManagerSettingStrings) {
|
async function handleSubmit(values: NodeManagerSettingStrings) {
|
||||||
try {
|
try {
|
||||||
@@ -56,7 +56,10 @@ export function SettingsStringsEditor() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return <Form onSubmit={handleSubmit} class="flex flex-col gap-4">
|
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 name="network">
|
||||||
{(field, props) => (
|
{(field, props) => (
|
||||||
// TODO: make a cool select component
|
// TODO: make a cool select component
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { cva, VariantProps } from "class-variance-authority";
|
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 { Dynamic } from "solid-js/web";
|
||||||
import { A } from "solid-start";
|
import { A } from "solid-start";
|
||||||
import { LoadingSpinner } from ".";
|
import { LoadingSpinner } from ".";
|
||||||
|
|||||||
@@ -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>)
|
return (<h1 class="text-4xl font-semibold uppercase border-b-2 border-b-white my-4">{props.children}</h1>)
|
||||||
}
|
}
|
||||||
|
|
||||||
const VStack: ParentComponent = (props) => {
|
const VStack: ParentComponent<{ biggap?: boolean }> = (props) => {
|
||||||
return (<div class="flex flex-col gap-4">{props.children}</div>)
|
return (<div class={`flex flex-col gap-${props.biggap ? "8" : "4"}`}>{props.children}</div>)
|
||||||
}
|
}
|
||||||
|
|
||||||
const SmallAmount: ParentComponent<{ amount: number | bigint }> = (props) => {
|
const SmallAmount: ParentComponent<{ amount: number | bigint }> = (props) => {
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ export default function WaitlistForm() {
|
|||||||
</h2>
|
</h2>
|
||||||
<Form onSubmit={newHandleSubmit} class="flex flex-col gap-8">
|
<Form onSubmit={newHandleSubmit} class="flex flex-col gap-8">
|
||||||
<Field name="user_type">
|
<Field name="user_type">
|
||||||
{(field, props) => (
|
{(field, _props) => (
|
||||||
// TODO: there's probably a "real" way to do this with modular-forms
|
// 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} />
|
<StyledRadioGroup value={field.value || "nostr"} onValueChange={(newValue) => setValue(waitlistForm, "user_type", newValue as "nostr" | "email")} choices={COMMUNICATION_METHODS} />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,18 +1,25 @@
|
|||||||
|
import { DeleteEverything } from "~/components/DeleteEverything";
|
||||||
import KitchenSink from "~/components/KitchenSink";
|
import KitchenSink from "~/components/KitchenSink";
|
||||||
import NavBar from "~/components/NavBar";
|
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() {
|
export default function Admin() {
|
||||||
return (
|
return (
|
||||||
<SafeArea>
|
<NodeManagerGuard>
|
||||||
<DefaultMain>
|
<SafeArea>
|
||||||
<LargeHeader>Admin</LargeHeader>
|
<DefaultMain>
|
||||||
<VStack>
|
<LargeHeader>Admin</LargeHeader>
|
||||||
<Card><p>If you know what you're doing you're in the right place!</p></Card>
|
<VStack>
|
||||||
<KitchenSink />
|
<Card><p>If you know what you're doing you're in the right place!</p></Card>
|
||||||
</VStack>
|
<KitchenSink />
|
||||||
</DefaultMain>
|
<div class='rounded-xl p-4 flex flex-col gap-2 bg-m-red overflow-x-hidden'>
|
||||||
<NavBar activeTab="none" />
|
<SmallHeader>Danger zone</SmallHeader>
|
||||||
</SafeArea>
|
<DeleteEverything />
|
||||||
|
</div>
|
||||||
|
</VStack>
|
||||||
|
</DefaultMain>
|
||||||
|
<NavBar activeTab="none" />
|
||||||
|
</SafeArea>
|
||||||
|
</NodeManagerGuard>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import Reader from "~/components/Reader";
|
import Reader from "~/components/Reader";
|
||||||
import { createEffect, createSignal, Show } from "solid-js";
|
import { createEffect, createSignal, Show } from "solid-js";
|
||||||
import { useNavigate } from "solid-start";
|
import { useNavigate } from "solid-start";
|
||||||
import { Button, SafeArea } from "~/components/layout";
|
import { Button } from "~/components/layout";
|
||||||
|
|
||||||
export default function Scanner() {
|
export default function Scanner() {
|
||||||
const [scanResult, setScanResult] = createSignal<string>();
|
const [scanResult, setScanResult] = createSignal<string>();
|
||||||
|
|||||||
@@ -1,66 +1,28 @@
|
|||||||
import { createSignal } from "solid-js";
|
import { ButtonLink, DefaultMain, LargeHeader, NodeManagerGuard, SafeArea, VStack } from "~/components/layout";
|
||||||
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 NavBar from "~/components/NavBar";
|
||||||
|
import { SeedWords } from "~/components/SeedWords";
|
||||||
import { SettingsStringsEditor } from "~/components/SettingsStringsEditor";
|
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 [store, _actions] = useMegaStore();
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeArea>
|
<NodeManagerGuard>
|
||||||
<DefaultMain>
|
<SafeArea>
|
||||||
<LargeHeader>Settings</LargeHeader>
|
<DefaultMain>
|
||||||
<SettingsStringsEditor />
|
<LargeHeader>Settings</LargeHeader>
|
||||||
<Card title="Random utilities">
|
<VStack biggap>
|
||||||
{/* <Button onClick={clearWaitlistId}>Clear waitlist_id</Button> */}
|
<VStack>
|
||||||
{/* <Button onClick={setTestWaitlistId}>Use test waitlist_id</Button> */}
|
<p class="text-2xl font-light">Write down these words or you'll die!</p>
|
||||||
<Button onClick={confirmReset}>Delete Everything</Button>
|
<SeedWords words={store.node_manager?.show_seed() || ""} />
|
||||||
<ConfirmDialog loading={confirmLoading()} isOpen={confirmOpen()} onConfirm={resetNode} onCancel={() => setConfirmOpen(false)} />
|
</VStack>
|
||||||
</Card>
|
<SettingsStringsEditor />
|
||||||
</DefaultMain>
|
<ButtonLink href="/admin">"I know what I'm doing"</ButtonLink>
|
||||||
<NavBar activeTab="settings" />
|
</VStack>
|
||||||
</SafeArea>
|
</DefaultMain>
|
||||||
|
<NavBar activeTab="settings" />
|
||||||
|
</SafeArea>
|
||||||
|
</NodeManagerGuard>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
export function prettyPrintTime(ts: number) {
|
export function prettyPrintTime(ts: number) {
|
||||||
const options = {
|
const options: Intl.DateTimeFormatOptions = {
|
||||||
weekday: 'long',
|
weekday: 'long',
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
month: 'short',
|
month: 'short',
|
||||||
@@ -8,5 +8,5 @@ export function prettyPrintTime(ts: number) {
|
|||||||
minute: 'numeric'
|
minute: 'numeric'
|
||||||
};
|
};
|
||||||
|
|
||||||
return new Date(ts * 1000).toLocaleString('en-US', options as any);
|
return new Date(ts * 1000).toLocaleString('en-US', options);
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user