diff --git a/smart-hut/src/components/SceneModal.js b/smart-hut/src/components/SceneModal.js index 2219a80..be03283 100644 --- a/smart-hut/src/components/SceneModal.js +++ b/smart-hut/src/components/SceneModal.js @@ -7,6 +7,7 @@ import { Responsive, Form, Input, + Dropdown, } from "semantic-ui-react"; import SelectIcons from "./SelectIcons"; import { connect } from "react-redux"; @@ -22,6 +23,15 @@ class SceneModal extends Component { this.modifySceneModal = this.modifySceneModal.bind(this); this.deleteScene = this.deleteScene.bind(this); this.updateIcon = this.updateIcon.bind(this); + this.setCopyFrom = this.setCopyFrom.bind(this); + } + + componentDidUpdate(oldProps) { + // this might bug out since we are just checking the length + // to see if the elements inside this.props.scenes are changing + if (this.props.scenes.length !== oldProps.scenes.length) { + this.setState({ ...this.state, scenes: this.scenes }); + } } get initialState() { @@ -29,9 +39,19 @@ class SceneModal extends Component { name: this.type === "new" ? "New Scene" : this.props.scene.name, openModal: false, selectedIcon: "home", + scenes: this.scenes, + copyFrom: null, }; } + get scenes() { + return this.props.scenes.map((s) => ({ + key: s.id, + text: s.name, + value: s.id, + })); + } + setInitialState() { this.setState(this.initialState); } @@ -47,7 +67,7 @@ class SceneModal extends Component { }; this.props - .saveScene(data, null) + .saveScene(data, null, this.state.copyFrom) .then(() => { this.setInitialState(); this.closeModal(); @@ -95,6 +115,10 @@ class SceneModal extends Component { this.setState({ selectedIcon: e }); } + setCopyFrom(_, copyFrom) { + this.setState({ ...this.state, copyFrom: copyFrom.value }); + } + render() { const spaceDiv = { background: "#f4f4f4", @@ -155,6 +179,7 @@ class SceneModal extends Component {

Insert the name of the scene:

-

Select an icon:

+
+ {this.type === "new" && ( + + + + + )} {this.type === "modify" ? ( @@ -215,6 +253,7 @@ const setActiveScene = (activeScene) => { const mapStateToProps = (state, ownProps) => ({ scene: ownProps.id ? state.scenes[ownProps.id] : null, + scenes: Object.values(state.scenes), }); const SceneModalContainer = connect( mapStateToProps, diff --git a/smart-hut/src/components/dashboard/devices/Videocam.js b/smart-hut/src/components/dashboard/devices/Videocam.js index 610fb28..de77219 100644 --- a/smart-hut/src/components/dashboard/devices/Videocam.js +++ b/smart-hut/src/components/dashboard/devices/Videocam.js @@ -40,8 +40,8 @@ class Videocam extends Component { .catch((err) => console.error("videocamera update error", err)); } else { this.props.updateState( - { id: this.props.state.id, on: turn }, - this.props.state.kind + { id: this.props.stateOrDevice.id, on: turn }, + this.props.stateOrDevice.kind ); } } @@ -72,11 +72,7 @@ class Videocam extends Component { this.setOnOff(val.checked)} /> diff --git a/smart-hut/src/remote.js b/smart-hut/src/remote.js index 695e347..666910b 100644 --- a/smart-hut/src/remote.js +++ b/smart-hut/src/remote.js @@ -447,11 +447,14 @@ export const RemoteService = { * Creates/Updates a scene with the given data * @param {String} data.name the scene's name, * @param {Number|null} sceneId the scene's id if update, null for creation + * @param {Number|null} copyFrom the id of the scene from which the states must be copied from. + * (ignored for updates) * @returns {Promise} promise that resolves to void and rejects * with user-fiendly errors as a RemoteError */ - saveScene: (data, sceneId = null) => { + saveScene: (data, sceneId = null, copyFrom = null) => { return (dispatch) => { + copyFrom = sceneId === null ? copyFrom : null; data = { name: data.name, icon: data.icon, @@ -461,12 +464,31 @@ export const RemoteService = { ? Endpoint.put(`/scene/${sceneId}`, {}, data) : Endpoint.post(`/scene`, {}, data) ) - .then((res) => void dispatch(actions.sceneSave(res.data))) + .then(async (res) => { + let states = []; + + if (copyFrom) { + const sceneId = res.data.id; + try { + const res = await Endpoint.post( + `/scene/${sceneId}/copyFrom/${copyFrom}` + ); + states = res.data; + } catch (e) { + console.warn("Error in state cloning from scene " + copyFrom, e); + throw new RemoteError(["Network error"]); + } + } + + dispatch(actions.sceneSave(res.data)); + if (states.length > 0) { + dispatch(actions.statesUpdate(sceneId, states)); + } + }) .catch(parseValidationErrors); }; }, - // updateState: (data, type) => { return (dispatch) => { let url; diff --git a/smart-hut/src/store.js b/smart-hut/src/store.js index 6af48ed..5583473 100644 --- a/smart-hut/src/store.js +++ b/smart-hut/src/store.js @@ -3,7 +3,6 @@ import thunk from "redux-thunk"; import update from "immutability-helper"; import reduxWebSocket, { connect } from "@giantmachines/redux-websocket"; import { socketURL } from "./endpoint"; -import actions from "./storeActions"; function reducer(previousState, action) { let newState, change; @@ -125,9 +124,9 @@ function reducer(previousState, action) { //console.log(action.sceneStates); change = null; - // if room is given, delete all devices in that room - // and remove any join between that room and deleted - // devices + // if scene is given, delete all sceneStates in that scene + // and remove any join between that scene and deleted + // sceneStates change = { scenes: { [action.sceneId]: { sceneStates: { $set: new Set() } } }, sceneStates: { $unset: [] }, @@ -431,7 +430,7 @@ function reducer(previousState, action) { // This update does not ensure the consistent update of switchId/dimmerId properties // on output devices connected to an input device in this room. Please manually request // all devices again if consistent update is desired - change = { states: { $unset: [] } }; + change = { sceneStates: { $unset: [] } }; for (const id of previousState.scenes[action.sceneId].sceneStates) { change.sceneStates.$unset.push(id);