diff --git a/src/areas/elementsCache.js b/src/areas/elementsCache.js new file mode 100644 index 000000000..9e920745f --- /dev/null +++ b/src/areas/elementsCache.js @@ -0,0 +1,140 @@ +/* + elementsCache.js - ESP3D WebUI MainPage file + + Copyright (c) 2020 Luc Lebosse. All rights reserved. + Original code inspiration : 2021 Alexandre Aussourd + + This code is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with This code; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +import { h, render } from "preact" +import { ExtraContentItem } from "../components/ExtraContent" + +const ElementsCache = () => { + return (
) +} + +const elementsCache = { + + has: (id) => { + const cacheHost = document.getElementById("elementsCache") + if (!cacheHost) return false + return cacheHost.querySelector('#' + id) !== null + }, + + create: (id, props) => { + if (!elementsCache.has(id)) { + const cacheHost = document.getElementById("elementsCache") + console.log("Creating element, because it doesn't exist: " + id) + console.log("Current host size is " + cacheHost.children.length) + try { + const container = document.getElementById("elementsCache") + const vnode = container.appendChild(document.createElement('div')) + vnode.id = "new_element" + id + const new_vnode = render(, container, vnode) + console.log("Element created: " + id + ", exists in cache: " + elementsCache.has(id)) + console.log("Now Current host size is " + cacheHost.children.length) + console.log(cacheHost.innerHTML) + return true + } catch (error) { + console.error(`Error creating element ${id}:`, error) + return false + } + } else { + //console.log("Element already exists: " + id) + return true + } + }, + + get: (id) => { + const cacheHost = document.getElementById("elementsCache") + if (!cacheHost) return null + return cacheHost.querySelector('#' + id) + }, + + remove: (id) => { + const cacheItem = elementsCache.get(id) + if (cacheItem) { + try { + const cacheHost = document.getElementById("elementsCache") + if (!cacheHost) return false + removeCache = removeChild(cacheHost, cacheItem) + if (removeCache) { + removeCache.remove() + return true + } else { + return false + } + } catch (error) { + console.error(`Error removing element ${id}:`, error) + return false + } + } + return false + }, + + updateState: (id, newState) => { + const element = document.getElementById(id); + console.log("Updating state for element " + id) + console.log(newState) + if (element) { + if ('isVisible' in newState) { + element.style.display = newState.isVisible ? 'block' : 'none'; + } + if ('isFullScreen' in newState) { + if (newState.isFullScreen) { + element.style.position = 'fixed'; + element.style.top = '0'; + element.style.left = '0'; + element.style.width = '100%'; + element.style.height = '100%'; + element.style.zIndex = '9999'; + } else { + // RĂ©tablir les styles normaux + element.style.position = 'absolute'; + element.style.top = ''; + element.style.left = ''; + element.style.width = ''; + element.style.height = ''; + element.style.zIndex = ''; + } + } + return true; + } + return false; + }, + + updatePosition: (id, position) => { + const cacheItem = elementsCache.get(id) + //console.log("Updating position for element " + id + ", exists: " + elementsCache.has(id)) + if (cacheItem) { + try { + //console.log("Updating positions to", position) + cacheItem.style.top = `${position.top}px`; + cacheItem.style.left = `${position.left}px`; + cacheItem.style.width = `${position.width}px`; + cacheItem.style.height = `${position.height}px`; + return true + } catch (error) { + console.error(`Error updating position for element ${id}:`, error) + return false + } + } else { + //console.log("Element " + id + " doesn't exist") + } + return false + } +} + +export { ElementsCache, elementsCache } \ No newline at end of file diff --git a/src/components/App/index.js b/src/components/App/index.js index 7f92d3c11..d8e49da3f 100644 --- a/src/components/App/index.js +++ b/src/components/App/index.js @@ -31,10 +31,12 @@ import { TargetContextProvider } from "../../targets" import { ToastsContainer } from "../Toast" import { ModalContainer } from "../Modal" import { ContentContainer } from "../../areas" +import { ElementsCache } from "../../areas/elementsCache" const App = () => { return (
+ diff --git a/src/components/Controls/FullScreenButton.js b/src/components/Controls/FullScreenButton.js index 11b9bba47..dcc0c354b 100644 --- a/src/components/Controls/FullScreenButton.js +++ b/src/components/Controls/FullScreenButton.js @@ -25,12 +25,17 @@ const isFullScreen = (element) => { return document.fullscreenElement === element } -const FullScreenButton = ({ panelRef, hideOnFullScreen, asButton }) => { +const FullScreenButton = ({ panelRef,panelId, hideOnFullScreen, asButton, onclick }) => { const [isFullScreenMode, setIsFullScreenMode] = useState(false) - + const getPanelRef = () => { + if(panelRef) return panelRef.current + if (panelId)return document.getElementById(panelId) + return null + } useEffect(() => { const handleFullscreenChange = () => { - setIsFullScreenMode(isFullScreen(panelRef.current)) + if(getPanelRef) + setIsFullScreenMode(isFullScreen(getPanelRef)) } document.addEventListener("fullscreenchange", handleFullscreenChange) @@ -41,11 +46,18 @@ const FullScreenButton = ({ panelRef, hideOnFullScreen, asButton }) => { handleFullscreenChange ) } - }, [panelRef]) + }, []) const toggleFullScreen = () => { - if (!isFullScreenMode) { - panelRef.current.requestFullscreen() + if (!isFullScreenMode) {// + if (onclick) { + onclick() + } + if (getPanelRef()){ + getPanelRef().requestFullscreen() + } + + console.log("Fullscreen activated pour " + panelId) } else { if (document.fullscreenElement) { document.exitFullscreen() diff --git a/src/components/ExtraContent/extraContentItem.js b/src/components/ExtraContent/extraContentItem.js new file mode 100644 index 000000000..2aac080aa --- /dev/null +++ b/src/components/ExtraContent/extraContentItem.js @@ -0,0 +1,185 @@ +/* + extraContentItem.js - ESP3D WebUI navigation page file + + Copyright (c) 2020 Luc Lebosse. All rights reserved. + + This code is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with This code; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +import { Fragment, h } from "preact" +import { useState, useEffect, useCallback } from "preact/hooks" +import { espHttpURL } from "../Helpers" +import { useHttpFn } from "../../hooks" +import { + ButtonImg, + FullScreenButton, + CloseButton, + ContainerHelper, +} from "../Controls" +import { T } from "../Translations" +import { Play, Pause, Aperture, RefreshCcw } from "preact-feather" +import { elementsCache } from "../../areas/elementsCache" + +const ExtraContentItem = ({ id, source, type, label, target, refreshtime, isVisible = true, isFullScreen = false, refreshPaused = false }) => { + const [contentUrl, setContentUrl] = useState("") + const { createNewRequest } = useHttpFn + console.log( id) + //console.log("ExtraContentItem rendering for " + id, "is in cache: " + elementsCache.has(id)) + /*const loadContent = useCallback((forceReload = false) => { + if ( elementsCache.has(id) && !forceReload) { + console.log("Content already loaded for " + id) + return + } + if (source.startsWith("http") || forceReload) { + setContentUrl(source) + } else { + const idquery = type === "content" ? type + id : "download" + id + createNewRequest( + espHttpURL(source), + { method: "GET", id: idquery, max: 2 }, + { + onSuccess: handleContentSuccess, + onFail: handleContentError, + } + ) + } + }, [id, source, type, createNewRequest]) + + const handleContentSuccess = useCallback((result) => { + let blob + switch (type) { + case "camera": + case "image": + blob = new Blob([result], { type: "image/jpeg" }) + break + case "extension": + case "content": + blob = new Blob([result], { type: "text/html" }) + break + default: + blob = new Blob([result], { type: "text/plain" }) + } + const url = URL.createObjectURL(blob) + setContentUrl(url) + }, [type]) + + const handleContentError = useCallback((error) => { + console.error(`Error loading content for ${id}:`, error) + const errorContent = `

Error loading content

` + const errorBlob = new Blob([errorContent], { type: "text/html" }) + setContentUrl(URL.createObjectURL(errorBlob)) + }, [id]) +*/ + useEffect(() => { + console.log("ExtraContentItem rendering for " + id, "is in cache: " + elementsCache.has(id)) + }, []) +/* + const handleRefresh = () => loadContent(true) + const toggleFullScreen = () => elementsCache.updateState(id, { isFullScreen: !isFullScreen }) + const toggleRefreshPause = () => elementsCache.updateState(id, { refreshPaused: !refreshPaused }) + + const renderControls = () => ( +
+ {parseInt(refreshtime) === 0 && target === "page" && ( + } + onclick={handleRefresh} + /> + )} + {parseInt(refreshtime) > 0 && type !== "extension" && ( + <> + : } + onclick={toggleRefreshPause} + /> + {type !== "content" && ( + } + onclick={() => { + // Handle screenshot functionality here + }} + /> + )} + + )} + + {target === "panel" && ( + + elementsCache.updateState(id, { isVisible: false })} + /> + + + )} +
+ ) +*/ + const contentStyle = { + position: 'absolute', + top: 0, + left: 0, + width: '100%', + height: '100%', + zIndex: isFullScreen ? '9999' : 'auto', + display: isVisible ? 'block' : 'none', + } + + let content + if (type === "camera" || type === "image") { + content = {label} + } else { + content = ( + - ) - } + + const handleRefresh = () => { + useUiContextFn.haptic() + console.log("Refreshing element " + extra_content_id) + elementsCache.updateState(extra_content_id, { forceRefresh: true }) } - useEffect(() => { - //load using internal http manager - if (!pageSource.startsWith("http")) loadContent(true) - //init timer if any + const handleFullScreen = () => { + setIsFullScreen(!isFullScreen) + console.log("Toggling fullscreen for element " + extra_content_id) + elementsCache.updateState(extra_content_id, { isFullScreen: !isFullScreen }) + } - if (refreshtime != 0 && type != "extension") { - clearInterval(timerIDs[id]) - timerIDs[id] = setInterval(loadContent, refreshtime) - } + const renderControls = () => ( +
+ } + onclick={handleRefresh} + /> + + + elementsCache.updateState(extra_content_id, { isVisible: false })} + /> + +
+ ) - return () => { - //cleanup - if (refreshtime != 0 && type != "extension") { - clearInterval(timerIDs[id]) - } - } - }) - if (target == "page") + if (target === "page") { + console.log("Rendering page element " + extra_content_id) return ( -
- - - +
+ + {renderControls()}
) - if (target == "panel") { - const displayIcon = iconsList[icon] ? iconsList[icon] : "" - //console.log("Panel :", id, "Ref :", panelRef.current) + } + + if (target === "panel") { + // console.log("Rendering panel element " + extra_content_id) + const displayIcon = iconsList[icon] || "" return ( -
- - + + ) } } -export default ExtraContent +export { ExtraContent, ExtraContentItem } \ No newline at end of file diff --git a/src/components/Panels/ExtraPanel.js b/src/components/Panels/ExtraPanel.js index 6449679bd..d07a71254 100644 --- a/src/components/Panels/ExtraPanel.js +++ b/src/components/Panels/ExtraPanel.js @@ -17,7 +17,7 @@ Files.js - ESP3D WebUI component file */ import { h } from "preact" -import ExtraContent from "../ExtraContent" +import {ExtraContent} from "../ExtraContent" /* * Local const diff --git a/src/pages/extrapages/index.js b/src/pages/extrapages/index.js index 06973735a..f6b5c3ffc 100644 --- a/src/pages/extrapages/index.js +++ b/src/pages/extrapages/index.js @@ -18,7 +18,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ import { h } from "preact" -import ExtraContent from "../../components/ExtraContent" +import {ExtraContent} from "../../components/ExtraContent" const ExtraPage = ({ id, source, refreshtime, label, type }) => { return ( diff --git a/src/style/components/_app.scss b/src/style/components/_app.scss index a94a0115c..bc4de2b22 100644 --- a/src/style/components/_app.scss +++ b/src/style/components/_app.scss @@ -109,3 +109,19 @@ body { .modal-container { max-height: 95vh; } + +.page_extra { + height: 100%; + display: flex; + flex-direction: column; +} +.extra-content-container { + height: 100%; + width: 100%; + position:absolute; + background-color: red!important; + color: white!important; + border: 1px solid red!important; + z-index: 99999; + opacity: 1; +} \ No newline at end of file