From 9c2a16732fb6f045635fd3942f59bbb11cbe01f8 Mon Sep 17 00:00:00 2001 From: britea Date: Sat, 18 Apr 2020 16:26:12 +0200 Subject: [PATCH] Fixed bugs and Initial Scene Navbar --- smart-hut/src/components/RoomModal.js | 1 - smart-hut/src/components/SceneModal.js | 36 ++++---- .../src/components/dashboard/DevicePanel.js | 2 +- .../src/components/dashboard/ScenesPanel.js | 1 - .../components/dashboard/devices/Device.js | 16 ++-- .../dashboard/devices/DeviceSettingsModal.js | 5 +- .../src/components/dashboard/devices/Light.js | 1 + smart-hut/src/remote.js | 75 +++++++++++++++ smart-hut/src/store.js | 69 +++++++++++++- smart-hut/src/storeActions.js | 12 +++ smart-hut/src/views/Dashboard.js | 17 +++- smart-hut/src/views/ScenesNavbar.js | 91 ++++++++++++++----- 12 files changed, 266 insertions(+), 60 deletions(-) diff --git a/smart-hut/src/components/RoomModal.js b/smart-hut/src/components/RoomModal.js index 08f75d2..eabdb89 100644 --- a/smart-hut/src/components/RoomModal.js +++ b/smart-hut/src/components/RoomModal.js @@ -53,7 +53,6 @@ class RoomModal extends Component { } get type() { - console.log(this.props.id); return !this.props.id ? "new" : "modify"; } diff --git a/smart-hut/src/components/SceneModal.js b/smart-hut/src/components/SceneModal.js index 4f4901d..9183a58 100644 --- a/smart-hut/src/components/SceneModal.js +++ b/smart-hut/src/components/SceneModal.js @@ -26,7 +26,7 @@ class SceneModal extends Component { get initialState() { return { - name: this.type === "new" ? "New Scene" : this.props.room.name, + name: this.type === "new" ? "New Scene" : this.props.scene.name, openModal: false, }; } @@ -40,40 +40,38 @@ class SceneModal extends Component { } addSceneModal = (e) => { - /*let data = { - // DATA HERE - };*/ - // TODO CALL TO REMOTE SERVER TO ADD SCENE - /*this.props - .saveRoom(data, null) + let data = { + name: this.state.name, + }; + + this.props + .saveScene(data, null) .then(() => { this.setInitialState(); this.closeModal(); }) - .catch((err) => console.error("error in creating room", err));*/ + .catch((err) => console.error("error in creating room", err)); }; modifySceneModal = (e) => { - /*let data = { - // DATA HERE - };*/ - // TODO CALL TO REMOTE SERVER TO MODIFY SCENE - /*this.props - .saveRoom(data, this.props.id) + let data = { + name: this.state.name, + }; + + this.props + .saveScene(data, this.props.id) .then(() => { this.setInitialState(); this.closeModal(); }) - .catch((err) => console.error("error in updating room", err));*/ + .catch((err) => console.error("error in updating room", err)); }; deleteScene = (e) => { - // TODO CALL TO REMOTE SERVER TO DELETE SCENE - /* this.props - .deleteRoom(this.props.id) + .deleteScene(this.props.id) .then(() => this.closeModal()) - .catch((err) => console.error("error in deleting room", err));*/ + .catch((err) => console.error("error in deleting room", err)); }; changeSomething = (event) => { diff --git a/smart-hut/src/components/dashboard/DevicePanel.js b/smart-hut/src/components/dashboard/DevicePanel.js index 92cdb7c..2b45083 100644 --- a/smart-hut/src/components/dashboard/DevicePanel.js +++ b/smart-hut/src/components/dashboard/DevicePanel.js @@ -26,7 +26,7 @@ class DevicePanel extends Component { {this.props.devices.map((e, i) => { return ( - + ); })} diff --git a/smart-hut/src/components/dashboard/ScenesPanel.js b/smart-hut/src/components/dashboard/ScenesPanel.js index ca96e0b..0395e0e 100644 --- a/smart-hut/src/components/dashboard/ScenesPanel.js +++ b/smart-hut/src/components/dashboard/ScenesPanel.js @@ -7,7 +7,6 @@ import { Grid } from "semantic-ui-react"; class ScenesPanel extends Component { constructor(props) { super(props); - console.log(this.props.activeScene); } render() { diff --git a/smart-hut/src/components/dashboard/devices/Device.js b/smart-hut/src/components/dashboard/devices/Device.js index 6ad0ac4..c019ddb 100644 --- a/smart-hut/src/components/dashboard/devices/Device.js +++ b/smart-hut/src/components/dashboard/devices/Device.js @@ -32,21 +32,21 @@ class Device extends React.Component { renderDeviceComponent() { switch (this.props.device.kind) { case "regularLight": - return ; + return ; case "sensor": - return ; + return ; case "motionSensor": - return ; + return ; case "buttonDimmer": - return ; + return ; case "knobDimmer": - return ; + return ; case "smartPlug": - return ; + return ; case "switch": - return ; + return ; case "dimmableLight": - return ; + return ; default: throw new Error("Device type unknown"); } diff --git a/smart-hut/src/components/dashboard/devices/DeviceSettingsModal.js b/smart-hut/src/components/dashboard/devices/DeviceSettingsModal.js index 846e8f0..ec979d6 100644 --- a/smart-hut/src/components/dashboard/devices/DeviceSettingsModal.js +++ b/smart-hut/src/components/dashboard/devices/DeviceSettingsModal.js @@ -36,12 +36,12 @@ const SettingsForm = (props) => { return (
- + @@ -112,6 +112,7 @@ class DeviceSettingsModal extends Component { Settings of {this.props.device.name} } promise that resolves to void and rejects + * with user-fiendly errors as a RemoteError + */ + fetchAllScenes: () => { + return (dispatch) => { + return Endpoint.get("/scene") + .then((res) => void dispatch(actions.scenesUpdate(res.data))) + .catch((err) => { + console.error("Fetch all scenes error", err); + throw new RemoteError(["Network error"]); + }); + }; + }, + /** * Fetches all devices in a particular room, or fetches all devices. * This also updates the devices attribute on values in the map rooms. @@ -290,6 +307,25 @@ export const RemoteService = { }; }, + /** + * Fetches all devices in a particular scene, or fetches all devices. + * This also updates the devices attribute on values in the map scenes. + * @param {Number} sceneId the scene to which fetch devices + * from, null to fetch from all scenes + * @returns {Promise} promise that resolves to void and rejects + * with user-fiendly errors as a RemoteError + */ + fetchStates: (sceneId) => { + return (dispatch) => { + return Endpoint.get(`/scene/${sceneId}/states`) + .then((res) => void dispatch(actions.statesUpdate(sceneId, res.data))) + .catch((err) => { + console.error(`Fetch devices sceneId=${sceneId} error`, err); + throw new RemoteError(["Network error"]); + }); + }; + }, + /** * Creates/Updates a room with the given data * @param {String} data.name the room's name, @@ -316,6 +352,28 @@ 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 + * @returns {Promise} promise that resolves to void and rejects + * with user-fiendly errors as a RemoteError + */ + saveScene: (data, sceneId = null) => { + return (dispatch) => { + data = { + name: data.name, + }; + + return (sceneId + ? Endpoint.put(`/scene/${sceneId}`, {}, data) + : Endpoint.post(`/scene`, {}, data) + ) + .then((res) => void dispatch(actions.sceneSave(res.data))) + .catch(parseValidationErrors); + }; + }, + /** * Creates/Updates a device with the given data. If * data.id is truthy, then a update call is performed, @@ -496,6 +554,23 @@ export const RemoteService = { }; }, + /** + * Deletes a scene + * @param {Number} sceneId the id of the scene to delete + * @returns {Promise} promise that resolves to void and rejects + * with user-fiendly errors as a RemoteError + */ + deleteScene: (sceneId) => { + return (dispatch) => { + return Endpoint.delete(`/scene/${sceneId}`) + .then((_) => dispatch(actions.sceneDelete(sceneId))) + .catch((err) => { + console.warn("Scene deletion error", err); + throw new RemoteError(["Network error"]); + }); + }; + }, + /** * Deletes a device * @param {Device} device the device to delete diff --git a/smart-hut/src/store.js b/smart-hut/src/store.js index e8f0d83..984c5a3 100644 --- a/smart-hut/src/store.js +++ b/smart-hut/src/store.js @@ -38,6 +38,35 @@ function reducer(previousState, action) { } }; + const createOrUpdateScene = (scene) => { + if (!newState.scenes[scene.id]) { + newState = update(newState, { + scenes: { [scene.id]: { $set: { ...scene, states: new Set() } } }, + }); + } else { + newState = update(newState, { + scenes: { + [scene.id]: { + name: { $set: scene.name }, + }, + }, + }); + } + + if (newState.pendingJoins.scenes[scene.id]) { + newState = update(newState, { + pendingJoins: { scenes: { $unset: [scene.id] } }, + scenes: { + [scene.id]: { + states: { + $add: [...newState.pendingJoins.scenes[scene.id]], + }, + }, + }, + }); + } + }; + const updateDeviceProps = (device) => { // In some updates the information regarding a device is incomplete // due to a fault in the type system and JPA repository management @@ -63,6 +92,13 @@ function reducer(previousState, action) { createOrUpdateRoom(room); } break; + case "SCENES_UPDATE": + newState = previousState; + console.log(action.scenes); + for (const scene of action.scenes) { + createOrUpdateScene(scene); + } + break; case "DEVICES_UPDATE": change = null; @@ -156,6 +192,10 @@ function reducer(previousState, action) { newState = previousState; createOrUpdateRoom(action.room); break; + case "SCENE_SAVE": + newState = previousState; + createOrUpdateScene(action.scene); + break; case "DEVICE_SAVE": change = { devices: { [action.device.id]: { $set: action.device } }, @@ -201,6 +241,30 @@ function reducer(previousState, action) { change.active = { activeRoom: { $set: -1 } }; } + newState = update(previousState, change); + break; + case "SCENE_DELETE": + console.log("SCENE", action.sceneId); + if (!(action.sceneId in previousState.scenes)) { + console.warn(`Scene to delete ${action.sceneId} does not exist`); + break; + } + + // 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: [] } }; + + for (const id of previousState.scenes[action.sceneId].states) { + change.states.$unset.push(id); + } + + change.scenes = { $unset: [action.sceneId] }; + + if (previousState.active.activeScene === action.sceneId) { + change.active = { activeScene: { $set: -1 } }; + } + newState = update(previousState, change); break; case "DEVICE_DELETE": @@ -280,7 +344,6 @@ function reducer(previousState, action) { } const initState = { - errors: {}, pendingJoins: { rooms: {}, scenes: {}, @@ -299,9 +362,9 @@ const initState = { userInfo: null, /** @type {[integer]Room} */ rooms: {}, - /** @type {[integer]Scenes} */ + /** @type {[integer]Scene} */ scenes: {}, - /** @type {[integer]Automations} */ + /** @type {[integer]Automation} */ automations: {}, /** @type {[integer]Device} */ devices: {}, diff --git a/smart-hut/src/storeActions.js b/smart-hut/src/storeActions.js index e224b99..3afa192 100644 --- a/smart-hut/src/storeActions.js +++ b/smart-hut/src/storeActions.js @@ -17,6 +17,10 @@ const actions = { type: "ROOM_SAVE", room, }), + sceneSave: (scene) => ({ + type: "SCENE_SAVE", + scene, + }), deviceSave: (device) => ({ type: "DEVICE_SAVE", device, @@ -40,6 +44,14 @@ const actions = { type: "ROOM_DELETE", roomId, }), + sceneDelete: (sceneId) => ({ + type: "SCENE_DELETE", + sceneId, + }), + scenesUpdate: (scenes) => ({ + type: "SCENES_UPDATE", + scenes, + }), deviceDelete: (deviceId) => ({ type: "DEVICE_DELETE", deviceId, diff --git a/smart-hut/src/views/Dashboard.js b/smart-hut/src/views/Dashboard.js index f3c204f..11d6d7c 100644 --- a/smart-hut/src/views/Dashboard.js +++ b/smart-hut/src/views/Dashboard.js @@ -19,10 +19,22 @@ import { appActions } from "../storeActions"; class Dashboard extends Component { constructor(props) { super(props); + this.state = this.initialState; + this.setInitialState(); this.selectTab = this.selectTab.bind(this); } + get initialState() { + return { + activeTab: this.activeTab, + }; + } + + setInitialState() { + this.setState(this.initialState); + } + get activeTab() { return this.props.activeTab; } @@ -32,13 +44,14 @@ class Dashboard extends Component { } selectTab(e, { name }) { + this.setState({ activeTab: name }); this.activeTab = name; } renderTab(tab) { switch (tab) { case "Devices": - return ; + return ; case "Scenes": return ; case "Automations": @@ -148,7 +161,7 @@ class Dashboard extends Component { - + {this.renderNavbar(this.activeTab)} diff --git a/smart-hut/src/views/ScenesNavbar.js b/smart-hut/src/views/ScenesNavbar.js index 27862a6..74003ea 100644 --- a/smart-hut/src/views/ScenesNavbar.js +++ b/smart-hut/src/views/ScenesNavbar.js @@ -2,8 +2,8 @@ import React, { Component } from "react"; import { Menu, Button, - Grid, Icon, + Grid, Responsive, Dropdown, } from "semantic-ui-react"; @@ -19,9 +19,12 @@ class ScenesNavbar extends Component { this.state = { editMode: false, }; - + console.log(this.props.scenes); this.toggleEditMode = this.toggleEditMode.bind(this); this.openCurrentModalMobile = this.openCurrentModalMobile.bind(this); + this.selectScene = this.selectScene.bind(this); + + this.getScenes(); } get activeItemScene() { @@ -33,13 +36,21 @@ class ScenesNavbar extends Component { } get activeItemSceneName() { - if (this.props.activeScene === -1) return "Home"; + if (this.props.activeScene === -1) return "Scene"; return this.props.scenes[this.props.activeScene].name; } + getScenes() { + this.props + .fetchAllScenes() + .then(() => console.log(this.props.scenes)) + .catch(console.error); + } + openCurrentModalMobile() { - console.log(this.activeItem, this.props.roomModalRefs); - const currentModal = this.props.roomModalRefs[this.activeItem].current; + console.log(this.activeItemScene, this.props.sceneModalRefs); + const currentModal = this.props.sceneModalRefs[this.activeItemScene] + .current; currentModal.openModal(); } @@ -47,6 +58,10 @@ class ScenesNavbar extends Component { this.setState((prevState) => ({ editMode: !prevState.editMode })); } + selectScene(e, { id }) { + this.activeItemScene = id || -1; + } + render() { return (
@@ -66,18 +81,32 @@ class ScenesNavbar extends Component { active={this.activeItemScene === -1} onClick={this.selectScene} > - - - - - - SCENES - - + SCENES - { - //INSERT LIST OF SCENES HERE - } + + {Object.values(this.props.scenes).map((e, i) => { + return ( + + + + {e.name} + + {this.state.editMode ? ( + + ) : null} + + + + + ); + })} + @@ -103,17 +132,33 @@ class ScenesNavbar extends Component { > - - - Scene - { - //INSERT LIST OF SCENES HERE - } + {Object.values(this.props.scenes).map((e, i) => { + return ( + + + + {e.name} + + + + + ); + })} @@ -131,7 +176,7 @@ class ScenesNavbar extends Component { onClick={this.openCurrentModalMobile} > - EDIT ROOM + EDIT SCENE ) : null}