Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scene activation = put #36

Merged
merged 10 commits into from
Oct 30, 2022
2 changes: 2 additions & 0 deletions src/components/Bars/BarBottom.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import AddButton from '../AddButton';
import useStyles from './BarBottom.styles';
import YoutubeWidget from '../Integrations/Youtube/YoutubeWidget';
import SpotifyFabPro from '../Integrations/Spotify/SpotifyFabPro';
import EditSceneDialog from '../Dialogs/EditSceneDialog';

export default function BarBottom() {
const classes = useStyles();
Expand Down Expand Up @@ -213,6 +214,7 @@ export default function BarBottom() {
<AddDeviceDialog />
<AddVirtualDialog />
<AddIntegrationDialog />
<EditSceneDialog />
<AddButton
setBackdrop={setBackdrop}
className={`${clsx(classes.addButton, {
Expand Down
59 changes: 51 additions & 8 deletions src/components/Dialogs/AddSceneDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState } from 'react';
import { useEffect, useState } from 'react';
import {
Link,
TextField,
Expand All @@ -14,26 +14,42 @@ import useStore from '../../store/useStore';
const AddSceneDialog = () => {
const [name, setName] = useState('');
const [image, setImage] = useState('');
const [url, setUrl] = useState('');
const [payload, setPayload] = useState('');
const [overwrite, setOverwrite] = useState(false);
const [invalid, setInvalid] = useState(false);

const addScene = useStore((state) => state.addScene);
const getScenes = useStore((state) => state.getScenes);
const scenes = useStore((state) => state.scenes);
const open = useStore((state) => state.dialogs.addScene?.open || false);
const viewMode = useStore((state) => state.viewMode);
const setDialogOpenAddScene = useStore(
(state) => state.setDialogOpenAddScene
);

useEffect(() => {
setInvalid(false);
}, []);
function isValidURL(string: string) {
const res = string.match(
/(?![\s\S])|\d^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w.-]+)+[\w\-._~:/?#[\]@!$&'()*+,;=.]+$/g
);
return res !== null;
}
const handleClose = () => {
setDialogOpenAddScene(false);
};
const handleAddScene = () => {
addScene(name, image).then(() => {
getScenes();
});
setName('');
setImage('');
setDialogOpenAddScene(false);
if (!invalid) {
addScene(name, image, url, payload).then(() => {
getScenes();
});
setName('');
setImage('');
setUrl('');
setPayload('');
setDialogOpenAddScene(false);
}
};

return (
Expand Down Expand Up @@ -107,6 +123,33 @@ const AddSceneDialog = () => {
onChange={(e) => setImage(e.target.value)}
fullWidth
/>
{viewMode !== 'user' && (
<>
<TextField
margin="dense"
id="scene_url"
label="Url"
type="url"
value={url}
onChange={(e) => setUrl(e.target.value)}
fullWidth
error={invalid}
helperText={invalid && 'Enter valid URL!'}
onBlur={(e) => {
setInvalid(!isValidURL(e.target.value));
}}
/>
<TextField
margin="dense"
id="scene_payload"
label="Payload"
type="text"
value={payload}
onChange={(e) => setPayload(e.target.value)}
fullWidth
/>
</>
)}
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Cancel</Button>
Expand Down
171 changes: 171 additions & 0 deletions src/components/Dialogs/EditSceneDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import { useEffect, useState } from 'react';
import {
Link,
TextField,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
Button,
Typography,
} from '@material-ui/core';
import useStore from '../../store/useStore';

const EditSceneDialog = () => {
const [name, setName] = useState('');
const [image, setImage] = useState('');
const [url, setUrl] = useState('');
const [payload, setPayload] = useState('');
const [overwrite, setOverwrite] = useState(false);
const [invalid, setInvalid] = useState(false);
const addScene = useStore((state) => state.addScene);
const getScenes = useStore((state) => state.getScenes);
const scenes = useStore((state) => state.scenes);
const open = useStore((state) => state.dialogs.addScene?.edit || false);
// const key = useStore((state: any) => state.dialogs.addScene?.sceneKey || '');
const data = useStore((state: any) => state.dialogs.addScene?.editData || '');
const viewMode = useStore((state) => state.viewMode);
const setDialogOpenAddScene = useStore(
(state) => state.setDialogOpenAddScene
);

function isValidURL(string: string) {
const res = string.match(
/(?![\s\S])|\d(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_.~#?&//=]*)/g
);
return res !== null;
}

useEffect(() => {
if (data) {
setName(data?.name);
setImage(data?.scene_image);
setUrl(data?.scene_puturl);
setPayload(data?.scene_payload);
}
}, [data]);
const handleClose = () => {
setDialogOpenAddScene(false, false);
};

const handleAddScene = () => {
addScene(name, image, url, payload).then(() => {
getScenes();
});
setName('');
setImage('');
setUrl('');
setPayload('');
setDialogOpenAddScene(false, false);
};

return (
<Dialog
open={open}
onClose={handleClose}
aria-labelledby="form-dialog-title"
>
<DialogTitle id="form-dialog-title">Edit Scene</DialogTitle>
<DialogContent>
Image is optional and can be one of:
<ul style={{ paddingLeft: '1rem' }}>
<li>
iconName{' '}
<Link
href="https://material-ui.com/components/material-icons/"
target="_blank"
>
Find MUI icons here
</Link>
<Typography color="textSecondary" variant="subtitle1">
<em>eg. flare, AccessAlarms</em>
</Typography>
</li>
<li>
mdi:icon-name{' '}
<Link href="https://materialdesignicons.com" target="_blank">
Find Material Design icons here
</Link>
<Typography color="textSecondary" variant="subtitle1">
<em>eg. mdi:balloon, mdi:led-strip-variant</em>
</Typography>
</li>
<li>
image:custom-url
<Typography
color="textSecondary"
variant="subtitle1"
style={{ wordBreak: 'break-all' }}
>
<em>
eg. image:https://i.ytimg.com/vi/4G2unzNoOnY/maxresdefault.jpg
</em>
</Typography>
</li>
</ul>
<TextField
autoFocus
margin="dense"
id="name"
label="Name"
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
onBlur={(e) => {
setOverwrite(
Object.keys(scenes).indexOf(e.target.value.toLowerCase()) > -1
);
}}
error={overwrite}
helperText={overwrite && 'Scene already existing!'}
required
fullWidth
/>
<TextField
margin="dense"
id="scene_image"
label="Image"
type="text"
value={image}
onChange={(e) => setImage(e.target.value)}
fullWidth
/>
{viewMode !== 'user' && (
<>
<TextField
margin="dense"
id="url"
label="Url"
type="url"
value={url}
onChange={(e) => setUrl(e.target.value)}
fullWidth
error={invalid}
helperText={invalid && 'Enter valid URL!'}
onBlur={(e) => {
setInvalid(!isValidURL(e.target.value));
}}
/>
<TextField
margin="dense"
id="payload"
label="Payload"
type="text"
value={payload}
onChange={(e) => setPayload(e.target.value)}
fullWidth
/>
</>
)}
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Cancel</Button>
<Button onClick={handleAddScene}>
{overwrite ? 'Overwrite' : 'Save'}
</Button>
</DialogActions>
</Dialog>
);
};

export default EditSceneDialog;
23 changes: 20 additions & 3 deletions src/pages/Scenes/Scenes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
CardMedia,
Typography,
} from '@material-ui/core';
import { Info } from '@material-ui/icons';
import { Info, Edit } from '@material-ui/icons';
import { Grid } from '@mui/material';
import useStore from '../../store/useStore';
import Popover from '../../components/Popover/Popover';
Expand Down Expand Up @@ -63,7 +63,7 @@ const SceneDialog = ({ info, setInfo, scene }: any) => {
};

const handleUpdateScene = (s: any) => {
addScene(s.name, s.scene_image).then(() => {
addScene(s.name, s.scene_image, s.url, s.payload).then(() => {
getScenes();
});
setDialogOpenAddScene(false);
Expand Down Expand Up @@ -132,12 +132,18 @@ const Scenes = () => {
const getScenes = useStore((state) => state.getScenes);
const scenes = useStore((state) => state.scenes);
const activateScene = useStore((state) => state.activateScene);
const captivateScene = useStore((state) => state.captivateScene);
const deleteScene = useStore((state) => state.deleteScene);
const [info, setInfo] = useState(false);
const [scene, setScene] = useState();

const setDialogOpenAddScene = useStore(
(state) => state.setDialogOpenAddScene
);
const handleActivateScene = (e: string) => {
// console.log('captivate', e, scenes[e]);
activateScene(e);
if (scenes[e]?.scene_puturl && scenes[e]?.scene_payload)
captivateScene(scenes[e]?.scene_puturl, scenes[e]?.scene_payload);
};

const handleDeleteScene = (e: any) => {
Expand Down Expand Up @@ -199,10 +205,21 @@ const Scenes = () => {
{scenes[s].name || s}
</Typography>
<div>
<Button
onClick={() =>
setDialogOpenAddScene(false, true, s, scenes[s])
}
variant="outlined"
color="inherit"
size="small"
>
<Edit />
</Button>
<Popover
onConfirm={() => handleDeleteScene(s)}
variant="outlined"
color="inherit"
style={{ marginLeft: '0.5rem' }}
/>
<Button
onClick={() => handleInfoOpen(s)}
Expand Down
18 changes: 16 additions & 2 deletions src/store/api/storeScenes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
/* eslint-disable no-param-reassign */
/* eslint-disable import/no-cycle */
import produce from 'immer';
// import { string } from 'prop-types';
import { Ledfx } from '../../api/ledfx';

const storeScenes = (set: any) => ({
Expand All @@ -18,8 +19,18 @@ const storeScenes = (set: any) => ({
);
}
},
addScene: async (name: string, scene_image: string) =>
await Ledfx('/api/scenes', 'POST', { name, scene_image }),
addScene: async (
name: string,
scene_image: string,
scene_puturl: string,
scene_payload: string
) =>
await Ledfx('/api/scenes', 'POST', {
name,
scene_image,
scene_puturl,
scene_payload,
}),
activateScene: async (id: string) =>
await Ledfx('/api/scenes', 'PUT', {
id,
Expand All @@ -33,6 +44,9 @@ const storeScenes = (set: any) => ({
}),
deleteScene: async (name: string) =>
await Ledfx('/api/scenes', 'DELETE', { data: { id: name } }),

captivateScene: async (scene_puturl: string, scene_payload: string) =>
await Ledfx(scene_puturl, 'PUT', JSON.parse(scene_payload)),
});

export default storeScenes;
Loading