Login and logout work on the new system. Nasty error on logout
This commit is contained in:
parent
c36d298f10
commit
462cad0e53
7 changed files with 289 additions and 282 deletions
|
@ -10,6 +10,7 @@
|
||||||
"@testing-library/user-event": "^7.1.2",
|
"@testing-library/user-event": "^7.1.2",
|
||||||
"axios": "^0.19.2",
|
"axios": "^0.19.2",
|
||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
|
"immutability-helper": "^3.0.2",
|
||||||
"material-ui-image": "^3.2.3",
|
"material-ui-image": "^3.2.3",
|
||||||
"react": "^16.12.0",
|
"react": "^16.12.0",
|
||||||
"react-axios": "^2.0.3",
|
"react-axios": "^2.0.3",
|
||||||
|
|
|
@ -12,44 +12,17 @@ import ConfirmRegistration from "./views/ConfirmRegistration";
|
||||||
import ConfirmResetPassword from "./views/ConfirmResetPassword";
|
import ConfirmResetPassword from "./views/ConfirmResetPassword";
|
||||||
import Instruction from "./views/Instruction";
|
import Instruction from "./views/Instruction";
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
|
import { RemoteService } from "./remote";
|
||||||
import { call } from "./client_server";
|
import { connect } from "react-redux";
|
||||||
|
|
||||||
/*let userJsonString = JSON.parse(localStorage.getItem("token"));
|
|
||||||
let date = new Date().getTime().toString();
|
|
||||||
let delta = date - userJsonString.timestamp;
|
|
||||||
if (delta < 5*60*60*1000) {
|
|
||||||
loggedIn = true;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
class App extends Component {
|
class App extends Component {
|
||||||
constructor(props) {
|
constructor(props, context) {
|
||||||
super(props);
|
super(props, context);
|
||||||
|
|
||||||
let loggedIn = false;
|
|
||||||
let token = undefined;
|
|
||||||
|
|
||||||
try {
|
|
||||||
let userJsonString = localStorage.getItem("token");
|
|
||||||
let exp = localStorage.getItem("exp");
|
|
||||||
let date = new Date().getTime();
|
|
||||||
if (userJsonString && exp && date < exp) {
|
|
||||||
loggedIn = true;
|
|
||||||
token = userJsonString;
|
|
||||||
} else {
|
|
||||||
localStorage.removeItem("token");
|
|
||||||
localStorage.removeItem("exp");
|
|
||||||
}
|
|
||||||
} catch (exception) {}
|
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
loggedIn: loggedIn,
|
query: "",
|
||||||
token: token,
|
|
||||||
info: "",
|
info: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
this.auth = this.auth.bind(this);
|
|
||||||
this.logout = this.logout.bind(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
@ -58,70 +31,21 @@ class App extends Component {
|
||||||
this.setState({
|
this.setState({
|
||||||
query: values,
|
query: values,
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
this.setState({
|
|
||||||
query: "ciao",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auth(data) {
|
|
||||||
return call
|
|
||||||
.login(data.params)
|
|
||||||
.then((res) => {
|
|
||||||
if (res.data && res.status === 200) {
|
|
||||||
let expire = new Date().getTime() + 60 * 60 * 5 * 1000;
|
|
||||||
localStorage.setItem("token", res.data.jwttoken);
|
|
||||||
localStorage.setItem("exp", expire);
|
|
||||||
call.setToken(res.data.jwttoken);
|
|
||||||
this.setState({
|
|
||||||
user: data.params.user,
|
|
||||||
token: res.data.jwttoken,
|
|
||||||
loggedIn: true,
|
|
||||||
});
|
|
||||||
this.getInfo();
|
|
||||||
return res;
|
|
||||||
//this.props.history.push("/dashboard");
|
|
||||||
} else {
|
|
||||||
this.setState({
|
|
||||||
error: res.data.message,
|
|
||||||
});
|
|
||||||
return res.status;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
return err;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
logout() {
|
|
||||||
this.setState({
|
|
||||||
loggedIn: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
localStorage.removeItem("token");
|
|
||||||
localStorage.removeItem("exp");
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
console.log("rendering root", this.props.loggedIn, this.state.query);
|
||||||
return (
|
return (
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route path="/" exact component={Home} />
|
<Route path="/" exact component={Home} />
|
||||||
<Route path="/login">
|
<Route path="/login">
|
||||||
{this.state.loggedIn && this.state.token ? (
|
{this.props.loggedIn ? <Redirect to="/dashboard" /> : <Login />}
|
||||||
<Redirect tkn={this.state.token} to="/dashboard" />
|
|
||||||
) : (
|
|
||||||
<Login auth={this.auth} />
|
|
||||||
)}
|
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/signup" exact component={Signup} />
|
<Route path="/signup" exact component={Signup} />
|
||||||
<Route path="/dashboard">
|
<Route path="/dashboard">
|
||||||
{this.state.loggedIn ? (
|
{this.props.loggedIn ? <Dashboard /> : <Redirect to="/login" />}
|
||||||
<Dashboard tkn={this.state.token} logout={this.logout} />
|
|
||||||
) : (
|
|
||||||
<Redirect to="/login" />
|
|
||||||
)}
|
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/forgot-password">
|
<Route path="/forgot-password">
|
||||||
<ForgotPass />
|
<ForgotPass />
|
||||||
|
@ -149,4 +73,9 @@ class App extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App;
|
const mapStateToProps = (state, _) => {
|
||||||
|
console.log("malusae react", state);
|
||||||
|
return { loggedIn: !!(state.login && state.login.loggedIn) };
|
||||||
|
};
|
||||||
|
const AppContainer = connect(mapStateToProps, RemoteService)(App);
|
||||||
|
export default AppContainer;
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Grid, Divider, Button, Label, Responsive } from "semantic-ui-react";
|
import { Grid, Divider, Button, Label, Responsive } from "semantic-ui-react";
|
||||||
import { Segment, Image } from "semantic-ui-react";
|
import { Segment, Image } from "semantic-ui-react";
|
||||||
import { call } from "../client_server";
|
import { RemoteService } from "../remote";
|
||||||
|
import { withRouter } from "react-router-dom";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
|
||||||
const IconHomeImage = () => (
|
const IconHomeImage = () => (
|
||||||
<Image
|
<Image
|
||||||
|
@ -15,7 +17,7 @@ const IconHomeImage = () => (
|
||||||
|
|
||||||
const TitleImage = () => <Image src="sm_logo.png" size="medium" centered />;
|
const TitleImage = () => <Image src="sm_logo.png" size="medium" centered />;
|
||||||
|
|
||||||
export default class MyHeader extends React.Component {
|
export class MyHeader extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
|
@ -24,15 +26,13 @@ export default class MyHeader extends React.Component {
|
||||||
|
|
||||||
this.getInfo();
|
this.getInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
getInfo() {
|
getInfo() {
|
||||||
call.getUserInfo(this.state.token).then((res) => {
|
this.props
|
||||||
if (res.status === 200) {
|
.fetchUserInfo()
|
||||||
this.setState({
|
.catch((err) => console.error("MyHeader fetch user info error", err));
|
||||||
username: res.data.username,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
@ -86,3 +86,13 @@ export default class MyHeader extends React.Component {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = (state, _) => ({
|
||||||
|
username:
|
||||||
|
state.userInfo && state.userInfo.username ? state.userInfo.username : "",
|
||||||
|
});
|
||||||
|
const LoginContainer = connect(
|
||||||
|
mapStateToProps,
|
||||||
|
RemoteService
|
||||||
|
)(withRouter(MyHeader));
|
||||||
|
export default LoginContainer;
|
||||||
|
|
|
@ -2,30 +2,43 @@ import smartHutStore from "./store";
|
||||||
import actions from "./storeActions";
|
import actions from "./storeActions";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
class Endpoint {
|
/**
|
||||||
socket = null;
|
* An object returned by promise rejections in remoteservice
|
||||||
axiosInstance = axios.create({
|
* @typedef {Error} RemoteError
|
||||||
baseURL: this.URL,
|
* @property {String[]} messages a list of user-friendly error messages to show;
|
||||||
validateStatus: (status) => status >= 200 && status < 300,
|
*/
|
||||||
});
|
class RemoteError extends Error {
|
||||||
|
messages;
|
||||||
|
|
||||||
/**
|
constructor(messages) {
|
||||||
* Returns the endpoint URL (SmartHut backend URL)
|
super("remote error");
|
||||||
* @returns {String} endpoint URL
|
this.messages = messages;
|
||||||
*/
|
|
||||||
static get URL() {
|
|
||||||
return window.BACKEND_URL !== "__BACKEND_URL__"
|
|
||||||
? window.BACKEND_URL
|
|
||||||
: "http://localhost:8080";
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the endpoint URL (SmartHut backend URL)
|
||||||
|
* @returns {String} endpoint URL
|
||||||
|
*/
|
||||||
|
function endpointURL() {
|
||||||
|
return window.BACKEND_URL !== "__BACKEND_URL__"
|
||||||
|
? window.BACKEND_URL
|
||||||
|
: "http://localhost:8080";
|
||||||
|
}
|
||||||
|
|
||||||
|
const Endpoint = {
|
||||||
|
axiosInstance: axios.create({
|
||||||
|
baseURL: endpointURL(),
|
||||||
|
validateStatus: (status) => status >= 200 && status < 300,
|
||||||
|
}),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns token for current session, null if logged out
|
* Returns token for current session, null if logged out
|
||||||
* @returns {String|null} the token
|
* @returns {String|null} the token
|
||||||
*/
|
*/
|
||||||
static get token() {
|
get token() {
|
||||||
return smartHutStore.getState().login.token;
|
return smartHutStore.getState().login.token;
|
||||||
}
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs an authenticated request
|
* Performs an authenticated request
|
||||||
|
@ -34,99 +47,106 @@ class Endpoint {
|
||||||
* @param {[String]String} query query ('?') parameters (no params by default)
|
* @param {[String]String} query query ('?') parameters (no params by default)
|
||||||
* @param {any} body the JSON request body
|
* @param {any} body the JSON request body
|
||||||
*/
|
*/
|
||||||
static send(method, route, query = {}, body = null) {
|
send: (method, route, query = {}, body = null) => {
|
||||||
if (!this.token) {
|
if (!Endpoint.token) {
|
||||||
throw new Error("No token while performing authenticated request");
|
throw new Error("No token while performing authenticated request");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (method !== "get")
|
return Endpoint.axiosInstance(route, {
|
||||||
return this.axiosInstance(route, {
|
method: method,
|
||||||
method: method,
|
params: query,
|
||||||
params: query,
|
data: ["put", "post"].indexOf(method) !== -1 ? body : null,
|
||||||
data: ["put", "post"].indexOf(method) !== -1 ? body : null,
|
headers: {
|
||||||
headers: {
|
Authorization: `Bearer ${Endpoint.token}`,
|
||||||
Authorization: `Bearer ${this.token}`,
|
},
|
||||||
},
|
}).then((res) => {
|
||||||
}).then((res) => {
|
if (!res.data) {
|
||||||
if (!res.data) {
|
console.error("Response body is empty");
|
||||||
console.error("Response body is empty");
|
return null;
|
||||||
return null;
|
} else {
|
||||||
} else {
|
return res;
|
||||||
return res;
|
}
|
||||||
}
|
});
|
||||||
});
|
},
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Performs login
|
* Performs login
|
||||||
* @param {String} usernameOrEmail
|
* @param {String} usernameOrEmail
|
||||||
* @param {String} password
|
* @param {String} password
|
||||||
* @returns {Promise<Undefined, String[]>} promise that resolves to void and rejects
|
* @returns {Promise<String, *>} promise that resolves to the token string
|
||||||
* with user-fiendly errors as a String array
|
* and rejects to the axios error.
|
||||||
*/
|
*/
|
||||||
static login(dispatch, usernameOrEmail, password) {
|
login: (usernameOrEmail, password) => {
|
||||||
return Endpoint.axiosInstance
|
return Endpoint.axiosInstance
|
||||||
.post(`${Endpoint.URL}/auth/login`, {
|
.post(`/auth/login`, {
|
||||||
usernameOrEmail,
|
usernameOrEmail,
|
||||||
password,
|
password,
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
localStorage.setItem("token", res.token);
|
localStorage.setItem("token", res.data.jwttoken);
|
||||||
localStorage.setItem("exp", new Date().getTime() + 5 * 60 * 60 * 1000);
|
localStorage.setItem("exp", new Date().getTime() + 5 * 60 * 60 * 1000);
|
||||||
this.socket = new ServiceSocket(res.data.token);
|
Endpoint.socket = new ServiceSocket(res.data.jwttoken);
|
||||||
return res.data.token;
|
return res.data.jwttoken;
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
|
||||||
static logout() {
|
/**
|
||||||
this.socket.close();
|
* Returns an immediately resolved promise for the socket logouts
|
||||||
this.socket = null;
|
* @return {Promise<Undefined, _>} An always-resolved promise
|
||||||
}
|
*/
|
||||||
|
logout: () => {
|
||||||
|
return Promise.resolve(void 0);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs an authenticated GET request
|
* Performs an authenticated GET request
|
||||||
* @param {String} route the desired route (e.g. "/rooms/1/devices")
|
* @param {String} route the desired route (e.g. "/rooms/1/devices")
|
||||||
* @param {[String]String} query query ('?') parameters (no params by default)
|
* @param {[String]String} query query ('?') parameters (no params by default)
|
||||||
|
* @returns {Promise<*, *>} The Axios-generated promise
|
||||||
*/
|
*/
|
||||||
static get(route, query) {
|
get(route, query = {}) {
|
||||||
return this.send("get", route, query);
|
return this.send("get", route, query);
|
||||||
}
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs an authenticated POST request
|
* Performs an authenticated POST request
|
||||||
* @param {String} route the desired route (e.g. "/rooms/1/devices")
|
* @param {String} route the desired route (e.g. "/rooms/1/devices")
|
||||||
* @param {[String]String} query query ('?') parameters (no params by default)
|
* @param {[String]String} query query ('?') parameters (no params by default)
|
||||||
* @param {any} body the JSON request body
|
* @param {any} body the JSON request body
|
||||||
|
* @returns {Promise<*, *>} The Axios-generated promise
|
||||||
*/
|
*/
|
||||||
static post(route, query, body) {
|
post(route, query, body) {
|
||||||
return this.send("post", route, query, body);
|
return this.send("post", route, query, body);
|
||||||
}
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs an authenticated PUT request
|
* Performs an authenticated PUT request
|
||||||
* @param {String} route the desired route (e.g. "/rooms/1/devices")
|
* @param {String} route the desired route (e.g. "/rooms/1/devices")
|
||||||
* @param {[String]String} query query ('?') parameters (no params by default)
|
* @param {[String]String} query query ('?') parameters (no params by default)
|
||||||
* @param {any} body the JSON request body
|
* @param {any} body the JSON request body
|
||||||
|
* @returns {Promise<*, *>} The Axios-generated promise
|
||||||
*/
|
*/
|
||||||
static put(route, query, body) {
|
put(route, query = {}, body = {}) {
|
||||||
return this.send("put", route, query, body);
|
return this.send("put", route, query, body);
|
||||||
}
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs an authenticated DELETE request
|
* Performs an authenticated DELETE request
|
||||||
* @param {get|post|put|delete} the desired method
|
* @param {get|post|put|delete} the desired method
|
||||||
* @param {String} route the desired route (e.g. "/rooms/1/devices")
|
* @param {String} route the desired route (e.g. "/rooms/1/devices")
|
||||||
* @param {[String]String} query query ('?') parameters (no params by default)
|
* @param {[String]String} query query ('?') parameters (no params by default)
|
||||||
|
* @returns {Promise<*, *>} The Axios-generated promise
|
||||||
*/
|
*/
|
||||||
static delete(route, query) {
|
delete(route, query = {}) {
|
||||||
return this.send("delete", route, query);
|
return this.send("delete", route, query);
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given an error response, returns an array of user
|
* Given an error response, returns an array of user
|
||||||
* friendly messages to display to the user
|
* friendly messages to display to the user
|
||||||
* @param {*} err the Axios error reponse object
|
* @param {*} err the Axios error reponse object
|
||||||
* @returns {String[]} user friendly error messages
|
* @returns {RemoteError} user friendly error messages
|
||||||
*/
|
*/
|
||||||
function parseValidationErrors(err) {
|
function parseValidationErrors(err) {
|
||||||
if (
|
if (
|
||||||
|
@ -135,96 +155,97 @@ function parseValidationErrors(err) {
|
||||||
err.response.data &&
|
err.response.data &&
|
||||||
Array.isArray(err.response.data.errors)
|
Array.isArray(err.response.data.errors)
|
||||||
) {
|
) {
|
||||||
return [...new Set(err.response.data.errors.map((e) => e.defaultMessage))];
|
throw new RemoteError([
|
||||||
|
...new Set(err.response.data.errors.map((e) => e.defaultMessage)),
|
||||||
|
]);
|
||||||
} else {
|
} else {
|
||||||
console.warn("Non validation error", err);
|
console.warn("Non validation error", err);
|
||||||
return ["Network error"];
|
throw new RemoteError(["Network error"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RemoteService {
|
export const RemoteService = {
|
||||||
/**
|
/**
|
||||||
* Performs login
|
* Performs login
|
||||||
* @param {String} usernameOrEmail
|
* @param {String} usernameOrEmail
|
||||||
* @param {String} password
|
* @param {String} password
|
||||||
* @returns {Promise<Undefined, String[]>} promise that resolves to void and rejects
|
* @returns {Promise<Undefined, RemoteError>} promise that resolves to void and rejects
|
||||||
* with user-fiendly errors as a String array
|
* with user-fiendly errors as a RemoteError
|
||||||
*/
|
*/
|
||||||
static login(usernameOrEmail, password) {
|
login: (usernameOrEmail, password) => {
|
||||||
return (dispatch) => {
|
return (dispatch) => {
|
||||||
return Endpoint.login(dispatch, usernameOrEmail, password)
|
return Endpoint.login(usernameOrEmail, password)
|
||||||
.then((token) => dispatch(actions.loginSuccess(token)))
|
.then((token) => dispatch(actions.loginSuccess(token)))
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.warn("login error", err);
|
console.warn("login error", err);
|
||||||
return [
|
throw new RemoteError([
|
||||||
err.response.status === 401
|
err.response && err.response.status === 401
|
||||||
? "Wrong credentials"
|
? "Wrong credentials"
|
||||||
: "An error occurred while logging in",
|
: "An error occurred while logging in",
|
||||||
];
|
]);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs logout
|
* Performs logout
|
||||||
* @param {String} usernameOrEmail
|
|
||||||
* @param {String} password
|
|
||||||
*/
|
*/
|
||||||
static logout() {
|
logout: () => {
|
||||||
return (dispatch) => Endpoint.logout.then(void dispatch(actions.logout()));
|
return (dispatch) =>
|
||||||
}
|
Endpoint.logout().then(void dispatch(actions.logout()));
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches user information via REST calls, if it is logged in
|
* Fetches user information via REST calls, if it is logged in
|
||||||
* @returns {Promise<Undefined, String[]>} promise that resolves to void and rejects
|
* @returns {Promise<Undefined, RemoteError>} promise that resolves to void and rejects
|
||||||
* with user-fiendly errors as a String array
|
* with user-fiendly errors as a RemoteError
|
||||||
*/
|
*/
|
||||||
static fetchUserInfo() {
|
fetchUserInfo: () => {
|
||||||
return (dispatch) => {
|
return (dispatch) => {
|
||||||
return Endpoint.get("/auth/profile")
|
return Endpoint.get("/auth/profile")
|
||||||
.then((res) => void dispatch(actions.userInfoUpdate(res.data)))
|
.then((res) => void dispatch(actions.userInfoUpdate(res.data)))
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.warn("Fetch user info error", err);
|
console.warn("Fetch user info error", err);
|
||||||
return ["Network error"];
|
throw new RemoteError(["Network error"]);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches all rooms that belong to this user. This call does not
|
* Fetches all rooms that belong to this user. This call does not
|
||||||
* populate the devices attribute in rooms.
|
* populate the devices attribute in rooms.
|
||||||
* @returns {Promise<Undefined, String[]>} promise that resolves to void and rejects
|
* @returns {Promise<Undefined, RemoteError>} promise that resolves to void and rejects
|
||||||
* with user-fiendly errors as a String array
|
* with user-fiendly errors as a RemoteError
|
||||||
*/
|
*/
|
||||||
static fetchAllRooms() {
|
fetchAllRooms: () => {
|
||||||
return (dispatch) => {
|
return (dispatch) => {
|
||||||
return Endpoint.get("/room")
|
return Endpoint.get("/room")
|
||||||
.then((res) => void dispatch(actions.roomsUpdate(res.data)))
|
.then((res) => void dispatch(actions.roomsUpdate(res.data)))
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.error("Fetch all rooms error", err);
|
console.error("Fetch all rooms error", err);
|
||||||
return ["Network error"];
|
throw new RemoteError(["Network error"]);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches all devices in a particular room, or fetches all devices.
|
* Fetches all devices in a particular room, or fetches all devices.
|
||||||
* This also updates the devices attribute on values in the map rooms.
|
* This also updates the devices attribute on values in the map rooms.
|
||||||
* @param {Number|null} roomId the room to which fetch devices
|
* @param {Number|null} roomId the room to which fetch devices
|
||||||
* from, null to fetch from all rooms
|
* from, null to fetch from all rooms
|
||||||
* @returns {Promise<Undefined, String[]>} promise that resolves to void and rejects
|
* @returns {Promise<Undefined, RemoteError>} promise that resolves to void and rejects
|
||||||
* with user-fiendly errors as a String array
|
* with user-fiendly errors as a RemoteError
|
||||||
*/
|
*/
|
||||||
static fetchDevices(roomId = null) {
|
fetchDevices: (roomId = null) => {
|
||||||
return (dispatch) => {
|
return (dispatch) => {
|
||||||
return Endpoint.get(roomId ? `/room/${roomId}/device` : "/device")
|
return Endpoint.get(roomId ? `/room/${roomId}/device` : "/device")
|
||||||
.then((res) => void dispatch(actions.devicesUpdate(roomId, res.data)))
|
.then((res) => void dispatch(actions.devicesUpdate(roomId, res.data)))
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.error(`Fetch devices roomId=${roomId} error`, err);
|
console.error(`Fetch devices roomId=${roomId} error`, err);
|
||||||
return ["Network error"];
|
throw new RemoteError(["Network error"]);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates/Updates a room with the given data
|
* Creates/Updates a room with the given data
|
||||||
|
@ -232,10 +253,10 @@ export class RemoteService {
|
||||||
* @param {String} data.icon the room's icon name in SemanticUI icons
|
* @param {String} data.icon the room's icon name in SemanticUI icons
|
||||||
* @param {String} data.image ths room's image, as base64
|
* @param {String} data.image ths room's image, as base64
|
||||||
* @param {Number|null} roomId the room's id if update, null for creation
|
* @param {Number|null} roomId the room's id if update, null for creation
|
||||||
* @returns {Promise<Undefined, String[]>} promise that resolves to void and rejects
|
* @returns {Promise<Undefined, RemoteError>} promise that resolves to void and rejects
|
||||||
* with user-fiendly errors as a String array
|
* with user-fiendly errors as a RemoteError
|
||||||
*/
|
*/
|
||||||
static saveRoom(data, roomId = null) {
|
saveRoom: (data, roomId = null) => {
|
||||||
return (dispatch) => {
|
return (dispatch) => {
|
||||||
data = {
|
data = {
|
||||||
name: data.name,
|
name: data.name,
|
||||||
|
@ -250,7 +271,7 @@ export class RemoteService {
|
||||||
.then((res) => void dispatch(actions.roomSave(res.data)))
|
.then((res) => void dispatch(actions.roomSave(res.data)))
|
||||||
.catch(parseValidationErrors);
|
.catch(parseValidationErrors);
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates/Updates a device with the given data. If
|
* Creates/Updates a device with the given data. If
|
||||||
|
@ -261,10 +282,10 @@ export class RemoteService {
|
||||||
* is used for updates and the POST "/<device.kind>"
|
* is used for updates and the POST "/<device.kind>"
|
||||||
* endpoints are used for creation.
|
* endpoints are used for creation.
|
||||||
* @param {Device} data the device to update.
|
* @param {Device} data the device to update.
|
||||||
* @returns {Promise<Undefined, String[]>} promise that resolves to void and rejects
|
* @returns {Promise<Undefined, RemoteError>} promise that resolves to void and rejects
|
||||||
* with user-fiendly errors as a String array
|
* with user-fiendly errors as a RemoteError
|
||||||
*/
|
*/
|
||||||
static updateDevice(data) {
|
updateDevice: (data) => {
|
||||||
return (dispatch) => {
|
return (dispatch) => {
|
||||||
let url = "/device";
|
let url = "/device";
|
||||||
if ((data.id && data.flowType === "OUTPUT") || !data.id) {
|
if ((data.id && data.flowType === "OUTPUT") || !data.id) {
|
||||||
|
@ -275,12 +296,12 @@ export class RemoteService {
|
||||||
.then((res) => void dispatch(actions.deviceUpdate(res.data)))
|
.then((res) => void dispatch(actions.deviceUpdate(res.data)))
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.warn("Update device: ", data, "error: ", err);
|
console.warn("Update device: ", data, "error: ", err);
|
||||||
return ["Network error"];
|
throw new RemoteError(["Network error"]);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
|
|
||||||
static _operateInput(url, getUrl, action) {
|
_operateInput: (url, getUrl, action) => {
|
||||||
return (dispatch) => {
|
return (dispatch) => {
|
||||||
return Endpoint.put(url, {}, action)
|
return Endpoint.put(url, {}, action)
|
||||||
.then(async (res) => {
|
.then(async (res) => {
|
||||||
|
@ -290,10 +311,10 @@ export class RemoteService {
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.warn(`${url} error`, err);
|
console.warn(`${url} error`, err);
|
||||||
return ["Network error"];
|
throw new RemoteError(["Network error"]);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Changes the state of a switch, by turning it on, off or toggling it.
|
* Changes the state of a switch, by turning it on, off or toggling it.
|
||||||
|
@ -302,30 +323,30 @@ export class RemoteService {
|
||||||
*
|
*
|
||||||
* @param {Number} switchId the switch device id
|
* @param {Number} switchId the switch device id
|
||||||
* @param {SwitchOperation} type the operation to perform on the switch
|
* @param {SwitchOperation} type the operation to perform on the switch
|
||||||
* @returns {Promise<Undefined, String[]>} promise that resolves to void and rejects
|
* @returns {Promise<Undefined, RemoteError>} promise that resolves to void and rejects
|
||||||
* with user-fiendly errors as a String array
|
* with user-fiendly errors as a RemoteError
|
||||||
*/
|
*/
|
||||||
static switchOperate(switchId, type) {
|
switchOperate: (switchId, type) => {
|
||||||
return this._operateInput("/switch/operate", `/switch/${switchId}`, {
|
return this._operateInput("/switch/operate", `/switch/${switchId}`, {
|
||||||
type: type.toUpperCase(),
|
type: type.toUpperCase(),
|
||||||
id: switchId,
|
id: switchId,
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Turns a knob dimmer to a specific amount
|
* Turns a knob dimmer to a specific amount
|
||||||
*
|
*
|
||||||
* @param {Number} dimmerId the knob dimmer id
|
* @param {Number} dimmerId the knob dimmer id
|
||||||
* @param {number} intensity the absolute intensity to dim to. Must be >=0 and <= 100.
|
* @param {number} intensity the absolute intensity to dim to. Must be >=0 and <= 100.
|
||||||
* @returns {Promise<Undefined, String[]>} promise that resolves to void and rejects
|
* @returns {Promise<Undefined, RemoteError>} promise that resolves to void and rejects
|
||||||
* with user-fiendly errors as a String array
|
* with user-fiendly errors as a RemoteError
|
||||||
*/
|
*/
|
||||||
static knobDimmerDimTo(dimmerId, intensity) {
|
knobDimmerDimTo: (dimmerId, intensity) => {
|
||||||
return this._operateInput("/knobDimmer/dimTo", `/knobDimmer/${dimmerId}`, {
|
return this._operateInput("/knobDimmer/dimTo", `/knobDimmer/${dimmerId}`, {
|
||||||
intensity,
|
intensity,
|
||||||
id: dimmerId,
|
id: dimmerId,
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Turns a button dimmer up or down
|
* Turns a button dimmer up or down
|
||||||
|
@ -334,10 +355,10 @@ export class RemoteService {
|
||||||
*
|
*
|
||||||
* @param {Number} dimmerId the button dimmer id
|
* @param {Number} dimmerId the button dimmer id
|
||||||
* @param {ButtonDimmerDimType} dimType the type of dim to perform
|
* @param {ButtonDimmerDimType} dimType the type of dim to perform
|
||||||
* @returns {Promise<Undefined, String[]>} promise that resolves to void and rejects
|
* @returns {Promise<Undefined, RemoteError>} promise that resolves to void and rejects
|
||||||
* with user-fiendly errors as a String array
|
* with user-fiendly errors as a RemoteError
|
||||||
*/
|
*/
|
||||||
static buttonDimmerDim(dimmerId, dimType) {
|
buttonDimmerDim: (dimmerId, dimType) => {
|
||||||
return this._operateInput(
|
return this._operateInput(
|
||||||
"/buttonDimmer/dim",
|
"/buttonDimmer/dim",
|
||||||
`/buttonDimmer/${dimmerId}`,
|
`/buttonDimmer/${dimmerId}`,
|
||||||
|
@ -346,59 +367,63 @@ export class RemoteService {
|
||||||
id: dimmerId,
|
id: dimmerId,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets the meter on a smart plug
|
* Resets the meter on a smart plug
|
||||||
*
|
*
|
||||||
* @param {Number} smartPlugId the smart plug to reset
|
* @param {Number} smartPlugId the smart plug to reset
|
||||||
* @returns {Promise<Undefined, String[]>} promise that resolves to void and rejects
|
* @returns {Promise<Undefined, RemoteError>} promise that resolves to void and rejects
|
||||||
* with user-fiendly errors as a String array
|
* with user-fiendly errors as a RemoteError
|
||||||
*/
|
*/
|
||||||
static smartPlugReset(smartPlugId) {
|
smartPlugReset(smartPlugId) {
|
||||||
return (dispatch) => {
|
return (dispatch) => {
|
||||||
return Endpoint.delete(`/smartPlug/${smartPlugId}/meter`)
|
return Endpoint.delete(`/smartPlug/${smartPlugId}/meter`)
|
||||||
.then((res) => dispatch(actions.deviceOperationUpdate([res.data])))
|
.then((res) => dispatch(actions.deviceOperationUpdate([res.data])))
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.warn(`Smartplug reset error`, err);
|
console.warn(`Smartplug reset error`, err);
|
||||||
return ["Network error"];
|
throw new RemoteError(["Network error"]);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a room
|
* Deletes a room
|
||||||
* @param {Number} roomId the id of the room to delete
|
* @param {Number} roomId the id of the room to delete
|
||||||
* @returns {Promise<Undefined, String[]>} promise that resolves to void and rejects
|
* @returns {Promise<Undefined, RemoteError>} promise that resolves to void and rejects
|
||||||
* with user-fiendly errors as a String array
|
* with user-fiendly errors as a RemoteError
|
||||||
*/
|
*/
|
||||||
static deleteRoom(roomId) {
|
deleteRoom: (roomId) => {
|
||||||
return (dispatch) => {
|
return (dispatch) => {
|
||||||
Endpoint.delete(`/room/${roomId}`)
|
Endpoint.delete(`/room/${roomId}`)
|
||||||
.then((_) => dispatch(actions.roomDelete(roomId)))
|
.then((_) => dispatch(actions.roomDelete(roomId)))
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.warn("Room deletion error", err);
|
console.warn("Room deletion error", err);
|
||||||
return ["Network error"];
|
throw new RemoteError(["Network error"]);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a device
|
* Deletes a device
|
||||||
* @param {Number} deviceId the id of the device to delete
|
* @param {Number} deviceId the id of the device to delete
|
||||||
* @returns {Promise<Undefined, String[]>} promise that resolves to void and rejects
|
* @returns {Promise<Undefined, RemoteError>} promise that resolves to void and rejects
|
||||||
* with user-fiendly errors as a String array
|
* with user-fiendly errors as a RemoteError
|
||||||
*/
|
*/
|
||||||
static deleteDevice(deviceId) {
|
deleteDevice: (deviceId) => {
|
||||||
return (dispatch) => {
|
return (dispatch) => {
|
||||||
Endpoint.delete(`/device/${deviceId}`)
|
Endpoint.delete(`/device/${deviceId}`)
|
||||||
.then((_) => dispatch(actions.deviceDelete(deviceId)))
|
.then((_) => dispatch(actions.deviceDelete(deviceId)))
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.warn("Device deletion error", err);
|
console.warn("Device deletion error", err);
|
||||||
return ["Network error"];
|
throw new RemoteError(["Network error"]);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const key in RemoteService) {
|
||||||
|
RemoteService[key] = RemoteService[key].bind(RemoteService);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Class to handle connection with the sensor socket */
|
/** Class to handle connection with the sensor socket */
|
||||||
|
@ -409,7 +434,7 @@ class ServiceSocket {
|
||||||
connection;
|
connection;
|
||||||
|
|
||||||
static get URL() {
|
static get URL() {
|
||||||
const httpURL = new URL(Endpoint.URL);
|
const httpURL = new URL(endpointURL());
|
||||||
const isSecure = httpURL.protocol === "https:";
|
const isSecure = httpURL.protocol === "https:";
|
||||||
const protocol = isSecure ? "wss:" : "ws:";
|
const protocol = isSecure ? "wss:" : "ws:";
|
||||||
const port = httpURL.port || (isSecure ? 443 : 80);
|
const port = httpURL.port || (isSecure ? 443 : 80);
|
||||||
|
@ -512,7 +537,7 @@ export class Forms {
|
||||||
.then((_) => void 0)
|
.then((_) => void 0)
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.warn("Init reset password failed", err);
|
console.warn("Init reset password failed", err);
|
||||||
return ["Network error"];
|
throw new RemoteError(["Network error"]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,68 +1,100 @@
|
||||||
import { createStore, applyMiddleware } from "redux";
|
import { createStore, applyMiddleware } from "redux";
|
||||||
import thunk from "redux-thunk";
|
import thunk from "redux-thunk";
|
||||||
import actions from "./storeActions";
|
import actions from "./storeActions";
|
||||||
|
import update from "immutability-helper";
|
||||||
|
|
||||||
function reducer(previousState, action) {
|
function reducer(previousState, action) {
|
||||||
let newState = Object.assign({}, previousState);
|
let newState;
|
||||||
const createOrUpdateRoom = (room) => {
|
const createOrUpdateRoom = (room) => {
|
||||||
if (!(room.id in newState.rooms)) {
|
if (!(room.id in newState.rooms)) {
|
||||||
newState.rooms[room.id] = room;
|
newState = update(newState, {
|
||||||
newState.rooms[room.id].devices = new Set();
|
rooms: { [room.id]: { devices: { $set: new Set() } } },
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
newState.rooms[room.id].name = room.name;
|
newState = update(newState, {
|
||||||
newState.rooms[room.id].image = room.image;
|
rooms: {
|
||||||
newState.rooms[room.id].icon = room.icon;
|
[room.id]: {
|
||||||
|
name: { $set: room.name },
|
||||||
|
image: { $set: room.image },
|
||||||
|
icon: { $set: room.icon },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let change;
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case "LOGIN_UPDATE":
|
case "LOGIN_UPDATE":
|
||||||
newState.login = action.login;
|
newState = update(previousState, { login: { $set: action.login } });
|
||||||
delete newState.errors.login;
|
|
||||||
break;
|
break;
|
||||||
case "USER_INFO_UPDATE":
|
case "USER_INFO_UPDATE":
|
||||||
newState.user = action.user;
|
newState = update(previousState, { userInfo: { $set: action.user } });
|
||||||
delete newState.errors.userInfo;
|
|
||||||
break;
|
break;
|
||||||
case "ROOMS_UPDATE":
|
case "ROOMS_UPDATE":
|
||||||
|
newState = previousState;
|
||||||
for (const room of action.rooms) {
|
for (const room of action.rooms) {
|
||||||
createOrUpdateRoom(room);
|
createOrUpdateRoom(room);
|
||||||
}
|
}
|
||||||
delete newState.errors.rooms;
|
|
||||||
break;
|
break;
|
||||||
case "DEVICES_UPDATE":
|
case "DEVICES_UPDATE":
|
||||||
|
change = null;
|
||||||
|
|
||||||
// if room is given, delete all devices in that room
|
// if room is given, delete all devices in that room
|
||||||
// and remove any join between that room and deleted
|
// and remove any join between that room and deleted
|
||||||
// devices
|
// devices
|
||||||
if (action.roomId) {
|
if (action.roomId) {
|
||||||
|
change = {
|
||||||
|
rooms: { [action.roomId]: { devices: { $set: new Set() } } },
|
||||||
|
devices: { $unset: [] },
|
||||||
|
};
|
||||||
|
|
||||||
const room = newState.rooms[action.roomId];
|
const room = newState.rooms[action.roomId];
|
||||||
for (const deviceId of room.devices) {
|
for (const deviceId of room.devices) {
|
||||||
delete newState.devices[deviceId];
|
change.devices.$unset.push(deviceId);
|
||||||
}
|
}
|
||||||
room.devices = [];
|
|
||||||
} else if (action.partial) {
|
} else if (action.partial) {
|
||||||
// if the update is partial and caused by an operation on an input
|
// if the update is partial and caused by an operation on an input
|
||||||
// device (like /switch/operate), iteratively remove deleted
|
// device (like /switch/operate), iteratively remove deleted
|
||||||
// devices and their join with their corresponding room.
|
// devices and their join with their corresponding room.
|
||||||
|
change = {
|
||||||
|
devices: { $unset: [] },
|
||||||
|
rooms: {},
|
||||||
|
};
|
||||||
|
|
||||||
for (const device of action.devices) {
|
for (const device of action.devices) {
|
||||||
const room = newState.rooms[newState.devices[device.id].roomId];
|
const roomId = previousState.devices[device.id].roomId;
|
||||||
room.devices.delete(device.id);
|
change.rooms[roomId] = change.rooms[roomId] || {
|
||||||
delete newState.devices[device.id];
|
devices: { $remove: [] },
|
||||||
|
};
|
||||||
|
change.rooms[roomId].devices.$remove.push(device.id);
|
||||||
|
change.devices.$unset.push(device.id);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// otherwise, just delete all devices and all joins
|
// otherwise, just delete all devices and all joins
|
||||||
// between rooms and devices
|
// between rooms and devices
|
||||||
newState.devices = {};
|
change = {
|
||||||
|
devices: { $set: {} },
|
||||||
|
rooms: {},
|
||||||
|
};
|
||||||
|
|
||||||
for (const room of newState.rooms) {
|
for (const room of newState.rooms) {
|
||||||
room.devices = [];
|
change.rooms[room.id].devices = { $set: new Set() };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
newState = update(previousState, change);
|
||||||
|
|
||||||
|
change = {
|
||||||
|
devices: {},
|
||||||
|
rooms: {},
|
||||||
|
};
|
||||||
for (const device of action.devices) {
|
for (const device of action.devices) {
|
||||||
newState.devices[device.id] = device;
|
change.devices[device.id] = { $set: device };
|
||||||
|
|
||||||
if (device.roomId in newState.rooms) {
|
if (device.roomId in newState.rooms) {
|
||||||
newState.rooms[device.roomId].devices.add(device.id);
|
const devices = change.rooms[device.roomId].devices;
|
||||||
|
devices.$add = devices.$add || [];
|
||||||
|
devices.$add.push(device.id);
|
||||||
} else {
|
} else {
|
||||||
console.warn(
|
console.warn(
|
||||||
"Cannot join device",
|
"Cannot join device",
|
||||||
|
@ -72,14 +104,13 @@ function reducer(previousState, action) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
newState = update(newState, change);
|
||||||
delete newState.errors.devices;
|
|
||||||
break;
|
break;
|
||||||
case "ROOM_SAVE":
|
case "ROOM_SAVE":
|
||||||
createOrUpdateRoom(action.room);
|
createOrUpdateRoom(action.room);
|
||||||
break;
|
break;
|
||||||
case "ROOM_DELETE":
|
case "ROOM_DELETE":
|
||||||
if (!(actions.roomId in newState.rooms)) {
|
if (!(actions.roomId in previousState.rooms)) {
|
||||||
console.warn(`Room to delete ${actions.roomId} does not exist`);
|
console.warn(`Room to delete ${actions.roomId} does not exist`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -87,31 +118,41 @@ function reducer(previousState, action) {
|
||||||
// This update does not ensure the consistent update of switchId/dimmerId properties
|
// 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
|
// on output devices connected to an input device in this room. Please manually request
|
||||||
// all devices again if consistent update is desired
|
// all devices again if consistent update is desired
|
||||||
for (const id of newState.rooms[action.roomId].devices) {
|
change = { devices: { $unset: [] } };
|
||||||
delete newState.devices[id];
|
|
||||||
|
for (const id of previousState.rooms[action.roomId].devices) {
|
||||||
|
change.devices.$unset.push(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete newState.rooms[action.roomId];
|
change.rooms = { $unset: actions.roomId };
|
||||||
|
newState = update(previousState, change);
|
||||||
break;
|
break;
|
||||||
case "DEVICE_DELETE":
|
case "DEVICE_DELETE":
|
||||||
if (!(actions.deviceId in newState.devices)) {
|
if (!(actions.deviceId in previousState.devices)) {
|
||||||
console.warn(`Device to delete ${actions.deviceId} does not exist`);
|
console.warn(`Device to delete ${actions.deviceId} does not exist`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
newState.rooms[newState.devices[actions.deviceId].roomId].devices.delete(
|
newState = update(previousState, {
|
||||||
actions.deviceId
|
devices: { $unset: actions.deviceId },
|
||||||
);
|
rooms: {
|
||||||
delete newState.devices[actions.deviceId];
|
[previousState.devices[actions.deviceId].roomId]: {
|
||||||
|
devices: { $remove: actions.deviceId },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
case "LOGOUT":
|
case "LOGOUT":
|
||||||
newState.login = { token: null, loggedIn: false };
|
newState = {
|
||||||
newState.rooms = [];
|
login: { loggedIn: false, token: null },
|
||||||
newState.devices = [];
|
rooms: [],
|
||||||
delete newState.errors.login;
|
devices: [],
|
||||||
|
userInfo: null,
|
||||||
|
};
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
console.warn(`Action type ${action.type} unknown`, action);
|
console.warn(`Action type ${action.type} unknown`, action);
|
||||||
|
return previousState;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("new state: ", newState);
|
console.log("new state: ", newState);
|
||||||
|
@ -123,6 +164,7 @@ function createSmartHutStore() {
|
||||||
const exp = localStorage.getItem("exp");
|
const exp = localStorage.getItem("exp");
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
|
errors: {},
|
||||||
login: {
|
login: {
|
||||||
token: token,
|
token: token,
|
||||||
},
|
},
|
||||||
|
@ -133,7 +175,7 @@ function createSmartHutStore() {
|
||||||
devices: {},
|
devices: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
initialState.login.loggedIn = token && exp > new Date().getTime();
|
initialState.login.loggedIn = !!(token && exp > new Date().getTime());
|
||||||
if (!initialState.login.loggedIn) {
|
if (!initialState.login.loggedIn) {
|
||||||
localStorage.removeItem("token");
|
localStorage.removeItem("token");
|
||||||
localStorage.removeItem("exp");
|
localStorage.removeItem("exp");
|
||||||
|
|
|
@ -9,8 +9,11 @@ import {
|
||||||
Icon,
|
Icon,
|
||||||
Input,
|
Input,
|
||||||
} from "semantic-ui-react";
|
} from "semantic-ui-react";
|
||||||
|
import { RemoteService } from "../remote";
|
||||||
|
import { withRouter } from "react-router-dom";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
|
||||||
export default class Login extends Component {
|
class Login extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
|
@ -23,31 +26,15 @@ export default class Login extends Component {
|
||||||
|
|
||||||
handleLogin = (e) => {
|
handleLogin = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const params = {
|
|
||||||
usernameOrEmail: this.state.user,
|
|
||||||
password: this.state.password,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.props
|
this.props
|
||||||
.auth({
|
.login(this.state.user, this.state.password)
|
||||||
user: this.state.user,
|
.then(() => this.props.history.push("/dashboard"))
|
||||||
params: params,
|
|
||||||
})
|
|
||||||
.then((res) => {
|
|
||||||
if (res.response.status === 200) {
|
|
||||||
} else if (res.response.status === 401) {
|
|
||||||
this.setState({
|
|
||||||
error: { state: true, message: "Wrong credentials" },
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
console.log(res);
|
|
||||||
this.setState({
|
|
||||||
error: { state: true, message: "An error occurred while logging" },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
|
this.setState({
|
||||||
|
error: { state: true, message: err.messages.join(" - ") },
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -108,7 +95,7 @@ export default class Login extends Component {
|
||||||
color="blue"
|
color="blue"
|
||||||
fluid
|
fluid
|
||||||
size="large"
|
size="large"
|
||||||
onClick={this.handleLogin}
|
onClick={this.handleLogin.bind(this)}
|
||||||
>
|
>
|
||||||
Login
|
Login
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -127,3 +114,9 @@ export default class Login extends Component {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = (state, _) => ({ loggedIn: state.login.loggedIn });
|
||||||
|
const LoginContainer = withRouter(connect(mapStateToProps, RemoteService))(
|
||||||
|
Login
|
||||||
|
);
|
||||||
|
export default LoginContainer;
|
||||||
|
|
|
@ -5228,6 +5228,13 @@ immer@1.10.0:
|
||||||
resolved "https://registry.yarnpkg.com/immer/-/immer-1.10.0.tgz#bad67605ba9c810275d91e1c2a47d4582e98286d"
|
resolved "https://registry.yarnpkg.com/immer/-/immer-1.10.0.tgz#bad67605ba9c810275d91e1c2a47d4582e98286d"
|
||||||
integrity sha512-O3sR1/opvCDGLEVcvrGTMtLac8GJ5IwZC4puPrLuRj3l7ICKvkmA0vGuU9OW8mV9WIBRnaxp5GJh9IEAaNOoYg==
|
integrity sha512-O3sR1/opvCDGLEVcvrGTMtLac8GJ5IwZC4puPrLuRj3l7ICKvkmA0vGuU9OW8mV9WIBRnaxp5GJh9IEAaNOoYg==
|
||||||
|
|
||||||
|
immutability-helper@^3.0.2:
|
||||||
|
version "3.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/immutability-helper/-/immutability-helper-3.0.2.tgz#e9187158b47c93368a92e84c31714c4b3dff30b0"
|
||||||
|
integrity sha512-fcrJ26wpvUcuGRpoGY4hyQ/JOeR1HAunMmE3C0XYXSe6plAGtgTlB2S4BzueBANCPrDJ7AByL1yrIRLIlVfwpA==
|
||||||
|
dependencies:
|
||||||
|
invariant "^2.2.4"
|
||||||
|
|
||||||
import-cwd@^2.0.0:
|
import-cwd@^2.0.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9"
|
resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9"
|
||||||
|
|
Loading…
Reference in a new issue