diff --git a/functions/handlers/post.js b/functions/handlers/post.js index 20528c0..5f361fe 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, @@ -29,7 +28,7 @@ exports.putPost = (req, res) => { exports.getallPostsforUser = (req, res) => { - admin.firestore().collection('posts').where('userHandle', '==', 'user' ).get() + admin.firestore().collection('posts').where('userHandle', '==', 'new user' ).get() .then((data) => { let posts = []; data.forEach(function(doc) { @@ -42,5 +41,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 2a83ee7..cdeb3f6 100644 --- a/functions/handlers/users.js +++ b/functions/handlers/users.js @@ -1,210 +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) => { - // FIXME: Delete this after login is implemented - req.user = {}; - req.user.handle = 'itsjimmy'; + // 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 }); + }); }; +// 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); + }); +}; + +// Updates the data in the database of the user who is currently logged in exports.updateProfileInfo = (req, res) => { - // FIXME: Delete this after login is implemented - req.user = {}; - req.user.handle = 'itsjimmy'; + // TODO: Add functionality for adding/updating profile images - // 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); - - // 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' - }); - }) + // 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 b9c73d4..eb99cbf 100644 --- a/functions/index.js +++ b/functions/index.js @@ -1,50 +1,47 @@ /* eslint-disable promise/always-return */ -const functions = require('firebase-functions'); -const app = require('express')(); -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()); -const fbAuth = require('./util/fbAuth'); - - -const {db} = require('./util/admin'); - -// const firebase = require('firebase'); -// firebase.initializeApp(config); - - - - - /*------------------------------------------------------------------* -* handlers/users.js * -*------------------------------------------------------------------*/ -const {getUserDetails, getProfileInfo, updateProfileInfo, signup, login} = require('./handlers/users'); + * handlers/users.js * + *------------------------------------------------------------------*/ +const { + 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 -// TODO: Add fbAuth -app.get('/getProfileInfo', getProfileInfo); +app.get("/getProfileInfo", fbAuth, getProfileInfo); // Updates the currently logged in user's profile information -// TODO: Add fbAuth -app.post('/updateProfileInfo', updateProfileInfo); +app.post("/updateProfileInfo", fbAuth, updateProfileInfo); /*------------------------------------------------------------------* * handlers/post.js * *------------------------------------------------------------------*/ -const {putPost, getallPostsforUser} = 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', firebaseAuth, 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 00e7b76..8189649 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; @@ -52,7 +48,3 @@ color: #1da1f2; } -.post-container { - max-width: 345px; - max-height: 125px; -} \ No newline at end of file diff --git a/twistter-frontend/src/App.js b/twistter-frontend/src/App.js index 54c0997..2725cc5 100644 --- a/twistter-frontend/src/App.js +++ b/twistter-frontend/src/App.js @@ -10,7 +10,7 @@ import Navbar from './components/layout/NavBar'; import home from './pages/Home'; import register from './pages/Register'; import login from './pages/Login'; -import user from './pages/User'; +import user from './pages/user'; import writeMicroblog from './Writing_Microblogs.js'; import edit from './pages/edit.js'; @@ -38,4 +38,4 @@ class App extends Component { } } -export default App; \ No newline at end of file +export default App; diff --git a/twistter-frontend/src/Userline.js b/twistter-frontend/src/Userline.js index e8168ce..aa75716 100644 --- a/twistter-frontend/src/Userline.js +++ b/twistter-frontend/src/Userline.js @@ -2,6 +2,12 @@ import React, { Component } from "react"; import { BrowserRouter as Router } from 'react-router-dom'; import Route from 'react-router-dom/Route'; import axios from 'axios'; +import Box from '@material-ui/core/Box' +import {borders} from '@material-ui/system'; +import { sizing } from '@material-ui/system'; +// var moment = require('moment'); + + class Userline extends Component { @@ -23,20 +29,36 @@ class Userline extends Component { this.setState({microBlogs : post}) }) - - - - - } + } render() { - let sortedPosts = []; - + const sortedPosts = (this.state.microBlogs).sort((a,b) => + -a.createdAt.localeCompare(b.createdAt) + ) + return ( -
{microBlog.body}
)} -Userline
++ {sortedPosts.map((microBlog) =>
Microblog Title: {microBlog.microBlogTitle}
+
When post was created: {microBlog.createdAt.substring(0,10) +
+ " " + microBlog.createdAt.substring(11,19)}
+
Number of comments: {microBlog.commentCount}
+
Number of likes: {microBlog.likeCount}
+
Body of post: {microBlog.body}
+
Tagged topics: {microBlog.microBlogTopics.join("," + " ")}
+