check for browser features

This commit is contained in:
Paul Miller
2023-06-06 14:56:52 -05:00
committed by Tony Giorgio
parent 379f7e502a
commit 03f5ab667e
4 changed files with 161 additions and 25 deletions

View File

@@ -0,0 +1,47 @@
import { Title } from "solid-start";
import { DefaultMain, LargeHeader, NiceP, SafeArea } from "~/components/layout";
import { ExternalLink } from "./layout/ExternalLink";
export default function SetupErrorDisplay(props: { error: Error }) {
return (
<SafeArea>
<Title>Incompatible browser</Title>
<DefaultMain>
<LargeHeader>Incompatible browser detected</LargeHeader>
<p class="bg-white/10 rounded-xl p-4 font-mono">
<span class="font-bold">{props.error.name}</span>:{" "}
{props.error.message}
</p>
<NiceP>
Mutiny requires a modern browser that supports WebAssembly,
LocalStorage, and IndexedDB. Some browsers disable these
features in private mode.
</NiceP>
<NiceP>
Please make sure your browser supports all these features,
or consider trying another browser. You might also try
disabling certain extensions or "shields" that block these
features.
</NiceP>
<NiceP>
(We'd love to support more private browsers, but we have to
save your wallet data to browser storage or else you will
lose funds.)
</NiceP>
<ExternalLink href="https://github.com/MutinyWallet/mutiny-web/wiki/Browser-Compatibility">
Supported Browsers
</ExternalLink>
<div class="h-full" />
<p class="self-center text-neutral-500 mt-4">
Bugs? Feedback?{" "}
<span class="text-neutral-400">
<ExternalLink href="https://github.com/MutinyWallet/mutiny-web/issues">
Create an issue
</ExternalLink>
</span>
</p>
</DefaultMain>
</SafeArea>
);
}

View File

@@ -0,0 +1,66 @@
export async function checkBrowserCompatibility(): Promise<boolean> {
// Check if we can write to localstorage
console.debug("Checking localstorage");
try {
localStorage.setItem("test", "test");
localStorage.removeItem("test");
} catch (e) {
console.error(e);
throw new Error("LocalStorage is not supported.");
}
// Check if the browser supports WebAssembly
console.debug("Checking WebAssembly");
try {
if (
typeof WebAssembly === "object" &&
typeof WebAssembly.instantiate === "function"
) {
const module = new WebAssembly.Module(
Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00)
);
if (module instanceof WebAssembly.Module) {
// continue
} else {
throw new Error("WebAssembly is not supported.");
}
}
} catch (e) {
console.error(e);
throw new Error("WebAssembly is not supported.");
}
console.debug("Checking indexedDB");
// Check if we can write to IndexedDB
try {
await openDatabase();
} catch (e) {
console.error(e);
throw new Error("IndexedDB is not supported.");
}
return true;
}
function openDatabase(): Promise<void> {
return new Promise((resolve, reject) => {
console.debug("Checking IndexedDB is on the window object");
if (!("indexedDB" in window)) {
reject("IndexedDB not supported.");
return;
}
console.debug("Opening IndexedDB");
const dbName = "compatibility-test-db";
const request = indexedDB.open(dbName, 1);
request.onsuccess = (_event) => {
indexedDB.deleteDatabase(dbName);
resolve();
};
request.onerror = (_event) => {
reject("Error opening database.");
};
});
}

View File

@@ -4,24 +4,25 @@ import { WaitlistAlreadyIn } from "~/components/waitlist/WaitlistAlreadyIn";
import WaitlistForm from "~/components/waitlist/WaitlistForm";
import { useMegaStore } from "~/state/megaStore";
import { FullscreenLoader } from "~/components/layout";
import SetupErrorDisplay from "~/components/SetupErrorDisplay";
export default function Home() {
const [state, _] = useMegaStore();
return (
<>
<Switch fallback={<FullscreenLoader />}>
{/* TODO: can you put a suspense around a match? */}
<Match when={state.user_status === "approved"}>
<App />
</Match>
<Match when={state.user_status === "waitlisted"}>
<WaitlistAlreadyIn />
</Match>
<Match when={state.user_status === "new_here"}>
<WaitlistForm />
</Match>
</Switch>
</>
<Switch fallback={<FullscreenLoader />}>
<Match when={state.setup_error}>
<SetupErrorDisplay error={state.setup_error!} />
</Match>
<Match when={state.user_status === "approved"}>
<App />
</Match>
<Match when={state.user_status === "waitlisted"}>
<WaitlistAlreadyIn />
</Match>
<Match when={state.user_status === "new_here"}>
<WaitlistForm />
</Match>
</Switch>
);
}

View File

@@ -21,6 +21,8 @@ import {
} from "@mutinywallet/mutiny-wasm";
import { ParsedParams } from "~/routes/Scanner";
import { MutinyTagItem } from "~/utils/tags";
import { checkBrowserCompatibility } from "~/logic/browserCompatibility";
import eify from "~/utils/eify";
const MegaStoreContext = createContext<MegaStore>();
@@ -43,6 +45,7 @@ export type MegaStore = [
wallet_loading: boolean;
nwc_enabled: boolean;
activity: MutinyActivity[];
setup_error?: Error;
},
{
fetchUserStatus(): Promise<UserStatus>;
@@ -56,6 +59,7 @@ export type MegaStore = [
listTags(): Promise<MutinyTagItem[]>;
setNwc(enabled: boolean): void;
syncActivity(): Promise<void>;
checkBrowserCompat(): Promise<boolean>;
}
];
@@ -78,7 +82,8 @@ export const Provider: ParentComponent = (props) => {
localStorage.getItem("dismissed_restore_prompt") === "true",
wallet_loading: true,
nwc_enabled: localStorage.getItem("nwc_enabled") === "true",
activity: [] as MutinyActivity[]
activity: [] as MutinyActivity[],
setup_error: undefined as Error | undefined
});
const actions = {
@@ -189,6 +194,14 @@ export const Provider: ParentComponent = (props) => {
setNwc(enabled: boolean) {
localStorage.setItem("nwc_enabled", enabled.toString());
setState({ nwc_enabled: enabled });
},
async checkBrowserCompat(): Promise<boolean> {
try {
return await checkBrowserCompatibility();
} catch (e) {
setState({ setup_error: eify(e) });
return false;
}
}
};
@@ -204,17 +217,26 @@ export const Provider: ParentComponent = (props) => {
!state.mutiny_wallet &&
!state.deleting
) {
console.log("running setup node manager...");
actions
.setupMutinyWallet()
.then(() => console.log("node manager setup done"));
console.log("checking for browser compatibility...");
actions.checkBrowserCompat().then((browserIsGood) => {
if (browserIsGood) {
console.log("running setup node manager...");
actions
.setupMutinyWallet()
.then(() => console.log("node manager setup done"))
.catch((e) => {
console.error(e);
setState({ setup_error: eify(e) });
});
// Setup an event listener to stop the mutiny wallet when the page unloads
window.onunload = async (_e) => {
console.log("stopping mutiny_wallet");
await state.mutiny_wallet?.stop();
console.log("mutiny_wallet stopped");
};
// Setup an event listener to stop the mutiny wallet when the page unloads
window.onunload = async (_e) => {
console.log("stopping mutiny_wallet");
await state.mutiny_wallet?.stop();
console.log("mutiny_wallet stopped");
};
}
});
}
});
});