add load bar home and emergency kit

This commit is contained in:
Paul Miller
2023-07-07 14:38:16 -05:00
parent d8467ca1bb
commit 6c717a7f06
8 changed files with 184 additions and 75 deletions

View File

@@ -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 (
<SafeArea>
<DefaultMain>
<LoadingIndicator />
<header class="w-full flex justify-between items-center mt-4 mb-2">
<div class="flex items-center gap-2">
<Switch>

View File

@@ -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 (
<Progress.Root
value={props.value}
minValue={0}
maxValue={props.max}
getValueLabel={({ value }) => `Loading: ${valueToStage(value)}`}
class="w-full flex flex-col gap-2"
>
<Progress.ValueLabel class="text-sm text-m-grey-400" />
<Progress.Track class="h-6 bg-white/10 rounded">
<Progress.Fill class="bg-m-blue rounded h-full w-[var(--kb-progress-fill-width)] transition-[width]" />
</Progress.Track>
</Progress.Root>
);
}
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 (
<Show when={state.load_stage !== "done"}>
<LoadingBar value={loadStageValue()} max={5} />
</Show>
);
}

View File

@@ -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<MutinyWallet> {
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<MutinyWallet> {
console.log("Starting setup...");
const { network, proxy, esplora, rgs, lsp, auth, subscriptions } =
await setAndGetMutinySettings(settings);

58
src/logic/waila.ts Normal file
View File

@@ -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<ParsedParams> {
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
}
};
}

View File

@@ -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<ParsedParams> {
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();

View File

@@ -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";

View File

@@ -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 (
<VStack>
<ImportExport emergency />
<Logs />
<div class="rounded-xl p-4 flex flex-col gap-2 bg-m-red overflow-x-hidden">
<SmallHeader>Danger zone</SmallHeader>
<DeleteEverything emergency />
</div>
</VStack>
);
}
export default function EmergencyKit() {
return (
<SafeArea>
@@ -20,6 +34,7 @@ export default function EmergencyKit() {
<BackLink href="/settings" title="Settings" />
<LargeHeader>Emergency Kit</LargeHeader>
<VStack>
<LoadingIndicator />
<NiceP>
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.
</ExternalLink>
</NiceP>
<ImportExport emergency />
<Logs />
<div class="rounded-xl p-4 flex flex-col gap-2 bg-m-red overflow-x-hidden">
<SmallHeader>Danger zone</SmallHeader>
<DeleteEverything emergency />
</div>
<EmergencyStack />
</VStack>
</DefaultMain>
<NavBar activeTab="settings" />

View File

@@ -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<MegaStore>();
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<UserStatus>;
@@ -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");
};
}
}