diff --git a/src/components/BalanceBox.tsx b/src/components/BalanceBox.tsx index cd0f4ae..d470e3a 100644 --- a/src/components/BalanceBox.tsx +++ b/src/components/BalanceBox.tsx @@ -2,8 +2,8 @@ import { Motion, Presence } from "@motionone/solid"; import { MutinyBalance } from "@mutinywallet/node-manager"; import { createResource, Show, Suspense } from "solid-js"; -import { useNodeManager } from "~/state/nodeManagerState"; import { ButtonLink } from "./Button"; +import { useMegaStore } from "~/state/megaStore"; function prettyPrintAmount(n?: number | bigint): string { if (!n || n.valueOf() === 0) { @@ -17,16 +17,20 @@ function prettyPrintBalance(b: MutinyBalance): string { } export default function BalanceBox() { - const { nodeManager } = useNodeManager(); + const [state, _] = useMegaStore(); const fetchBalance = async () => { - console.log("Refetching balance"); - await nodeManager()?.sync(); - const balance = await nodeManager()?.get_balance(); - return balance + if (state.node_manager) { + console.log("Refetching balance"); + await state.node_manager.sync(); + const balance = await state.node_manager.get_balance(); + return balance + } else { + return undefined + } }; - const [balance, { refetch: refetchBalance }] = createResource(nodeManager, fetchBalance); + const [balance, { refetch: refetchBalance }] = createResource(fetchBalance); return ( diff --git a/src/root.tsx b/src/root.tsx index 26c9126..579af7b 100644 --- a/src/root.tsx +++ b/src/root.tsx @@ -13,7 +13,7 @@ import { Title, } from "solid-start"; import "./root.css"; -import { NodeManagerProvider } from "./state/nodeManagerState"; +import { Provider as MegaStoreProvider } from "./state/megaStore"; export default function Root() { return ( @@ -35,11 +35,11 @@ export default function Root() { - + - + diff --git a/src/routes/Receive.tsx b/src/routes/Receive.tsx index 81f9140..fb49d3a 100644 --- a/src/routes/Receive.tsx +++ b/src/routes/Receive.tsx @@ -3,20 +3,24 @@ import { QRCodeSVG } from "solid-qr-code"; import { Button } from "~/components/Button"; import NavBar from "~/components/NavBar"; import SafeArea from "~/components/SafeArea"; -import { useNodeManager } from "~/state/nodeManagerState"; +import { useMegaStore } from "~/state/megaStore"; import { useCopy } from "~/utils/useCopy"; export default function Receive() { - const { nodeManager } = useNodeManager(); + const [state, _] = useMegaStore() // TODO: would be nice if this was just newest unused address const getNewAddress = async () => { - console.log("Getting new address"); - const address = await nodeManager()?.get_new_address(); - return address + if (state.node_manager) { + console.log("Getting new address"); + const address = await state.node_manager?.get_new_address(); + return address + } else { + return undefined + } }; - const [address, { refetch: refetchAddress }] = createResource(nodeManager, getNewAddress); + const [address, { refetch: refetchAddress }] = createResource(getNewAddress); const [copy, copied] = useCopy({ copiedTimeout: 1000 }); @@ -27,7 +31,7 @@ export default function Receive() { copied(); } - let shareData: ShareData = { + const shareData: ShareData = { title: "Mutiny Wallet", text: address(), } @@ -41,23 +45,25 @@ export default function Receive() { return ( -
- -
- -
-
- - -
-
-
- Address / Invoice -
- {address()} - -
-
+
+
+ +
+ +
+
+ + +
+
+
+ Address / Invoice +
+ {address()} + +
+
+
diff --git a/src/routes/Settings.tsx b/src/routes/Settings.tsx index 5828dc0..b9e9def 100644 --- a/src/routes/Settings.tsx +++ b/src/routes/Settings.tsx @@ -1,18 +1,22 @@ +import { useNavigate } from "solid-start"; import { Button } from "~/components/Button"; import NavBar from "~/components/NavBar"; import SafeArea from "~/components/SafeArea"; +import { useMegaStore } from "~/state/megaStore"; export default function Settings() { + const navigate = useNavigate(); + + const [_, actions] = useMegaStore(); function clearWaitlistId() { - localStorage.removeItem('waitlist_id'); - window.location.reload(); + actions.setWaitlistId(''); + navigate("/") } function setTestWaitlistId() { - localStorage.setItem('waitlist_id', 'npub17zcnktw7svnechf5g666t33d7slw36sz8el3ep4c7kmyfwjhxn9qjvavs6'); - // reload the window - window.location.reload(); + actions.setWaitlistId('npub17zcnktw7svnechf5g666t33d7slw36sz8el3ep4c7kmyfwjhxn9qjvavs6'); + navigate("/") } function resetNode() { @@ -23,8 +27,7 @@ export default function Settings() { localStorage.removeItem(key); } }); - // reload the window - window.location.reload(); + navigate("/") } return ( diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 93170e8..0bb4ac3 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -1,45 +1,27 @@ import App from "~/components/App"; -import { Accessor, createEffect, createResource, Setter, createSignal, Switch, Match } from "solid-js"; +import { Switch, Match } from "solid-js"; import { WaitlistAlreadyIn } from "~/components/waitlist/WaitlistAlreadyIn"; import WaitlistForm from "~/components/waitlist/WaitlistForm"; import ReloadPrompt from "~/components/Reload"; - -function createWaitListSignal(): [Accessor, Setter] { - const [state, setState] = createSignal(""); - const originalState = localStorage.getItem("waitlist_id") - if (originalState) { - setState(localStorage.getItem("waitlist_id") || ""); - } - createEffect(() => localStorage.setItem("waitlist_id", state())); - return [state, setState]; -} - -async function fetchData(source: string) { - if (source) { - const data = await fetch(`https://waitlist.mutiny-waitlist.workers.dev/waitlist/${source}`); - return data.json(); - } else { - return null - } -} +import { useMegaStore } from "~/state/megaStore"; export default function Home() { - // On load, check if the user is already on the waitlist - const [waitlistId] = createWaitListSignal(); - const [waitlistData] = createResource(waitlistId, fetchData); + const [state, _] = useMegaStore(); return ( <> + Loading...} > - + {/* TODO: might need this state.node_manager guard on all wallet routes */} + - + - + diff --git a/src/state/megaStore.tsx b/src/state/megaStore.tsx new file mode 100644 index 0000000..199f71d --- /dev/null +++ b/src/state/megaStore.tsx @@ -0,0 +1,100 @@ +// Inspired by https://github.com/solidjs/solid-realworld/blob/main/src/store/index.js + +import { ParentComponent, createContext, createEffect, useContext } from "solid-js"; +import { createStore } from "solid-js/store"; +import { setupNodeManager } from "~/logic/nodeManagerSetup"; +import { NodeManager } from "@mutinywallet/node-manager"; + +const MegaStoreContext = createContext(); + +type UserStatus = undefined | "new_here" | "waitlisted" | "approved" | "paid" + +export type MegaStore = [{ + waitlist_id: string | null; + node_manager: NodeManager | undefined; + user_status: UserStatus; +}, { + status(): Promise; + setupNodeManager(): Promise; + setWaitlistId(waitlist_id: string): void; +}]; + +export const Provider: ParentComponent = (props) => { + const [state, setState] = createStore({ + + waitlist_id: localStorage.getItem("waitlist_id"), + node_manager: undefined as NodeManager | undefined, + user_status: undefined as UserStatus, + }); + + const actions = { + async status(): Promise { + if (!state.waitlist_id) { + return "new_here" + } + try { + const res = await fetch(`https://waitlist.mutiny-waitlist.workers.dev/waitlist/${state.waitlist_id}`) + const data = await res.json(); + + if (data.approval_date) { + return "approved" + } else { + return "waitlisted" + } + + // TODO: handle paid status + + } catch (e) { + return "new_here" + } + + }, + async setupNodeManager(): Promise { + try { + const nodeManager = await setupNodeManager() + setState({ node_manager: nodeManager }) + } catch (e) { + console.error(e) + } + }, + setWaitlistId(waitlist_id: string) { + setState({ waitlist_id }) + } + }; + + // Fetch status from remote on load + createEffect(async () => { + const status = await actions.status() + setState({ user_status: status }) + }) + + // Only node manager when status is approved + createEffect(async () => { + if (state.user_status === "approved" && !state.node_manager) { + console.log("running setup node manager...") + await actions.setupNodeManager() + } + }) + + // Be reactive to changes in waitlist_id + createEffect(() => { + state.waitlist_id ? localStorage.setItem("waitlist_id", state.waitlist_id) : localStorage.removeItem("waitlist_id"); + }); + + const store = [state, actions] as MegaStore; + + return ( + + {props.children} + + ) +} + +export function useMegaStore() { + // This is a trick to narrow the typescript types: https://docs.solidjs.com/references/api-reference/component-apis/createContext + const context = useContext(MegaStoreContext); + if (!context) { + throw new Error("useMegaStore: cannot find a MegaStoreContext") + } + return context; +} \ No newline at end of file diff --git a/src/state/nodeManagerState.tsx b/src/state/nodeManagerState.tsx deleted file mode 100644 index 1ef4da5..0000000 --- a/src/state/nodeManagerState.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { NodeManager } from "@mutinywallet/node-manager"; -import { createContext, JSX, useContext, createResource, Resource } from "solid-js"; -import { setupNodeManager } from "~/logic/nodeManagerSetup"; - -const NodeManagerContext = createContext<{ nodeManager: Resource }>(); - -export function NodeManagerProvider(props: { children: JSX.Element }) { - const [nodeManager] = createResource(setupNodeManager); - - const value = { - nodeManager, - }; - - return ( - - {props.children} - - ) -} - -export function useNodeManager() { - // This is a trick to narrow the typescript types: https://docs.solidjs.com/references/api-reference/component-apis/createContext - const context = useContext(NodeManagerContext); - if (!context) { - throw new Error("useNodeManager: cannot find a NodeManagerContext") - } - return context; -}