mirror of
https://github.com/aljazceru/mutiny-web.git
synced 2025-12-20 07:44:24 +01:00
detect if multiple tabs are open
This commit is contained in:
@@ -1,10 +1,39 @@
|
|||||||
import { Title } from "solid-start";
|
import { Title } from "solid-start";
|
||||||
import { DefaultMain, LargeHeader, NiceP, SafeArea } from "~/components/layout";
|
import { DefaultMain, LargeHeader, NiceP, SafeArea } from "~/components/layout";
|
||||||
import { ExternalLink } from "./layout/ExternalLink";
|
import { ExternalLink } from "./layout/ExternalLink";
|
||||||
|
import { Match, Switch } from "solid-js";
|
||||||
|
|
||||||
export default function SetupErrorDisplay(props: { error: Error }) {
|
export default function SetupErrorDisplay(props: { error: Error }) {
|
||||||
return (
|
return (
|
||||||
<SafeArea>
|
<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>
|
<Title>Incompatible browser</Title>
|
||||||
<DefaultMain>
|
<DefaultMain>
|
||||||
<LargeHeader>Incompatible browser detected</LargeHeader>
|
<LargeHeader>Incompatible browser detected</LargeHeader>
|
||||||
@@ -13,20 +42,20 @@ export default function SetupErrorDisplay(props: { error: Error }) {
|
|||||||
{props.error.message}
|
{props.error.message}
|
||||||
</p>
|
</p>
|
||||||
<NiceP>
|
<NiceP>
|
||||||
Mutiny requires a modern browser that supports WebAssembly,
|
Mutiny requires a modern browser that supports
|
||||||
LocalStorage, and IndexedDB. Some browsers disable these
|
WebAssembly, LocalStorage, and IndexedDB. Some
|
||||||
features in private mode.
|
browsers disable these features in private mode.
|
||||||
</NiceP>
|
</NiceP>
|
||||||
<NiceP>
|
<NiceP>
|
||||||
Please make sure your browser supports all these features,
|
Please make sure your browser supports all these
|
||||||
or consider trying another browser. You might also try
|
features, or consider trying another browser. You
|
||||||
disabling certain extensions or "shields" that block these
|
might also try disabling certain extensions or
|
||||||
features.
|
"shields" that block these features.
|
||||||
</NiceP>
|
</NiceP>
|
||||||
<NiceP>
|
<NiceP>
|
||||||
(We'd love to support more private browsers, but we have to
|
(We'd love to support more private browsers, but we
|
||||||
save your wallet data to browser storage or else you will
|
have to save your wallet data to browser storage or
|
||||||
lose funds.)
|
else you will lose funds.)
|
||||||
</NiceP>
|
</NiceP>
|
||||||
<ExternalLink href="https://github.com/MutinyWallet/mutiny-web/wiki/Browser-Compatibility">
|
<ExternalLink href="https://github.com/MutinyWallet/mutiny-web/wiki/Browser-Compatibility">
|
||||||
Supported Browsers
|
Supported Browsers
|
||||||
@@ -42,6 +71,8 @@ export default function SetupErrorDisplay(props: { error: Error }) {
|
|||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</DefaultMain>
|
</DefaultMain>
|
||||||
|
</Match>
|
||||||
|
</Switch>
|
||||||
</SafeArea>
|
</SafeArea>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import { ParsedParams } from "~/routes/Scanner";
|
|||||||
import { MutinyTagItem } from "~/utils/tags";
|
import { MutinyTagItem } from "~/utils/tags";
|
||||||
import { checkBrowserCompatibility } from "~/logic/browserCompatibility";
|
import { checkBrowserCompatibility } from "~/logic/browserCompatibility";
|
||||||
import eify from "~/utils/eify";
|
import eify from "~/utils/eify";
|
||||||
|
import { timeout } from "~/utils/timeout";
|
||||||
|
|
||||||
const MegaStoreContext = createContext<MegaStore>();
|
const MegaStoreContext = createContext<MegaStore>();
|
||||||
|
|
||||||
@@ -47,6 +48,7 @@ export type MegaStore = [
|
|||||||
activity: MutinyActivity[];
|
activity: MutinyActivity[];
|
||||||
setup_error?: Error;
|
setup_error?: Error;
|
||||||
is_pwa: boolean;
|
is_pwa: boolean;
|
||||||
|
existing_tab_detected: boolean;
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fetchUserStatus(): Promise<UserStatus>;
|
fetchUserStatus(): Promise<UserStatus>;
|
||||||
@@ -85,7 +87,8 @@ export const Provider: ParentComponent = (props) => {
|
|||||||
nwc_enabled: localStorage.getItem("nwc_enabled") === "true",
|
nwc_enabled: localStorage.getItem("nwc_enabled") === "true",
|
||||||
activity: [] as MutinyActivity[],
|
activity: [] as MutinyActivity[],
|
||||||
setup_error: undefined as Error | undefined,
|
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 = {
|
const actions = {
|
||||||
@@ -236,10 +239,22 @@ export const Provider: ParentComponent = (props) => {
|
|||||||
console.log("checking for browser compatibility...");
|
console.log("checking for browser compatibility...");
|
||||||
actions.checkBrowserCompat().then((browserIsGood) => {
|
actions.checkBrowserCompat().then((browserIsGood) => {
|
||||||
if (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...");
|
console.log("running setup node manager...");
|
||||||
actions
|
actions
|
||||||
.setupMutinyWallet()
|
.setupMutinyWallet()
|
||||||
.then(() => console.log("node manager setup done"))
|
.then(() =>
|
||||||
|
console.log("node manager setup done")
|
||||||
|
)
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
setState({ setup_error: eify(e) });
|
setState({ setup_error: eify(e) });
|
||||||
@@ -255,6 +270,8 @@ export const Provider: ParentComponent = (props) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Be reactive to changes in waitlist_id
|
// 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;
|
const store = [state, actions] as MegaStore;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
3
src/utils/timeout.ts
Normal file
3
src/utils/timeout.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
// A promise version of timeout
|
||||||
|
export const timeout = (ms: number) =>
|
||||||
|
new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
Reference in New Issue
Block a user