Browse Source

Start chromecast SDK only once

Manav Rathi 1 year ago
parent
commit
3c3955017e

+ 5 - 7
web/apps/cast/src/pages/index.tsx

@@ -5,17 +5,13 @@ import { useRouter } from "next/router";
 import { useEffect, useState } from "react";
 import { storeCastData } from "services/cast";
 import { advertiseCode, getCastData, register } from "services/pair";
-import { useCastReceiver } from "../utils/useCastReceiver";
+import { castReceiverLoadingIfNeeded } from "../utils/cast-receiver";
 
 export default function PairingMode() {
     const [publicKeyB64, setPublicKeyB64] = useState<string | undefined>();
     const [privateKeyB64, setPrivateKeyB64] = useState<string | undefined>();
     const [pairingCode, setPairingCode] = useState<string | undefined>();
 
-    // The returned cast object is a reference to a global instance and can be
-    // used in a useEffect dependency list.
-    const cast = useCastReceiver();
-
     const router = useRouter();
 
     const init = () => {
@@ -31,8 +27,10 @@ export default function PairingMode() {
     }, []);
 
     useEffect(() => {
-        if (cast) advertiseCode(cast, () => pairingCode);
-    }, [cast]);
+        castReceiverLoadingIfNeeded().then((cast) =>
+            advertiseCode(cast, () => pairingCode),
+        );
+    }, []);
 
     const pollTick = async () => {
         const registration = { publicKeyB64, privateKeyB64, pairingCode };

+ 1 - 1
web/apps/cast/src/services/pair.ts

@@ -3,7 +3,7 @@ import { boxSealOpen, toB64 } from "@ente/shared/crypto/internal/libsodium";
 import castGateway from "@ente/shared/network/cast";
 import { wait } from "@ente/shared/utils";
 import _sodium from "libsodium-wrappers";
-import { type Cast } from "../utils/useCastReceiver";
+import { type Cast } from "../utils/cast-receiver";
 
 export interface Registration {
     /** A pairing code shown on the screen. A client can use this to connect. */

+ 32 - 0
web/apps/cast/src/utils/cast-receiver.tsx

@@ -0,0 +1,32 @@
+/// <reference types="chromecast-caf-receiver" />
+
+export type Cast = typeof cast;
+
+let _cast: Cast | undefined;
+let _loader: Promise<Cast> | undefined;
+
+/**
+ * Load the Chromecast Web Receiver SDK and return a reference to the `cast`
+ * global object that the SDK attaches to the window.
+ *
+ * Calling this function multiple times is fine, once the Chromecast SDK is
+ * loaded it'll thereafter return the reference to the same object always.
+ *
+ * https://developers.google.com/cast/docs/web_receiver/basic
+ */
+export const castReceiverLoadingIfNeeded = async (): Promise<Cast> => {
+    if (_cast) return _cast;
+    if (_loader) return await _loader;
+
+    _loader = new Promise((resolve) => {
+        const script = document.createElement("script");
+        script.src =
+            "https://www.gstatic.com/cast/sdk/libs/caf_receiver/v3/cast_receiver_framework.js";
+
+        script.addEventListener("load", () => resolve(cast));
+        document.body.appendChild(script);
+    });
+    const c = await _loader;
+    _cast = c;
+    return c;
+};

+ 0 - 25
web/apps/cast/src/utils/useCastReceiver.tsx

@@ -1,25 +0,0 @@
-/// <reference types="chromecast-caf-receiver" />
-import { useEffect, useState } from "react";
-
-export type Cast = typeof cast;
-
-/**
- * Load the Chromecast Web Receiver SDK and return a reference to the `cast`
- * global object that the SDK attaches to the window.
- *
- * https://developers.google.com/cast/docs/web_receiver/basic
- */
-export const useCastReceiver = () => {
-    const [receiver, setReceiver] = useState<Cast | undefined>();
-
-    useEffect(() => {
-        const script = document.createElement("script");
-        script.src =
-            "https://www.gstatic.com/cast/sdk/libs/caf_receiver/v3/cast_receiver_framework.js";
-
-        script.addEventListener("load", () => setReceiver(cast));
-        document.body.appendChild(script);
-    }, []);
-
-    return receiver;
-};