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,10 +1,39 @@
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>
<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>
@@ -13,20 +42,20 @@ export default function SetupErrorDisplay(props: { error: Error }) {
{props.error.message}
</p>
<NiceP>
Mutiny requires a modern browser that supports WebAssembly,
LocalStorage, and IndexedDB. Some browsers disable these
features in private mode.
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.
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.)
(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
@@ -42,6 +71,8 @@ export default function SetupErrorDisplay(props: { error: Error }) {
</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,10 +239,22 @@ export const Provider: ParentComponent = (props) => {
console.log("checking for browser compatibility...");
actions.checkBrowserCompat().then((browserIsGood) => {
if (browserIsGood) {
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"))
.then(() =>
console.log("node manager setup done")
)
.catch((e) => {
console.error(e);
setState({ setup_error: eify(e) });
@@ -255,6 +270,8 @@ export const Provider: ParentComponent = (props) => {
});
}
});
}
});
});
// Be reactive to changes in waitlist_id
@@ -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));