login
This commit is contained in:
		
							parent
							
								
									4f57e7fd32
								
							
						
					
					
						commit
						5d9bfa97af
					
				
							
								
								
									
										184
									
								
								src/actions/authActions.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										184
									
								
								src/actions/authActions.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,184 @@ | ||||
| import { USER_LOADED, USER_LOADING, AUTH_ERROR, LOGIN_SUCCESS, LOGIN_FAIL, LOGOUT_SUCCESS, LOGOUT_FAIL, REFRESH_TOKEN_SUCCESS } from '../actions/types'; | ||||
| 
 | ||||
| import axios from 'axios'; | ||||
| import { returnErrors, returnSuccess } from './messageActions' | ||||
| 
 | ||||
| 
 | ||||
| // // Check token & load user
 | ||||
| // export const loadUser = () => (dispatch) => {
 | ||||
| //   // user loading
 | ||||
| //   dispatch({
 | ||||
| //     type: USER_LOADING
 | ||||
| //   });
 | ||||
| //   const config = {
 | ||||
| //     success: res => {
 | ||||
| //       dispatch({
 | ||||
| //         type: USER_LOADED,
 | ||||
| //         payload: res.data.user
 | ||||
| //       });
 | ||||
| //     },
 | ||||
| //     error: err => {
 | ||||
| //       if(err.response){
 | ||||
| //         dispatch(returnErrors(err.response.data.message, err.response.status));
 | ||||
| //       }
 | ||||
| //       dispatch({
 | ||||
| //         type: AUTH_ERROR
 | ||||
| //       });
 | ||||
| //     }
 | ||||
| //   };
 | ||||
| //   axios.get('/api/v1/user/me', config, dispatch(authInterceptor()))
 | ||||
| //     .then(res => {
 | ||||
| //       res.config.success(res);
 | ||||
| //     })
 | ||||
| //     .catch(err => {
 | ||||
| //       err.config.error(err);
 | ||||
| //     });
 | ||||
| // };
 | ||||
| 
 | ||||
| 
 | ||||
| var logoutTimerId; | ||||
| const timeToLogout = 14.9*60*1000; // nearly 15 minutes corresponding to the API
 | ||||
| 
 | ||||
| // Login user
 | ||||
| export const login = ({ email, password }) => (dispatch) => { | ||||
|   // Headers
 | ||||
|   const config = { | ||||
|     headers: { | ||||
|       'Content-Type': 'application/json' | ||||
|     } | ||||
|   }; | ||||
|   // Request Body
 | ||||
|   const body = JSON.stringify({ email, password }); | ||||
|   axios.post('https://api.opensensemap.org/users/sign-in', body, config) | ||||
|   .then(res => { | ||||
|     // Logout automatically if refreshToken "expired"
 | ||||
|     const logoutTimer = () => setTimeout( | ||||
|       () => dispatch(logout()), | ||||
|       timeToLogout | ||||
|     ); | ||||
|     logoutTimerId = logoutTimer(); | ||||
|     dispatch(returnSuccess(res.data.message, res.status, 'LOGIN_SUCCESS')); | ||||
|     dispatch({ | ||||
|       type: LOGIN_SUCCESS, | ||||
|       payload: res.data | ||||
|     }); | ||||
|   }) | ||||
|   .catch(err => { | ||||
|     console.log('hier'); | ||||
|     console.log(err); | ||||
|     dispatch(returnErrors(err.response.data.message, err.response.status, 'LOGIN_FAIL')); | ||||
|     dispatch({ | ||||
|       type: LOGIN_FAIL | ||||
|     }); | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| // Logout User
 | ||||
| export const logout = () => (dispatch) => { | ||||
|   const config = { | ||||
|     success: res => { | ||||
|       dispatch({ | ||||
|         type: LOGOUT_SUCCESS | ||||
|       }); | ||||
|       dispatch(returnSuccess(res.data.message, res.status, 'LOGOUT_SUCCESS')); | ||||
|       clearTimeout(logoutTimerId); | ||||
|     }, | ||||
|     error: err => { | ||||
|       dispatch(returnErrors(err.response.data.message, err.response.status, 'LOGOUT_FAIL')); | ||||
|       dispatch({ | ||||
|         type: LOGOUT_FAIL | ||||
|       }); | ||||
|       clearTimeout(logoutTimerId); | ||||
|     } | ||||
|   }; | ||||
|   axios.post('https://api.opensensemap.org/users/sign-out', {}, config, dispatch(authInterceptor())) | ||||
|   .then(res => { | ||||
|     res.config.success(res); | ||||
|   }) | ||||
|   .catch(err => { | ||||
|     if(err.response.status !== 401){ | ||||
|       err.config.error(err); | ||||
|     } | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| export const authInterceptor = () => (dispatch, getState) => { | ||||
|   // Add a request interceptor
 | ||||
|   axios.interceptors.request.use( | ||||
|     config => { | ||||
|       config.headers['Content-Type'] = 'application/json'; | ||||
|       const token = getState().auth.token; | ||||
|       if (token) { | ||||
|        config.headers['Authorization'] = `Bearer ${token}`; | ||||
|       } | ||||
|       return config; | ||||
|    }, | ||||
|    error => { | ||||
|      Promise.reject(error); | ||||
|    } | ||||
|   ); | ||||
| 
 | ||||
|   // Add a response interceptor
 | ||||
|   axios.interceptors.response.use( | ||||
|     response => { | ||||
|       // request was successfull
 | ||||
|       return response; | ||||
|     }, | ||||
|     error => { | ||||
|       const originalRequest = error.config; | ||||
|       const refreshToken = getState().auth.refreshToken; | ||||
|       if(refreshToken){ | ||||
|         // try to refresh the token failed
 | ||||
|         if (error.response.status === 401 && originalRequest._retry) { | ||||
|               // router.push('/login');
 | ||||
|               return Promise.reject(error); | ||||
|         } | ||||
|         // token was not valid and 1st try to refresh the token
 | ||||
|         if (error.response.status === 401 && !originalRequest._retry) { | ||||
|           originalRequest._retry = true; | ||||
|           const refreshToken = getState().auth.refreshToken; | ||||
|           // request to refresh the token, in request-body is the refreshToken
 | ||||
|           axios.post('/api/v1/user/token/refresh', {"refreshToken": refreshToken}) | ||||
|                .then(res => { | ||||
|                  if (res.status === 200) { | ||||
|                    clearTimeout(logoutTimerId); | ||||
|                    const logoutTimer = () => setTimeout( | ||||
|                      () => dispatch(logout()), | ||||
|                      timeToLogout | ||||
|                    ); | ||||
|                    logoutTimerId = logoutTimer(); | ||||
|                    dispatch({ | ||||
|                      type: REFRESH_TOKEN_SUCCESS, | ||||
|                      payload: res.data | ||||
|                    }); | ||||
|                    axios.defaults.headers.common['Authorization'] = 'Bearer ' + getState().auth.token; | ||||
|                    // request was successfull, new request with the old parameters and the refreshed token
 | ||||
|                    return axios(originalRequest) | ||||
|                           .then(res => { | ||||
|                              originalRequest.success(res); | ||||
|                            }) | ||||
|                            .catch(err => { | ||||
|                              originalRequest.error(err); | ||||
|                            }); | ||||
|                  } | ||||
|                  return Promise.reject(error); | ||||
|                }) | ||||
|                .catch(err => { | ||||
|                  // request failed, token could not be refreshed
 | ||||
|                  if(err.response){ | ||||
|                    dispatch(returnErrors(err.response.data.message, err.response.status)); | ||||
|                  } | ||||
|                  dispatch({ | ||||
|                    type: AUTH_ERROR | ||||
|                  }); | ||||
|                  return Promise.reject(error); | ||||
|                }); | ||||
|         } | ||||
|       } | ||||
|       // request status was unequal to 401, no possibility to refresh the token
 | ||||
|       return Promise.reject(error); | ||||
|     } | ||||
|   ); | ||||
| }; | ||||
| @ -1,3 +1,14 @@ | ||||
| // authentication
 | ||||
| export const USER_LOADING = 'USER_LOADING'; | ||||
| export const USER_LOADED = 'USER_LOADED'; | ||||
| export const AUTH_ERROR = 'AUTH_ERROR'; | ||||
| export const LOGIN_SUCCESS = 'LOGIN_SUCCESS'; | ||||
| export const LOGIN_FAIL = 'LOGIN_FAIL'; | ||||
| export const LOGOUT_SUCCESS = 'LOGOUT_SUCCESS'; | ||||
| export const LOGOUT_FAIL = 'LOGOUT_FAIL'; | ||||
| export const REFRESH_TOKEN_FAIL = 'REFRESH_TOKEN_FAIL'; | ||||
| export const REFRESH_TOKEN_SUCCESS = 'REFRESH_TOKEN_SUCCESS'; | ||||
| 
 | ||||
| export const NEW_CODE = 'NEW_CODE'; | ||||
| export const CHANGE_WORKSPACE = 'CHANGE_WORKSPACE'; | ||||
| export const CREATE_BLOCK = 'CREATE_BLOCK'; | ||||
|  | ||||
| @ -2,6 +2,7 @@ import React, { Component } from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import { connect } from 'react-redux'; | ||||
| import { Link } from 'react-router-dom'; | ||||
| import { logout } from '../actions/authActions'; | ||||
| 
 | ||||
| import senseboxLogo from './sensebox_logo.svg'; | ||||
| 
 | ||||
| @ -20,7 +21,7 @@ import ListItemIcon from '@material-ui/core/ListItemIcon'; | ||||
| import ListItemText from '@material-ui/core/ListItemText'; | ||||
| import LinearProgress from '@material-ui/core/LinearProgress'; | ||||
| 
 | ||||
| import { faBars, faChevronLeft, faLayerGroup, faBuilding, faIdCard, faEnvelope, faCog, faChalkboardTeacher, faFolderPlus, faTools, faLightbulb } from "@fortawesome/free-solid-svg-icons"; | ||||
| import { faBars, faChevronLeft, faLayerGroup, faSignInAlt, faSignOutAlt, faCertificate, faUserCircle, faIdCard, faEnvelope, faCog, faChalkboardTeacher, faFolderPlus, faTools, faLightbulb } from "@fortawesome/free-solid-svg-icons"; | ||||
| import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; | ||||
| 
 | ||||
| const styles = (theme) => ({ | ||||
| @ -102,8 +103,7 @@ class Navbar extends Component { | ||||
|             {[{ text: 'Tutorials', icon: faChalkboardTeacher, link: "/tutorial" }, | ||||
|               { text: 'Tutorial-Builder', icon: faTools, link: "/tutorial/builder" }, | ||||
|               { text: 'Galerie', icon: faLightbulb, link: "/gallery" }, | ||||
|               { text: 'Projekte', icon: faLayerGroup, link: "/project" }, | ||||
|               { text: 'Einstellungen', icon: faCog, link: "/settings" }].map((item, index) => ( | ||||
|               { text: 'Projekte', icon: faLayerGroup, link: "/project" }].map((item, index) => ( | ||||
|               <Link to={item.link} key={index} style={{ textDecoration: 'none', color: 'inherit' }}> | ||||
|                 <ListItem button onClick={this.toggleDrawer}> | ||||
|                   <ListItemIcon><FontAwesomeIcon icon={item.icon} /></ListItemIcon> | ||||
| @ -113,14 +113,25 @@ class Navbar extends Component { | ||||
|             ))} | ||||
|           </List> | ||||
|           <Divider classes={{ root: this.props.classes.appBarColor }} style={{ marginTop: 'auto' }} /> | ||||
|           {/* <List> | ||||
|             {[{ text: 'Über uns', icon: faBuilding }, { text: 'Kontakt', icon: faEnvelope }, { text: 'Impressum', icon: faIdCard }].map((item, index) => ( | ||||
|               <ListItem button key={index} onClick={this.toggleDrawer}> | ||||
|           <List> | ||||
|             {[{ text: 'Anmelden', icon: faSignInAlt, link: '/user/login', restriction: !this.props.isAuthenticated }, | ||||
|               { text: 'Konto', icon: faUserCircle, link: '/user', restriction: this.props.isAuthenticated }, | ||||
|               { text: 'MyBadges', icon: faCertificate, link: '/user/badge', restriction: this.props.isAuthenticated }, | ||||
|               { text: 'Abmelden', icon: faSignOutAlt, function: this.props.logout, restriction: this.props.isAuthenticated }, | ||||
|               { text: 'Einstellungen', icon: faCog, link: "/settings" }].map((item, index) => { | ||||
|                 if(item.restriction || Object.keys(item).filter(attribute => attribute === 'restriction').length === 0){ | ||||
|                   return( | ||||
|                     <Link to={item.link} key={index} style={{ textDecoration: 'none', color: 'inherit' }}> | ||||
|                       <ListItem button onClick={item.function ? () => {item.function(); this.toggleDrawer();} : this.toggleDrawer}> | ||||
|                         <ListItemIcon><FontAwesomeIcon icon={item.icon} /></ListItemIcon> | ||||
|                         <ListItemText primary={item.text} /> | ||||
|                       </ListItem> | ||||
|             ))} | ||||
|           </List> */} | ||||
|                     </Link> | ||||
|                   ); | ||||
|                 } | ||||
|               } | ||||
|             )} | ||||
|           </List> | ||||
|         </Drawer> | ||||
|         {this.props.tutorialIsLoading || this.props.projectIsLoading ? | ||||
|           <LinearProgress style={{marginBottom: '30px', boxShadow: '0px 2px 4px -1px rgba(0,0,0,0.2),0px 4px 5px 0px rgba(0,0,0,0.14),0px 1px 10px 0px rgba(0,0,0,0.12)'}}/> | ||||
| @ -132,12 +143,14 @@ class Navbar extends Component { | ||||
| 
 | ||||
| Navbar.propTypes = { | ||||
|   tutorialIsLoading: PropTypes.bool.isRequired, | ||||
|   projectIsLoading: PropTypes.bool.isRequired | ||||
|   projectIsLoading: PropTypes.bool.isRequired, | ||||
|   isAuthenticated: PropTypes.bool.isRequired | ||||
| }; | ||||
| 
 | ||||
| const mapStateToProps = state => ({ | ||||
|   tutorialIsLoading: state.tutorial.progress, | ||||
|   projectIsLoading: state.project.progress | ||||
|   projectIsLoading: state.project.progress, | ||||
|   isAuthenticated: state.auth.isAuthenticated | ||||
| }); | ||||
| 
 | ||||
| export default connect(mapStateToProps, null)(withStyles(styles, { withTheme: true })(withRouter(Navbar))); | ||||
| export default connect(mapStateToProps, { logout })(withStyles(styles, { withTheme: true })(withRouter(Navbar))); | ||||
|  | ||||
| @ -15,6 +15,7 @@ import Project from './Project/Project'; | ||||
| import Settings from './Settings/Settings'; | ||||
| import Impressum from './Impressum'; | ||||
| import Privacy from './Privacy'; | ||||
| import Login from './User/Login'; | ||||
| 
 | ||||
| 
 | ||||
| class Routes extends Component { | ||||
| @ -40,6 +41,8 @@ class Routes extends Component { | ||||
|           // User-Projects
 | ||||
|           <Route path="/project" exact component={ProjectHome} /> | ||||
|           <Route path="/project/:projectId" exact component={Project} /> | ||||
|           // User
 | ||||
|           <Route path="/user/login" exact component={Login} /> | ||||
|           // settings
 | ||||
|           <Route path="/settings" exact component={Settings} /> | ||||
|           // privacy
 | ||||
|  | ||||
							
								
								
									
										157
									
								
								src/components/User/Login.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								src/components/User/Login.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,157 @@ | ||||
| import React, { Component } from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import { connect } from 'react-redux'; | ||||
| import { login } from '../../actions/authActions' | ||||
| import { clearMessages } from '../../actions/messageActions' | ||||
| 
 | ||||
| import { withRouter } from 'react-router-dom'; | ||||
| 
 | ||||
| import Snackbar from '../Snackbar'; | ||||
| import Breadcrumbs from '../Breadcrumbs'; | ||||
| 
 | ||||
| import Button from '@material-ui/core/Button'; | ||||
| import IconButton from '@material-ui/core/IconButton'; | ||||
| import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; | ||||
| import { faEye, faEyeSlash } from "@fortawesome/free-solid-svg-icons"; | ||||
| import TextField from '@material-ui/core/TextField'; | ||||
| import Divider from '@material-ui/core/Divider'; | ||||
| import InputAdornment from '@material-ui/core/InputAdornment'; | ||||
| import CircularProgress from '@material-ui/core/CircularProgress'; | ||||
| import Link from '@material-ui/core/Link'; | ||||
| 
 | ||||
| 
 | ||||
| export class Login extends Component { | ||||
| 
 | ||||
|   constructor(props) { | ||||
|     super(props); | ||||
|     this.state = { | ||||
|       email: '', | ||||
|       password: '', | ||||
|       snackbar: false, | ||||
|       type: '', | ||||
|       key: '', | ||||
|       message: '', | ||||
|       showPassword: false | ||||
|     }; | ||||
|   } | ||||
| 
 | ||||
|   componentDidUpdate(props){ | ||||
|     const { message } = this.props; | ||||
|     if (message !== props.message) { | ||||
|       if(message.id === 'LOGIN_SUCCESS'){ | ||||
|         this.props.history.goBack(); | ||||
|       } | ||||
|       // Check for login error
 | ||||
|       else if(message.id === 'LOGIN_FAIL'){ | ||||
|         this.setState({ email: '', password: '', snackbar: true, key: Date.now(), message: 'Der Benutzername oder das Passwort ist nicht korrekt.', type: 'error' }); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   onChange = e => { | ||||
|     this.setState({ [e.target.name]: e.target.value }); | ||||
|   }; | ||||
| 
 | ||||
|   onSubmit = e => { | ||||
|     e.preventDefault(); | ||||
|     const {email, password} = this.state; | ||||
|     if(email !== '' && password !== ''){ | ||||
|       // create user object
 | ||||
|       const user = { | ||||
|         email, | ||||
|         password | ||||
|       }; | ||||
|       this.props.login(user); | ||||
|     } else { | ||||
|       this.setState({ snackbar: true, key: Date.now(), message: 'Gib sowohl ein Benutzername als auch ein Passwort ein.', type: 'error' }); | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
|   handleClickShowPassword = () => { | ||||
|     this.setState({ showPassword: !this.state.showPassword }); | ||||
|   }; | ||||
| 
 | ||||
|   handleMouseDownPassword = (e) => { | ||||
|     e.preventDefault(); | ||||
|   }; | ||||
| 
 | ||||
|   render(){ | ||||
|     return( | ||||
|       <div> | ||||
|         <Breadcrumbs content={[{ link: '/user/login', title: 'Anmelden' }]} /> | ||||
| 
 | ||||
|         <div style={{maxWidth: '500px', marginLeft: 'auto', marginRight: 'auto'}}> | ||||
|           <h1>Anmelden</h1> | ||||
|           <Snackbar | ||||
|             open={this.state.snackbar} | ||||
|             message={this.state.message} | ||||
|             type={this.state.type} | ||||
|             key={this.state.key} | ||||
|           /> | ||||
|           <TextField | ||||
|             style={{marginBottom: '10px'}} | ||||
|             // variant='outlined'
 | ||||
|             type='text' | ||||
|             label='E-Mail oder Nutzername' | ||||
|             name='email' | ||||
|             value={this.state.email} | ||||
|             onChange={this.onChange} | ||||
|             fullWidth={true} | ||||
|           /> | ||||
|           <TextField | ||||
|             // variant='outlined'
 | ||||
|             type={this.state.showPassword ? 'text' : 'password'} | ||||
|             label='Passwort' | ||||
|             name='password' | ||||
|             value={this.state.password} | ||||
|             InputProps={{ | ||||
|               endAdornment: | ||||
|                 <InputAdornment | ||||
|                   position="end" | ||||
|                 > | ||||
|                   <IconButton | ||||
|                     onClick={this.handleClickShowPassword} | ||||
|                     onMouseDown={this.handleMouseDownPassword} | ||||
|                     edge="end" | ||||
|                   > | ||||
|                     <FontAwesomeIcon size='xs' icon={this.state.showPassword ? faEyeSlash : faEye} /> | ||||
|                   </IconButton> | ||||
|                 </InputAdornment> | ||||
|             }} | ||||
|             onChange={this.onChange} | ||||
|             fullWidth={true} | ||||
|           /> | ||||
|           <p> | ||||
|             <Button color="primary" variant='contained' onClick={this.onSubmit} style={{width: '100%'}}> | ||||
|               {this.props.progress ? | ||||
|                 <div style={{height: '24.5px'}}><CircularProgress color="inherit" size={20}/></div> | ||||
|               : 'Anmelden'} | ||||
|             </Button> | ||||
|           </p> | ||||
|           <p style={{textAlign: 'center', fontSize: '0.8rem'}}> | ||||
|             <Link rel="noreferrer" target="_blank" href={'https://opensensemap.org/'} color="primary">Passwort vergessen?</Link> | ||||
|           </p> | ||||
|           <Divider variant='fullWidth'/> | ||||
|           <p style={{textAlign: 'center', paddingRight: "34px", paddingLeft: "34px"}}> | ||||
|             Du hast noch kein Konto? <Link rel="noreferrer" target="_blank" href={'https://opensensemap.org/'}>Registrieren</Link> | ||||
|           </p> | ||||
|         </div> | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| Login.propTypes = { | ||||
|   message: PropTypes.object.isRequired, | ||||
|   login: PropTypes.func.isRequired, | ||||
|   clearMessages: PropTypes.func.isRequired, | ||||
|   message: PropTypes.object.isRequired, | ||||
|   progress: PropTypes.bool.isRequired | ||||
| }; | ||||
| 
 | ||||
| const mapStateToProps = state => ({ | ||||
|   message: state.message, | ||||
|   progress: state.auth.progress | ||||
| }); | ||||
| 
 | ||||
| export default connect(mapStateToProps, { login, clearMessages })(withRouter(Login)); | ||||
							
								
								
									
										56
									
								
								src/reducers/authReducer.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/reducers/authReducer.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,56 @@ | ||||
| import { USER_LOADED, USER_LOADING, AUTH_ERROR, LOGIN_SUCCESS, LOGIN_FAIL, LOGOUT_SUCCESS, LOGOUT_FAIL, REFRESH_TOKEN_SUCCESS } from '../actions/types'; | ||||
| 
 | ||||
| 
 | ||||
| const initialState = { | ||||
|   token: localStorage.getItem('token'), | ||||
|   refreshToken: localStorage.getItem('refreshToken'), | ||||
|   isAuthenticated: null, | ||||
|   progress: false, | ||||
|   user: null | ||||
| }; | ||||
| 
 | ||||
| export default function(state = initialState, action){ | ||||
|   switch(action.type){ | ||||
|     case USER_LOADING: | ||||
|       return { | ||||
|         ...state, | ||||
|         progress: true | ||||
|       }; | ||||
|     case USER_LOADED: | ||||
|       return { | ||||
|         ...state, | ||||
|         isAuthenticated: true, | ||||
|         progress: false, | ||||
|         user: action.payload | ||||
|       }; | ||||
|     case LOGIN_SUCCESS: | ||||
|     case REFRESH_TOKEN_SUCCESS: | ||||
|       localStorage.setItem('token', action.payload.token); | ||||
|       localStorage.setItem('refreshToken', action.payload.refreshToken); | ||||
|       console.log(action.payload); | ||||
|       return { | ||||
|         ...state, | ||||
|         user: action.payload.data.user, | ||||
|         token: action.payload.token, | ||||
|         refreshToken: action.payload.refreshToken, | ||||
|         isAuthenticated: true, | ||||
|         progress: false | ||||
|       }; | ||||
|     case AUTH_ERROR: | ||||
|     case LOGIN_FAIL: | ||||
|     case LOGOUT_SUCCESS: | ||||
|     case LOGOUT_FAIL: | ||||
|       localStorage.removeItem('token'); | ||||
|       localStorage.removeItem('refreshToken'); | ||||
|       return { | ||||
|         ...state, | ||||
|         token: null, | ||||
|         refreshToken: null, | ||||
|         user: null, | ||||
|         isAuthenticated: false, | ||||
|         progress: false | ||||
|       }; | ||||
|     default: | ||||
|       return state; | ||||
|   } | ||||
| } | ||||
| @ -5,8 +5,10 @@ import tutorialBuilderReducer from './tutorialBuilderReducer'; | ||||
| import generalReducer from './generalReducer'; | ||||
| import projectReducer from './projectReducer'; | ||||
| import messageReducer from './messageReducer'; | ||||
| import authReducer from './authReducer'; | ||||
| 
 | ||||
| export default combineReducers({ | ||||
|   auth: authReducer, | ||||
|   workspace: workspaceReducer, | ||||
|   tutorial: tutorialReducer, | ||||
|   builder: tutorialBuilderReducer, | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user