diff --git a/index.html b/index.html index f5eebb8..f93684f 100644 --- a/index.html +++ b/index.html @@ -10,7 +10,6 @@ -
diff --git a/src/App.jsx b/src/App.jsx index 06b7ec2..299c7e1 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -8,9 +8,9 @@ import { PresentationProvider } from "./components/PresentationContext"; function App() { return ( - - - + + + ); } diff --git a/src/components/Controls.jsx b/src/components/Controls.jsx index e8cfd84..94497f8 100644 --- a/src/components/Controls.jsx +++ b/src/components/Controls.jsx @@ -1,8 +1,7 @@ -import { Show, createEffect, createSignal, onMount } from "solid-js"; +import { Show } from "solid-js"; import { useFullscreeen } from "../fullscreen"; -import { usePresentationApi, usePresentiationState } from "../presentation"; +import { usePresentationApi } from "../presentation"; import { - CancelPresentationIcon, CastIcon, CastIconFill, FullscreenExitIcon, @@ -13,8 +12,8 @@ import { } from "../icons"; import IconButton from "./IconButton"; import { selectScreens } from "./screen"; -import { useGlobalPageContext, usePage } from "./PresentationContext"; -import { A, useLocation, useNavigate, useParams } from "@solidjs/router"; +import { useGlobalPageContext } from "./PresentationContext"; +import { A } from "@solidjs/router"; import { SLIDE_COUNT } from "./Presentation"; /** @@ -33,33 +32,41 @@ function openWindow(left, top, width, height, url) { return newWindow; } -function PageControls() { - const page = usePage(); +/** + * @typedef {Object} PageControlsProperties + * @property {string} currentPath + */ - const paths = () => ({ - next: SLIDE_COUNT > page() + 1 ? `/${page() + 1}` : "", - previous: page() - 1 >= 0 ? `/${page() - 1}` : "", - }); +/** + * @param {PageControlsProperties} properties + * @returns {import("solid-js").JSX.Element} + */ +function PageControls({ currentPath }) { + const [globalPage, setPage] = useGlobalPageContext(); + const page = () => globalPage() ?? 0; - createEffect(() => console.debug("paths changed", paths())); return ( <> - + setPage(page() - 1)}> - - + + setPage(page() + 1)}> - + ); } -export default function () { + +/** + * @typedef {Object} ControlProperties + * @property {string} currentPath + */ + +/** + * @param {ControlProperties} properties + * @returns {import("solid-js").JSX.Element} + */ +export default function ({ currentPath }) { const { isFullscreen, toggleFullscreen } = useFullscreeen(); const { present, isConnected, isAvailable, close, terminate } = usePresentationApi(); @@ -75,7 +82,6 @@ export default function () { // The order of requesting fullscreen and then opening the window is important // The other way around does not work - // Then fullscreen the current screen with the presentation to the screen await document.documentElement.requestFullscreen({ screen: presentationScreen, }); @@ -98,7 +104,7 @@ export default function () { id="controls" class="in-fullscreen:hidden absolute right-0 bottom-0 flex gap-4 p-4" > - + {document.fullscreenEnabled && ( }> diff --git a/src/components/Home.jsx b/src/components/Home.jsx index ef3efc8..d3ba6ea 100644 --- a/src/components/Home.jsx +++ b/src/components/Home.jsx @@ -10,7 +10,7 @@ export default function () { return ( <> - + ); } diff --git a/src/components/Notes.jsx b/src/components/Notes.jsx index a7b999e..6fb04df 100644 --- a/src/components/Notes.jsx +++ b/src/components/Notes.jsx @@ -1,3 +1,29 @@ +import { Dynamic, Show } from "solid-js/web"; +import { SLIDES, SLIDE_COUNT } from "./Presentation"; +import { useGlobalPageContext } from "./PresentationContext"; +import Controls from "./Controls"; + export default function Control() { - return

Notes

; + const [globalPage, setPage] = useGlobalPageContext(); + const page = () => globalPage() ?? 0; + return ( +
+

+ {page() + 1}/{SLIDE_COUNT} +

+ {/* Show presentation page and next page */} +
+ +
+
+ End of presentation

} + > + +
+
+ +
+ ); } diff --git a/src/components/Presentation.jsx b/src/components/Presentation.jsx index 146fcc7..56049c2 100644 --- a/src/components/Presentation.jsx +++ b/src/components/Presentation.jsx @@ -1,7 +1,7 @@ import { useParams } from "@solidjs/router"; import { Show, createEffect } from "solid-js"; import { Dynamic } from "solid-js/web"; -import { usePage } from "./PresentationContext"; +import { useGlobalPageContext } from "./PresentationContext"; function Slide0() { return ( @@ -28,11 +28,11 @@ function Slide1() { ); } -const SLIDES = [Slide0, Slide1]; +export const SLIDES = [Slide0, Slide1]; export default function () { - const page = usePage(); - + const [globalPage, setPage] = useGlobalPageContext(); + const page = () => globalPage() ?? 0; return ( <> {/* Temporary div to have element containing slides */} diff --git a/src/components/PresentationContext.jsx b/src/components/PresentationContext.jsx index 9ffe399..5f918dc 100644 --- a/src/components/PresentationContext.jsx +++ b/src/components/PresentationContext.jsx @@ -9,9 +9,19 @@ import { import { SLIDE_COUNT } from "./Presentation"; const channel = new BroadcastChannel(""); +const id = crypto.randomUUID(); /** - * @returns {import("solid-js").Signal} + * + * @param {number} page + */ +function updateGlobalPage(page) { + // Persist page + localStorage.setItem("page", page.toString()); + channel.postMessage({ type: "set page", page, id }); +} +/** + * @returns {[import("solid-js").Accessor, (page: number) => void]} */ function createContextValue() { // Can not access solid-js router as this happens before router is set up and outside of it's context @@ -25,21 +35,39 @@ function createContextValue() { // Don't try to extract page number from url for now. It's too hard and I don't feel like it // Setting the number to the same number should not cause any effect as signals handle it internally const [page, setPage] = createSignal(pageNumber); - createEffect(() => { - const currentPage = page(); - if (currentPage === null) return; - // Persist page - localStorage.setItem("page", currentPage.toString()); - channel.postMessage({ type: "set page", page: currentPage }); - }); + console.debug("id", id); + + /** + * + * @param {MessageEvent} event + */ + function handleMessage(event) { + console.debug("message received", event.data); + if (event.data.type === "set page") setPage(event.data.page); + } onMount(() => { - channel.addEventListener("message", (event) => { - if (event.data.type === "set page") setPage(event.data.page); - }); + channel.addEventListener("message", handleMessage); + return () => channel.removeEventListener("message", handleMessage); }); - return [page, setPage]; + /** + * + * @param {number} page + */ + function set(page) { + if (page < 0 || page >= SLIDE_COUNT) return; + // Calling broadcast as effect would cause infinite loop + setPage((previous) => { + if (previous !== page) { + updateGlobalPage(page); + } + + return page; + }); + } + + return [page, set]; } const GlobalPageContext = createContext(createContextValue()); @@ -64,50 +92,3 @@ export function PresentationProvider({ children, location, params, data }) { ); } - -function urlPageNumber() { - const parameters = useParams(); - - // Don't use `"page" in parameters` as it does not trigger the accessor and thus not the signal/reactive system - if (parameters.page === undefined || parameters.page.length === 0) - return null; - - const page = Number(parameters.page); - if (Number.isNaN(page)) return null; - - return page; -} - -/** - * Can only be used inside a route - */ -export function usePage() { - // Manage state from url and global state - const [page, setPage] = useGlobalPageContext(); - - // Url page number always takes precedence as it is set by user interaction - - // Update global page number if user set the state through the url - createEffect(() => { - const urlPage = urlPageNumber(); - if (urlPage === null) return; - - setPage(urlPageNumber()); - }); - - const pageNumber = () => { - const urlPage = urlPageNumber(); - const globalPage = page(); - - if (urlPage !== null) { - // Fix global state - if (urlPage !== globalPage) setPage(urlPage); - - return urlPage; - } - - return globalPage ?? 0; - }; - - return pageNumber; -}