diff --git a/web/apps/cast/src/pages/index.tsx b/web/apps/cast/src/pages/index.tsx index fd250c574..ac29edbf2 100644 --- a/web/apps/cast/src/pages/index.tsx +++ b/web/apps/cast/src/pages/index.tsx @@ -4,22 +4,15 @@ import { styled } from "@mui/material"; import { PairingCode } from "components/PairingCode"; import { useRouter } from "next/router"; import { useEffect, useState } from "react"; +import { readCastData, storeCastData } from "services/cast-data"; import { getCastData, register } from "services/pair"; -import { storeCastData } from "services/render"; -import { - advertiseCode, - castReceiverLoadingIfNeeded, -} from "../services/cast-receiver"; +import { advertiseOnChromecast } from "../services/cast-receiver"; export default function Index() { const [publicKeyB64, setPublicKeyB64] = useState(); const [privateKeyB64, setPrivateKeyB64] = useState(); const [pairingCode, setPairingCode] = useState(); - // Keep a boolean flag to ensure that Cast Receiver starts only once even if - // pairing codes change. - const [haveInitializedCast, setHaveInitializedCast] = useState(false); - const router = useRouter(); useEffect(() => { @@ -30,12 +23,10 @@ export default function Index() { setPairingCode(r.pairingCode); }); } else { - if (!haveInitializedCast) { - castReceiverLoadingIfNeeded().then((cast) => { - setHaveInitializedCast(true); - advertiseCode(cast, () => pairingCode); - }); - } + advertiseOnChromecast( + () => pairingCode, + () => readCastData()?.collectionID, + ); } }, [pairingCode]); diff --git a/web/apps/cast/src/pages/slideshow.tsx b/web/apps/cast/src/pages/slideshow.tsx index 8cbc1b3d0..cdf7cab92 100644 --- a/web/apps/cast/src/pages/slideshow.tsx +++ b/web/apps/cast/src/pages/slideshow.tsx @@ -1,10 +1,12 @@ import log from "@/next/log"; +import { ensure } from "@/utils/ensure"; import { styled } from "@mui/material"; import { FilledCircleCheck } from "components/FilledCircleCheck"; import { SlideView } from "components/Slide"; import { useRouter } from "next/router"; import { useEffect, useState } from "react"; -import { readCastData, renderableImageURLs } from "services/render"; +import { readCastData } from "services/cast-data"; +import { renderableImageURLs } from "services/render"; export default function Slideshow() { const [loading, setLoading] = useState(true); @@ -22,7 +24,9 @@ export default function Slideshow() { const loop = async () => { try { - const urlGenerator = renderableImageURLs(readCastData()); + const urlGenerator = renderableImageURLs( + ensure(readCastData()), + ); while (!stop) { const { value: urls, done } = await urlGenerator.next(); if (done) { diff --git a/web/apps/cast/src/services/cast-data.ts b/web/apps/cast/src/services/cast-data.ts new file mode 100644 index 000000000..587d1db32 --- /dev/null +++ b/web/apps/cast/src/services/cast-data.ts @@ -0,0 +1,41 @@ +export interface CastData { + /** The ID of the callection we are casting. */ + collectionID: string; + /** A key to decrypt the collection we are casting. */ + collectionKey: string; + /** A credential to use for fetching media files for this cast session. */ + castToken: string; +} + +/** + * Save the data received after pairing with a sender into local storage. + * + * We will read in back when we start the slideshow. + */ +export const storeCastData = (payload: unknown) => { + if (!payload || typeof payload != "object") + throw new Error("Unexpected cast data"); + + // Iterate through all the keys of the payload object and save them to + // localStorage. We don't validate here, we'll validate when we read these + // values back in `readCastData`. + for (const key in payload) { + window.localStorage.setItem(key, payload[key]); + } +}; + +/** + * Read back the cast data we got after pairing. + * + * Sibling of {@link storeCastData}. It returns undefined if the expected data + * is not present in localStorage. + */ +export const readCastData = (): CastData | undefined => { + const collectionID = localStorage.getItem("collectionID"); + const collectionKey = localStorage.getItem("collectionKey"); + const castToken = localStorage.getItem("castToken"); + + return collectionID && collectionKey && castToken + ? { collectionID, collectionKey, castToken } + : undefined; +}; diff --git a/web/apps/cast/src/services/pair.ts b/web/apps/cast/src/services/pair.ts index 179276abc..6262ed5ec 100644 --- a/web/apps/cast/src/services/pair.ts +++ b/web/apps/cast/src/services/pair.ts @@ -3,7 +3,6 @@ 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 "./cast-receiver"; export interface Registration { /** A pairing code shown on the screen. A client can use this to connect. */ diff --git a/web/apps/cast/src/services/render.ts b/web/apps/cast/src/services/render.ts index 38f203db2..419b9cd49 100644 --- a/web/apps/cast/src/services/render.ts +++ b/web/apps/cast/src/services/render.ts @@ -4,11 +4,12 @@ import { decodeLivePhoto } from "@/media/live-photo"; import { nameAndExtension } from "@/next/file"; import log from "@/next/log"; import { shuffled } from "@/utils/array"; -import { ensure, ensureString } from "@/utils/ensure"; +import { ensure } from "@/utils/ensure"; import ComlinkCryptoWorker from "@ente/shared/crypto"; import HTTPService from "@ente/shared/network/HTTPService"; import { getCastFileURL, getEndpoint } from "@ente/shared/network/api"; import { wait } from "@ente/shared/utils"; +import type { CastData } from "services/cast-data"; import { detectMediaMIMEType } from "services/detect-type"; import { EncryptedEnteFile, @@ -17,42 +18,6 @@ import { FilePublicMagicMetadata, } from "types/file"; -/** - * Save the data received after pairing with a sender into local storage. - * - * We will read in back when we start the slideshow. - */ -export const storeCastData = (payload: unknown) => { - if (!payload || typeof payload != "object") - throw new Error("Unexpected cast data"); - - // Iterate through all the keys of the payload object and save them to - // localStorage. We don't validate here, we'll validate when we read these - // values back in `readCastData`. - for (const key in payload) { - window.localStorage.setItem(key, payload[key]); - } -}; - -interface CastData { - /** A key to decrypt the collection we are casting. */ - collectionKey: string; - /** A credential to use for fetching media files for this cast session. */ - castToken: string; -} - -/** - * Read back the cast data we got after pairing. - * - * Sibling of {@link storeCastData}. It throws an error if the expected data is - * not present in localStorage. - */ -export const readCastData = (): CastData => { - const collectionKey = ensureString(localStorage.getItem("collectionKey")); - const castToken = ensureString(localStorage.getItem("castToken")); - return { collectionKey, castToken }; -}; - type RenderableImageURLPair = [url: string, nextURL: string]; /**