From fc057bf988cb00c1636bef2aeda910316ff83879 Mon Sep 17 00:00:00 2001
From: Robert Long <robert@robertlong.me>
Date: Thu, 10 Feb 2022 17:10:36 -0800
Subject: [PATCH] Prevent opening multiple tabs of the same account

---
 src/ClientContext.jsx | 37 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 36 insertions(+), 1 deletion(-)

diff --git a/src/ClientContext.jsx b/src/ClientContext.jsx
index 2034629..20ccd89 100644
--- a/src/ClientContext.jsx
+++ b/src/ClientContext.jsx
@@ -23,6 +23,7 @@ import React, {
   useContext,
 } from "react";
 import { useHistory } from "react-router-dom";
+import { ErrorView } from "./FullScreenView";
 import { initClient, defaultHomeserver } from "./matrix-utils";
 
 const ClientContext = createContext();
@@ -30,7 +31,7 @@ const ClientContext = createContext();
 export function ClientProvider({ children }) {
   const history = useHistory();
   const [
-    { loading, isAuthenticated, isPasswordlessUser, client, userName },
+    { loading, isAuthenticated, isPasswordlessUser, client, userName, error },
     setState,
   ] = useState({
     loading: true,
@@ -38,6 +39,7 @@ export function ClientProvider({ children }) {
     isPasswordlessUser: false,
     client: undefined,
     userName: null,
+    error: undefined,
   });
 
   useEffect(() => {
@@ -172,6 +174,35 @@ export function ClientProvider({ children }) {
     window.location = "/";
   }, [history]);
 
+  useEffect(() => {
+    if ("BroadcastChannel" in window) {
+      const loadTime = Date.now();
+      const broadcastChannel = new BroadcastChannel("matrix-video-chat");
+
+      function onMessage({ data }) {
+        if (data.load !== undefined && data.load > loadTime) {
+          if (client) {
+            client.stopClient();
+          }
+
+          setState((prev) => ({
+            ...prev,
+            error: new Error(
+              "This application has been opened in another tab."
+            ),
+          }));
+        }
+      }
+
+      broadcastChannel.addEventListener("message", onMessage);
+      broadcastChannel.postMessage({ load: loadTime });
+
+      return () => {
+        broadcastChannel.removeEventListener("message", onMessage);
+      };
+    }
+  }, [client]);
+
   const context = useMemo(
     () => ({
       loading,
@@ -195,6 +226,10 @@ export function ClientProvider({ children }) {
     ]
   );
 
+  if (error) {
+    return <ErrorView error={error} />;
+  }
+
   return (
     <ClientContext.Provider value={context}>{children}</ClientContext.Provider>
   );