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 ;
+ 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;
-}