diff --git a/src/components/SetupErrorDisplay.tsx b/src/components/SetupErrorDisplay.tsx new file mode 100644 index 0000000..82a1b08 --- /dev/null +++ b/src/components/SetupErrorDisplay.tsx @@ -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 ( + + Incompatible browser + + Incompatible browser detected +

+ {props.error.name}:{" "} + {props.error.message} +

+ + Mutiny requires a modern browser that supports WebAssembly, + LocalStorage, and IndexedDB. Some browsers disable these + features in private mode. + + + 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. + + + (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.) + + + Supported Browsers + + +
+

+ Bugs? Feedback?{" "} + + + Create an issue + + +

+ + + ); +} diff --git a/src/logic/browserCompatibility.ts b/src/logic/browserCompatibility.ts new file mode 100644 index 0000000..dd1966d --- /dev/null +++ b/src/logic/browserCompatibility.ts @@ -0,0 +1,66 @@ +export async function checkBrowserCompatibility(): Promise { + // 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 { + 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."); + }; + }); +} diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 8ca4825..b34dd05 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -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 ( - <> - }> - {/* TODO: can you put a suspense around a match? */} - - - - - - - - - - - + }> + + + + + + + + + + + + + ); } diff --git a/src/state/megaStore.tsx b/src/state/megaStore.tsx index c945e73..ddb8f93 100644 --- a/src/state/megaStore.tsx +++ b/src/state/megaStore.tsx @@ -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(); @@ -43,6 +45,7 @@ export type MegaStore = [ wallet_loading: boolean; nwc_enabled: boolean; activity: MutinyActivity[]; + setup_error?: Error; }, { fetchUserStatus(): Promise; @@ -56,6 +59,7 @@ export type MegaStore = [ listTags(): Promise; setNwc(enabled: boolean): void; syncActivity(): Promise; + checkBrowserCompat(): Promise; } ]; @@ -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 { + 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"); + }; + } + }); } }); });