From b31a571b298f42ebdddaf79221d236912968ee22 Mon Sep 17 00:00:00 2001 From: Clayton Wilson Date: Fri, 25 Oct 2019 19:27:45 -0400 Subject: [PATCH 1/7] Add followedTopics array on signup --- functions/handlers/users.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/functions/handlers/users.js b/functions/handlers/users.js index 1af539d..335215a 100644 --- a/functions/handlers/users.js +++ b/functions/handlers/users.js @@ -77,7 +77,8 @@ exports.signup = (req, res) => { email: newUser.email, handle: newUser.handle, createdAt: newUser.createdAt, - userId + userId, + followedTopics: [] }; handle2Email.set(userCred.handle, userCred.email); return db.doc(`/users/${newUser.handle}`).set(userCred); From d9f6fb5d8ecb79c66e2785d5ba68c724b2b4c3c1 Mon Sep 17 00:00:00 2001 From: Leon Liang Date: Fri, 25 Oct 2019 23:04:09 -0400 Subject: [PATCH 2/7] added function to delete topic --- functions/handlers/topic.js | 28 +++++++++++++++++++++++----- functions/index.js | 8 +++++++- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/functions/handlers/topic.js b/functions/handlers/topic.js index bac0322..498cd18 100644 --- a/functions/handlers/topic.js +++ b/functions/handlers/topic.js @@ -1,5 +1,5 @@ /* eslint-disable promise/always-return */ -const admin = require('firebase-admin'); +const { admin, db } = require("../util/admin"); exports.putTopic = (req, res) => { const newTopic = { @@ -9,6 +9,7 @@ exports.putTopic = (req, res) => { admin.firestore().collection('topics').add(newTopic) .then((doc) => { const resTopic = newTopic; + newTopic.topicId = doc.id; return res.status(200).json(resTopic); }) .catch((err) => { @@ -17,8 +18,6 @@ exports.putTopic = (req, res) => { }); }; - - exports.getAllTopics = (req, res) => { admin.firestore().collection('topics').get() .then((data) => { @@ -30,6 +29,25 @@ exports.getAllTopics = (req, res) => { }) .catch((err) => { console.error(err); - return res.status(500).json({error: 'Failed to fetch all posts written by specific user.'}) + return res.status(500).json({error: 'Failed to fetch all topics.'}) }) -}; \ No newline at end of file +}; + +exports.deleteTopic = (req, res) => { + // TODO: handle add and delete by topic id + const topic = db.doc(`/topics/${req.params.topicId}`); + topic.get().then((doc) => { + if (!doc.exists) { + return res.status(404).json({error: 'Topic not found'}); + } else { + return topic.delete(); + } + }) + .then(() => { + res.json({ message: 'Topic successfully deleted!'}); + }) + .catch((err) => { + console.error(err); + return res.status(500).json({error: 'Failed to delete topic.'}) + }) +} \ No newline at end of file diff --git a/functions/index.js b/functions/index.js index 737d44f..3be20a4 100644 --- a/functions/index.js +++ b/functions/index.js @@ -55,7 +55,10 @@ app.post("/putPost", fbAuth, putPost); /*------------------------------------------------------------------* * handlers/topic.js * *------------------------------------------------------------------*/ -const { putTopic, getAllTopics +const { + putTopic, + getAllTopics, + deleteTopic } = require("./handlers/topic"); // add topic to database @@ -64,4 +67,7 @@ app.post("/putTopic", fbAuth, putTopic); // get all topics from database app.get("/getAllTopics", fbAuth, getAllTopics); +// delete a specific topic +app.delete("/deleteTopic/:topicId", fbAuth, deleteTopic); + exports.api = functions.https.onRequest(app); From ec041732d9bb57b85aab0c3150ed3fbacc561f7b Mon Sep 17 00:00:00 2001 From: Leon Liang Date: Fri, 25 Oct 2019 23:14:25 -0400 Subject: [PATCH 3/7] reformat --- functions/handlers/topic.js | 1 - 1 file changed, 1 deletion(-) diff --git a/functions/handlers/topic.js b/functions/handlers/topic.js index 498cd18..4d3dc3d 100644 --- a/functions/handlers/topic.js +++ b/functions/handlers/topic.js @@ -34,7 +34,6 @@ exports.getAllTopics = (req, res) => { }; exports.deleteTopic = (req, res) => { - // TODO: handle add and delete by topic id const topic = db.doc(`/topics/${req.params.topicId}`); topic.get().then((doc) => { if (!doc.exists) { From 981795b2880e6b98a33821ba246fb19b33a8a649 Mon Sep 17 00:00:00 2001 From: Aditya Sankaran Date: Sat, 26 Oct 2019 16:57:25 -0400 Subject: [PATCH 4/7] writing microblogs not hardcoded anymore --- functions/handlers/post.js | 4 +- functions/package-lock.json | 67 ++++++++++++++++++++- functions/package.json | 3 +- twistter-frontend/src/Writing_Microblogs.js | 3 +- 4 files changed, 71 insertions(+), 6 deletions(-) diff --git a/functions/handlers/post.js b/functions/handlers/post.js index c14c61c..f37cc62 100644 --- a/functions/handlers/post.js +++ b/functions/handlers/post.js @@ -4,9 +4,9 @@ exports.putPost = (req, res) => { const newPost = { body: req.body.body, - userHandle: req.body.userHandle, + userHandle: req.userData.handle, userImage: req.body.userImage, - userID: req.userData.userID, + userID: req.userData.userId, microBlogTitle: req.body.microBlogTitle, createdAt: new Date().toISOString(), likeCount: 0, diff --git a/functions/package-lock.json b/functions/package-lock.json index 439a2e2..acfe9ce 100644 --- a/functions/package-lock.json +++ b/functions/package-lock.json @@ -535,6 +535,11 @@ "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", "dev": true }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -1190,11 +1195,18 @@ "progress": "^2.0.0", "regexpp": "^2.0.1", "semver": "^5.5.1", + "strip-ansi": "^4.0.0", "strip-json-comments": "^2.0.1", "table": "^5.2.3", "text-table": "^0.2.0" }, "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, "debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", @@ -1209,6 +1221,15 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } } } }, @@ -2355,6 +2376,7 @@ "mute-stream": "0.0.7", "run-async": "^2.2.0", "rxjs": "^6.4.0", + "string-width": "^2.1.0", "strip-ansi": "^5.1.0", "through": "^2.3.6" }, @@ -2403,6 +2425,12 @@ "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", "optional": true }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, "is-obj": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", @@ -3252,7 +3280,8 @@ "dev": true, "requires": { "ansi-styles": "^3.2.0", - "astral-regex": "^1.0.0" + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" } }, "snakeize": { @@ -3292,12 +3321,47 @@ "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, "string_decoder": { "version": "0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", "optional": true }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + }, "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", @@ -3344,6 +3408,7 @@ "dev": true, "requires": { "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^5.1.0" } }, diff --git a/functions/package.json b/functions/package.json index 2e308b3..29b3e89 100644 --- a/functions/package.json +++ b/functions/package.json @@ -16,7 +16,8 @@ "axios": "^0.19.0", "firebase": "^6.6.2", "firebase-admin": "^8.6.0", - "firebase-functions": "^3.1.0" + "firebase-functions": "^3.1.0", + "strip-ansi": "^5.2.0" }, "devDependencies": { "eslint": "^5.12.0", diff --git a/twistter-frontend/src/Writing_Microblogs.js b/twistter-frontend/src/Writing_Microblogs.js index b1570d8..6ddb974 100644 --- a/twistter-frontend/src/Writing_Microblogs.js +++ b/twistter-frontend/src/Writing_Microblogs.js @@ -36,7 +36,6 @@ class Writing_Microblogs extends Component { // alert('A title for the microblog was inputted: ' + this.state.title + '\nA microblog was posted: ' + this.state.value); const postData = { body: this.state.value, - userHandle: "new user", userImage: "bing-url", microBlogTitle: this.state.title, microBlogTopics: this.state.topics.split(', ') @@ -46,7 +45,7 @@ class Writing_Microblogs extends Component { } axios - .post('/putPost', postData, headers) + .post("/putPost", postData, headers) .then((res) =>{ alert('Post was shared successfully!') console.log(res.data); From 19780a1395314b3d89f334da7c0cf048a5656b6c Mon Sep 17 00:00:00 2001 From: Aditya Sankaran Date: Sat, 26 Oct 2019 17:06:42 -0400 Subject: [PATCH 5/7] made userline not hardcoded --- functions/handlers/post.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/handlers/post.js b/functions/handlers/post.js index f37cc62..1db0144 100644 --- a/functions/handlers/post.js +++ b/functions/handlers/post.js @@ -28,7 +28,7 @@ exports.putPost = (req, res) => { }; exports.getallPostsforUser = (req, res) => { - admin.firestore().collection('posts').where('userHandle', '==', 'new user' ).get() + admin.firestore().collection('posts').where('userHandle', '==', req.userData.handle ).get() .then((data) => { let posts = []; data.forEach(function(doc) { From c356dd18fa3833d3148558bd9adf9f3d26e50fd6 Mon Sep 17 00:00:00 2001 From: Leon Liang Date: Sun, 27 Oct 2019 00:08:38 -0400 Subject: [PATCH 6/7] added UI allowing topic creation --- twistter-frontend/src/App.js | 2 +- twistter-frontend/src/pages/user.js | 95 ++++++++++++++++------------- 2 files changed, 52 insertions(+), 45 deletions(-) diff --git a/twistter-frontend/src/App.js b/twistter-frontend/src/App.js index db0c81c..c413374 100644 --- a/twistter-frontend/src/App.js +++ b/twistter-frontend/src/App.js @@ -38,7 +38,7 @@ const token = localStorage.FBIdToken; if (token) { const decodedToken = jwtDecode(token); if (decodedToken.exp * 1000 < Date.now()) { - store.dispatch(logoutUser); + store.dispatch(logoutUser()); window.location.href = "/login"; } else { store.dispatch({ type: SET_AUTHENTICATED }); diff --git a/twistter-frontend/src/pages/user.js b/twistter-frontend/src/pages/user.js index 435c92c..01b0e59 100644 --- a/twistter-frontend/src/pages/user.js +++ b/twistter-frontend/src/pages/user.js @@ -6,66 +6,63 @@ import axios from 'axios'; import { makeStyles, styled } from '@material-ui/core/styles'; import Grid from '@material-ui/core/Grid'; import Card from '@material-ui/core/Card'; -import CardMedia from '@material-ui/core/CardMedia'; -import CardContent from '@material-ui/core/CardContent'; import Chip from '@material-ui/core/Chip'; -import Paper from '@material-ui/core/Paper'; import Typography from "@material-ui/core/Typography"; import AddCircle from '@material-ui/icons/AddCircle'; +import TextField from '@material-ui/core/TextField'; // component -import Profile from '../components/profile/Profile'; import Userline from '../Userline'; import noImage from '../images/no-img.png'; - -const PostCard = styled(Card)({ - background: 'linear-gradient(45deg, #1da1f2 90%)', - border: 3, - borderRadius: 3, - height:325, - width: 345, - padding: '0 30px', -}); - const MyChip = styled(Chip)({ margin: 2, color: 'primary' }); - -const styles = (theme) => ({ - ...theme -}); - -const handleDelete = () => { - alert("Delete this topic!"); -} - -const handleAddCircle = () => { - alert("Add topic"); -} - class user extends Component { state = { profile: null, - topics: null + imageUrl: null, + topics: null, + newTopic: null }; + + handleDelete = (topic) => { + alert(`Delete topic: ${topic}!`); + } + + handleAddCircle = () => { + axios.post('/putTopic', { + topic: this.state.newTopic + }) + .then(function () { + location.reload(); + }) + .catch(function (err) { + console.log(err); + }); + } + + handleChange(event) { + this.setState({ + newTopic: event.target.value + }) + } componentDidMount() { axios .get("/user") .then(res => { - console.log(res.data.credentials.handle); this.setState({ - profile: res.data.credentials.handle + profile: res.data.credentials.handle, + imageUrl: res.data.credentials.imageUrl }); }) .catch(err => console.log(err)); axios .get("/getAllTopics") .then(res => { - console.log(res.data[1]); this.setState({ topics: res.data }) @@ -83,22 +80,40 @@ class user extends Component { let topicsMarkup = this.state.topics ? ( this.state.topics.map(topic => ) + key={{topic}.topic.topicId} + onDelete={ (topic) => this.handleDelete(topic)}/>) ) : (

loading topics...

); + let imageMarkup = this.state.imageUrl ? ( + + ) : (); + return (

Post

- + {imageMarkup} {profileMarkup} {topicsMarkup} - } + this.handleChange(event)} + /> +
@@ -106,12 +121,4 @@ class user extends Component { } } -Userline.PropTypes = { - handle: PropTypes.object.isRequired -}; - -const mapStateToProps = (state) => ({ - user: state.user -}); - export default user; From 1c81ae1663f2376446622ed90ea5a1cc5f041711 Mon Sep 17 00:00:00 2001 From: Leon Liang Date: Sun, 27 Oct 2019 00:09:17 -0400 Subject: [PATCH 7/7] show user's profile image --- twistter-frontend/src/pages/user.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/twistter-frontend/src/pages/user.js b/twistter-frontend/src/pages/user.js index 01b0e59..27a0c94 100644 --- a/twistter-frontend/src/pages/user.js +++ b/twistter-frontend/src/pages/user.js @@ -85,7 +85,7 @@ class user extends Component { ) : (

loading topics...

); let imageMarkup = this.state.imageUrl ? ( -