From a196ba7cf403eb410e4a7acd7f358e7cace1732d Mon Sep 17 00:00:00 2001 From: Gevorg Zoroghlyan Date: Tue, 31 Jul 2018 05:34:38 +0400 Subject: [PATCH] Adding authorization logic. Implementing functionalities to get/post images. --- package.json | 2 + src/Constants.js | 6 ++ src/components/App.js | 36 +++++------- src/components/AuthTabs/SignIn.js | 18 +++++- src/components/AuthTabs/SignUp.js | 49 +++++++++++++++-- src/components/Dashboard/Dashboard.js | 55 +++++++++++++------ .../Dashboard/DashboardTabs/ImagesInfo.js | 6 +- .../Dashboard/DashboardTabs/Upload.js | 20 ++++--- .../Dashboard/DashboardTabs/View.js | 43 +++++++++++++++ src/daos/AuthDAO.js | 51 +++++++++++++++++ src/daos/DashboardDAO.js | 25 +++++++++ src/index.js | 2 - src/messages/messages.js | 8 ++- src/routes.js | 2 - src/stores/appStore.js | 8 --- src/stores/authStore.js | 38 +++++++++++++ src/stores/dashboardStore.js | 28 +++++++++- 17 files changed, 322 insertions(+), 75 deletions(-) create mode 100644 src/Constants.js create mode 100644 src/daos/AuthDAO.js create mode 100644 src/daos/DashboardDAO.js delete mode 100644 src/stores/appStore.js diff --git a/package.json b/package.json index e5d164e..ffc6b33 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "case-sensitive-paths-webpack-plugin": "2.1.1", "chalk": "1.1.3", "css-loader": "0.28.7", + "dateformat": "^3.0.3", "dotenv": "4.0.0", "dotenv-expand": "4.2.0", "eslint": "4.10.0", @@ -43,6 +44,7 @@ "react-fontawesome": "^1.6.1", "react-form-login": "^1.1.4", "react-loading-screen": "0.0.17", + "react-photo-feed": "^1.0.13", "react-router-dom": "^4.3.1", "resolve": "1.6.0", "style-loader": "0.19.0", diff --git a/src/Constants.js b/src/Constants.js new file mode 100644 index 0000000..26b07f7 --- /dev/null +++ b/src/Constants.js @@ -0,0 +1,6 @@ +const Constants = { + SERVER_URL: 'http://localhost:3003/' +}; + +export default Constants; + diff --git a/src/components/App.js b/src/components/App.js index 6a4a815..d53ac04 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -2,41 +2,31 @@ import React from 'react'; import PropTypes from 'prop-types'; import {inject, observer} from 'mobx-react'; -// import LoadingScreen from 'react-loading-screen'; -// -// import M from '../messages/messages'; +import LoadingScreen from 'react-loading-screen'; + +import M from '../messages/messages'; import Dashboard from './Dashboard/Dashboard'; +import Auth from './Auth'; -@inject('appStore') +@inject('authStore') @observer class App extends React.Component { static propTypes = { - appStore: PropTypes.object.isRequired + authStore: PropTypes.object.isRequired }; - componentDidMount() { - //TODO check user credentials - } - render() { - //const store = this.props.appStore; - // let component = ; - - // if (store.authPassed) { - // component = ; - // } + const authStore = this.props.authStore; + let component = ; + + if (authStore.successLogin) { + component = ; + } return ( - + component ); } } diff --git a/src/components/AuthTabs/SignIn.js b/src/components/AuthTabs/SignIn.js index 3a2ba8b..be12616 100644 --- a/src/components/AuthTabs/SignIn.js +++ b/src/components/AuthTabs/SignIn.js @@ -1,5 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; +import {withRouter} from 'react-router-dom'; import FontAwesome from 'react-fontawesome'; import {Row, FormGroup, FormControl, ControlLabel, Col, Button} from 'react-bootstrap'; @@ -24,10 +25,23 @@ class SignIn extends React.Component { this.props.store.setPassword(e.target.value); }; + onSubmit = (event) => { + event.preventDefault(); + event.stopPropagation(); + if (event.target.elements) { + const data = { + username: this.props.store.username, + pwd: this.props.store.password + }; + this.props.store.login(data); + } + }; + render() { const {store} = this.props; + return ( -
+ @@ -78,5 +92,5 @@ class SignIn extends React.Component { } } -export default SignIn; +export default withRouter(SignIn); diff --git a/src/components/AuthTabs/SignUp.js b/src/components/AuthTabs/SignUp.js index 56f30c5..3ce2dcc 100644 --- a/src/components/AuthTabs/SignUp.js +++ b/src/components/AuthTabs/SignUp.js @@ -1,7 +1,8 @@ import React from 'react'; import PropTypes from 'prop-types'; -import {Row, FormGroup, FormControl, ControlLabel, Button} from 'react-bootstrap'; +import {Row, FormGroup, FormControl, + ControlLabel, Button, Modal} from 'react-bootstrap'; import {inject, observer} from 'mobx-react'; import M from '../../messages/messages'; @@ -27,14 +28,30 @@ class SignUp extends React.Component { this.props.store.setRepeatPassword(e.target.value); }; - onSubmit = () => { - //this.props.store.submitForm(); + handleHide = () => { + if (this.props.store.successSignUp) { + this.props.store.setTab(1); + } + this.props.store.hideModal(); + }; + + onSubmit = (event) => { + event.preventDefault(); + event.stopPropagation(); + if (event.target.elements) { + const data = { + username: this.props.store.usernameRegister, + pwd: this.props.store.passwordRegister, + pwd2: this.props.store.repeatPassword + } + this.props.store.registerUser(data); + } }; render() { const {store} = this.props; return ( - + {M.authBlock.signUp} @@ -42,6 +59,7 @@ class SignUp extends React.Component { + disabled={!store.isSignUpFormValid}> {M.authBlock.signUp} + + + + {M.authBlock.register} + + + + {store.successSignUp ? M.authBlock.successSignUp : store.signUpError} + + + + + diff --git a/src/components/Dashboard/Dashboard.js b/src/components/Dashboard/Dashboard.js index 181bd0b..9f8bde9 100644 --- a/src/components/Dashboard/Dashboard.js +++ b/src/components/Dashboard/Dashboard.js @@ -7,6 +7,8 @@ import {inject, observer} from 'mobx-react'; import M from '../../messages/messages'; import Upload from './DashboardTabs/Upload'; +import View from './DashboardTabs/View'; +import LoadingScreen from "react-loading-screen"; @inject('dashboardStore') @observer @@ -15,28 +17,49 @@ class Dashboard extends React.Component { static propTypes = { dashboardStore: PropTypes.object.isRequired }; - + handleSelect = (key) => { + if (key === 1) { + this.props.dashboardStore.changeImagesLoaded(false); + } this.props.dashboardStore.setTab(key); }; + loadImages = (user) => { + this.props.dashboardStore.getAllImages(user); + }; + render() { + let component = + ; + if (this.props.dashboardStore.imagesLoaded) { + component = + + + + + + + + + + Coming soon.. + + + ; + } else { + this.loadImages(this.props.user); + } return ( - - - - - - - Tab 2 content - - - Tab 3 content - - - + component ); } } diff --git a/src/components/Dashboard/DashboardTabs/ImagesInfo.js b/src/components/Dashboard/DashboardTabs/ImagesInfo.js index 6b2a254..646a06a 100644 --- a/src/components/Dashboard/DashboardTabs/ImagesInfo.js +++ b/src/components/Dashboard/DashboardTabs/ImagesInfo.js @@ -4,6 +4,8 @@ import PropTypes from "prop-types"; import {Col, ListGroup, ListGroupItem} from 'react-bootstrap'; import {inject, observer} from 'mobx-react'; +import dateFormat from 'dateformat'; + import M from '../../../messages/messages'; import '../../../../node_modules/react-dropzone-component/styles/filepicker.css'; @@ -21,9 +23,9 @@ class ImagesInfo extends React.Component { const images = this.props.store.images.map(image => { return ( - {`${M.uploadBlock.imagesBlock.info} ${image.location.lng}\n`} + {`${M.uploadBlock.imagesBlock.longitude} ${image.location.lng}\n`} {`${M.uploadBlock.imagesBlock.latitude} ${image.location.lat}\n`} - {`${M.uploadBlock.imagesBlock.state} ${image.state}\n`} + {`${M.uploadBlock.imagesBlock.date} ${dateFormat(image.date, "dddd, mmmm dS, yyyy, h:MM TT")}\n`} {`${M.uploadBlock.imagesBlock.title} ${image.title}`} ); diff --git a/src/components/Dashboard/DashboardTabs/Upload.js b/src/components/Dashboard/DashboardTabs/Upload.js index ef69467..df631a9 100644 --- a/src/components/Dashboard/DashboardTabs/Upload.js +++ b/src/components/Dashboard/DashboardTabs/Upload.js @@ -20,16 +20,11 @@ class Upload extends React.Component { constructor() { super(); - this.djsConfig = { - addRemoveLinks: true, - acceptedFiles: "image/jpeg,image/png", - autoProcessQueue: false - }; this.componentConfig = { iconFiletypes: ['.jpg', '.png'], showFiletypeIcon: true, - postUrl: '/uploadHandler' + postUrl: 'http://localhost:3003/images' }; @@ -53,14 +48,23 @@ class Upload extends React.Component { render() { const config = this.componentConfig; - const djsConfig = this.djsConfig; - + const djsConfig = this.djsConfig = { + addRemoveLinks: true, + acceptedFiles: "image/jpeg,image/png", + autoProcessQueue: false, + parallelUploads: 10, + params: { + location: JSON.stringify(this.props.store.location) + } + }; + const eventHandlers = { init: dz => this.dropzone = dz, addedfile: this.handleFile, removedfile: this.handleRemoveFile }; + return ( diff --git a/src/components/Dashboard/DashboardTabs/View.js b/src/components/Dashboard/DashboardTabs/View.js index e69de29..49307bc 100644 --- a/src/components/Dashboard/DashboardTabs/View.js +++ b/src/components/Dashboard/DashboardTabs/View.js @@ -0,0 +1,43 @@ +import React from 'react'; +import PropTypes from "prop-types"; + +import {Button, Grid, Row} from 'react-bootstrap'; +import {inject, observer} from 'mobx-react'; + +import './Upload.css'; +import PhotoGrid from 'react-photo-feed'; +import 'react-photo-feed/library/style.css'; +import Constants from '../../../Constants'; + +@inject('dashboardStore') +@observer +class View extends React.Component { + + static propTypes = { + store: PropTypes.object.isRequired + }; + + render() { + const userImages = this.props.dashboardStore.userImages.map((image, index) => { + return { + id: index+1, + src: `${Constants.SERVER_URL}uploads/${image}`, + bigSrc: `${Constants.SERVER_URL}uploads/${image}` + } + }); + let component = + "You have not images, in order to add them click to "Upload" tab" + ; + if (userImages.length) { + component = ; + } + return ( + + {component} + + ); + } +} + +export default View; + diff --git a/src/daos/AuthDAO.js b/src/daos/AuthDAO.js new file mode 100644 index 0000000..4aa1524 --- /dev/null +++ b/src/daos/AuthDAO.js @@ -0,0 +1,51 @@ +import Constants from '../Constants'; + +const AuthDAO = { + register: (data) => { + return new Promise((resolve, reject) => { + fetch(Constants.SERVER_URL + 'auth/signup', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(data) + }) + .then(response => response.json()) + .then(result => { + if (result.status !== 201) { + return reject(result.error); + } + return resolve() + }) + + .catch(err => { + return reject(err); + }) + }); + }, + + login: (data) => { + return new Promise((resolve, reject) => { + fetch(Constants.SERVER_URL + 'auth/login', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(data) + }) + .then(response => response.json()) + .then(response => { + if (response.status !== 200) { + return reject(response.error); + } + return resolve(response.result) + }) + + .catch(err => { + return reject(err); + }) + }); + } +}; + +export default AuthDAO; diff --git a/src/daos/DashboardDAO.js b/src/daos/DashboardDAO.js new file mode 100644 index 0000000..134736f --- /dev/null +++ b/src/daos/DashboardDAO.js @@ -0,0 +1,25 @@ +import Constants from '../Constants'; + +const DashboardDAO = { + getAllImages: (user) => { + return new Promise((resolve, reject) => { + fetch(Constants.SERVER_URL + `images?name=${user}`, { + method: 'GET', + credentials: 'same-origin' + }) + .then(response => response.json()) + .then(result => { + if (result.status !== 200) { + return reject(result.error); + } + return resolve(result) + }) + + .catch(err => { + return reject(err); + }) + }); + } +}; + +export default DashboardDAO; diff --git a/src/index.js b/src/index.js index ca933d6..d9e26e2 100644 --- a/src/index.js +++ b/src/index.js @@ -6,12 +6,10 @@ import registerServiceWorker from './registerServiceWorker'; import Routes from './routes'; import authStore from './stores/authStore'; -import appStore from './stores/appStore'; import dashboardStore from './stores/dashboardStore'; const stores = { authStore, - appStore, dashboardStore }; diff --git a/src/messages/messages.js b/src/messages/messages.js index c8c8b73..77cd614 100644 --- a/src/messages/messages.js +++ b/src/messages/messages.js @@ -1,5 +1,5 @@ const MESSAGES = { - checkCredentials: 'Checking user credentials', + loadingImages: 'Loading images...', authBlock: { signUp: 'Sign Up', signIn: 'Sign In', @@ -7,7 +7,9 @@ const MESSAGES = { passwordPlaceholder: 'Password', name: 'Username', password: 'Password', - repeatPassword: 'Repeat password' + repeatPassword: 'Repeat password', + register: 'Registration', + successSignUp: 'Sign up process finished successfully, after closing this modal you automatically will be redirected to login page' }, mainPage: { uploadImages: 'Upload', @@ -21,7 +23,7 @@ const MESSAGES = { info: 'Images information', longitude: 'Longitude:', latitude: 'Latitute:', - state: 'State:', + date: 'Date:', title: 'Title:' }, importantInfo: '*Please select location before image upload, otherwise image location will be default : lat-40.18111, lng-44.51361', diff --git a/src/routes.js b/src/routes.js index be07d21..ec3600b 100644 --- a/src/routes.js +++ b/src/routes.js @@ -2,13 +2,11 @@ import React from 'react'; import {BrowserRouter, Route} from 'react-router-dom'; import App from './components/App'; -import Auth from './components/Auth'; const Routes = (props) => (
-
); diff --git a/src/stores/appStore.js b/src/stores/appStore.js deleted file mode 100644 index 18e8014..0000000 --- a/src/stores/appStore.js +++ /dev/null @@ -1,8 +0,0 @@ -import {observable} from 'mobx'; - -export class AppStore { - @observable authPassed = false; - @observable checkCreds = true; -} - -export default new AppStore(); diff --git a/src/stores/authStore.js b/src/stores/authStore.js index 7ca8ef0..4473471 100644 --- a/src/stores/authStore.js +++ b/src/stores/authStore.js @@ -1,5 +1,7 @@ import {observable, action, computed} from 'mobx'; +import AuthDAO from '../daos/AuthDAO'; + export class AuthStore { @observable tab = 1; @observable username = ''; @@ -7,6 +9,11 @@ export class AuthStore { @observable usernameRegister = ''; @observable passwordRegister = ''; @observable repeatPassword = ''; + @observable signUpInProgress = false; + @observable showSignUpModal = false; + @observable successSignUp = false; + @observable successLogin = false; + @observable signUpError = ''; @action setTab(key) { this.tab = key; @@ -32,6 +39,37 @@ export class AuthStore { this.repeatPassword = repeatPassword; } + @action registerUser(data) { + this.signUpInProgress = true; + AuthDAO.register(data) + .then((user) => { + this.signUpInProgress = false; + this.showSignUpModal = true; + this.successSignUp = true; + }) + .catch(e => { + this.signUpInProgress = false; + this.successSignUp = false; + this.showSignUpModal = true; + this.signUpError = e; + }); + } + + @action login(data) { + AuthDAO.login(data) + .then((result) => { + this.successLogin = true; + }) + .catch(e => { + throw e; + }); + } + + @action hideModal() { + this.showSignUpModal = false; + this.signUpError = ''; + } + @computed get isSignUpFormValid() { return this.usernameRegister && this.passwordRegister && (this.passwordRegister === this.repeatPassword); } diff --git a/src/stores/dashboardStore.js b/src/stores/dashboardStore.js index 86e8157..d900db9 100644 --- a/src/stores/dashboardStore.js +++ b/src/stores/dashboardStore.js @@ -1,16 +1,25 @@ import {observable, action} from 'mobx'; +import DashboardDAO from '../daos/DashboardDAO'; + export class DashboardStore { @observable tab = 1; @observable mapLoaded = false; - @observable center = {lat:40.18111, lng: 44.51361}; - @observable location = {lat:40.18111, lng: 44.51361}; + @observable center = {lat: 40.18111, lng: 44.51361}; + @observable location = {lat: 40.18111, lng: 44.51361}; @observable images = []; + @observable userImages = []; + @observable user = ''; + @observable imagesLoaded = false; @action setTab(key) { this.tab = key; } + @action changeImagesLoaded(state) { + this.imagesLoaded = state; + } + @action setMapState(state) { this.mapLoaded = state; } @@ -25,13 +34,26 @@ export class DashboardStore { }); } + @action setUser(user) { + this.user = user; + } + @action addFile(file) { file['location'] = this.location; file['title'] = file.name; - file['state'] = file.status; + file['date'] = Date.now(); this.images.push(file); } + @action getAllImages(user) { + const User = user || this.user; + DashboardDAO.getAllImages(User) + .then((images) => { + this.userImages = images.result; + this.imagesLoaded = true; + }) + } + @action removeFile(file) { this.images.push(file); this.images = this.images.filter(image => image.name !== file.name);