diff --git a/web/apps/cast/src/components/PairedSuccessfullyOverlay.tsx b/web/apps/cast/src/components/PairedSuccessfullyOverlay.tsx deleted file mode 100644 index 88f4d7c1f..000000000 --- a/web/apps/cast/src/components/PairedSuccessfullyOverlay.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { FilledCircleCheck } from "./FilledCircleCheck"; - -export const PairedSuccessfullyOverlay: React.FC = () => { - return ( -
-
- -

- Pairing Complete -

-

- We're preparing your album. -
This should only take a few seconds. -

-
-
- ); -}; diff --git a/web/apps/cast/src/components/LargeType.tsx b/web/apps/cast/src/components/PairingCode.tsx similarity index 75% rename from web/apps/cast/src/components/LargeType.tsx rename to web/apps/cast/src/components/PairingCode.tsx index 42ccb65e9..fa1474baf 100644 --- a/web/apps/cast/src/components/LargeType.tsx +++ b/web/apps/cast/src/components/PairingCode.tsx @@ -1,6 +1,6 @@ import { styled } from "@mui/material"; -const colourPool = [ +const colors = [ "#87CEFA", // Light Blue "#90EE90", // Light Green "#F08080", // Light Coral @@ -23,27 +23,34 @@ const colourPool = [ "#808000", // Light Olive ]; -export const LargeType = ({ chars }: { chars: string[] }) => { +interface PairingCodeProps { + code: string; +} + +export const PairingCode: React.FC = ({ code }) => { return ( - - {chars.map((char, i) => ( + + {code.split("").map((char, i) => ( {char} ))} - + ); }; -const Container = styled("div")` +const PairingCode_ = styled("div")` + border-radius: 10px; + overflow: hidden; + font-size: 4rem; font-weight: bold; font-family: monospace; diff --git a/web/apps/cast/src/pages/index.tsx b/web/apps/cast/src/pages/index.tsx index 175d75bbc..e703c879f 100644 --- a/web/apps/cast/src/pages/index.tsx +++ b/web/apps/cast/src/pages/index.tsx @@ -1,6 +1,7 @@ import log from "@/next/log"; import EnteSpinner from "@ente/shared/components/EnteSpinner"; -import { LargeType } from "components/LargeType"; +import { styled } from "@mui/material"; +import { PairingCode } from "components/PairingCode"; import { useRouter } from "next/router"; import { useEffect, useState } from "react"; import { storeCastData } from "services/cast"; @@ -65,65 +66,52 @@ export default function Index() { }; return ( - <> -
-
- -

- Enter this code on Ente Photos to pair this - screen -

-
- {pairingCode ? ( - - ) : ( - - )} -
-

- Visit{" "} - - ente.io/cast - {" "} - for help -

-
-
- + + +

+ Enter this code on Ente Photos to pair this screen +

+ {pairingCode ? : } +

+ Visit{" "} + + ente.io/cast + {" "} + for help +

+
); } + +const Container = styled("div")` + height: 100%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + text-align: center; + + h1 { + font-weight: normal; + } + + p { + font-size: 1.2rem; + } + a { + text-decoration: none; + color: #87cefa; + font-weight: bold; + } +`; + +const Spinner: React.FC = () => ( + + + +); + +const Spinner_ = styled("div")` + /* Roughly same height as the pairing code section to roduce layout shift */ + margin-block: 1.7rem; +`; diff --git a/web/apps/cast/src/pages/slideshow.tsx b/web/apps/cast/src/pages/slideshow.tsx index 98426a857..d117f6da7 100644 --- a/web/apps/cast/src/pages/slideshow.tsx +++ b/web/apps/cast/src/pages/slideshow.tsx @@ -1,5 +1,6 @@ import log from "@/next/log"; -import { PairedSuccessfullyOverlay } from "components/PairedSuccessfullyOverlay"; +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"; @@ -9,6 +10,7 @@ export default function Slideshow() { const [loading, setLoading] = useState(true); const [imageURL, setImageURL] = useState(); const [nextImageURL, setNextImageURL] = useState(); + const [isEmpty, setIsEmpty] = useState(false); const router = useRouter(); @@ -24,8 +26,10 @@ export default function Slideshow() { while (!stop) { const { value: urls, done } = await urlGenerator.next(); if (done) { - log.warn("Empty collection"); - pair(); + // No items in this callection can be shown. + setIsEmpty(true); + // Go back to pairing screen after 3 seconds. + setTimeout(pair, 5000); return; } @@ -48,7 +52,61 @@ export default function Slideshow() { console.log("Rendering slideshow", { loading, imageURL, nextImageURL }); - if (loading) return ; + if (loading) return ; + if (isEmpty) return ; return ; } + +const PairingComplete: React.FC = () => { + return ( + + +

Pairing Complete

+

+ We're preparing your album. +
This should only take a few seconds. +

+
+ ); +}; + +const Message: React.FC = ({ children }) => { + return ( + + {children} + + ); +}; + +const Message_ = styled("div")` + display: flex; + min-height: 100svh; + justify-content: center; + align-items: center; + + line-height: 1.5rem; + + h2 { + margin-block-end: 0; + } +`; + +const MessageItems = styled("div")` + display: flex; + flex-direction: column; + align-items: center; + text-align: center; +`; + +const NoItems: React.FC = () => { + return ( + +

Try another album

+

+ This album has no photos that can be shown here +
Please try another album +

+
+ ); +}; diff --git a/web/apps/cast/src/services/cast.ts b/web/apps/cast/src/services/cast.ts index 8cb99c241..38f203db2 100644 --- a/web/apps/cast/src/services/cast.ts +++ b/web/apps/cast/src/services/cast.ts @@ -103,10 +103,10 @@ export const renderableImageURLs = async function* (castData: CastData) { */ let lastYieldTime = Date.now(); - // The first time around advance the lastYieldTime into the future so that + // The first time around regress the lastYieldTime into the past so that // we don't wait around too long for the first slide (we do want to wait a // bit, for the user to see the checkmark animation as reassurance). - lastYieldTime += 7500; /* 7.5 s */ + lastYieldTime -= slideDuration - 2500; /* wait at most 2.5 s */ while (true) { const encryptedFiles = shuffled( @@ -120,6 +120,7 @@ export const renderableImageURLs = async function* (castData: CastData) { if (!isFileEligibleForCast(file)) continue; + console.log("will start createRenderableURL", new Date()); try { urls.push(await createRenderableURL(castToken, file)); haveEligibleFiles = true; @@ -128,6 +129,8 @@ export const renderableImageURLs = async function* (castData: CastData) { continue; } + console.log("did end createRenderableURL", new Date()); + // Need at least a pair. // // There are two scenarios: @@ -137,7 +140,7 @@ export const renderableImageURLs = async function* (castData: CastData) { // - Subsequently, urls will have the "next" / "preloaded" URL left // over from the last time. We'll promote that to being the one // that'll get displayed, and preload another one. - if (urls.length < 2) continue; + // if (urls.length < 2) continue; // The last element of previousURLs is the URL that is currently // being shown on screen. @@ -150,15 +153,17 @@ export const renderableImageURLs = async function* (castData: CastData) { // The URL that'll now get displayed on screen. const url = ensure(urls.shift()); // The URL that we're preloading for next time around. - const nextURL = ensure(urls[0]); + const nextURL = ""; //ensure(urls[0]); previousURLs.push(url); const urlPair: RenderableImageURLPair = [url, nextURL]; const elapsedTime = Date.now() - lastYieldTime; - if (elapsedTime > 0 && elapsedTime < slideDuration) + if (elapsedTime > 0 && elapsedTime < slideDuration) { + console.log("waiting", slideDuration - elapsedTime); await wait(slideDuration - elapsedTime); + } lastYieldTime = Date.now(); yield urlPair; diff --git a/web/apps/cast/src/services/pair.ts b/web/apps/cast/src/services/pair.ts index 0364cf491..893681d32 100644 --- a/web/apps/cast/src/services/pair.ts +++ b/web/apps/cast/src/services/pair.ts @@ -118,11 +118,6 @@ export const advertiseCode = ( const options = new cast.framework.CastReceiverOptions(); // We don't use the media features of the Cast SDK. options.skipPlayersLoad = true; - // TODO:Is this required? The docs say "(The default type of a message bus - // is JSON; if not provided here)." - options.customNamespaces = Object.assign({}); - options.customNamespaces[namespace] = - cast.framework.system.MessageType.JSON; // Do not stop the casting if the receiver is unreachable. A user should be // able to start a cast on their phone and then put it away, leaving the // cast running on their big screen.