Skip to content

Commit

Permalink
Fix Scenes
Browse files Browse the repository at this point in the history
  • Loading branch information
Yeon Vinzenz Varapragasam authored and Yeon Vinzenz Varapragasam committed Jan 29, 2024
1 parent e7c454b commit 6681717
Show file tree
Hide file tree
Showing 4 changed files with 283 additions and 80 deletions.
192 changes: 191 additions & 1 deletion src/components/Dialogs/HostManager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<Record<string, any>>({});
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(
Expand Down Expand Up @@ -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<string, any> = {};

// 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<HTMLElement>,
newAlignment: string,
) => {
setAlignment(newAlignment);
};

return (
<div key="nohost-dialog">
Expand Down Expand Up @@ -147,6 +244,99 @@ export default function HostManager() {
{Object.keys(coreParams).map((h, i)=><Instances handleDeleteHost={handleDelete} handleSave={handleSave} instances={Object.keys(coreParams).map((ho)=>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'} />)}
<Instances handleSave={handleSave} handleDeleteHost={handleDelete} instances={Object.keys(coreParams).map((ho)=>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}`} />
</div>)}

<div style={{ marginTop: '1rem'}}>
<div style={{ marginBottom: '1rem'}}>
<Stack direction="row" spacing={2} justifyContent="space-between" alignItems="center">
<Tooltip title="Same scene id with different scene config across all running cors. ATTENTION: You have to make sure yourself, they are not colliding!">
<Typography variant='caption' sx={{ marginBottom: '1rem' }}>Common Scenes <span style={{ background: theme.palette.success.dark, padding: '0 1rem', borderRadius: 3, marginLeft: 8 }}>alpha</span></Typography>
</Tooltip>
{Object.keys(commonScenes).length > 0 && (
<ToggleButtonGroup
size="small"
value={alignment}
exclusive
onChange={handleChange}
aria-label="text alignment"
>
<ToggleButton value="list" aria-label="left aligned">
<FormatListBulleted />
</ToggleButton>
<ToggleButton value="cards" aria-label="centered">
<CalendarViewDay />
</ToggleButton>
</ToggleButtonGroup>
)}
</Stack>
<Divider sx={{ marginBottom: '1rem' }} />
</div>
{Object.keys(commonScenes).length > 0 && (
<>
{alignment === 'list' && <><Box display="flex">
<Box sx={{width: '240px', margin: '0 2rem 0 1rem'}}>Scene</Box>
<Box sx={{flexGrow: 1, marginRight: '0.5rem', textAlign: 'left', paddingLeft: '0.5rem'}}>Actions</Box>
</Box>
<Divider sx={{ marginBottom: '1rem' }} />
<Divider/></>}
{Object.keys(commonScenes).map((sc: string) => alignment === 'cards' ? (
<Card key={sc}
className={classes.root}
sx={{
border: '1px solid',
borderColor: theme.palette.divider,
margin: '0.5rem'
}}>
<CardActionArea
style={{ background: theme.palette.background.default }}
onClick={() => activateCommon(sc)}>
<SceneImage
iconName={commonScenes[sc].scene_image || 'Wallpaper'}
/>
</CardActionArea>
<CardActions>
<Typography key={sc}
className={classes.sceneTitle}
variant="h5"
component="h2"
>
{commonScenes[sc].name}
</Typography>
</CardActions>
</Card>
) : (
<>
<Stack direction="row" spacing={2} alignItems="center" height="calc(32px + 0.5rem)" >
<Box width={90} height={32} marginTop="0.25rem" marginBottom="0.25rem" marginRight="2rem" overflow="hidden">
<SceneImage key={sc} iconName={commonScenes[sc].scene_image || 'Wallpaper'} />
</Box>
<Typography key={sc}
className={classes.sceneTitle}
variant="h5"
component="h2"
width={165}
>
{commonScenes[sc].name}
</Typography>

<Button
variant="text"
sx={{ minWidth: '32px', width: '32px' }}
aria-label="connect"
onClick={() => {
activateCommon(sc)
}}
>
<PlayArrow />
</Button>
</Stack>
<Divider />
</>
)
)}
</>
)}
</div>

</DialogContent>
<DialogActions>
{/* <DialogActions sx={{ justifyContent: 'space-between'}}>
Expand Down
37 changes: 37 additions & 0 deletions src/pages/Scenes/Scenes.styles.tsx
Original file line number Diff line number Diff line change
@@ -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
86 changes: 7 additions & 79 deletions src/pages/Scenes/Scenes.tsx
Original file line number Diff line number Diff line change
@@ -1,60 +1,25 @@
import { useCallback, useEffect, useState } from 'react'
import { makeStyles } from '@mui/styles'
import { useEffect } from 'react'

import {
Card,
CardActionArea,
CardActions,
CardMedia,
Typography,
Grid,
Chip,
useTheme,
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()
Expand All @@ -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
Expand All @@ -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() ? (
<CardMedia
className={classes.media}
image={iconName.split('image:')[1]}
title="Contemplative Reptile"
/>
) : (
<div
className={classes.media}
style={{
height: 140,
maxWidth: 'calc(100% - 2px)',
backgroundSize: 'cover',
backgroundImage: `url("data:image/png;base64,${imageData}")`
}}
title="SceneImage"
/>
)
) : (
<BladeIcon scene className={classes.iconMedia} name={iconName} />
)
}

useEffect(() => {
getScenes()
}, [getScenes])
Expand Down Expand Up @@ -262,7 +188,9 @@ const Scenes = () => {
style={{ background: theme.palette.background.default }}
onClick={() => handleActivateScene(s)}
>
{sceneImage(scenes[s].scene_image || 'Wallpaper')}
<SceneImage
iconName={scenes[s].scene_image || 'Wallpaper'}
/>
<div style={{ position: 'absolute', top: 0, right: 0 }}>
{scenes[s].scene_tags?.split(',').map(
(t: string) =>
Expand Down
Loading

0 comments on commit 6681717

Please sign in to comment.