diff --git a/src/components/Dialogs/HostManager.tsx b/src/components/Dialogs/HostManager.tsx index c69202b3..1e6c5907 100644 --- a/src/components/Dialogs/HostManager.tsx +++ b/src/components/Dialogs/HostManager.tsx @@ -13,20 +13,32 @@ import { Divider, Box, useTheme, + Card, + CardActionArea, + CardActions, + Stack, + ToggleButtonGroup, + ToggleButton, + Tooltip, } from '@mui/material'; -import { Add, Delete } from '@mui/icons-material'; +import { Add, CalendarViewDay, Delete, FormatListBulleted, PlayArrow } from '@mui/icons-material'; import isElectron from 'is-electron'; import useStore from '../../store/useStore'; import Instances from './Instances'; +import SceneImage from '../../pages/Scenes/ScenesImage'; +import useStyles from '../../pages/Scenes/Scenes.styles' export default function HostManager() { const theme = useTheme() + const classes = useStyles() const [instanceVariant] = useState<'buttons' | 'line'>('line'); + const [commonScenes, setCommonScenes] = useState>({}); const dialogOpen = useStore((state) => state.hostManager || false); const edit = useStore((state) => state.dialogs.nohost?.edit || false); const setDialogOpen = useStore((state) => state.setHostManager); const setDisconnected = useStore((state) => state.setDisconnected); const coreParams = useStore((state) => state.coreParams); + const coreStatus = useStore((state) => state.coreStatus); const setHost = useStore((state) => state.setHost); const storedURL = window.localStorage.getItem('ledfx-host'); const storedURLs = JSON.parse( @@ -88,6 +100,91 @@ export default function HostManager() { window.location.href = window.location.href; } }, []); + const runningCores = Object.keys(coreStatus).filter((h)=>coreStatus[h] === 'running').map((h)=>parseInt(coreParams[h][1], 10) || 8888) + + + useEffect(() => { + async function getCommonScenes(rcores: number[]) { + try { + // Fetch all scenes from each core + const allScenes = await Promise.all( + rcores.map(async (h:number) => { + const res = await fetch(`http://localhost:${h}/api/scenes`); + const json = await res.json(); + return json.scenes; + }) + ); + + // Get the keys of the scenes from each core + const sceneKeys = allScenes.map((scenes) => Object.keys(scenes)); + + // Find the common keys + const commonKeys = sceneKeys.reduce((a, b) => a.filter(c => b.includes(c))); + + // Prepare an empty object for the final scenes + const finalScenes: Record = {}; + + // Iterate over the common keys + commonKeys.forEach((key) => { + // Iterate over each scene from all cores + allScenes.forEach((scenes) => { + // If the scene is not in finalScenes, add it + if (!finalScenes[key]) { + finalScenes[key] = { + name: scenes[key]?.name, + scene_image: scenes[key]?.scene_image + }; + } + // If the current scene's image is not "Wallpaper", update it in finalScenes + else if (scenes[key]?.scene_image !== 'Wallpaper') { + finalScenes[key] = { + name: scenes[key]?.name, + scene_image: scenes[key]?.scene_image + }; + } + }); + }); + + return finalScenes; + } catch (e) { + console.log(e); + return {}; + } + } + + getCommonScenes(runningCores).then((res)=>setCommonScenes(res)); + + + + }, [coreStatus, coreParams]); + console.log(commonScenes) + const activateCommon = async (scene: string) => { + try { + await Promise.all( + runningCores.map(async (h:number) => { + const res = await fetch(`http://localhost:${h}/api/scenes`, {method: 'PUT', body: JSON.stringify({ + id: scene, + action: 'activate_in', + ms: 0 + })}); + const json = await res.json(); + return json.scenes; + }) + ); + } + catch (e) { + console.log(e); + } + } + + const [alignment, setAlignment] = useState('cards'); + + const handleChange = ( + event: React.MouseEvent, + newAlignment: string, + ) => { + setAlignment(newAlignment); + }; return (
@@ -147,6 +244,99 @@ export default function HostManager() { {Object.keys(coreParams).map((h, i)=>parseInt(coreParams[ho][1], 10) || 8888)} variant={instanceVariant} i={i} instance={h} port={coreParams[h].length > 0 ? coreParams[h][1] : '8888'} key={coreParams[h].length > 0 ? coreParams[h][1] : '8888'} />)} parseInt(coreParams[ho][1], 10) || 8888)}variant={instanceVariant} instance={false} i={Object.keys(coreParams).length + 1} port={`${parseInt(coreParams[`instance${ Object.keys(coreParams).length }`]?.[1] || '8888', 10) + 1}`} />
)} + +
+
+ + + Common Scenes alpha + + {Object.keys(commonScenes).length > 0 && ( + + + + + + + + + )} + + +
+ {Object.keys(commonScenes).length > 0 && ( + <> + {alignment === 'list' && <> + Scene + Actions + + + } + {Object.keys(commonScenes).map((sc: string) => alignment === 'cards' ? ( + + activateCommon(sc)}> + + + + + {commonScenes[sc].name} + + + + ) : ( + <> + + + + + + {commonScenes[sc].name} + + + + + + + ) + )} + + )} +
+ {/* diff --git a/src/pages/Scenes/Scenes.styles.tsx b/src/pages/Scenes/Scenes.styles.tsx new file mode 100644 index 00000000..2c1b2b99 --- /dev/null +++ b/src/pages/Scenes/Scenes.styles.tsx @@ -0,0 +1,37 @@ +import { makeStyles } from '@mui/styles' + +const useStyles = makeStyles({ + root: { + width: 'min(92vw, 345px)' + }, + sceneTitle: { + fontSize: '1.1rem', + whiteSpace: 'nowrap', + overflow: 'hidden', + textOverflow: 'ellipsis' + }, + '@media (max-width: 580px)': { + root: { + width: '95vw' + }, + sceneTitle: { + fontSize: '1rem', + cursor: 'default' + } + }, + media: { + height: 140 + }, + iconMedia: { + height: 140, + display: 'flex', + alignItems: 'center', + margin: '0 auto', + fontSize: 100, + '& > span:before': { + position: 'relative' + } + } +}) + +export default useStyles diff --git a/src/pages/Scenes/Scenes.tsx b/src/pages/Scenes/Scenes.tsx index ba4a22aa..037869ff 100644 --- a/src/pages/Scenes/Scenes.tsx +++ b/src/pages/Scenes/Scenes.tsx @@ -1,10 +1,9 @@ -import { useCallback, useEffect, useState } from 'react' -import { makeStyles } from '@mui/styles' +import { useEffect } from 'react' + import { Card, CardActionArea, CardActions, - CardMedia, Typography, Grid, Chip, @@ -12,49 +11,15 @@ import { Alert, Collapse } from '@mui/material' -import isElectron from 'is-electron' import useStore from '../../store/useStore' import NoYet from '../../components/NoYet' -import BladeIcon from '../../components/Icons/BladeIcon/BladeIcon' // import ScenesTable from './ScenesTable'; import ScenesRecent from './ScenesRecent' import ScenesMostUsed from './ScenesMostUsed' import ScenesPlaylist from './ScenesPlaylist' import ScenesMenu from './ScenesMenu' - -const useStyles = makeStyles({ - root: { - width: 'min(92vw, 345px)' - }, - sceneTitle: { - fontSize: '1.1rem', - whiteSpace: 'nowrap', - overflow: 'hidden', - textOverflow: 'ellipsis' - }, - '@media (max-width: 580px)': { - root: { - width: '95vw' - }, - sceneTitle: { - fontSize: '1rem', - cursor: 'default' - } - }, - media: { - height: 140 - }, - iconMedia: { - height: 140, - display: 'flex', - alignItems: 'center', - margin: '0 auto', - fontSize: 100, - '& > span:before': { - position: 'relative' - } - } -}) +import useStyles from './Scenes.styles' +import SceneImage from './ScenesImage' const Scenes = () => { const classes = useStyles() @@ -66,7 +31,6 @@ const Scenes = () => { const infoAlerts = useStore((state) => state.ui.infoAlerts) const setInfoAlerts = useStore((state) => state.ui.setInfoAlerts) const sceneActiveTags = useStore((state) => state.ui.sceneActiveTags) - const getImage = useStore((state) => state.getImage) const toggletSceneActiveTag = useStore( (state) => state.ui.toggletSceneActiveTag @@ -79,44 +43,6 @@ const Scenes = () => { captivateScene(scenes[e]?.scene_puturl, scenes[e]?.scene_payload) } - const sceneImage = (iconName: string) => { - const [imageData, setImageData] = useState(null) - const fetchImage = useCallback(async (ic: string) => { - const result = await getImage( - ic.split('image:')[1]?.replaceAll('file:///', '') - ) - setImageData(result.image) - }, []) - useEffect(() => { - if (iconName?.startsWith('image:')) { - fetchImage(iconName) - } - }, [iconName, fetchImage]) - - return iconName && iconName.startsWith('image:') ? ( - isElectron() ? ( - - ) : ( -
- ) - ) : ( - - ) - } - useEffect(() => { getScenes() }, [getScenes]) @@ -262,7 +188,9 @@ const Scenes = () => { style={{ background: theme.palette.background.default }} onClick={() => handleActivateScene(s)} > - {sceneImage(scenes[s].scene_image || 'Wallpaper')} +
{scenes[s].scene_tags?.split(',').map( (t: string) => diff --git a/src/pages/Scenes/ScenesImage.tsx b/src/pages/Scenes/ScenesImage.tsx new file mode 100644 index 00000000..2a78cfa8 --- /dev/null +++ b/src/pages/Scenes/ScenesImage.tsx @@ -0,0 +1,48 @@ +import { useCallback, useEffect, useState } from 'react' +import isElectron from 'is-electron' +import { CardMedia } from '@mui/material' +import useStore from '../../store/useStore' +import useStyles from './Scenes.styles' +import BladeIcon from '../../components/Icons/BladeIcon/BladeIcon' + +const SceneImage = ({ iconName }: { iconName: string }) => { + const classes = useStyles() + const [imageData, setImageData] = useState(null) + const getImage = useStore((state) => state.getImage) + const fetchImage = useCallback(async (ic: string) => { + const result = await getImage( + ic.split('image:')[1]?.replaceAll('file:///', '') + ) + setImageData(result.image) + }, []) + useEffect(() => { + if (iconName?.startsWith('image:')) { + fetchImage(iconName) + } + }, [iconName, fetchImage]) + + return iconName && iconName.startsWith('image:') ? ( + isElectron() ? ( + + ) : ( +
+ ) + ) : ( + + ) +} + +export default SceneImage