detect if multiple tabs are open

This commit is contained in:
Paul Miller
2023-06-12 16:51:04 -05:00
parent ed5cddc1b4
commit c1b024baf6
3 changed files with 127 additions and 51 deletions

View File

@@ -1,47 +1,78 @@
import { Title } from "solid-start";
import { DefaultMain, LargeHeader, NiceP, SafeArea } from "~/components/layout";
import { ExternalLink } from "./layout/ExternalLink";
import { Match, Switch } from "solid-js";
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
<Switch>
<Match when={props.error.message.startsWith("Existing tab")}>
<Title>Multiple tabs detected</Title>
<DefaultMain>
<LargeHeader>Multiple tabs 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 currently only supports use in one tab at a
time. It looks like you have another tab open with
Mutiny running. Please close that tab and refresh
this page, or close this tab and refresh the other
one.
</NiceP>
<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>
</Match>
<Match when={true}>
<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>
</span>
</p>
</DefaultMain>
<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>
</Match>
</Switch>
</SafeArea>
);
}

View File

@@ -23,6 +23,7 @@ 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";
const MegaStoreContext = createContext<MegaStore>();
@@ -47,6 +48,7 @@ export type MegaStore = [
activity: MutinyActivity[];
setup_error?: Error;
is_pwa: boolean;
existing_tab_detected: boolean;
},
{
fetchUserStatus(): Promise<UserStatus>;
@@ -85,7 +87,8 @@ export const Provider: ParentComponent = (props) => {
nwc_enabled: localStorage.getItem("nwc_enabled") === "true",
activity: [] as MutinyActivity[],
setup_error: undefined as Error | undefined,
is_pwa: window.matchMedia("(display-mode: standalone)").matches
is_pwa: window.matchMedia("(display-mode: standalone)").matches,
existing_tab_detected: false
});
const actions = {
@@ -236,21 +239,35 @@ export const Provider: ParentComponent = (props) => {
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) });
});
console.log("checking if any other tabs are open");
// 500ms should hopefully be enough time for any tabs to reply
timeout(500).then(() => {
if (state.existing_tab_detected) {
setState({
setup_error: new Error(
"Existing tab detected, aborting setup"
)
});
} else {
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");
};
}
});
}
});
}
@@ -274,6 +291,31 @@ export const Provider: ParentComponent = (props) => {
});
});
onMount(() => {
const channel = new BroadcastChannel("tab-detector");
// First we let everyone know we exist
channel.postMessage({ type: "NEW_TAB" });
channel.onmessage = (e) => {
// If any tabs reply, we know there's an existing tab so abort setup
if (e.data.type === "EXISTING_TAB") {
console.debug("there's an existing tab");
setState({ existing_tab_detected: true });
}
// If we get notified of a new tab, we let it know we exist
if (e.data.type === "NEW_TAB") {
console.debug("a new tab just came online");
channel.postMessage({ type: "EXISTING_TAB" });
}
};
onCleanup(() => {
channel.close();
});
});
const store = [state, actions] as MegaStore;
return (

3
src/utils/timeout.ts Normal file
View File

@@ -0,0 +1,3 @@
// A promise version of timeout
export const timeout = (ms: number) =>
new Promise((resolve) => setTimeout(resolve, ms));