From c2180817595455aaf3f84bbe7b1d8573e2e130de Mon Sep 17 00:00:00 2001 From: Clayton Wilson Date: Tue, 1 Oct 2019 20:39:21 -0400 Subject: [PATCH] Making everything look pretty --- functions/handlers/post.js | 5 +- functions/handlers/users.js | 372 +++++++++++++++------------- functions/index.js | 55 ++-- functions/util/admin.js | 1 - functions/util/config.js | 2 +- functions/util/validator.js | 56 ++--- twistter-frontend/src/App.css | 6 +- twistter-frontend/src/App.js | 2 +- twistter-frontend/src/pages/edit.js | 35 ++- 9 files changed, 274 insertions(+), 260 deletions(-) diff --git a/functions/handlers/post.js b/functions/handlers/post.js index d15ea33..583a2ae 100644 --- a/functions/handlers/post.js +++ b/functions/handlers/post.js @@ -1,7 +1,6 @@ -const admin = require('firebase-admin'); /* eslint-disable promise/always-return */ +const admin = require('firebase-admin'); exports.putPost = (req, res) => { - const newPost = { body: req.body.body, @@ -41,5 +40,3 @@ exports.getallPostsforUser = (req, res) => { return res.status(500).json({error: 'Failed to fetch all posts written by specific user.'}) }) } - - diff --git a/functions/handlers/users.js b/functions/handlers/users.js index c4ceba4..cdeb3f6 100644 --- a/functions/handlers/users.js +++ b/functions/handlers/users.js @@ -1,202 +1,216 @@ /* eslint-disable promise/catch-or-return */ -const {admin, db} = require('../util/admin'); -const config = require('../util/config'); +const { admin, db } = require("../util/admin"); +const config = require("../util/config"); +const { validateUpdateProfileInfo } = require("../util/validator"); -const {validateUpdateProfileInfo} = require('../util/validator'); - -const firebase = require('firebase'); +const firebase = require("firebase"); firebase.initializeApp(config); - - exports.signup = (req, res) => { - const newUser = { + const newUser = { + email: req.body.email, + handle: req.body.handle, + password: req.body.password, + confirmPassword: req.body.confirmPassword, + createdAt: new Date().toISOString() + }; + + let errors = {}; + + const emailRegEx = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + + // Email check + if (newUser.email.trim() === "") { + errors.email = "Email must not be blank."; + } else if (!newUser.email.match(emailRegEx)) { + errors.email = "Email is invalid."; + } + + // handle check + if (newUser.handle.trim() === "") { + errors.handle = "Username must not be blank."; + } else if (newUser.handle.length < 4 || newUser.handle.length > 30) { + errors.handle = "Username must be between 4-30 characters long."; + } + + // Password check + if (newUser.password.trim() === "") { + errors.password = "Password must not be blank."; + } else if (newUser.password.length < 8 || newUser.password.length > 20) { + errors.password = "Password must be between 8-20 characters long."; + } + + // Confirm password check + if (newUser.confirmPassword !== newUser.password) { + errors.confirmPassword = "Passwords must match."; + } + + // Overall check + if (Object.keys(errors).length > 0) { + return res.status(400).json(errors); + } + + let idToken, userId; + + db.doc(`/users/${newUser.handle}`) + .get() + .then((doc) => { + if (doc.exists) { + return res + .status(400) + .json({ handle: "This username is already taken." }); + } + return firebase + .auth() + .createUserWithEmailAndPassword(newUser.email, newUser.password); + }) + .then((data) => { + userId = data.user.uid; + return data.user.getIdToken(); + }) + .then((token) => { + idToken = token; + const userCred = { email: req.body.email, - handle: req.body.handle, - password: req.body.password, - confirmPassword: req.body.confirmPassword, - createdAt: new Date().toISOString() - }; - - // console.log(newUser); - - let errors = {}; - - const emailRegEx = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; - - //Email check - if(newUser.email.trim() === '') { - errors.email = 'Email must not be blank.'; - } - else if(!newUser.email.match(emailRegEx)) { - errors.email = 'Email is invalid.'; - } - - //handle check - if(newUser.handle.trim() === '') { - errors.handle = 'Username must not be blank.'; - } - else if(newUser.handle.length < 4 || newUser.handle.length > 30) { - errors.handle = 'Username must be between 4-30 characters long.'; - } - - //Password check - if(newUser.password.trim() === '') { - errors.password = 'Password must not be blank.'; - } - else if(newUser.password.length < 8 || newUser.password.length > 20) { - errors.password = 'Password must be between 8-20 characters long.'; - } - - //Confirm password check - if(newUser.confirmPassword !== newUser.password) { - errors.confirmPassword = 'Passwords must match.'; - } - - //Overall check - if(Object.keys(errors).length > 0) { - return res.status(400).json(errors); - } - - let idToken, userId; - - db.doc(`/users/${newUser.handle}`).get() - .then(doc => { - if(doc.exists) { - return res.status(400).json({ handle: 'This username is already taken.' }); - } - return firebase.auth().createUserWithEmailAndPassword(newUser.email, newUser.password); - }) - .then(data => { - userId = data.user.uid; - return data.user.getIdToken(); - }) - .then(token => { - idToken = token; - const userCred = { - email: req.body.email, - handle: newUser.handle, - createdAt: newUser.createdAt, - userId - } - return db.doc(`/users/${newUser.handle}`).set(userCred); + handle: newUser.handle, + createdAt: newUser.createdAt, + userId + }; + return db.doc(`/users/${newUser.handle}`).set(userCred); }) .then(() => { - return res.status(201).json({ idToken }); + return res.status(201).json({ idToken }); }) - .catch(err => { - console.error(err); - if(err.code === 'auth/email-already-in-use') { - return res.status(500).json({ email: 'This email is already taken.' }); - } - return res.status(500).json({ error: err.code }); + .catch((err) => { + console.error(err); + if (err.code === "auth/email-already-in-use") { + return res.status(500).json({ email: "This email is already taken." }); + } + return res.status(500).json({ error: err.code }); }); }; exports.login = (req, res) => { - const user = { - email: req.body.email, - password: req.body.password - } - - //Auth validation - let errors = {}; - - //Email check - if(user.email.trim() === '') { - errors.email = 'Email must not be blank.'; - } - - //Password check - if(user.password.trim() === '') { - errors.password = 'Password must not be blank.'; - } - - //Overall check - if(Object.keys(errors).length > 0) { - return res.status(400).json(errors); - } - - firebase.auth().signInWithEmailAndPassword(user.email, user.password) - .then(data => { - return data.user.getIdToken(); - }) - .then(token => { - return res.json({token}); - }) - .catch(err => { - console.error(err); - if(err.code === 'auth/wrong-password') { - return res.status(403).json({ general: 'Invalid credentials. Please try again.' }); - } - return res.status(500).json({ error: err.code }); - }); - }; + const user = { + email: req.body.email, + password: req.body.password + }; -exports.getProfileInfo = (req, res) => { + // Auth validation + let errors = {}; - db.collection('users').doc(req.user.handle).get() - .then((data) => { - return res.status(200).json(data.data()); - }) - .catch((err) => { - console.error(err); - return res.status(500).json(err); - }); + // Email check + if (user.email.trim() === "") { + errors.email = "Email must not be blank."; + } + + // Password check + if (user.password.trim() === "") { + errors.password = "Password must not be blank."; + } + + // Checking if any errors have been raised + if (Object.keys(errors).length > 0) { + return res.status(400).json(errors); + } + + firebase + .auth() + .signInWithEmailAndPassword(user.email, user.password) + .then((data) => { + return data.user.getIdToken(); + }) + .then((token) => { + return res.json({ token }); + }) + .catch((err) => { + console.error(err); + if (err.code === "auth/wrong-password") { + return res + .status(403) + .json({ general: "Invalid credentials. Please try again." }); + } + return res.status(500).json({ error: err.code }); + }); }; -exports.updateProfileInfo = (req, res) => { - // TODO: Add functionality for adding/updating profile images - - // Data validation - const {valid, errors, profileData} = validateUpdateProfileInfo(req.body); - if (!valid) return res.status(400).json(errors); - +// Returns all data in the database for the user who is currently signed in +exports.getProfileInfo = (req, res) => { + db.collection("users") + .doc(req.user.handle) + .get() + .then((data) => { + return res.status(200).json(data.data()); + }) + .catch((err) => { + console.error(err); + return res.status(500).json(err); + }); +}; - // Update the database entry for this user - db.collection('users').doc(req.user.handle).set(profileData, {merge: true}) - .then(() => { - console.log(`${req.user.handle}'s profile info has been updated.`) - return res.status(201).json({general: `${req.user.handle}'s profile info has been updated.`}); - }) - .catch((err) => { - console.error(err); - return res.status(500).json({ - error: 'Error updating profile data' - }); - }) +// Updates the data in the database of the user who is currently logged in +exports.updateProfileInfo = (req, res) => { + // TODO: Add functionality for adding/updating profile images + + // Data validation + const { valid, errors, profileData } = validateUpdateProfileInfo(req.body); + if (!valid) return res.status(400).json(errors); + + // Update the database entry for this user + db.collection("users") + .doc(req.user.handle) + .set(profileData, { merge: true }) + .then(() => { + console.log(`${req.user.handle}'s profile info has been updated.`); + return res + .status(201) + .json({ + general: `${req.user.handle}'s profile info has been updated.` + }); + }) + .catch((err) => { + console.error(err); + return res.status(500).json({ + error: "Error updating profile data" + }); + }); }; exports.getUserDetails = (req, res) => { - let userData = {}; - db.doc(`/users/${req.params.handle}`).get().then((doc) => { - if (doc.exists) { - userData.user = doc.data(); - return db.collection('post').where('userHandle', '==', req.params.handle) - .orderBy('createdAt', 'desc').get(); - } else { - return res.status(404).json({ - error: 'User not found' - }); - } - }) - .then((data) => { - userData.posts = []; - data.forEach((doc) => { - userData.posts.push({ - body: doc.data().body, - createAt: doc.data().createAt, - userHandle: doc.data().userHandle, - userImage: doc.data().userImage, - likeCount: doc.data().likeCount, - commentCount: doc.data().commentCount, - postId: doc.id - }); - }); - return res.json(userData); - }) - .catch((err) => { - console.error(err); - return res.status(500).json({ error: err.code}); + let userData = {}; + db.doc(`/users/${req.params.handle}`) + .get() + .then((doc) => { + if (doc.exists) { + userData.user = doc.data(); + return db + .collection("post") + .where("userHandle", "==", req.params.handle) + .orderBy("createdAt", "desc") + .get(); + } else { + return res.status(404).json({ + error: "User not found" }); + } + }) + .then((data) => { + userData.posts = []; + data.forEach((doc) => { + userData.posts.push({ + body: doc.data().body, + createAt: doc.data().createAt, + userHandle: doc.data().userHandle, + userImage: doc.data().userImage, + likeCount: doc.data().likeCount, + commentCount: doc.data().commentCount, + postId: doc.id + }); + }); + return res.json(userData); + }) + .catch((err) => { + console.error(err); + return res.status(500).json({ error: err.code }); + }); }; diff --git a/functions/index.js b/functions/index.js index def41b9..eb99cbf 100644 --- a/functions/index.js +++ b/functions/index.js @@ -1,48 +1,47 @@ /* eslint-disable promise/always-return */ -const functions = require('firebase-functions'); -const app = require('express')(); -const fbAuth = require('./util/fbAuth'); -const {db} = require('./util/admin'); -const cors = require('cors'); +const app = require("express")(); +const cors = require("cors"); +const { db } = require("./util/admin"); +const fbAuth = require("./util/fbAuth"); +const functions = require("firebase-functions"); app.use(cors()); - /*------------------------------------------------------------------* -* handlers/users.js * -*------------------------------------------------------------------*/ + * handlers/users.js * + *------------------------------------------------------------------*/ const { - getUserDetails, - getProfileInfo, - login, - signup, - updateProfileInfo, -} = require('./handlers/users'); + getUserDetails, + getProfileInfo, + login, + signup, + updateProfileInfo +} = require("./handlers/users"); -app.post('/signup', signup); +// Adds a user to the database and registers them in firebase with +// an email and password pair +// Returns a token for the new user +app.post("/signup", signup); -app.post('/login', login); +// Returns a token for the user that matches the provided username +// and password +app.post("/login", login); -app.get('/getUser/:handle', getUserDetails); +app.get("/getUser/:handle", getUserDetails); // Returns all profile data of the currently logged in user -app.get('/getProfileInfo', fbAuth, getProfileInfo); +app.get("/getProfileInfo", fbAuth, getProfileInfo); // Updates the currently logged in user's profile information -app.post('/updateProfileInfo', fbAuth, updateProfileInfo); - +app.post("/updateProfileInfo", fbAuth, updateProfileInfo); /*------------------------------------------------------------------* * handlers/post.js * *------------------------------------------------------------------*/ -const { - getallPostsforUser, - putPost, -} = require('./handlers/post'); +const { getallPostsforUser, putPost } = require("./handlers/post"); -app.get('/getallPostsforUser', getallPostsforUser); +app.get("/getallPostsforUser", getallPostsforUser); // Adds one post to the database -app.post('/putPost', fbAuth, putPost); +app.post("/putPost", fbAuth, putPost); - -exports.api = functions.https.onRequest(app); \ No newline at end of file +exports.api = functions.https.onRequest(app); diff --git a/functions/util/admin.js b/functions/util/admin.js index 2431f09..ca7191d 100644 --- a/functions/util/admin.js +++ b/functions/util/admin.js @@ -1,5 +1,4 @@ const admin = require('firebase-admin'); - admin.initializeApp(); const db = admin.firestore(); diff --git a/functions/util/config.js b/functions/util/config.js index 903fed5..6014967 100644 --- a/functions/util/config.js +++ b/functions/util/config.js @@ -6,4 +6,4 @@ module.exports = { storageBucket: "twistter-e4649.appspot.com", messagingSenderId: "20131817365", appId: "1:20131817365:web:633c95fb08b16d4526b89c" -}; \ No newline at end of file +}; diff --git a/functions/util/validator.js b/functions/util/validator.js index 3ab6226..c2b1240 100644 --- a/functions/util/validator.js +++ b/functions/util/validator.js @@ -1,36 +1,36 @@ -const isEmpty = (str) => { - if (str.trim() === '') return true; - else return false; +const isEmail = (str) => { + const emailRegEx = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + if (str.match(emailRegEx)) return true; + else return false; }; -const isEmail = (str) => { - const emailRegEx = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; - if (str.match(emailRegEx)) return true; - else return false; -} +const isEmpty = (str) => { + if (str.trim() === "") return true; + else return false; +}; exports.validateUpdateProfileInfo = (data) => { - let errors = {}; - let profileData = {}; + let errors = {}; + let profileData = {}; - // ?: Should users be able to change their handles and emails? + // ?: Should users be able to change their handles and emails? - // Only adds the key to the DB if the values are not empty - if (!isEmpty(data.firstName)) profileData.firstName = data.firstName.trim(); - if (!isEmpty(data.lastName)) profileData.lastName = data.lastName.trim(); - if (!isEmpty(data.bio)) profileData.bio = data.bio.trim(); + // Only adds the key to the database if the values are not empty + if (!isEmpty(data.firstName)) profileData.firstName = data.firstName.trim(); + if (!isEmpty(data.lastName)) profileData.lastName = data.lastName.trim(); + if (!isEmpty(data.bio)) profileData.bio = data.bio.trim(); - if (isEmpty(data.email)) { - errors.email = "Must not be empty."; - } else if (!isEmail(data.email)) { - errors.email = "Must be a valid email." - } else { - profileData.email = data.email; - } + if (isEmpty(data.email)) { + errors.email = "Must not be empty."; + } else if (!isEmail(data.email)) { + errors.email = "Must be a valid email."; + } else { + profileData.email = data.email; + } - return { - errors, - valid: Object.keys(errors).length === 0 ? true : false, - profileData - } -}; \ No newline at end of file + return { + errors, + valid: Object.keys(errors).length === 0 ? true : false, + profileData + }; +}; diff --git a/twistter-frontend/src/App.css b/twistter-frontend/src/App.css index 55d854d..d828517 100644 --- a/twistter-frontend/src/App.css +++ b/twistter-frontend/src/App.css @@ -1,7 +1,3 @@ -/* body { - -} */ - .app { font-family: "Segoe UI"; font-size: large; @@ -49,4 +45,4 @@ .container { margin: 80px auto 0 auto; max-width: 1200px; -} \ No newline at end of file +} diff --git a/twistter-frontend/src/App.js b/twistter-frontend/src/App.js index f7157c9..afd3646 100644 --- a/twistter-frontend/src/App.js +++ b/twistter-frontend/src/App.js @@ -39,4 +39,4 @@ class App extends Component { } } -export default App; \ No newline at end of file +export default App; diff --git a/twistter-frontend/src/pages/edit.js b/twistter-frontend/src/pages/edit.js index 9166379..306e19b 100644 --- a/twistter-frontend/src/pages/edit.js +++ b/twistter-frontend/src/pages/edit.js @@ -1,41 +1,41 @@ import React, { Component } from "react"; import axios from "axios"; import PropTypes from "prop-types"; -// TODO: Fix font, so that it is roboto // TODO: Add a read-only '@' in the left side of the handle input // TODO: Add a cancel button, that takes the user back to their profile page -// TODO: Sort imports -// TODO: Add comments // Material-UI stuff +import Button from "@material-ui/core/Button"; +import CircularProgress from "@material-ui/core/CircularProgress"; +import Grid from "@material-ui/core/Grid"; import TextField from "@material-ui/core/TextField"; import Typography from "@material-ui/core/Typography"; import withStyles from "@material-ui/core/styles/withStyles"; -import Grid from "@material-ui/core/Grid"; -import Button from "@material-ui/core/Button"; -import CircularProgress from '@material-ui/core/CircularProgress'; const styles = { form: { textAlign: "center" }, textField: { - marginBottom: 40 + marginBottom: 30 }, pageTitle: { - marginTop: 40, + // marginTop: 20, marginBottom: 40 }, button: { - positon: 'relative', + positon: "relative", + marginBottom: 30 }, progress: { - position: 'absolute', + position: "absolute" } }; export class edit extends Component { - + // Runs as soon as the page loads. + // Sets the default values of all the textboxes to the data + // that is stored in the database for the user. componentDidMount() { axios .get("/getProfileInfo") @@ -53,6 +53,7 @@ export class edit extends Component { }); } + // Constructor for the state constructor() { super(); this.state = { @@ -66,6 +67,9 @@ export class edit extends Component { }; } + // Runs whenever the submit button is clicked. + // Updates the database entry of the signed in user with the + // data stored in the state. handleSubmit = (event) => { event.preventDefault(); this.setState({ @@ -95,11 +99,14 @@ export class edit extends Component { }); }; + // Updates the state whenever one of the textboxes changes. + // The key is the name of the textbox and the value is the + // value in the text box. handleChange = (event) => { this.setState({ [event.target.name]: event.target.value, errors: { - [event.target.name]: null, + [event.target.name]: null } }); }; @@ -154,6 +161,7 @@ export class edit extends Component { value={this.state.email} disabled helperText="(disabled)" + // INFO: These will be uncommented if changing emails is allowed // helperText={errors.email} // error={errors.email ? true : false} variant="outlined" @@ -168,6 +176,7 @@ export class edit extends Component { value={this.state.handle} disabled helperText="(disabled)" + // INFO: These will be uncommented if changing usernames is allowed // helperText={errors.handle} // error={errors.handle ? true : false} variant="outlined" @@ -197,7 +206,7 @@ export class edit extends Component { > Submit {loading && ( - + )}