diff --git a/functions/handlers/post.js b/functions/handlers/post.js index d15ea33..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, @@ -11,6 +10,7 @@ exports.putPost = (req, res) => { createdAt: new Date().toISOString(), likeCount: 0, commentCount: 0, + microBlogTopics: req.body.microBlogTopics }; @@ -28,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) { @@ -41,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 04d3b4e..eb99cbf 100644 --- a/functions/index.js +++ b/functions/index.js @@ -1,49 +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', 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/package-lock.json b/twistter-frontend/package-lock.json index 2fb6aa4..f7ffc91 100644 --- a/twistter-frontend/package-lock.json +++ b/twistter-frontend/package-lock.json @@ -62,9 +62,9 @@ } }, "@material-ui/styles": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.4.3.tgz", - "integrity": "sha512-kNUdHFWsrvWKIEPx8Xy2/qayqsGMrYmCMq+FIiJiYczVZl5hiS8j5+KayonnpVta/O+Dktk+cxWkVcgwtxMrHg==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.5.0.tgz", + "integrity": "sha512-O0NSAECHK9f3DZK6wy56PZzp8b/7KSdfpJs8DSC7vnXUAoMPCTtchBKLzMtUsNlijiJFeJjSxNdQfjWXgyur5A==", "requires": { "@babel/runtime": "^7.4.4", "@emotion/hash": "^0.7.1", @@ -74,21 +74,21 @@ "csstype": "^2.5.2", "deepmerge": "^4.0.0", "hoist-non-react-statics": "^3.2.1", - "jss": "10.0.0-alpha.25", - "jss-plugin-camel-case": "10.0.0-alpha.25", - "jss-plugin-default-unit": "10.0.0-alpha.25", - "jss-plugin-global": "10.0.0-alpha.25", - "jss-plugin-nested": "10.0.0-alpha.25", - "jss-plugin-props-sort": "10.0.0-alpha.25", - "jss-plugin-rule-value-function": "10.0.0-alpha.25", - "jss-plugin-vendor-prefixer": "10.0.0-alpha.25", + "jss": "^10.0.0", + "jss-plugin-camel-case": "^10.0.0", + "jss-plugin-default-unit": "^10.0.0", + "jss-plugin-global": "^10.0.0", + "jss-plugin-nested": "^10.0.0", + "jss-plugin-props-sort": "^10.0.0", + "jss-plugin-rule-value-function": "^10.0.0", + "jss-plugin-vendor-prefixer": "^10.0.0", "prop-types": "^15.7.2" } }, "@material-ui/system": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.4.3.tgz", - "integrity": "sha512-Cb05vLXsaCzssXD/iZKa0/qC6YOwbFWnYdnOEdkXZ3Fn2Ytz7rsnMgFejUSQV1luVhUBlEIm8DVz40N25WwW7w==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.5.0.tgz", + "integrity": "sha512-vR0PbMTzLnuuVCoYNQ13zyhLa/4s/UA9P9JbNuHBOOkfrHn53ShINiG0v05EgfwizfULLtc7mNvsGAgIyyp/hQ==", "requires": { "@babel/runtime": "^7.4.4", "deepmerge": "^4.0.0", @@ -119,9 +119,9 @@ "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==" }, "@types/react": { - "version": "16.9.3", - "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.3.tgz", - "integrity": "sha512-Ogb2nSn+2qQv5opoCv7Ls5yFxtyrdUYxp5G+SWTrlGk7dmFKw331GiezCgEZj9U7QeXJi1CDtws9pdXU1zUL4g==", + "version": "16.9.4", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.4.tgz", + "integrity": "sha512-ItGNmJvQ0IvWt8rbk5PLdpdQhvBVxAaXI9hDlx7UMd8Ie1iMIuwMNiKeTfmVN517CdplpyXvA22X4zm4jGGZnw==", "requires": { "@types/prop-types": "*", "csstype": "^2.2.0" @@ -5196,9 +5196,9 @@ } }, "jss": { - "version": "10.0.0-alpha.25", - "resolved": "https://registry.npmjs.org/jss/-/jss-10.0.0-alpha.25.tgz", - "integrity": "sha512-zqKnXv181B9vue2yYhmVhc+6ggbbxHF/33rjXfXEjaa22nOvknTI21QDfq3oZ8uCC50kcFp3Z8KU1ghUXdFvIA==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/jss/-/jss-10.0.0.tgz", + "integrity": "sha512-TPpDFsiBjuERiL+dFDq8QCdiF9oDasPcNqCKLGCo/qED3fNYOQ8PX2lZhknyTiAt3tZrfOFbb0lbQ9lTjPZxsQ==", "requires": { "@babel/runtime": "^7.3.1", "csstype": "^2.6.5", @@ -5207,69 +5207,69 @@ } }, "jss-plugin-camel-case": { - "version": "10.0.0-alpha.25", - "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.0.0-alpha.25.tgz", - "integrity": "sha512-J5ZEGDTy9ddqdTUPAF4SJQ25u5kiG1ORP8F+ZPEZAkkiMQJp+/Aol4I7xhTS2aW1Lhg8xNxdhdRfBi5yU7wOvg==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.0.0.tgz", + "integrity": "sha512-yALDL00+pPR4FJh+k07A8FeDvfoPPuXU48HLy63enAubcVd3DnS+2rgqPXglHDGixIDVkCSXecl/l5GAMjzIbA==", "requires": { "@babel/runtime": "^7.3.1", "hyphenate-style-name": "^1.0.3", - "jss": "10.0.0-alpha.25" + "jss": "10.0.0" } }, "jss-plugin-default-unit": { - "version": "10.0.0-alpha.25", - "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.0.0-alpha.25.tgz", - "integrity": "sha512-auOG459B+yEqkojgaXH02SYO9+xjmAxlmP+WbzhVpXqOFJ2CN/kaxd8P4NJZLdj3BQxHiM7WIyMVh786StE+EA==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.0.0.tgz", + "integrity": "sha512-sURozIOdCtGg9ap18erQ+ijndAfEGtTaetxfU3H4qwC18Bi+fdvjlY/ahKbuu0ASs7R/+WKCP7UaRZOjUDMcdQ==", "requires": { "@babel/runtime": "^7.3.1", - "jss": "10.0.0-alpha.25" + "jss": "10.0.0" } }, "jss-plugin-global": { - "version": "10.0.0-alpha.25", - "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.0.0-alpha.25.tgz", - "integrity": "sha512-cS98Q8X8jwltuaBZd9eYuxMXxkUL+mJGl2Ok3/nmJzH9nLzj6i7kLxSoDtuJNqsRmbP7ogIXVozJUq9lUu2hlQ==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.0.0.tgz", + "integrity": "sha512-80ofWKSQUo62bxLtRoTNe0kFPtHgUbAJeOeR36WEGgWIBEsXLyXOnD5KNnjPqG4heuEkz9eSLccjYST50JnI7Q==", "requires": { "@babel/runtime": "^7.3.1", - "jss": "10.0.0-alpha.25" + "jss": "10.0.0" } }, "jss-plugin-nested": { - "version": "10.0.0-alpha.25", - "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.0.0-alpha.25.tgz", - "integrity": "sha512-7sk7/6mX1YTgXe+AyeD1zEyKTgIGbbhYtg+wWQcHJlE1flW2JHfcQ5mw84FgHcHQRQ8Dq3l9I3aEY51ev0J1Wg==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.0.0.tgz", + "integrity": "sha512-waxxwl/po1hN3azTyixKnr8ReEqUv5WK7WsO+5AWB0bFndML5Yqnt8ARZ90HEg8/P6WlqE/AB2413TkCRZE8bA==", "requires": { "@babel/runtime": "^7.3.1", - "jss": "10.0.0-alpha.25", + "jss": "10.0.0", "tiny-warning": "^1.0.2" } }, "jss-plugin-props-sort": { - "version": "10.0.0-alpha.25", - "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.0.0-alpha.25.tgz", - "integrity": "sha512-8B/6QLQuUX8cIlZbXdjEm5l0jCX4EgacYMcFJhdKwDKEZYeAghpgQQrCKl0/CYHW7iFge5wim67P+uL6QxMzyw==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.0.0.tgz", + "integrity": "sha512-41mf22CImjwNdtOG3r+cdC8+RhwNm616sjHx5YlqTwtSJLyLFinbQC/a4PIFk8xqf1qpFH1kEAIw+yx9HaqZ3g==", "requires": { "@babel/runtime": "^7.3.1", - "jss": "10.0.0-alpha.25" + "jss": "10.0.0" } }, "jss-plugin-rule-value-function": { - "version": "10.0.0-alpha.25", - "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.0.0-alpha.25.tgz", - "integrity": "sha512-CQQtWO+/OZRGaFRBSGQUgAci9YlVtdoXcWQKBNo70tmpp+kaXKlFNCYaL3jmHbJHMiwKQYG2RYFQNIrwJ9SGmA==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.0.0.tgz", + "integrity": "sha512-Jw+BZ8JIw1f12V0SERqGlBT1JEPWax3vuZpMym54NAXpPb7R1LYHiCTIlaJUyqvIfEy3kiHMtgI+r2whGgRIxQ==", "requires": { "@babel/runtime": "^7.3.1", - "jss": "10.0.0-alpha.25" + "jss": "10.0.0" } }, "jss-plugin-vendor-prefixer": { - "version": "10.0.0-alpha.25", - "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.0.0-alpha.25.tgz", - "integrity": "sha512-5FXpB/TiwckbrkoDCmd27YsWCESl1K4hAX/oro2/geEXgnVQvDgQOf2eWCsjYO2K1lYPPXtskMfws/Q3eKmbYg==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.0.0.tgz", + "integrity": "sha512-qslqvL0MUbWuzXJWdUxpj6mdNUX8jr4FFTo3aZnAT65nmzWL7g8oTr9ZxmTXXgdp7ANhS1QWE7036/Q2isFBpw==", "requires": { "@babel/runtime": "^7.3.1", "css-vendor": "^2.0.6", - "jss": "10.0.0-alpha.25" + "jss": "10.0.0" } }, "jsx-ast-utils": { diff --git a/twistter-frontend/package.json b/twistter-frontend/package.json index bc4672f..003bf65 100644 --- a/twistter-frontend/package.json +++ b/twistter-frontend/package.json @@ -4,6 +4,8 @@ "private": true, "dependencies": { "@material-ui/core": "^4.4.3", + "@material-ui/styles": "^4.5.0", + "@material-ui/system": "^4.5.0", "axios": "^0.19.0", "clsx": "^1.0.4", "create-react-app": "^3.1.2", @@ -36,5 +38,5 @@ "last 1 safari version" ] }, - "proxy": "https://us-central1-twistter-e4649.cloudfunctions.net/api" + "proxy": "https://us-central1-twistter-e4649.cloudfunctions.net/api" } diff --git a/twistter-frontend/src/App.css b/twistter-frontend/src/App.css index ea9e7c5..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; @@ -50,4 +46,5 @@ margin: 80px auto 0 auto; max-width: 1200px; color: #1da1f2; -} \ No newline at end of file +} + diff --git a/twistter-frontend/src/App.js b/twistter-frontend/src/App.js index ef684da..3438b32 100644 --- a/twistter-frontend/src/App.js +++ b/twistter-frontend/src/App.js @@ -12,7 +12,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'; @@ -40,4 +40,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 51d74f4..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 { @@ -9,7 +15,8 @@ class Userline extends Component { { super(props); this.state = { - microBlogs : [] + microBlogs : [], + } } @@ -21,17 +28,37 @@ class Userline extends Component { const post = res.data; this.setState({microBlogs : post}) - }) - - - } + }) + } render() { + const sortedPosts = (this.state.microBlogs).sort((a,b) => + -a.createdAt.localeCompare(b.createdAt) + ) + return ( - - ) - } +
+
+

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("," + " ")} +

)} +

+
+
+
+ + ) + } + } export default Userline; diff --git a/twistter-frontend/src/Writing_Microblogs.js b/twistter-frontend/src/Writing_Microblogs.js index 1858193..3bda6cc 100644 --- a/twistter-frontend/src/Writing_Microblogs.js +++ b/twistter-frontend/src/Writing_Microblogs.js @@ -11,7 +11,8 @@ class Writing_Microblogs extends Component { this.state = { value: '', title: '', - characterCount: 10 + topics: '', + characterCount: 250 }; @@ -19,12 +20,18 @@ class Writing_Microblogs extends Component { this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); this.handleChangeforPost = this.handleChangeforPost.bind(this); + this.handleChangeforTopics = this.handleChangeforTopics.bind(this); + } handleChange(event) { this.setState( {title: event.target.value }); } + handleChangeforTopics(event) { + this.setState( {topics: event.target.value}); + } + handleSubmit(event) { // alert('A title for the microblog was inputted: ' + this.state.title + '\nA microblog was posted: ' + this.state.value); @@ -33,15 +40,17 @@ class Writing_Microblogs extends Component { { body: this.state.value, userHandle: "new user", userImage: "bing-url", - microBlogTitle: this.state.title - + microBlogTitle: this.state.title, + microBlogTopics: this.state.topics.split(', ') + }, { headers: { 'Content-Type': 'application/json'} } ) console.log(response.data); + alert('Post was shared successfully!'); event.preventDefault(); - this.setState({value: '', title: '',characterCount: 10}) + this.setState({value: '', title: '',characterCount: 250, topics: ''}) } handleChangeforPost(event) { @@ -50,24 +59,29 @@ class Writing_Microblogs extends Component { handleChangeforCharacterCount(event) { const charCount = event.target.value.length - const charRemaining = 10 - charCount + const charRemaining = 250 - charCount this.setState({characterCount: charRemaining }) } - render() { return (
-
+
- +