35
									
								
								client/app/components/Admin.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								client/app/components/Admin.jsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | |||||||
|  | /* | ||||||
|  | 	./app/components/App.jsx | ||||||
|  | 	 | ||||||
|  | 	<FetchData url={"https://apollo.kevinmidboe.com/api/v1/plex/playing"} /> | ||||||
|  | */ | ||||||
|  | import React from 'react'; | ||||||
|  | import { Link } from 'react-router-dom' | ||||||
|  |  | ||||||
|  | import FetchData from './FetchData.js'; | ||||||
|  | import ListStrays from './ListStrays.jsx'; | ||||||
|  |  | ||||||
|  | import FetchRequested from './FetchRequested.jsx'; | ||||||
|  |  | ||||||
|  | import LoginForm from './LoginForm/LoginForm.jsx'; | ||||||
|  | import { Provider } from 'react-redux'; | ||||||
|  | import store from './redux/store.jsx'; | ||||||
|  |  | ||||||
|  | import { getCookie } from './Cookie.jsx'; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | function getLoginStatus() { | ||||||
|  | 	const logged_in = getCookie('logged_in'); | ||||||
|  | 	if (logged_in) { | ||||||
|  | 		return <FetchRequested /> | ||||||
|  | 	} | ||||||
|  | 	return <LoginForm /> | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const Admin = () => ( | ||||||
|  | 	<Provider store={store}> | ||||||
|  | 		{ getLoginStatus() } | ||||||
|  |     </Provider> | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | export default Admin | ||||||
| @@ -1,27 +1,12 @@ | |||||||
| /* | import React, { Component } from "react"; | ||||||
| 	./app/components/App.jsx | import Header from './Header.jsx'; | ||||||
|  | import Main from './Main.jsx'; | ||||||
|  |  | ||||||
| 	<FetchData url={"https://apollo.kevinmidboe.com/api/v1/plex/playing"} /> | const App = () => ( | ||||||
| */ | 	<div> | ||||||
| import React from 'react'; | 		<Header /> | ||||||
| import FetchData from './FetchData.js'; | 		<Main /> | ||||||
| import ListStrays from './ListStrays.jsx' | 	</div> | ||||||
| import SearchRequest from './SearchRequest.jsx'; | ) | ||||||
|  |  | ||||||
| export default class App extends React.Component { | export default App | ||||||
|  |  | ||||||
| 				// <div> |  | ||||||
| 				// 	<h1>Welcome to Seasoned</h1> |  | ||||||
| 				// </div> |  | ||||||
| 					// <ListStrays /> |  | ||||||
|  |  | ||||||
| 					// <FetchData /> |  | ||||||
| 	render() { |  | ||||||
| 		return ( |  | ||||||
| 			<div> |  | ||||||
|  |  | ||||||
| 					<SearchRequest /> |  | ||||||
| 			</div> |  | ||||||
| 		); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										26
									
								
								client/app/components/Cookie.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								client/app/components/Cookie.jsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | |||||||
|  |  | ||||||
|  | import React from 'react'; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | export function getCookie(cname) { | ||||||
|  |     var name = cname + "="; | ||||||
|  |     var decodedCookie = decodeURIComponent(document.cookie); | ||||||
|  |     var ca = decodedCookie.split(';'); | ||||||
|  |     for(var i = 0; i <ca.length; i++) { | ||||||
|  |         var c = ca[i]; | ||||||
|  |         while (c.charAt(0) == ' ') { | ||||||
|  |             c = c.substring(1); | ||||||
|  |         } | ||||||
|  |         if (c.indexOf(name) == 0) { | ||||||
|  |             return c.substring(name.length, c.length); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function setCookie(cname, cvalue, exdays) { | ||||||
|  |     var d = new Date(); | ||||||
|  |     d.setTime(d.getTime() + (exdays*24*60*60*1000)); | ||||||
|  |     var expires = "expires="+ d.toUTCString(); | ||||||
|  |     document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/"; | ||||||
|  | } | ||||||
							
								
								
									
										165
									
								
								client/app/components/FetchRequested.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								client/app/components/FetchRequested.jsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,165 @@ | |||||||
|  | import React from 'react'; | ||||||
|  |  | ||||||
|  | import requestElement from './styles/requestElementStyle.jsx' | ||||||
|  |  | ||||||
|  | import { getCookie } from './Cookie.jsx'; | ||||||
|  |  | ||||||
|  | class RequestElement extends React.Component { | ||||||
|  | 	constructor(props) { | ||||||
|  | 		super(props); | ||||||
|  | 		this.default_requestList = null; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	filterRequestList(requestList, filter) { | ||||||
|  | 		if (filter === 'all') | ||||||
|  | 			return requestList | ||||||
|  |  | ||||||
|  | 		return requestList.filter(item => item.status === filter) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	sortRequestList(requestList, sort_type, reversed) { | ||||||
|  | 		requestList.sort(function(a,b) { | ||||||
|  | 			if(a[sort_type] < b[sort_type]) return -1; | ||||||
|  | 		  if(a[sort_type] > b[sort_type]) return 1; | ||||||
|  | 		  return 0; | ||||||
|  | 		}); | ||||||
|  |  | ||||||
|  | 		if (reversed) | ||||||
|  | 			requestList.reverse(); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	createHTMLElement(data, index) { | ||||||
|  | 		var posterPath = 'https://image.tmdb.org/t/p/w300' + data.image_path; | ||||||
|  | 		 | ||||||
|  | 		if (data.user_agent !== null) { | ||||||
|  | 			var user_agent = data.user_agent.split(" "); | ||||||
|  | 			var agent_shortened = user_agent[1].replace(/[\(\;]/g, '') | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return ( | ||||||
|  | 			<div style={requestElement.wrappingDiv} key={index}> | ||||||
|  | 				<img style={requestElement.requestPoster} src={posterPath}></img> | ||||||
|  | 				<div style={requestElement.infoDiv}> | ||||||
|  | 					<span><b>Name</b>: {data.name} </span> | ||||||
|  | 					<span><b>Year</b>: {data.year}</span><br></br> | ||||||
|  | 					<span><b>Status</b>: {data.status}</span><br></br> | ||||||
|  | 					<span><b>Address</b>: {data.ip}</span><br></br> | ||||||
|  | 					<span><b>Requested Data:</b> {data.requested_date}</span><br></br> | ||||||
|  | 					<span><b>Requested By:</b> {data.requested_by}</span><br></br> | ||||||
|  | 					<span><b>Agent</b>: {agent_shortened}</span><br></br> | ||||||
|  | 				</div> | ||||||
|  | 			</div> | ||||||
|  | 		) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  |   render() { | ||||||
|  |     const {requestedElementsList, requestedElementsFilter, requestedElementsSort, props} = this.props; | ||||||
|  |  | ||||||
|  |     var filteredRequestedList = this.filterRequestList(requestedElementsList, requestedElementsFilter) | ||||||
|  |  | ||||||
|  | 		this.sortRequestList(filteredRequestedList, requestedElementsSort.value, requestedElementsSort.reversed) | ||||||
|  |  | ||||||
|  |     return ( | ||||||
|  |       <div {...props} style={requestElement.bodyDiv}> | ||||||
|  |         {filteredRequestedList.map((requestItem, index) => this.createHTMLElement(requestItem, index))} | ||||||
|  |       </div> | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class FetchRequested extends React.Component { | ||||||
|  | 	constructor(props){ | ||||||
|  | 		super(props) | ||||||
|  | 		this.state = { | ||||||
|  | 			requested_objects: [], | ||||||
|  | 			filter: 'all', | ||||||
|  | 			sort: { | ||||||
|  | 				value: 'requested_date', | ||||||
|  | 				reversed: false | ||||||
|  | 			}, | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	componentDidMount(){ | ||||||
|  | 		Promise.resolve() | ||||||
|  | 		fetch('https://apollo.kevinmidboe.com/api/v1/plex/requests/all', { | ||||||
|  | 			method: 'GET', | ||||||
|  | 			headers: { | ||||||
|  | 				'Content-type': 'application/json', | ||||||
|  | 			  'authorization': getCookie('token') | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 		.then(response =>  { | ||||||
|  | 				if (response.status !== 200) { | ||||||
|  | 					console.log('error'); | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				response.json() | ||||||
|  | 				.then(data => { | ||||||
|  | 					if (data.success === true) { | ||||||
|  | 						this.setState({ | ||||||
|  | 							requested_objects: data.requestedItems | ||||||
|  | 						}) | ||||||
|  | 					} | ||||||
|  | 				}) | ||||||
|  | 		}) | ||||||
|  | 		.catch(error => { | ||||||
|  | 			new Error(error); | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	changeFilter(filter) { | ||||||
|  | 		this.setState({ | ||||||
|  | 			filter: filter | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	updateSort(sort=null, reverse=false) { | ||||||
|  | 		if (sort) { | ||||||
|  | 			this.setState({ | ||||||
|  | 				sort: { value: sort, reversed: reverse } | ||||||
|  | 			}) | ||||||
|  | 		}  | ||||||
|  | 		else { | ||||||
|  | 			this.setState({ | ||||||
|  | 				sort: { value: this.state.sort.value, reversed: reverse } | ||||||
|  | 			}) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	render(){ | ||||||
|  | 		return( | ||||||
|  | 			<div> | ||||||
|  | 				<select id="lang" onChange={event => this.changeFilter(event.target.value)} value={this.state.value}> | ||||||
|  | 					<option value="all">All</option> | ||||||
|  | 					<option value="requested">Requested</option> | ||||||
|  | 					<option value="downloading">Downloading</option> | ||||||
|  | 					<option value="downloaded">Downloaded</option> | ||||||
|  | 				</select> | ||||||
|  |  | ||||||
|  | 				<select id="lang" onChange={event => this.updateSort(event.target.value)} value={this.state.value}> | ||||||
|  | 					<option value='requested_date'>Date</option> | ||||||
|  | 					<option value='name'>Title</option> | ||||||
|  | 					<option value='status'>Status</option> | ||||||
|  | 					<option value='requested_by'>Requested By</option> | ||||||
|  | 					<option value='ip'>Address</option> | ||||||
|  | 					<option value='user_agent'>Agent</option> | ||||||
|  | 				</select> | ||||||
|  |  | ||||||
|  | 				<button onClick={() => {this.updateSort(null, !this.state.sort.reversed)}}>Reverse</button> | ||||||
|  | 				 | ||||||
|  | 				<RequestElement | ||||||
|  | 					requestedElementsList={this.state.requested_objects} | ||||||
|  | 					requestedElementsFilter={this.state.filter} | ||||||
|  | 					requestedElementsSort={this.state.sort} | ||||||
|  | 				/> | ||||||
|  | 				 | ||||||
|  | 			</div> | ||||||
|  | 		) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export default FetchRequested; | ||||||
							
								
								
									
										11
									
								
								client/app/components/Header.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								client/app/components/Header.jsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | import React from 'react' | ||||||
|  | import { Link } from 'react-router-dom' | ||||||
|  |  | ||||||
|  | // The Header creates links that can be used to navigate | ||||||
|  | // between routes. | ||||||
|  | const Header = () => ( | ||||||
|  |   <header> | ||||||
|  |   </header> | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | export default Header | ||||||
							
								
								
									
										66
									
								
								client/app/components/LoginForm/LoginForm.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								client/app/components/LoginForm/LoginForm.jsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | |||||||
|  | import React, { Component } from 'react'; | ||||||
|  | import { connect } from 'react-redux'; | ||||||
|  | import { login } from '../redux/reducer.jsx'; | ||||||
|  |  | ||||||
|  | class LoginForm extends Component { | ||||||
|  |  | ||||||
|  |   constructor(props) { | ||||||
|  |     super(props); | ||||||
|  |     this.state = {}; | ||||||
|  |     this.onSubmit = this.onSubmit.bind(this); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   render() { | ||||||
|  |     let {email, password} = this.state; | ||||||
|  |     let {isLoginPending, isLoginSuccess, loginError} = this.props; | ||||||
|  |     return ( | ||||||
|  |       <form name="loginForm" onSubmit={this.onSubmit}> | ||||||
|  |         <div className="form-group-collection"> | ||||||
|  |           <div className="form-group"> | ||||||
|  |             <label>Email:</label> | ||||||
|  |             <input type="" name="email" onChange={e => this.setState({email: e.target.value})} value={email}/> | ||||||
|  |           </div> | ||||||
|  |  | ||||||
|  |           <div className="form-group"> | ||||||
|  |             <label>Password:</label> | ||||||
|  |             <input type="password" name="password" onChange={e => this.setState({password: e.target.value})} value={password}/> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |  | ||||||
|  |         <input type="submit" value="Login" /> | ||||||
|  |  | ||||||
|  |         <div className="message"> | ||||||
|  |           { isLoginPending && <div>Please wait...</div> } | ||||||
|  |           { isLoginSuccess && <div>Success.</div> } | ||||||
|  |           { loginError && <div>{loginError.message}</div> } | ||||||
|  |         </div> | ||||||
|  |       </form> | ||||||
|  |     ) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   onSubmit(e) { | ||||||
|  |     e.preventDefault(); | ||||||
|  |     let { email, password } = this.state; | ||||||
|  |     this.props.login(email, password); | ||||||
|  |     this.setState({ | ||||||
|  |       email: '', | ||||||
|  |       password: '' | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const mapStateToProps = (state) => { | ||||||
|  |   return { | ||||||
|  |     isLoginPending: state.isLoginPending, | ||||||
|  |     isLoginSuccess: state.isLoginSuccess, | ||||||
|  |     loginError: state.loginError | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const mapDispatchToProps = (dispatch) => { | ||||||
|  |   return { | ||||||
|  |     login: (email, password) => dispatch(login(email, password)) | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export default connect(mapStateToProps, mapDispatchToProps)(LoginForm); | ||||||
							
								
								
									
										21
									
								
								client/app/components/Main.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								client/app/components/Main.jsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | import React from 'react'; | ||||||
|  | import { HashRouter as Router, Route, Switch} from 'react-router-dom'; | ||||||
|  | import { createBrowserHistory } from 'history'; | ||||||
|  |  | ||||||
|  | import SearchRequest from './SearchRequest.jsx'; | ||||||
|  | import Admin from './Admin.jsx'; | ||||||
|  | import NotFound from './NotFound.js'; | ||||||
|  |  | ||||||
|  | export const history = createBrowserHistory(); | ||||||
|  |  | ||||||
|  | const Main = () => ( | ||||||
|  | 	<Router> | ||||||
|  | 		<Switch> | ||||||
|  | 			<Route exact path='/' component={SearchRequest} /> | ||||||
|  | 			<Route path='/admin' component={Admin} /> | ||||||
|  | 			<Route component={NotFound} /> | ||||||
|  | 		</Switch> | ||||||
|  | 	</Router> | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | export default Main | ||||||
| @@ -34,7 +34,7 @@ class MovieObject { | |||||||
| 		notify.show(this.title + ' requested!', 'success', 3000); | 		notify.show(this.title + ' requested!', 'success', 3000); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	getElement() { | 	getElement(index) { | ||||||
| 		// TODO set the poster image async by updating the dom after this is returned | 		// TODO set the poster image async by updating the dom after this is returned | ||||||
| 		if (this.poster == null || this.poster == undefined) { | 		if (this.poster == null || this.poster == undefined) { | ||||||
| 			var posterPath = 'https://openclipart.org/image/2400px/svg_to_png/211479/Simple-Image-Not-Found-Icon.png' | 			var posterPath = 'https://openclipart.org/image/2400px/svg_to_png/211479/Simple-Image-Not-Found-Icon.png' | ||||||
| @@ -60,7 +60,7 @@ class MovieObject { | |||||||
| 		 | 		 | ||||||
|  |  | ||||||
| 		return ( | 		return ( | ||||||
| 		<div> | 		<div key={index}> | ||||||
| 		 	<Notifications /> | 		 	<Notifications /> | ||||||
| 			<div style={movieStyle.resultItem} key={this.id}> | 			<div style={movieStyle.resultItem} key={this.id}> | ||||||
| 				<MediaQuery minWidth={600}> | 				<MediaQuery minWidth={600}> | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								client/app/components/NotFound.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								client/app/components/NotFound.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | // components/NotFound.js | ||||||
|  | import React from 'react'; | ||||||
|  |  | ||||||
|  | const NotFound = () => | ||||||
|  |   <div> | ||||||
|  |     <h3>404 page not found</h3> | ||||||
|  |     <p>We are sorry but the page you are looking for does not exist.</p> | ||||||
|  |   </div> | ||||||
|  |  | ||||||
|  | export default NotFound; | ||||||
| @@ -156,11 +156,11 @@ class SearchRequest extends React.Component { | |||||||
|             .then(responseData => { |             .then(responseData => { | ||||||
|                 if (this.state.page === 1) { |                 if (this.state.page === 1) { | ||||||
|                     this.setState({ |                     this.setState({ | ||||||
|                         responseMovieList: responseData.results.map(searchResultItem => this.createMovieObjects(searchResultItem)), |                         responseMovieList: responseData.results.map((searchResultItem, index) => this.createMovieObjects(searchResultItem, index)), | ||||||
|                         lastApiCallURI: uri  // Save the value of the last sucessfull api call |                         lastApiCallURI: uri  // Save the value of the last sucessfull api call | ||||||
|                     }) |                     }) | ||||||
|                 } else { |                 } else { | ||||||
|                     let responseMovieObjects = responseData.results.map(searchResultItem => this.createMovieObjects(searchResultItem)); |                     let responseMovieObjects = responseData.results.map((searchResultItem, index) => this.createMovieObjects(searchResultItem, index)); | ||||||
|                     let growingReponseMovieObjectList = this.state.responseMovieList.concat(responseMovieObjects); |                     let growingReponseMovieObjectList = this.state.responseMovieList.concat(responseMovieObjects); | ||||||
|                     this.setState({ |                     this.setState({ | ||||||
|                         responseMovieList: growingReponseMovieObjectList, |                         responseMovieList: growingReponseMovieObjectList, | ||||||
| @@ -202,11 +202,11 @@ class SearchRequest extends React.Component { | |||||||
|             .then(responseData => { |             .then(responseData => { | ||||||
|                 if (this.state.page === 1) { |                 if (this.state.page === 1) { | ||||||
|                     this.setState({ |                     this.setState({ | ||||||
|                         responseMovieList: responseData.results.map(searchResultItem => this.createMovieObjects(searchResultItem)), |                         responseMovieList: responseData.results.map((searchResultItem, index) => this.createMovieObjects(searchResultItem, index)), | ||||||
|                         lastApiCallURI: uri  // Save the value of the last sucessfull api call |                         lastApiCallURI: uri  // Save the value of the last sucessfull api call | ||||||
|                     }) |                     }) | ||||||
|                 } else { |                 } else { | ||||||
|                     let responseMovieObjects = responseData.results.map(searchResultItem => this.createMovieObjects(searchResultItem)); |                     let responseMovieObjects = responseData.results.map((searchResultItem, index) => this.createMovieObjects(searchResultItem, index)); | ||||||
|                     let growingReponseMovieObjectList = this.state.responseMovieList.concat(responseMovieObjects); |                     let growingReponseMovieObjectList = this.state.responseMovieList.concat(responseMovieObjects); | ||||||
|                     this.setState({ |                     this.setState({ | ||||||
|                         responseMovieList: growingReponseMovieObjectList, |                         responseMovieList: growingReponseMovieObjectList, | ||||||
| @@ -288,9 +288,9 @@ class SearchRequest extends React.Component { | |||||||
|  |  | ||||||
|   // When called passes the variable to MovieObject and calls it's interal function for  |   // When called passes the variable to MovieObject and calls it's interal function for  | ||||||
|   // generating the wanted HTML |   // generating the wanted HTML | ||||||
|   createMovieObjects(item) { |   createMovieObjects(item, index) { | ||||||
|     let movie = new MovieObject(item); |     let movie = new MovieObject(item); | ||||||
|     return movie.getElement(); |     return movie.getElement(index); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   toggleFilter(filterType) { |   toggleFilter(filterType) { | ||||||
|   | |||||||
							
								
								
									
										108
									
								
								client/app/components/redux/reducer.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								client/app/components/redux/reducer.jsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,108 @@ | |||||||
|  |  | ||||||
|  | import { setCookie } from '../Cookie.jsx'; | ||||||
|  |  | ||||||
|  | const SET_LOGIN_PENDING = 'SET_LOGIN_PENDING'; | ||||||
|  | const SET_LOGIN_SUCCESS = 'SET_LOGIN_SUCCESS'; | ||||||
|  | const SET_LOGIN_ERROR = 'SET_LOGIN_ERROR'; | ||||||
|  |  | ||||||
|  | export function login(email, password) { | ||||||
|  |   return dispatch => { | ||||||
|  |     dispatch(setLoginPending(true)); | ||||||
|  |     dispatch(setLoginSuccess(false)); | ||||||
|  |     dispatch(setLoginError(null)); | ||||||
|  |  | ||||||
|  |     callLoginApi(email, password, error => { | ||||||
|  |       dispatch(setLoginPending(false)); | ||||||
|  |       if (!error) { | ||||||
|  |         dispatch(setLoginSuccess(true)); | ||||||
|  |       } else { | ||||||
|  |         dispatch(setLoginError(error)); | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function setLoginPending(isLoginPending) { | ||||||
|  |   return { | ||||||
|  |     type: SET_LOGIN_PENDING, | ||||||
|  |     isLoginPending | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function setLoginSuccess(isLoginSuccess) { | ||||||
|  |   return { | ||||||
|  |     type: SET_LOGIN_SUCCESS, | ||||||
|  |     isLoginSuccess | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function setLoginError(loginError) { | ||||||
|  |   return { | ||||||
|  |     type: SET_LOGIN_ERROR, | ||||||
|  |     loginError | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | function callLoginApi(email, password, callback) { | ||||||
|  |   | ||||||
|  |   Promise.resolve() | ||||||
|  |   fetch('https://apollo.kevinmidboe.com/api/v1/user/login', { | ||||||
|  |     method: 'POST', | ||||||
|  |     headers: { | ||||||
|  |       'Content-type': 'application/json' | ||||||
|  |     }, | ||||||
|  |     body: JSON.stringify({ | ||||||
|  |       username: email, | ||||||
|  |       password: password, | ||||||
|  |     }) | ||||||
|  |   }) | ||||||
|  |   .then(response => {    | ||||||
|  |     switch (response.status) { | ||||||
|  |       case 200: | ||||||
|  |         response.json() | ||||||
|  |         .then((data) => { | ||||||
|  |           if (data.success === true) { | ||||||
|  |             let token = data.token; | ||||||
|  |             setCookie('token', token, 10); | ||||||
|  |             setCookie('logged_in', true, 10); | ||||||
|  |  | ||||||
|  |             window.location.reload(); | ||||||
|  |           } | ||||||
|  |           return callback(null); | ||||||
|  |         }) | ||||||
|  |  | ||||||
|  |       case 401: | ||||||
|  |         return callback(new Error(response.statusText)); | ||||||
|  |     } | ||||||
|  |   }) | ||||||
|  |   .catch(error => { | ||||||
|  |       return callback(new Error('Invalid email and password')); | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export default function reducer(state = { | ||||||
|  |   isLoginSuccess: false, | ||||||
|  |   isLoginPending: false, | ||||||
|  |   loginError: null | ||||||
|  | }, action) { | ||||||
|  |   switch (action.type) { | ||||||
|  |     case SET_LOGIN_PENDING: | ||||||
|  |       return Object.assign({}, state, { | ||||||
|  |         isLoginPending: action.isLoginPending | ||||||
|  |       }); | ||||||
|  |  | ||||||
|  |     case SET_LOGIN_SUCCESS: | ||||||
|  |       return Object.assign({}, state, { | ||||||
|  |         isLoginSuccess: action.isLoginSuccess | ||||||
|  |       }); | ||||||
|  |  | ||||||
|  |     case SET_LOGIN_ERROR: | ||||||
|  |       return Object.assign({}, state, { | ||||||
|  |         loginError: action.loginError | ||||||
|  |       }); | ||||||
|  |  | ||||||
|  |     default: | ||||||
|  |       return state; | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										7
									
								
								client/app/components/redux/store.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								client/app/components/redux/store.jsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | |||||||
|  | import { createStore, applyMiddleware } from 'redux'; | ||||||
|  | import thunk from 'redux-thunk'; | ||||||
|  | import logger from 'redux-logger'; | ||||||
|  | import reducer from './reducer.jsx'; | ||||||
|  |  | ||||||
|  | const store = createStore(reducer, {}, applyMiddleware(thunk, logger)); | ||||||
|  | export default store; | ||||||
							
								
								
									
										23
									
								
								client/app/components/styles/requestElementStyle.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								client/app/components/styles/requestElementStyle.jsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | |||||||
|  |  | ||||||
|  | export default { | ||||||
|  | 	bodyDiv: { | ||||||
|  | 		display: 'flex', | ||||||
|  | 		flexDirection: 'row', | ||||||
|  | 		flexWrap: 'wrap', | ||||||
|  | 		flexFlow: 'row wrap', | ||||||
|  | 	}, | ||||||
|  |  | ||||||
|  | 	wrappingDiv: { | ||||||
|  |  | ||||||
|  | 	}, | ||||||
|  |  | ||||||
|  | 	requestPoster: { | ||||||
|  | 		height: '150px', | ||||||
|  | 	}, | ||||||
|  |  | ||||||
|  | 	infoDiv: { | ||||||
|  | 		marginTop: 0, | ||||||
|  | 		marginLeft: '10px', | ||||||
|  | 		float: 'right', | ||||||
|  | 	}, | ||||||
|  | } | ||||||
| @@ -2,14 +2,19 @@ | |||||||
| * @Author: KevinMidboe | * @Author: KevinMidboe | ||||||
| * @Date:   2017-06-01 21:08:55 | * @Date:   2017-06-01 21:08:55 | ||||||
| * @Last Modified by:   KevinMidboe | * @Last Modified by:   KevinMidboe | ||||||
| * @Last Modified time: 2017-06-01 21:34:32 | * @Last Modified time: 2017-10-05 13:47:37 | ||||||
|  |  | ||||||
|     ./client/index.js |     ./client/index.js | ||||||
|     which is the webpack entry file |     which is the webpack entry file | ||||||
| */ | */ | ||||||
|  |  | ||||||
| import React from 'react'; | import React from 'react'; | ||||||
| import ReactDOM from 'react-dom'; | import { render } from 'react-dom'; | ||||||
|  | import { HashRouter } from 'react-router-dom'; | ||||||
| import App from './components/App.jsx'; | import App from './components/App.jsx'; | ||||||
|  |  | ||||||
| ReactDOM.render(<App />, document.getElementById('root')); | render(( | ||||||
|  | 	<HashRouter> | ||||||
|  | 		<App /> | ||||||
|  | 	</HashRouter> | ||||||
|  | ), document.getElementById('root')); | ||||||
| @@ -17,7 +17,13 @@ | |||||||
|     "react-dom": "^15.5.4", |     "react-dom": "^15.5.4", | ||||||
|     "react-infinite-scroller": "^1.0.15", |     "react-infinite-scroller": "^1.0.15", | ||||||
|     "react-notify-toast": "^0.3.2", |     "react-notify-toast": "^0.3.2", | ||||||
|  |     "react-redux": "^5.0.6", | ||||||
|     "react-responsive": "^1.3.4", |     "react-responsive": "^1.3.4", | ||||||
|  |     "react-router": "^4.2.0", | ||||||
|  |     "react-router-dom": "^4.2.2", | ||||||
|  |     "redux": "^3.7.2", | ||||||
|  |     "redux-logger": "^3.0.6", | ||||||
|  |     "redux-thunk": "^2.2.0", | ||||||
|     "urijs": "^1.18.12", |     "urijs": "^1.18.12", | ||||||
|     "webfontloader": "^1.6.28", |     "webfontloader": "^1.6.28", | ||||||
|     "webpack": "^3.5.5", |     "webpack": "^3.5.5", | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user