);
@@ -176,7 +180,7 @@ class Sensor extends Component {
dy="0.4em"
fontWeight="bold"
>
- {this.setName()} ({this.props.device.id})
+ {this.name}
diff --git a/smart-hut/src/remote.js b/smart-hut/src/remote.js
index 825ba1d..f2b0555 100644
--- a/smart-hut/src/remote.js
+++ b/smart-hut/src/remote.js
@@ -39,8 +39,8 @@ const Endpoint = {
* @param {[String]String} query query ('?') parameters (no params by default)
* @param {any} body the JSON request body
*/
- send: (method, route, query = {}, body = null, registration) => {
- if (!registration && !Endpoint.token) {
+ send: (method, route, query = {}, body = null) => {
+ if (!Endpoint.token) {
throw new Error("No token while performing authenticated request");
}
@@ -48,11 +48,9 @@ const Endpoint = {
method: method,
params: query,
data: ["put", "post"].indexOf(method) !== -1 ? body : null,
- headers: !registration
- ? {
- Authorization: `Bearer ${Endpoint.token}`,
- }
- : {},
+ headers: {
+ Authorization: `Bearer ${Endpoint.token}`,
+ },
}).then((res) => {
if (!res.data && method !== "delete") {
console.error("Response body is empty");
@@ -63,6 +61,28 @@ const Endpoint = {
});
},
+ /**
+ * Performs a non-authenticated post and put request for registration, reset-password
+ * @param {post} the desired method
+ * @param {String} route the desired route (e.g. "/rooms/1/devices")
+ * @param {[String]String} query query ('?') parameters (no params by default)
+ * @param {any} body the JSON request body
+ */
+ sendNA: (method, route, query = {}, body = null) => {
+ return Endpoint.axiosInstance(route, {
+ method: method,
+ params: query,
+ data: ["put", "post"].indexOf(method) !== -1 ? body : null,
+ }).then((res) => {
+ if (!res.data) {
+ console.error("Response body is empty");
+ return null;
+ } else {
+ return res;
+ }
+ });
+ },
+
/**
* Performs login
* @param {String} usernameOrEmail
@@ -111,7 +131,18 @@ const Endpoint = {
* @returns {Promise<*, *>} The Axios-generated promise
*/
post(route, query, body) {
- return this.send("post", route, query, body, true);
+ return this.send("post", route, query, body);
+ },
+
+ /**
+ * Performs a non-authenticated POST request
+ * @param {String} route the desired route (e.g. "/rooms/1/devices")
+ * @param {[String]String} query query ('?') parameters (no params by default)
+ * @param {any} body the JSON request body
+ * @returns {Promise<*, *>} The Axios-generated promise
+ */
+ postNA(route, query, body) {
+ return this.sendNA("post", route, query, body);
},
/**
@@ -125,6 +156,17 @@ const Endpoint = {
return this.send("put", route, query, body);
},
+ /**
+ * Performs a non-authenticated PUT request
+ * @param {String} route the desired route (e.g. "/rooms/1/devices")
+ * @param {[String]String} query query ('?') parameters (no params by default)
+ * @param {any} body the JSON request body
+ * @returns {Promise<*, *>} The Axios-generated promise
+ */
+ putNA(route, query = {}, body = {}) {
+ return this.sendNA("put", route, query, body);
+ },
+
/**
* Performs an authenticated DELETE request
* @param {get|post|put|delete} the desired method
@@ -229,6 +271,23 @@ export const RemoteService = {
};
},
+ /**
+ * Fetches all scenes that belong to this user. This call does not
+ * populate the devices attribute in scenes.
+ * @returns {Promise} 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.
@@ -248,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,
@@ -274,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,
@@ -454,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
@@ -489,7 +606,7 @@ export class Forms {
* with validation errors as a String array
*/
static submitRegistration(data) {
- return Endpoint.post(
+ return Endpoint.postNA(
"/register",
{},
{
@@ -512,7 +629,7 @@ export class Forms {
* with validation errors as a String array
*/
static submitInitResetPassword(email) {
- return Endpoint.post(
+ return Endpoint.postNA(
"/register/init-reset-password",
{},
{
@@ -537,7 +654,7 @@ export class Forms {
* with validation errors as a String array
*/
static submitResetPassword(confirmationToken, password) {
- return Endpoint.post(
+ return Endpoint.putNA(
"/register/reset-password",
{},
{
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/ConfirmForgotPassword.js b/smart-hut/src/views/ConfirmForgotPassword.js
index 99b194b..627b123 100644
--- a/smart-hut/src/views/ConfirmForgotPassword.js
+++ b/smart-hut/src/views/ConfirmForgotPassword.js
@@ -1,79 +1,43 @@
import React, { Component } from "react";
import HomeNavbar from "./../components/HomeNavbar";
-import { Image, Divider, Message, Grid } from "semantic-ui-react";
-
-class Paragraph extends Component {
- state = { visible: true };
-
- handleDismiss = () => {
- this.setState({ visible: false });
-
- setTimeout(() => {
- this.setState({ visible: true });
- }, 2000);
- };
+import {
+ Image,
+ Divider,
+ Message,
+ Grid,
+ Button,
+ Icon,
+ Header,
+ Container,
+} from "semantic-ui-react";
+export default class ConfirmForgotPasswrod extends Component {
render() {
- if (this.state.visible) {
- return (
-
- );
- }
-
return (
-
-
- The message will return in 2s
-
-
-
+
+
+
+ Go Home{" "}
+
+
+
+
+ Link has been sent!
+
+
+
+ An E-mail has been sent to your address, please follow the
+ instructions to create a new password. If you don't find the
+ E-mail please check also the spam folder.
+
+ An E-mail has been sent to your address, confirm your
+ registration by following the enclosed link. If you don't find
+ the E-mail please check also the spam folder.
+