From 6c717a7f061efbbe7429a7da4e4d8b7c33ff8ef4 Mon Sep 17 00:00:00 2001 From: Paul Miller Date: Fri, 7 Jul 2023 14:38:16 -0500 Subject: [PATCH] add load bar home and emergency kit --- src/components/App.tsx | 2 + src/components/LoadingIndicator.tsx | 67 ++++++++++++++++++++++++++++ src/logic/mutinyWalletSetup.ts | 17 ++++--- src/logic/waila.ts | 58 ++++++++++++++++++++++++ src/routes/Scanner.tsx | 56 +---------------------- src/routes/Send.tsx | 2 +- src/routes/settings/EmergencyKit.tsx | 22 ++++++--- src/state/megaStore.tsx | 35 ++++++++++++--- 8 files changed, 184 insertions(+), 75 deletions(-) create mode 100644 src/components/LoadingIndicator.tsx create mode 100644 src/logic/waila.ts diff --git a/src/components/App.tsx b/src/components/App.tsx index b2bfe11..30c1760 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -15,6 +15,7 @@ import plusLogo from "~/assets/mutiny-plus-logo.png"; import { PendingNwc } from "./PendingNwc"; import { useI18n } from "~/i18n/context"; import { DecryptDialog } from "./DecryptDialog"; +import { LoadingIndicator } from "./LoadingIndicator"; export default function App() { const [state, _actions] = useMegaStore(); @@ -23,6 +24,7 @@ export default function App() { return ( +
diff --git a/src/components/LoadingIndicator.tsx b/src/components/LoadingIndicator.tsx new file mode 100644 index 0000000..90c5722 --- /dev/null +++ b/src/components/LoadingIndicator.tsx @@ -0,0 +1,67 @@ +import { Progress } from "@kobalte/core"; +import { Show } from "solid-js"; +import { useMegaStore } from "~/state/megaStore"; + +export function LoadingBar(props: { value: number; max: number }) { + function valueToStage(value: number) { + switch (value) { + case 0: + return "Just getting started"; + case 1: + return "Checking user status"; + case 2: + return "Double checking something"; + case 3: + return "Downloading"; + case 4: + return "Setup"; + case 5: + return "Done"; + default: + return "Just getting started"; + } + } + return ( + `Loading: ${valueToStage(value)}`} + class="w-full flex flex-col gap-2" + > + + + + + + ); +} + +export function LoadingIndicator() { + const [state, _actions] = useMegaStore(); + + const loadStageValue = () => { + switch (state.load_stage) { + case "fresh": + return 0; + case "checking_user": + return 1; + case "checking_double_init": + return 2; + case "downloading": + return 3; + case "setup": + return 4; + case "done": + return 5; + default: + return 0; + } + }; + + return ( + + + + ); +} diff --git a/src/logic/mutinyWalletSetup.ts b/src/logic/mutinyWalletSetup.ts index 5bc3684..22bd34c 100644 --- a/src/logic/mutinyWalletSetup.ts +++ b/src/logic/mutinyWalletSetup.ts @@ -1,7 +1,6 @@ /* @refresh reload */ import initMutinyWallet, { MutinyWallet } from "@mutinywallet/mutiny-wasm"; -import initWaila from "@mutinywallet/waila-wasm"; export type Network = "bitcoin" | "testnet" | "regtest" | "signet"; export type MutinyWalletSettingStrings = { @@ -105,10 +104,8 @@ export async function checkForWasm() { } } -export async function setupMutinyWallet( - settings?: MutinyWalletSettingStrings, - password?: string -): Promise { +export async function doubleInitDefense() { + console.log("Starting init..."); // Ultimate defense against getting multiple instances of the wallet running. // If we detect that the wallet has already been initialized in this session, we'll reload the page. // A successful stop of the wallet in onCleanup will clear this flag @@ -121,11 +118,17 @@ export async function setupMutinyWallet( sessionStorage.removeItem("MUTINY_WALLET_INITIALIZED"); window.location.reload(); } +} +export async function initializeWasm() { + // Actually intialize the WASM, this should be the first thing that requires the WASM blob to be downloaded await initMutinyWallet(); - // Might as well init waila while we're at it - await initWaila(); +} +export async function setupMutinyWallet( + settings?: MutinyWalletSettingStrings, + password?: string +): Promise { console.log("Starting setup..."); const { network, proxy, esplora, rgs, lsp, auth, subscriptions } = await setAndGetMutinySettings(settings); diff --git a/src/logic/waila.ts b/src/logic/waila.ts new file mode 100644 index 0000000..2309e71 --- /dev/null +++ b/src/logic/waila.ts @@ -0,0 +1,58 @@ +import initWaila, { PaymentParams } from "@mutinywallet/waila-wasm"; +import { Result } from "~/utils/typescript"; + +// Make sure we've initialzied waila before we try to use it +await initWaila(); + +export type ParsedParams = { + address?: string; + invoice?: string; + amount_sats?: bigint; + network?: string; + memo?: string; + node_pubkey?: string; + lnurl?: string; +}; + +export function toParsedParams( + str: string, + ourNetwork: string +): Result { + let params; + try { + params = new PaymentParams(str || ""); + } catch (e) { + console.error(e); + return { ok: false, error: new Error("Invalid payment request") }; + } + + // If WAILA doesn't return a network we should default to our own + // If the networks is testnet and we're on signet we should use signet + const network = !params.network + ? ourNetwork + : params.network === "testnet" && ourNetwork === "signet" + ? "signet" + : params.network; + + if (network !== ourNetwork) { + return { + ok: false, + error: new Error( + `Destination is for ${params.network} but you're on ${ourNetwork}` + ) + }; + } + + return { + ok: true, + value: { + address: params.address, + invoice: params.invoice, + amount_sats: params.amount_sats, + network, + memo: params.memo, + node_pubkey: params.node_pubkey, + lnurl: params.lnurl + } + }; +} diff --git a/src/routes/Scanner.tsx b/src/routes/Scanner.tsx index 0e8764f..fe2b2dd 100644 --- a/src/routes/Scanner.tsx +++ b/src/routes/Scanner.tsx @@ -2,63 +2,9 @@ import Reader from "~/components/Reader"; import { createEffect, createSignal } from "solid-js"; import { useNavigate } from "solid-start"; import { Button } from "~/components/layout"; -import { PaymentParams } from "@mutinywallet/waila-wasm"; import { showToast } from "~/components/Toaster"; import { useMegaStore } from "~/state/megaStore"; -import { Result } from "~/utils/typescript"; - -export type ParsedParams = { - address?: string; - invoice?: string; - amount_sats?: bigint; - network?: string; - memo?: string; - node_pubkey?: string; - lnurl?: string; -}; - -export function toParsedParams( - str: string, - ourNetwork: string -): Result { - let params; - try { - params = new PaymentParams(str || ""); - } catch (e) { - console.error(e); - return { ok: false, error: new Error("Invalid payment request") }; - } - - // If WAILA doesn't return a network we should default to our own - // If the networks is testnet and we're on signet we should use signet - const network = !params.network - ? ourNetwork - : params.network === "testnet" && ourNetwork === "signet" - ? "signet" - : params.network; - - if (network !== ourNetwork) { - return { - ok: false, - error: new Error( - `Destination is for ${params.network} but you're on ${ourNetwork}` - ) - }; - } - - return { - ok: true, - value: { - address: params.address, - invoice: params.invoice, - amount_sats: params.amount_sats, - network, - memo: params.memo, - node_pubkey: params.node_pubkey, - lnurl: params.lnurl - } - }; -} +import { toParsedParams } from "~/logic/waila"; export default function Scanner() { const [state, actions] = useMegaStore(); diff --git a/src/routes/Send.tsx b/src/routes/Send.tsx index 6e698fb..6d76505 100644 --- a/src/routes/Send.tsx +++ b/src/routes/Send.tsx @@ -26,7 +26,6 @@ import { Scan } from "~/assets/svg/Scan"; import { useMegaStore } from "~/state/megaStore"; import { Contact, MutinyInvoice } from "@mutinywallet/mutiny-wasm"; import { StyledRadioGroup } from "~/components/layout/Radio"; -import { ParsedParams, toParsedParams } from "./Scanner"; import { showToast } from "~/components/Toaster"; import eify from "~/utils/eify"; import megacheck from "~/assets/icons/megacheck.png"; @@ -44,6 +43,7 @@ import { SuccessModal } from "~/components/successfail/SuccessModal"; import { ExternalLink } from "~/components/layout/ExternalLink"; import { InfoBox } from "~/components/InfoBox"; import { useI18n } from "~/i18n/context"; +import { ParsedParams, toParsedParams } from "~/logic/waila"; export type SendSource = "lightning" | "onchain"; diff --git a/src/routes/settings/EmergencyKit.tsx b/src/routes/settings/EmergencyKit.tsx index e9a6a71..1939e63 100644 --- a/src/routes/settings/EmergencyKit.tsx +++ b/src/routes/settings/EmergencyKit.tsx @@ -1,5 +1,6 @@ import { DeleteEverything } from "~/components/DeleteEverything"; import { ImportExport } from "~/components/ImportExport"; +import { LoadingIndicator } from "~/components/LoadingIndicator"; import { Logs } from "~/components/Logs"; import NavBar from "~/components/NavBar"; import { @@ -13,6 +14,19 @@ import { import { BackLink } from "~/components/layout/BackLink"; import { ExternalLink } from "~/components/layout/ExternalLink"; +function EmergencyStack() { + return ( + + + +
+ Danger zone + +
+
+ ); +} + export default function EmergencyKit() { return ( @@ -20,6 +34,7 @@ export default function EmergencyKit() { Emergency Kit + If your wallet seems broken, here are some tools to try to debug and repair it. @@ -31,12 +46,7 @@ export default function EmergencyKit() { reach out to us for support. - - -
- Danger zone - -
+
diff --git a/src/state/megaStore.tsx b/src/state/megaStore.tsx index 0d1ef38..42526f8 100644 --- a/src/state/megaStore.tsx +++ b/src/state/megaStore.tsx @@ -12,20 +12,30 @@ import { import { createStore, reconcile } from "solid-js/store"; import { MutinyWalletSettingStrings, + doubleInitDefense, + initializeWasm, setupMutinyWallet } from "~/logic/mutinyWalletSetup"; import { MutinyBalance, MutinyWallet } from "@mutinywallet/mutiny-wasm"; -import { ParsedParams } from "~/routes/Scanner"; import { MutinyTagItem } from "~/utils/tags"; import { checkBrowserCompatibility } from "~/logic/browserCompatibility"; import eify from "~/utils/eify"; import { timeout } from "~/utils/timeout"; import { ActivityItem } from "~/components/Activity"; +import { ParsedParams } from "~/logic/waila"; const MegaStoreContext = createContext(); type UserStatus = undefined | "new_here" | "waitlisted" | "approved"; +export type LoadStage = + | "fresh" + | "checking_user" + | "checking_double_init" + | "downloading" + | "setup" + | "done"; + export type MegaStore = [ { already_approved?: boolean; @@ -48,6 +58,7 @@ export type MegaStore = [ subscription_timestamp?: number; readonly mutiny_plus: boolean; needs_password: boolean; + load_stage: LoadStage; }, { fetchUserStatus(): Promise; @@ -100,7 +111,8 @@ export const Provider: ParentComponent = (props) => { return false; else return true; }, - needs_password: false + needs_password: false, + load_stage: "fresh" as LoadStage }); const actions = { @@ -175,7 +187,15 @@ export const Provider: ParentComponent = (props) => { throw state.setup_error; } - setState({ wallet_loading: true }); + setState({ + wallet_loading: true, + load_stage: "checking_double_init" + }); + + await doubleInitDefense(); + setState({ load_stage: "downloading" }); + await initializeWasm(); + setState({ load_stage: "setup" }); const mutinyWallet = await setupMutinyWallet( settings, @@ -185,9 +205,6 @@ export const Provider: ParentComponent = (props) => { // If we get this far then we don't need the password anymore setState({ needs_password: false }); - // Get balance optimistically - const balance = await mutinyWallet.get_balance(); - // Subscription stuff. Skip if it's not already in localstorage let subscription_timestamp = undefined; const stored_subscription_timestamp = localStorage.getItem( @@ -214,10 +231,14 @@ export const Provider: ParentComponent = (props) => { } } + // Get balance optimistically + const balance = await mutinyWallet.get_balance(); + setState({ mutiny_wallet: mutinyWallet, wallet_loading: false, subscription_timestamp: subscription_timestamp, + load_stage: "done", balance }); } catch (e) { @@ -318,6 +339,7 @@ export const Provider: ParentComponent = (props) => { // Fetch status from remote on load onMount(() => { + setState({ load_stage: "checking_user" }); // eslint-disable-next-line actions.fetchUserStatus().then((status) => { setState({ user_status: status }); @@ -341,6 +363,7 @@ export const Provider: ParentComponent = (props) => { console.log("stopping mutiny_wallet"); await state.mutiny_wallet?.stop(); console.log("mutiny_wallet stopped"); + sessionStorage.removeItem("MUTINY_WALLET_INITIALIZED"); }; } }