From 3d875e2bde289945a7cdfed3e15b6d12ab161262 Mon Sep 17 00:00:00 2001 From: Clayton Wilson Date: Mon, 28 Oct 2019 23:25:12 -0400 Subject: [PATCH 01/28] Fixing up profile UI --- twistter-frontend/src/pages/user.js | 274 ++++++++++++++++++---------- 1 file changed, 176 insertions(+), 98 deletions(-) diff --git a/twistter-frontend/src/pages/user.js b/twistter-frontend/src/pages/user.js index 27a0c94..d48279b 100644 --- a/twistter-frontend/src/pages/user.js +++ b/twistter-frontend/src/pages/user.js @@ -3,122 +3,200 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import axios from 'axios'; //import '../App.css'; +// Material-UI +import withStyles from '@material-ui/core/styles/withStyles'; import { makeStyles, styled } from '@material-ui/core/styles'; import Grid from '@material-ui/core/Grid'; import Card from '@material-ui/core/Card'; import Chip from '@material-ui/core/Chip'; -import Typography from "@material-ui/core/Typography"; +import Typography from '@material-ui/core/Typography'; import AddCircle from '@material-ui/icons/AddCircle'; import TextField from '@material-ui/core/TextField'; +import Button from '@material-ui/core/Button'; +import GridList from '@material-ui/core/GridList'; +import GridListTile from '@material-ui/core/GridListTile'; +import GridListTileBar from '@material-ui/core/GridListTileBar'; // component import Userline from '../Userline'; import noImage from '../images/no-img.png'; +import Paper from '@material-ui/core/Paper'; + +const styles = { + button: { + positon: 'relative', + float: 'left', + marginLeft: 30, + marginTop: 15 + }, + paper: { + // marginLeft: "10%", + // marginRight: "10%" + } +}; const MyChip = styled(Chip)({ - margin: 2, - color: 'primary' + margin: 2, + color: 'primary' }); -class user extends Component { - state = { - profile: 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); - }); - } +class user extends Component { + state = { + profile: null, + imageUrl: null, + topics: null, + newTopic: null + }; - handleChange(event) { - this.setState({ - newTopic: event.target.value - }) - } + handleDelete = (topic) => { + alert(`Delete topic: ${topic}!`); + }; - componentDidMount() { - axios - .get("/user") - .then(res => { - this.setState({ - profile: res.data.credentials.handle, - imageUrl: res.data.credentials.imageUrl - }); - }) - .catch(err => console.log(err)); - axios - .get("/getAllTopics") - .then(res => { - this.setState({ - topics: res.data - }) - }) - .catch(err => console.log(err)); - } - render() { - const classes = this.props; - let profileMarkup = this.state.profile ? ( -

- {this.state.profile} -

) : (

loading username...

); - + handleAddCircle = () => { + axios + .post('/putTopic', { + topic: this.state.newTopic + }) + .then(function() { + location.reload(); + }) + .catch(function(err) { + console.log(err); + }); + }; - let topicsMarkup = this.state.topics ? ( - this.state.topics.map(topic => this.handleDelete(topic)}/>) - ) : (

loading topics...

); + handleChange(event) { + this.setState({ + newTopic: event.target.value + }); + } - let imageMarkup = this.state.imageUrl ? ( - - ) : (); + componentDidMount() { + axios + .get('/user') + .then((res) => { + this.setState({ + profile: res.data.credentials.handle, + imageUrl: res.data.credentials.imageUrl + }); + }) + .catch((err) => console.log(err)); + axios + .get('/getAllTopics') + .then((res) => { + this.setState({ + topics: res.data + }); + }) + .catch((err) => console.log(err)); + } + render() { + const { classes } = this.props; + let profileMarkup = this.state.profile ? ( +

+ {this.state.profile} +

+ ) : ( +

loading username...

+ ); - return ( - - -

Post

-
- - {imageMarkup} - {profileMarkup} - {topicsMarkup} - this.handleChange(event)} - /> - - -
- ); - } + let topicsMarkup = this.state.topics ? ( + this.state.topics.map((topic) => ( + this.handleDelete(topic)} + /> + )) + ) : ( +

loading topics...

+ ); + + let imageMarkup = this.state.imageUrl ? ( + + ) : ( + + ); + + // FIX: This needs to check if user's profile page being displayed + // is the same as the user who is logged in + // Can't check for that right now, because this page is always + // showing the logged in users profile, instead of retreiving the + // profile based on the URL entered + let editButtonMarkup = true ? ( + + ) : null; + + return ( + // + // + //

Posts go here

+ //
+ // + // {editButtonMarkup} + // {imageMarkup} + // {profileMarkup} + // {topicsMarkup} + // this.handleChange(event)} + // /> + // + // + //
+
+ {/* */} + + + + {editButtonMarkup} + + + + + {imageMarkup} + {profileMarkup} + {topicsMarkup} + this.handleChange(event)} + /> + + + +

posts here

+
+
+
+ + + {/*
*/} + + {/* + +

Posts go here

+
+
*/} +
+ ); + } } -export default user; +// export default user; +export default withStyles(styles)(user); From 3c31db9bf57350bab96d9b61d6537a27dbe494c0 Mon Sep 17 00:00:00 2001 From: Clayton Wilson Date: Tue, 29 Oct 2019 23:02:36 -0400 Subject: [PATCH 02/28] Converted tabs back to spaces --- twistter-frontend/src/pages/user.js | 306 ++++++++++++++-------------- 1 file changed, 153 insertions(+), 153 deletions(-) diff --git a/twistter-frontend/src/pages/user.js b/twistter-frontend/src/pages/user.js index d48279b..a91dffe 100644 --- a/twistter-frontend/src/pages/user.js +++ b/twistter-frontend/src/pages/user.js @@ -23,179 +23,179 @@ import noImage from '../images/no-img.png'; import Paper from '@material-ui/core/Paper'; const styles = { - button: { - positon: 'relative', - float: 'left', - marginLeft: 30, - marginTop: 15 - }, - paper: { - // marginLeft: "10%", - // marginRight: "10%" - } + button: { + positon: 'relative', + float: 'left', + marginLeft: 30, + marginTop: 15 + }, + paper: { + // marginLeft: "10%", + // marginRight: "10%" + } }; const MyChip = styled(Chip)({ - margin: 2, - color: 'primary' + margin: 2, + color: 'primary' }); class user extends Component { - state = { - profile: null, - imageUrl: null, - topics: null, - newTopic: null - }; + state = { + profile: null, + imageUrl: null, + topics: null, + newTopic: null + }; - handleDelete = (topic) => { - alert(`Delete topic: ${topic}!`); - }; + handleDelete = (topic) => { + alert(`Delete topic: ${topic}!`); + }; - handleAddCircle = () => { - axios - .post('/putTopic', { - topic: this.state.newTopic - }) - .then(function() { - location.reload(); - }) - .catch(function(err) { - console.log(err); - }); - }; + 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 - }); - } + handleChange(event) { + this.setState({ + newTopic: event.target.value + }); + } - componentDidMount() { - axios - .get('/user') - .then((res) => { - this.setState({ - profile: res.data.credentials.handle, - imageUrl: res.data.credentials.imageUrl - }); - }) - .catch((err) => console.log(err)); - axios - .get('/getAllTopics') - .then((res) => { - this.setState({ - topics: res.data - }); - }) - .catch((err) => console.log(err)); - } - render() { - const { classes } = this.props; - let profileMarkup = this.state.profile ? ( -

- {this.state.profile} -

- ) : ( -

loading username...

- ); + componentDidMount() { + axios + .get('/user') + .then((res) => { + this.setState({ + profile: res.data.credentials.handle, + imageUrl: res.data.credentials.imageUrl + }); + }) + .catch((err) => console.log(err)); + axios + .get('/getAllTopics') + .then((res) => { + this.setState({ + topics: res.data + }); + }) + .catch((err) => console.log(err)); + } + render() { + const { classes } = this.props; + let profileMarkup = this.state.profile ? ( +

+ {this.state.profile} +

+ ) : ( +

loading username...

+ ); - let topicsMarkup = this.state.topics ? ( - this.state.topics.map((topic) => ( - this.handleDelete(topic)} - /> - )) - ) : ( -

loading topics...

- ); + let topicsMarkup = this.state.topics ? ( + this.state.topics.map((topic) => ( + this.handleDelete(topic)} + /> + )) + ) : ( +

loading topics...

+ ); - let imageMarkup = this.state.imageUrl ? ( - - ) : ( - - ); + let imageMarkup = this.state.imageUrl ? ( + + ) : ( + + ); - // FIX: This needs to check if user's profile page being displayed - // is the same as the user who is logged in - // Can't check for that right now, because this page is always - // showing the logged in users profile, instead of retreiving the - // profile based on the URL entered - let editButtonMarkup = true ? ( - - ) : null; + // FIX: This needs to check if user's profile page being displayed + // is the same as the user who is logged in + // Can't check for that right now, because this page is always + // showing the logged in users profile, instead of retreiving the + // profile based on the URL entered + let editButtonMarkup = true ? ( + + ) : null; - return ( - // - // - //

Posts go here

- //
- // - // {editButtonMarkup} - // {imageMarkup} - // {profileMarkup} - // {topicsMarkup} - // this.handleChange(event)} - // /> - // - // - //
-
- {/* */} + return ( + // + // + //

Posts go here

+ //
+ // + // {editButtonMarkup} + // {imageMarkup} + // {profileMarkup} + // {topicsMarkup} + // this.handleChange(event)} + // /> + // + // + //
+
+ {/* */} - - - {editButtonMarkup} - - - - - {imageMarkup} - {profileMarkup} - {topicsMarkup} - this.handleChange(event)} - /> - - - -

posts here

-
-
-
- - - {/*
*/} + + + {editButtonMarkup} + + + + + {imageMarkup} + {profileMarkup} + {topicsMarkup} + this.handleChange(event)} + /> + + + +

posts here

+
+
+
+ + + {/* */} - {/* + {/*

Posts go here

*/} -
- ); - } +
+ ); + } } // export default user; From 7476833f0a61ec6e316da03e03c8b9157b7094d4 Mon Sep 17 00:00:00 2001 From: Clayton Wilson Date: Tue, 5 Nov 2019 16:32:16 -0500 Subject: [PATCH 03/28] Main layout of profile page --- twistter-frontend/src/pages/user.js | 81 +++++++++++++++-------------- 1 file changed, 41 insertions(+), 40 deletions(-) diff --git a/twistter-frontend/src/pages/user.js b/twistter-frontend/src/pages/user.js index a91dffe..ec3fe4f 100644 --- a/twistter-frontend/src/pages/user.js +++ b/twistter-frontend/src/pages/user.js @@ -16,6 +16,7 @@ import Button from '@material-ui/core/Button'; import GridList from '@material-ui/core/GridList'; import GridListTile from '@material-ui/core/GridListTile'; import GridListTileBar from '@material-ui/core/GridListTileBar'; +import Container from '@material-ui/core/Container'; // component import Userline from '../Userline'; @@ -27,11 +28,26 @@ const styles = { positon: 'relative', float: 'left', marginLeft: 30, - marginTop: 15 + marginTop: 20 }, paper: { // marginLeft: "10%", // marginRight: "10%" + }, + profileImage: { + marginTop: 20 + }, + topicsContainer: { + border: "lightgray solid 1px", + marginTop: 20, + paddingTop: 10, + paddingBottom: 10, + height: 300 + }, + addCircle: { + width: 65, + height: 65, + marginTop: 10 } }; @@ -113,9 +129,9 @@ class user extends Component { ); let imageMarkup = this.state.imageUrl ? ( - + ) : ( - + ); // FIX: This needs to check if user's profile page being displayed @@ -130,31 +146,6 @@ class user extends Component { ) : null; return ( - // - // - //

Posts go here

- //
- // - // {editButtonMarkup} - // {imageMarkup} - // {profileMarkup} - // {topicsMarkup} - // this.handleChange(event)} - // /> - // - // - //
{/* */} @@ -167,24 +158,34 @@ class user extends Component { {imageMarkup} {profileMarkup} - {topicsMarkup} - this.handleChange(event)} - /> -

posts here

- + + + {topicsMarkup} + + this.handleChange(event)} + /> + + {/*
*/} From 7cc8a3f11f2885c0776463cf21587176c165e880 Mon Sep 17 00:00:00 2001 From: Clayton Wilson Date: Mon, 28 Oct 2019 23:25:12 -0400 Subject: [PATCH 04/28] Fixing up profile UI --- twistter-frontend/src/pages/user.js | 188 ++++++++++++++++++---------- 1 file changed, 125 insertions(+), 63 deletions(-) diff --git a/twistter-frontend/src/pages/user.js b/twistter-frontend/src/pages/user.js index 3340b47..c877e06 100644 --- a/twistter-frontend/src/pages/user.js +++ b/twistter-frontend/src/pages/user.js @@ -19,16 +19,36 @@ import Typography from "@material-ui/core/Typography"; import AddCircle from '@material-ui/icons/AddCircle'; import TextField from '@material-ui/core/TextField'; import VerifiedIcon from '@material-ui/icons/CheckSharp'; +import withStyles from '@material-ui/core/styles/withStyles'; +import GridList from '@material-ui/core/GridList'; +import GridListTile from '@material-ui/core/GridListTile'; +import GridListTileBar from '@material-ui/core/GridListTileBar'; +import Paper from '@material-ui/core/Paper'; + // component import '../App.css'; import noImage from '../images/no-img.png'; import Writing_Microblogs from '../Writing_Microblogs'; + const MyChip = styled(Chip)({ margin: 2, color: "primary" }); +const styles = { + button: { + positon: 'relative', + float: 'left', + marginLeft: 30, + marginTop: 15 + }, + paper: { + // marginLeft: "10%", + // marginRight: "10%" + } +}; + class user extends Component { state = { profile: null, @@ -70,18 +90,18 @@ class user extends Component { componentDidMount() { axios .get("/user") - .then(res => { + .then((res) => { this.setState({ profile: res.data.credentials.handle, imageUrl: res.data.credentials.imageUrl, verified: res.data.credentials.verified ? res.data.credentials.verified : false }); }) - .catch(err => console.log(err)); + .catch((err) => console.log(err)); axios .get("/getAllTopics") - .then(res => { + .then((res) => { this.setState({ topics: res.data }); @@ -100,8 +120,8 @@ class user extends Component { } render() { + const { classes } = this.props; let authenticated = this.props.user.authenticated; - let classes = this.props; let profileMarkup = this.state.profile ? (
@@ -149,66 +169,108 @@ class user extends Component { ) ) : (

My Posts

); + // FIX: This needs to check if user's profile page being displayed + // is the same as the user who is logged in + // Can't check for that right now, because this page is always + // showing the logged in users profile, instead of retreiving the + // profile based on the URL entered + let editButtonMarkup = true ? ( + + ) : null; + return ( - - - {imageMarkup} - {profileMarkup} - {topicsMarkup} - this.handleChange(event)} - /> - -
- - - { - authenticated && - } - - - { - authenticated && - this.state.profile === 'Admin' && - } + // + // + // { + // authenticated && + // this.state.profile === 'Admin' && + // } - - -
- - {postMarkup} - - - - -        -
+ // to='/verify' + // > + // Verify Users + // } + // + // + // + // + // {postMarkup} + // + // + // + // + //        + // + +
+ + + {editButtonMarkup} + + + + + {imageMarkup} + {profileMarkup} + {topicsMarkup} + this.handleChange(event)} + /> + + + +

posts here

+
+
+
+ + +
); } } @@ -221,4 +283,4 @@ user.propTypes = { user: PropTypes.object.isRequired } -export default connect(mapStateToProps)(user); +export default connect(mapStateToProps)(withStyles(styles)(user)); From 947e5b01a409effca105abbf9d4ff5348eca47e0 Mon Sep 17 00:00:00 2001 From: Clayton Wilson Date: Tue, 29 Oct 2019 23:02:36 -0400 Subject: [PATCH 05/28] Converted tabs back to spaces --- twistter-frontend/src/pages/user.js | 139 ++++++++-------------------- 1 file changed, 41 insertions(+), 98 deletions(-) diff --git a/twistter-frontend/src/pages/user.js b/twistter-frontend/src/pages/user.js index c877e06..763d31f 100644 --- a/twistter-frontend/src/pages/user.js +++ b/twistter-frontend/src/pages/user.js @@ -37,16 +37,16 @@ const MyChip = styled(Chip)({ }); const styles = { - button: { - positon: 'relative', - float: 'left', - marginLeft: 30, - marginTop: 15 - }, - paper: { - // marginLeft: "10%", - // marginRight: "10%" - } + button: { + positon: 'relative', + float: 'left', + marginLeft: 30, + marginTop: 15 + }, + paper: { + // marginLeft: "10%", + // marginRight: "10%" + } }; class user extends Component { @@ -181,96 +181,39 @@ class user extends Component { ) : null; return ( - // - // - // {imageMarkup} - // {profileMarkup} - // {topicsMarkup} - // this.handleChange(event)} - // /> - // - //
- // - // - // { - // authenticated && - // } - // - // - // { - // authenticated && - // this.state.profile === 'Admin' && - // } - // - // - //
- // - // {postMarkup} - // - // - // - // - //        - //
- - - {editButtonMarkup} - - - - - {imageMarkup} - {profileMarkup} - {topicsMarkup} - this.handleChange(event)} - /> - - - -

posts here

-
-
-
- - -
+ {/* */} + + + + {editButtonMarkup} + + + + + {imageMarkup} + {profileMarkup} + {topicsMarkup} + this.handleChange(event)} + /> + + + + {postMarkup} + + + + + +
); } } From 80670d054e2216b13ad96c9dc3e5b1a31a11ae55 Mon Sep 17 00:00:00 2001 From: Clayton Wilson Date: Tue, 5 Nov 2019 16:32:16 -0500 Subject: [PATCH 06/28] Main layout of profile page --- twistter-frontend/src/pages/user.js | 56 ++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/twistter-frontend/src/pages/user.js b/twistter-frontend/src/pages/user.js index 763d31f..82add8f 100644 --- a/twistter-frontend/src/pages/user.js +++ b/twistter-frontend/src/pages/user.js @@ -24,7 +24,7 @@ import GridList from '@material-ui/core/GridList'; import GridListTile from '@material-ui/core/GridListTile'; import GridListTileBar from '@material-ui/core/GridListTileBar'; import Paper from '@material-ui/core/Paper'; - +import Container from '@material-ui/core/Container'; // component import '../App.css'; @@ -41,11 +41,26 @@ const styles = { positon: 'relative', float: 'left', marginLeft: 30, - marginTop: 15 + marginTop: 20 }, paper: { // marginLeft: "10%", // marginRight: "10%" + }, + profileImage: { + marginTop: 20 + }, + topicsContainer: { + border: "lightgray solid 1px", + marginTop: 20, + paddingTop: 10, + paddingBottom: 10, + height: 300 + }, + addCircle: { + width: 65, + height: 65, + marginTop: 10 } }; @@ -151,7 +166,7 @@ class user extends Component { { - this.state.imageUrl ? () : + this.state.imageUrl ? () : () } @@ -181,7 +196,6 @@ class user extends Component { ) : null; return ( -
{/* */} @@ -194,24 +208,34 @@ class user extends Component { {imageMarkup} {profileMarkup} - {topicsMarkup} - this.handleChange(event)} - /> - {postMarkup} - + + + {topicsMarkup} + + this.handleChange(event)} + /> + +
); From 6149e15b359a6593859425a7c7675e30b0201357 Mon Sep 17 00:00:00 2001 From: Clayton Wilson Date: Tue, 5 Nov 2019 17:35:16 -0500 Subject: [PATCH 07/28] Profile page kinda works on mobile --- twistter-frontend/src/pages/user.js | 416 +++++++++++++++------------- 1 file changed, 226 insertions(+), 190 deletions(-) diff --git a/twistter-frontend/src/pages/user.js b/twistter-frontend/src/pages/user.js index 82add8f..8733ae9 100644 --- a/twistter-frontend/src/pages/user.js +++ b/twistter-frontend/src/pages/user.js @@ -6,16 +6,16 @@ import axios from 'axios'; //import '../App.css'; // Material UI and React Router -import { makeStyles, styled } from "@material-ui/core/styles"; +import { makeStyles, styled } from '@material-ui/core/styles'; import { Link } from 'react-router-dom'; -import Card from "@material-ui/core/Card"; +import Card from '@material-ui/core/Card'; import CardMedia from '@material-ui/core/CardMedia'; import CardContent from '@material-ui/core/CardContent'; import Button from '@material-ui/core/Button'; -import Grid from "@material-ui/core/Grid"; +import Grid from '@material-ui/core/Grid'; -import Chip from "@material-ui/core/Chip"; -import Typography from "@material-ui/core/Typography"; +import Chip from '@material-ui/core/Chip'; +import Typography from '@material-ui/core/Typography'; import AddCircle from '@material-ui/icons/AddCircle'; import TextField from '@material-ui/core/TextField'; import VerifiedIcon from '@material-ui/icons/CheckSharp'; @@ -32,159 +32,186 @@ import noImage from '../images/no-img.png'; import Writing_Microblogs from '../Writing_Microblogs'; const MyChip = styled(Chip)({ - margin: 2, - color: "primary" + margin: 2, + color: 'primary' }); const styles = { - button: { - positon: 'relative', - float: 'left', - marginLeft: 30, - marginTop: 20 - }, - paper: { - // marginLeft: "10%", - // marginRight: "10%" - }, - profileImage: { - marginTop: 20 - }, - topicsContainer: { - border: "lightgray solid 1px", - marginTop: 20, - paddingTop: 10, - paddingBottom: 10, - height: 300 - }, - addCircle: { - width: 65, - height: 65, - marginTop: 10 - } + button: { + positon: 'relative', + float: 'left', + marginLeft: 30, + marginTop: 20 + }, + paper: { + // marginLeft: "10%", + // marginRight: "10%" + }, + card: { + marginBottom: 10 + }, + profileImage: { + marginTop: 20 + }, + topicsContainer: { + border: 'lightgray solid 1px', + marginTop: 20, + paddingTop: 10, + paddingBottom: 10, + height: 300 + }, + addCircle: { + width: 65, + height: 65, + marginTop: 10 + }, + username: { + marginBottom: 100 + } }; class user extends Component { - state = { - profile: null, - imageUrl: null, - topics: null, - newTopic: null - }; + state = { + profile: null, + imageUrl: null, + topics: null, + newTopic: null + }; - handleDelete = topic => { - axios - .delete(`/deleteTopic/${topic.id}`) - .then(function() { - location.reload(); - }) - .catch(function(err) { - console.log(err); - }); - }; + handleDelete = (topic) => { + axios + .delete(`/deleteTopic/${topic.id}`) + .then(function() { + location.reload(); + }) + .catch(function(err) { + console.log(err); + }); + }; - handleAddCircle = () => { - axios - .post("/putTopic", { - topic: this.state.newTopic - }) - .then(function() { - location.reload(); - }) - .catch(function(err) { - console.log(err); - }); - }; + 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 - }); - } + handleChange(event) { + this.setState({ + newTopic: event.target.value + }); + } - componentDidMount() { - axios - .get("/user") - .then((res) => { - this.setState({ - profile: res.data.credentials.handle, - imageUrl: res.data.credentials.imageUrl, - verified: res.data.credentials.verified ? res.data.credentials.verified : false - }); - }) - .catch((err) => console.log(err)); + componentDidMount() { + axios + .get('/user') + .then((res) => { + this.setState({ + profile: res.data.credentials.handle, + imageUrl: res.data.credentials.imageUrl, + verified: res.data.credentials.verified ? res.data.credentials.verified : false + }); + }) + .catch((err) => console.log(err)); - axios - .get("/getAllTopics") - .then((res) => { - this.setState({ - topics: res.data - }); - }) - .catch(err => console.log(err)); + axios + .get('/getAllTopics') + .then((res) => { + this.setState({ + topics: res.data + }); + }) + .catch((err) => console.log(err)); - axios - .get("/getallPostsforUser") - .then(res => { - console.log(res.data); - this.setState({ - posts: res.data - }) - }) - .catch(err => console.log(err)); - } + axios + .get('/getallPostsforUser') + .then((res) => { + console.log(res.data); + this.setState({ + posts: res.data + }); + }) + .catch((err) => console.log(err)); + } - render() { - const { classes } = this.props; - let authenticated = this.props.user.authenticated; + render() { + const { classes } = this.props; + let authenticated = this.props.user.authenticated; - let profileMarkup = this.state.profile ? ( -
- @{this.state.profile} {this.state.verified ? (): (null)} -
) : (

loading username...

); - - let topicsMarkup = this.state.topics ? ( - this.state.topics.map( - topic => ( - this.handleDelete(topic)} - /> - ) // console.log({ topic }.topic.id) - ) - ) : ( -

 loading topics...

- ); + let profileMarkup = this.state.profile ? ( +
+ + @{this.state.profile} {this.state.verified ? : null} + +
+ ) : ( +

loading username...

+ ); - let imageMarkup = this.state.imageUrl ? () : - (); + let topicsMarkup = this.state.topics ? ( + this.state.topics.map( + (topic) => ( + this.handleDelete(topic)} + /> + ) // console.log({ topic }.topic.id) + ) + ) : ( +

 loading topics...

+ ); - let postMarkup = this.state.posts ? ( - this.state.posts.map(post => - - - - { - this.state.imageUrl ? () : - () - } - - {post.userHandle} - {post.createdAt} -
- {post.microBlogTitle} - {post.body} -
- Topics: {post.microBlogTopics} -
- Likes {post.likeCount} Comments {post.commentCount} -
-
- ) - ) : (

My Posts

); + let imageMarkup = this.state.imageUrl ? ( + + ) : ( + + ); - // FIX: This needs to check if user's profile page being displayed + let postMarkup = this.state.posts ? ( + this.state.posts.map((post) => ( + + + + {this.state.imageUrl ? ( + + ) : ( + + )} + + + {post.userHandle} + + + {post.createdAt} + +
+ + {post.microBlogTitle} + + {post.body} +
+ + Topics: {post.microBlogTopics} + +
+ + Likes {post.likeCount} Comments {post.commentCount} + +
+
+ )) + ) : ( +

My Posts

+ ); + + // FIX: This needs to check if user's profile page being displayed // is the same as the user who is logged in // Can't check for that right now, because this page is always // showing the logged in users profile, instead of retreiving the @@ -195,59 +222,68 @@ class user extends Component { ) : null; - return ( -
- {/* */} - - - - {editButtonMarkup} - - - - - {imageMarkup} - {profileMarkup} - - - {postMarkup} - - - - - - {topicsMarkup} - - this.handleChange(event)} - /> - - - -
- ); - } + return ( +
+ {/* */} + + + + + {editButtonMarkup} + + + {/* */} + {/* */} + {imageMarkup} + {profileMarkup} + {/* */} + {/* */} + {/* {postMarkup} */} + {/* */} + {/* */} + + + + {topicsMarkup} + + this.handleChange(event)} + /> + + + + + + + + {postMarkup} + + + + +
+ ); + } } const mapStateToProps = (state) => ({ - user: state.user -}) + user: state.user +}); user.propTypes = { - user: PropTypes.object.isRequired -} + user: PropTypes.object.isRequired +}; export default connect(mapStateToProps)(withStyles(styles)(user)); From 9372a092add73cbc6bc1b42f5bc1af49ececf5b7 Mon Sep 17 00:00:00 2001 From: Clayton Wilson Date: Tue, 5 Nov 2019 17:43:19 -0500 Subject: [PATCH 08/28] Fix edit profile button link --- twistter-frontend/src/pages/user.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/twistter-frontend/src/pages/user.js b/twistter-frontend/src/pages/user.js index 139a1a4..750fc0a 100644 --- a/twistter-frontend/src/pages/user.js +++ b/twistter-frontend/src/pages/user.js @@ -216,9 +216,11 @@ class user extends Component { // showing the logged in users profile, instead of retreiving the // profile based on the URL entered let editButtonMarkup = true ? ( + + ) : null; return ( From 1d26eb97adf2d84225ba2effecb449196bc89b6c Mon Sep 17 00:00:00 2001 From: Leon Liang Date: Fri, 15 Nov 2019 17:47:47 -0500 Subject: [PATCH 09/28] Added function to follow and unfollow users --- functions/handlers/users.js | 174 +++++++++++++++++++++++++----------- functions/index.js | 14 ++- 2 files changed, 137 insertions(+), 51 deletions(-) diff --git a/functions/handlers/users.js b/functions/handlers/users.js index a3cea0d..2fdcd51 100644 --- a/functions/handlers/users.js +++ b/functions/handlers/users.js @@ -201,10 +201,10 @@ exports.deleteUser = (req, res) => { // Get the profile image filename // `https://firebasestorage.googleapis.com/v0/b/${config.storageBucket}/o/${imageFileName}?alt=media` let imageFileName; - req.userData.imageUrl ? - imageFileName = req.userData.imageUrl.split('/o/')[1].split('?alt=')[0] : - imageFileName = 'no-img.png' - + req.userData.imageUrl + ? (imageFileName = req.userData.imageUrl.split("/o/")[1].split("?alt=")[0]) + : (imageFileName = "no-img.png"); + const userId = req.userData.userId; let errors = {}; @@ -221,56 +221,58 @@ exports.deleteUser = (req, res) => { let auth = admin.auth().deleteUser(userId); // Deletes database data - let data = db.collection("users").doc(`${req.user.handle}`).delete(); + let data = db + .collection("users") + .doc(`${req.user.handle}`) + .delete(); // Deletes any custom profile image let image; - if (imageFileName !== 'no-img.png') { - image = admin.storage().bucket().file(imageFileName).delete() + if (imageFileName !== "no-img.png") { + image = admin + .storage() + .bucket() + .file(imageFileName) + .delete(); } else { image = Promise.resolve(); } // Deletes all users posts - let posts = db.collection("posts") + let posts = db + .collection("posts") .where("userHandle", "==", req.user.handle) .get() - .then((query) => { - query.forEach((snap) => { + .then(query => { + query.forEach(snap => { snap.ref.delete(); }); return; - }) + }); let promises = [ - auth - .then(thenFunction('auth')) - .catch((err) => catchFunction('auth', err)), - data - .then(thenFunction('data')) - .catch((err) => catchFunction('data', err)), - image - .then(thenFunction('image')) - .catch((err) => catchFunction('image', err)), - posts - .then(thenFunction('posts')) - .catch((err) => catchFunction('image', err)) + auth.then(thenFunction("auth")).catch(err => catchFunction("auth", err)), + data.then(thenFunction("data")).catch(err => catchFunction("data", err)), + image.then(thenFunction("image")).catch(err => catchFunction("image", err)), + posts.then(thenFunction("posts")).catch(err => catchFunction("image", err)) ]; - // Wait for all promises to resolve let waitPromise = Promise.all(promises); - waitPromise.then(() => { - if (Object.keys(errors) > 0) { - return res.status(500).json(errors); - } else { - return res.status(200).json({message: `All data for ${req.userData.handle} has been deleted.`}); - } - }) - .catch((err) => { - return res.status(500).json({error: err}); - }) + waitPromise + .then(() => { + if (Object.keys(errors) > 0) { + return res.status(500).json(errors); + } else { + return res.status(200).json({ + message: `All data for ${req.userData.handle} has been deleted.` + }); + } + }) + .catch(err => { + return res.status(500).json({ error: err }); + }); }; // Returns all data in the database for the user who is currently signed in @@ -351,55 +353,67 @@ exports.getAuthenticatedUser = (req, res) => { // Must be run by the Admin user exports.verifyUser = (req, res) => { if (req.userData.handle !== "Admin") { - return res.status(403).json({error: "This must be done as Admin"}); + return res.status(403).json({ error: "This must be done as Admin" }); } db.doc(`/users/${req.body.user}`) .get() - .then((doc) => { + .then(doc => { if (doc.exists) { let verifiedUser = doc.data(); verifiedUser.verified = true; - return db.doc(`/users/${req.body.user}`).set(verifiedUser, {merge: true}); + return db + .doc(`/users/${req.body.user}`) + .set(verifiedUser, { merge: true }); } else { - return res.status(400).json({error: `User ${req.body.user} was not found`}); + return res + .status(400) + .json({ error: `User ${req.body.user} was not found` }); } }) .then(() => { - return res.status(201).json({message: `${req.body.user} is now verified`}); + return res + .status(201) + .json({ message: `${req.body.user} is now verified` }); }) - .catch((err) => { + .catch(err => { console.error(err); - return res.status(500).json({error: err.code}); + return res.status(500).json({ error: err.code }); }); -} +}; // Unverifies the user sent to the request // Must be run by admin exports.unverifyUser = (req, res) => { if (req.userData.handle !== "Admin") { - return res.status(403).json({error: "This must be done as Admin"}); + return res.status(403).json({ error: "This must be done as Admin" }); } db.doc(`/users/${req.body.user}`) .get() - .then((doc) => { + .then(doc => { if (doc.exists) { let unverifiedUser = doc.data(); unverifiedUser.verified = false; - return db.doc(`/users/${req.body.user}`).set(unverifiedUser, {merge: true}); + return db + .doc(`/users/${req.body.user}`) + .set(unverifiedUser, { merge: true }); } else { - return res.status(400).json({error: `User ${req.body.user} was not found`}); + return res + .status(400) + .json({ error: `User ${req.body.user} was not found` }); } }) .then(() => { - return res.status(201).json({message: `${req.body.user} is no longer verified`}); + return res + .status(201) + .json({ message: `${req.body.user} is no longer verified` }); }) - .catch((err) => { + .catch(err => { console.error(err); - return res.status(500).json({error: err.code}); + return res.status(500).json({ error: err.code }); }); -} +}; exports.getUserHandles = (req, res) => { admin .firestore() @@ -417,3 +431,63 @@ exports.getUserHandles = (req, res) => { return res.status(500).json({ error: "Failed to get all user handles." }); }); }; + +exports.addSubscription = (req, res) => { + let new_following = []; + let userRef = db.doc(`/users/${req.userData.handle}`); + userRef.get().then(doc => { + new_following = doc.data().following; + new_following.push(req.body.following); + + // add stuff + userRef + .set({ following: new_following }, { merge: true }) + .then(doc => { + return res + .status(201) + .json({ message: `Following ${req.body.following}` }); + }) + .catch(err => { + return res.status(500).json({ err }); + }); + }); +}; + +exports.getSubs = (req, res) => { + let data = []; + db.doc(`/users/${req.userData.handle}`) + .get() + .then(doc => { + data = doc.data().following; + return res.status(200).json({ data }); + }) + .catch(err => { + return res.status(500).json({ err }); + }); +}; + +exports.removeSub = (req, res) => { + let new_following = []; + let userRef = db.doc(`/users/${req.userData.handle}`); + userRef.get().then(doc => { + new_following = doc.data().following; + // remove username from array + new_following.forEach(function(follower, index) { + if (follower === `${req.body.unfollow}`) { + new_following.splice(index, 1); + } + }); + + // update database + userRef + .set({ following: new_following }, { merge: true }) + .then(doc => { + return res + .status(202) + .json({ message: `Successfully unfollow ${req.body.unfollow}` }); + }) + .catch(err => { + return res.status(500).json({ err }); + }); + }); +}; diff --git a/functions/index.js b/functions/index.js index f05c7b8..86231d0 100644 --- a/functions/index.js +++ b/functions/index.js @@ -19,7 +19,10 @@ const { updateProfileInfo, verifyUser, unverifyUser, - getUserHandles + getUserHandles, + addSubscription, + getSubs, + removeSub } = require("./handlers/users"); // Adds a user to the database and registers them in firebase with @@ -55,6 +58,15 @@ app.post("/unverifyUser", fbAuth, unverifyUser); // get user handles with search phase app.get("/getUserHandles", fbAuth, getUserHandles); +// get user's subscription +app.get("/getSubs", fbAuth, getSubs); + +// add user to another user's "following" data field +app.post("/addSubscription", fbAuth, addSubscription); + +// remove one subscription +app.delete("/removeSub", fbAuth, removeSub); + /*------------------------------------------------------------------* * handlers/post.js * *------------------------------------------------------------------*/ From c8aa1fd050753fed297c6a0a61dc3993bf1236b8 Mon Sep 17 00:00:00 2001 From: Leon Liang Date: Tue, 19 Nov 2019 13:57:24 -0500 Subject: [PATCH 10/28] Added page to display other users' profile --- twistter-frontend/src/App.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/twistter-frontend/src/App.js b/twistter-frontend/src/App.js index aeec8ee..9e29d2e 100644 --- a/twistter-frontend/src/App.js +++ b/twistter-frontend/src/App.js @@ -19,8 +19,6 @@ import { logoutUser, getUserData } from "./redux/actions/userActions"; // Components import AuthRoute from "./util/AuthRoute"; -// axios.defaults.baseURL = 'http://localhost:5006/twistter-e4649/us-central1/api'; - // Pages import home from "./pages/Home"; import signup from "./pages/Signup"; @@ -33,6 +31,7 @@ import editProfile from "./pages/editProfile"; import userLine from "./Userline.js"; import verify from "./pages/verify"; import Search from "./pages/Search.js"; +import otherUser from "./pages/otherUser"; const theme = createMuiTheme(themeObject); @@ -65,11 +64,10 @@ class App extends Component {
- {/* AuthRoute checks if the user is logged in and if they are it redirects them to /home */} - + @@ -77,11 +75,11 @@ class App extends Component { - + + -
From 42c53fdbc4ad9b0817c49ab8221e7044d63c7ff1 Mon Sep 17 00:00:00 2001 From: Leon Liang Date: Tue, 19 Nov 2019 15:56:39 -0500 Subject: [PATCH 11/28] Add profile image, topics for other user page --- functions/handlers/topic.js | 13 +++ functions/index.js | 12 +- twistter-frontend/src/pages/otherUser.js | 138 +++++++++++++++++++++++ 3 files changed, 161 insertions(+), 2 deletions(-) create mode 100644 twistter-frontend/src/pages/otherUser.js diff --git a/functions/handlers/topic.js b/functions/handlers/topic.js index a55f748..bb5b9f1 100644 --- a/functions/handlers/topic.js +++ b/functions/handlers/topic.js @@ -58,3 +58,16 @@ exports.deleteTopic = (req, res) => { return res.status(500).json({ error: "Failed to delete topic." }); }); }; + +exports.getUserTopics = (req, res) => { + let data = []; + db.doc(`/users/${req.body.handle}`) + .get() + .then(doc => { + data = doc.data().followedTopics; + return res.status(200).json({ data }); + }) + .catch(err => { + return res.status(500).json({ err }); + }); +}; diff --git a/functions/index.js b/functions/index.js index 86231d0..bf18777 100644 --- a/functions/index.js +++ b/functions/index.js @@ -37,7 +37,7 @@ app.post("/login", login); //Deletes user account app.delete("/delete", fbAuth, deleteUser); -app.get("/getUser", fbAuth, getUserDetails); +app.post("/getUserDetails", fbAuth, getUserDetails); // Returns all profile data of the currently logged in user app.get("/getProfileInfo", fbAuth, getProfileInfo); @@ -82,7 +82,12 @@ app.post("/putPost", fbAuth, putPost); /*------------------------------------------------------------------* * handlers/topic.js * *------------------------------------------------------------------*/ -const { putTopic, getAllTopics, deleteTopic } = require("./handlers/topic"); +const { + putTopic, + getAllTopics, + deleteTopic, + getUserTopics +} = require("./handlers/topic"); // add topic to database app.post("/putTopic", fbAuth, putTopic); @@ -93,4 +98,7 @@ app.get("/getAllTopics", fbAuth, getAllTopics); // delete a specific topic app.delete("/deleteTopic/:topicId", fbAuth, deleteTopic); +// get topic for this user +app.post("/getUserTopics", fbAuth, getUserTopics); + exports.api = functions.https.onRequest(app); diff --git a/twistter-frontend/src/pages/otherUser.js b/twistter-frontend/src/pages/otherUser.js new file mode 100644 index 0000000..5a1aa21 --- /dev/null +++ b/twistter-frontend/src/pages/otherUser.js @@ -0,0 +1,138 @@ +/* eslint-disable */ +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import { connect } from "react-redux"; +import axios from "axios"; +//import '../App.css'; + +// Material UI and React Router +import { makeStyles, styled } from "@material-ui/core/styles"; +import { Link } from "react-router-dom"; +import Card from "@material-ui/core/Card"; +import CardMedia from "@material-ui/core/CardMedia"; +import CardContent from "@material-ui/core/CardContent"; +import Button from "@material-ui/core/Button"; +import Grid from "@material-ui/core/Grid"; + +import Chip from "@material-ui/core/Chip"; +import Typography from "@material-ui/core/Typography"; +import AddCircle from "@material-ui/icons/AddCircle"; +import TextField from "@material-ui/core/TextField"; +import VerifiedIcon from "@material-ui/icons/CheckSharp"; + +// component +import "../App.css"; +import noImage from "../images/no-img.png"; +import Writing_Microblogs from "../Writing_Microblogs"; +const MyChip = styled(Chip)({ + margin: 2, + color: "primary" +}); + +class user extends Component { + state = { + profile: window.location.pathname.split("/").pop(), + imageUrl: null, + topics: null, + following: null // boolean value + }; + + componentDidMount() { + console.log(this.state.profile); + axios + .post("/getUserDetails", { + handle: this.state.profile + }) + .then(res => { + console.log(res.data.userData); + this.setState({ + imageUrl: res.data.userData.imageUrl, + topics: res.data.userData.followedTopics + }); + }) + .catch(err => console.log(err)); + + axios + .get("/getallPostsforUser") + .then(res => { + console.log(res.data); + this.setState({ + posts: res.data + }); + }) + .catch(err => console.log(err)); + } + + render() { + let authenticated = this.props.user.authenticated; + let classes = this.props; + + let profileMarkup = this.state.profile ? ( +
+ + @{this.state.profile}{" "} + {this.state.verified ? ( + + ) : null} + +
+ ) : ( +

loading username...

+ ); + let topicsMarkup = this.state.topics ? ( + this.state.topics.map( + topic => // console.log({ topic }.topic.id) + ) + ) : ( +

 loading topics...

+ ); + + let imageMarkup = this.state.imageUrl ? ( + + ) : ( + + ); + + let postMarkup = this.state.posts ? ( + this.state.posts.map(post => ( + + + + {this.state.imageUrl ? ( + + ) : ( + + )} + + + + )) + ) : ( +

My Posts

+ ); + + return ( + + + {imageMarkup} + {profileMarkup} + {topicsMarkup} +
+
+ + {postMarkup} + +
+ ); + } +} + +const mapStateToProps = state => ({ + user: state.user +}); + +user.propTypes = { + user: PropTypes.object.isRequired +}; + +export default connect(mapStateToProps)(user); From 2de3da928abf8b414f4ffb216f41622e2328cbe8 Mon Sep 17 00:00:00 2001 From: Leon Liang Date: Tue, 19 Nov 2019 19:03:28 -0500 Subject: [PATCH 12/28] added UI for follow and unfollow other user --- functions/handlers/users.js | 2 + functions/index.js | 2 +- twistter-frontend/src/pages/otherUser.js | 74 +++++++++++++++--------- 3 files changed, 50 insertions(+), 28 deletions(-) diff --git a/functions/handlers/users.js b/functions/handlers/users.js index 2fdcd51..84463c2 100644 --- a/functions/handlers/users.js +++ b/functions/handlers/users.js @@ -450,6 +450,7 @@ exports.addSubscription = (req, res) => { .catch(err => { return res.status(500).json({ err }); }); + return res.status(500).json({ error: "shouldn't execute" }); }); }; @@ -489,5 +490,6 @@ exports.removeSub = (req, res) => { .catch(err => { return res.status(500).json({ err }); }); + return res.status(500).json({ error: "shouldn't execute" }); }); }; diff --git a/functions/index.js b/functions/index.js index bf18777..eb87142 100644 --- a/functions/index.js +++ b/functions/index.js @@ -65,7 +65,7 @@ app.get("/getSubs", fbAuth, getSubs); app.post("/addSubscription", fbAuth, addSubscription); // remove one subscription -app.delete("/removeSub", fbAuth, removeSub); +app.post("/removeSub", fbAuth, removeSub); /*------------------------------------------------------------------* * handlers/post.js * diff --git a/twistter-frontend/src/pages/otherUser.js b/twistter-frontend/src/pages/otherUser.js index 5a1aa21..f128f72 100644 --- a/twistter-frontend/src/pages/otherUser.js +++ b/twistter-frontend/src/pages/otherUser.js @@ -24,6 +24,7 @@ import VerifiedIcon from "@material-ui/icons/CheckSharp"; import "../App.css"; import noImage from "../images/no-img.png"; import Writing_Microblogs from "../Writing_Microblogs"; + const MyChip = styled(Chip)({ margin: 2, color: "primary" @@ -34,17 +35,48 @@ class user extends Component { profile: window.location.pathname.split("/").pop(), imageUrl: null, topics: null, - following: null // boolean value + user: null, + following: null + }; + + handleSub = () => { + if (this.state.following === true) { + axios + .post("/removeSub", { + unfollow: this.state.profile + }) + .then(res => { + console.log("removed sub"); + this.setState({ + following: false + }); + }) + .catch(function(err) { + console.log(err); + }); + } else { + axios + .post("/addSubscription", { + following: this.state.profile + }) + .then(res => { + console.log("adding sub"); + this.setState({ + following: true + }); + }) + .catch(function(err) { + console.log(err); + }); + } }; componentDidMount() { - console.log(this.state.profile); axios .post("/getUserDetails", { handle: this.state.profile }) .then(res => { - console.log(res.data.userData); this.setState({ imageUrl: res.data.userData.imageUrl, topics: res.data.userData.followedTopics @@ -53,20 +85,16 @@ class user extends Component { .catch(err => console.log(err)); axios - .get("/getallPostsforUser") + .get("/user") .then(res => { - console.log(res.data); this.setState({ - posts: res.data + following: res.data.credentials.following.includes(this.state.profile) }); }) .catch(err => console.log(err)); } render() { - let authenticated = this.props.user.authenticated; - let classes = this.props; - let profileMarkup = this.state.profile ? (
@@ -93,35 +121,27 @@ class user extends Component { ); - let postMarkup = this.state.posts ? ( - this.state.posts.map(post => ( - - - - {this.state.imageUrl ? ( - - ) : ( - - )} - - - - )) + let followMarkup = this.state.following ? ( + ) : ( -

My Posts

+ ); + console.log(this.state.following); + return ( {imageMarkup} {profileMarkup} + {followMarkup} {topicsMarkup}
- - {postMarkup} -
); } From 1ccc195036b3b41f60503af8f604b82601544dd9 Mon Sep 17 00:00:00 2001 From: Aditya Sankaran Date: Tue, 19 Nov 2019 21:09:07 -0500 Subject: [PATCH 13/28] liking and unliking work --- functions/handlers/post.js | 93 +++++++++++++++++++++++++++-- functions/index.js | 5 +- twistter-frontend/package.json | 2 +- twistter-frontend/src/pages/Home.js | 63 ++++++++++++++++++- 4 files changed, 152 insertions(+), 11 deletions(-) diff --git a/functions/handlers/post.js b/functions/handlers/post.js index a57cfe1..b431e0a 100644 --- a/functions/handlers/post.js +++ b/functions/handlers/post.js @@ -1,6 +1,8 @@ /* eslint-disable prefer-arrow-callback */ /* eslint-disable promise/always-return */ const admin = require('firebase-admin'); +const { db } = require('../util/admin'); + exports.putPost = (req, res) => { const newPost = { @@ -39,11 +41,11 @@ exports.getallPostsforUser = (req, res) => { return res.status(200).json(posts); }) .then(function() { - res.status(200).send("Successfully retrieved all user's posts from database."); - return; + return res.status(200).json("Successfully retrieved all user's posts from database."); + }) .catch(function(err) { - res.status(500).send("Failed to retrieve user's posts from database.", err); + return res.status(500).json("Failed to retrieve user's posts from database.", err); }); }; @@ -58,14 +60,93 @@ exports.getallPosts = (req, res) => { return res.status(200).json(posts); }) .then(function() { - res.status(200).send("Successfully retrieved every post from database."); - return; + return res.status(200).json("Successfully retrieved every post from database."); }) .catch(function(err) { - res.status(500).send("Failed to retrieve posts from database.", err); + return res.status(500).json("Failed to retrieve posts from database.", err); }); }; +exports.likePost = (req, res) => { + let postData; + const likeDoc = admin.firestore().collection('likes').where('userHandle', '==', req.user.handle) + .where('postId', '==', req.params.postId).limit(1); + + const postDoc = db.doc(`/posts/${req.params.postId}`); + + postDoc.get() + .then((doc) => { + if(doc.exists) { + postData = doc.data(); + return likeDoc.get(); + } + else + { + return res.status(404).json({error: 'Post not found'}); + } + }) + .then((data) => { + if (data.empty) { + return admin.firestore().collection('likes').add({ + postId : req.params.postId, + userHandle: req.user.handle + + }) + .then(() => { + postData.likeCount++; + return postDoc.update({likeCount : postData.likeCount}) + }) + .then(() => { + return res.status(200).json(postData); + }) + } + }) + .catch((err) => { + return res.status(500).json({error: 'Something is wrong'}); + }) + +} + +exports.unlikePost = (req, res) => { + + let postData; + const likeDoc = admin.firestore().collection('likes').where('userHandle', '==', req.user.handle) + .where('postId', '==', req.params.postId).limit(1); + + const postDoc = db.doc(`/posts/${req.params.postId}`); + + postDoc.get() + .then((doc) => { + if(doc.exists) { + postData = doc.data(); + return likeDoc.get(); + } + else + { + return res.status(404).json({error: 'Post not found'}); + } + }) + .then((data) => { + return db + .doc(`/likes/${data.docs[0].id}`) + .delete() + .then(() => { + postData.likeCount--; + return postDoc.update({ likeCount: postData.likeCount }); + }) + .then(() => { + res.status(200).json(postData); + }); + + }) + .catch((err) => { + console.error(err); + return res.status(500).json({error: 'Something is wrong'}); + }) + +} + + exports.getFilteredPosts = (req, res) => { admin.firestore().collection('posts').where('userHandle', '==', 'new user').where('microBlogTopics', '==') }; diff --git a/functions/index.js b/functions/index.js index f05c7b8..2dd66ae 100644 --- a/functions/index.js +++ b/functions/index.js @@ -58,7 +58,7 @@ app.get("/getUserHandles", fbAuth, getUserHandles); /*------------------------------------------------------------------* * handlers/post.js * *------------------------------------------------------------------*/ -const { getallPostsforUser, getallPosts, putPost } = require("./handlers/post"); +const { getallPostsforUser, getallPosts, putPost, likePost, unlikePost} = require("./handlers/post"); app.get("/getallPostsforUser", fbAuth, getallPostsforUser); @@ -67,6 +67,9 @@ app.get("/getallPosts", getallPosts); // Adds one post to the database app.post("/putPost", fbAuth, putPost); +app.get("/like/:postId", fbAuth, likePost); +app.get("/unlike/:postId", fbAuth, unlikePost); + /*------------------------------------------------------------------* * handlers/topic.js * *------------------------------------------------------------------*/ diff --git a/twistter-frontend/package.json b/twistter-frontend/package.json index f7dd12f..a0cec16 100644 --- a/twistter-frontend/package.json +++ b/twistter-frontend/package.json @@ -41,5 +41,5 @@ "last 1 safari version" ] }, - "proxy": "https://us-central1-twistter-e4649.cloudfunctions.net/api" + "proxy": "http://localhost:5001/twistter-e4649/us-central1/api" } diff --git a/twistter-frontend/src/pages/Home.js b/twistter-frontend/src/pages/Home.js index cee0a93..fb28409 100644 --- a/twistter-frontend/src/pages/Home.js +++ b/twistter-frontend/src/pages/Home.js @@ -17,7 +17,10 @@ import noImage from '../images/no-img.png'; import Writing_Microblogs from '../Writing_Microblogs'; class Home extends Component { - state = {}; + state = { + + }; + componentDidMount() { axios @@ -31,8 +34,9 @@ class Home extends Component { .catch(err => console.log(err)); } + render() { - let authenticated = this.props.user.authenticated; + let authenticated = this.props.user.authenticated; let postMarkup = this.state.posts ? ( this.state.posts.map(post => @@ -50,9 +54,10 @@ class Home extends Component { {post.microBlogTitle} {post.body}
- Topics: {post.microBlogTopics} + Topics: {post.microBlogTopics}
Likes {post.likeCount} Comments {post.commentCount} + ) @@ -96,6 +101,7 @@ class Home extends Component { } } + const mapStateToProps = (state) => ({ user: state.user }) @@ -104,4 +110,55 @@ Home.propTypes = { user: PropTypes.object.isRequired } +class Like extends Component { + + constructor(props) { + super(props) + this.state = { + like: false + } + + this.handleClick = this.handleClick.bind(this); + } + + handleClick(){ + + this.setState({ + like: !this.state.like + }); + + if(this.state.like == false) + { + + axios.get(`/like/${this.props.microBlog}`) + .then((res) => { + console.log(res.data); + }) + .catch((err) => { + console.log(err); + }) + } + else + { + axios.get(`/unlike/${this.props.microBlog}`) + .then((res) => { + console.log(res.data); + }) + .catch((err) => { + console.log(err); + }) + } + + } + render() { + const label = this.state.like ? 'Unlike' : 'Like' + return( +
+ +
+ ) + } + +} + export default connect(mapStateToProps)(Home); \ No newline at end of file From 81749c19ce9593230c80023c189a03c963845c5e Mon Sep 17 00:00:00 2001 From: Leon Liang Date: Tue, 19 Nov 2019 22:30:15 -0500 Subject: [PATCH 14/28] completed search page and redirect to user profile page --- functions/handlers/users.js | 17 ++- functions/index.js | 2 +- .../src/components/layout/NavBar.js | 125 ++++++++++-------- twistter-frontend/src/pages/Search.js | 56 ++++---- 4 files changed, 109 insertions(+), 91 deletions(-) diff --git a/functions/handlers/users.js b/functions/handlers/users.js index 84463c2..283b97b 100644 --- a/functions/handlers/users.js +++ b/functions/handlers/users.js @@ -415,16 +415,15 @@ exports.unverifyUser = (req, res) => { }); }; exports.getUserHandles = (req, res) => { - admin - .firestore() - .collection("users") + db.doc(`/users/${req.body.userHandle}`) .get() - .then(data => { - let users = []; - data.forEach(function(doc) { - users.push(doc.data().handle); - }); - return res.status(200).json(users); + .then(doc => { + if (doc.exists) { + let userHandle = doc.data().handle; + return res.status(200).json(userHandle); + } else { + return res.status(404).json({ error: "user not found" }); + } }) .catch(err => { console.error(err); diff --git a/functions/index.js b/functions/index.js index eb87142..df44c82 100644 --- a/functions/index.js +++ b/functions/index.js @@ -56,7 +56,7 @@ app.post("/verifyUser", fbAuth, verifyUser); app.post("/unverifyUser", fbAuth, unverifyUser); // get user handles with search phase -app.get("/getUserHandles", fbAuth, getUserHandles); +app.post("/getUserHandles", fbAuth, getUserHandles); // get user's subscription app.get("/getSubs", fbAuth, getSubs); diff --git a/twistter-frontend/src/components/layout/NavBar.js b/twistter-frontend/src/components/layout/NavBar.js index 9c720b0..f1e1b4a 100644 --- a/twistter-frontend/src/components/layout/NavBar.js +++ b/twistter-frontend/src/components/layout/NavBar.js @@ -1,71 +1,84 @@ /* eslint-disable */ -import React, { Component } from 'react'; -import { Link } from 'react-router-dom'; -import PropTypes from 'prop-types'; +import React, { Component } from "react"; +import { Link } from "react-router-dom"; +import PropTypes from "prop-types"; // Material UI stuff -import AppBar from '@material-ui/core/AppBar'; -import ToolBar from '@material-ui/core/Toolbar'; -import Button from '@material-ui/core/Button'; +import AppBar from "@material-ui/core/AppBar"; +import ToolBar from "@material-ui/core/Toolbar"; +import Button from "@material-ui/core/Button"; import withStyles from "@material-ui/core/styles/withStyles"; // Redux stuff -import { logoutUser } from '../../redux/actions/userActions'; -import { connect } from 'react-redux'; +import { logoutUser } from "../../redux/actions/userActions"; +import { connect } from "react-redux"; const styles = { - form: { - textAlign: "center" - }, - textField: { - marginBottom: 30 - }, - pageTitle: { - marginBottom: 40 - }, - button: { - positon: "relative", - marginBottom: 30 - }, - progress: { - position: "absolute" - } - }; - + form: { + textAlign: "center" + }, + textField: { + marginBottom: 30 + }, + pageTitle: { + marginBottom: 40 + }, + button: { + positon: "relative", + marginBottom: 30 + }, + progress: { + position: "absolute" + } +}; + export class Navbar extends Component { - render() { - const authenticated = this.props.user.authenticated; - return ( - - - - {authenticated && } - {!authenticated && } - {!authenticated && } - {authenticated && } - - - ) - } + render() { + const authenticated = this.props.user.authenticated; + return ( + + + + {authenticated && ( + + )} + {!authenticated && ( + + )} + {!authenticated && ( + + )} + {authenticated && ( + + )} + {authenticated && ( + + )} + + + ); + } } -const mapStateToProps = (state) => ({ - user: state.user -}) +const mapStateToProps = state => ({ + user: state.user +}); Navbar.propTypes = { - user: PropTypes.object.isRequired, - classes: PropTypes.object.isRequired -} + user: PropTypes.object.isRequired, + classes: PropTypes.object.isRequired +}; export default connect(mapStateToProps)(withStyles(styles)(Navbar)); diff --git a/twistter-frontend/src/pages/Search.js b/twistter-frontend/src/pages/Search.js index 657a654..14cbc52 100644 --- a/twistter-frontend/src/pages/Search.js +++ b/twistter-frontend/src/pages/Search.js @@ -1,17 +1,10 @@ import React, { Component } from "react"; // import props -import { TextField, Paper } from "@material-ui/core"; +import { TextField, Button } from "@material-ui/core"; import Grid from "@material-ui/core/Grid"; import Axios from "axios"; -import user from "./user.js"; -import { - BrowserRouter as Router, - Switch, - Route, - Link, - useRouteMatch -} from "react-router-dom"; +import { BrowserRouter as Router } from "react-router-dom"; export class Search extends Component { state = { @@ -19,20 +12,28 @@ export class Search extends Component { searchResult: null }; - handleSearch(event) { - Axios.get("/getUserHandles").then(res => { - this.setState({ - searchResult: res.data - }); - }); + handleSearch = () => { console.log(this.state.searchPhase); - } + Axios.post("/getUserHandles", { + userHandle: this.state.searchPhase + }) + .then(res => { + console.log(res); + + this.setState({ + searchResult: res.data + }); + }) + .catch(err => { + console.log(err); + }); + }; handleInput(event) { this.setState({ searchPhase: event.target.value }); - this.handleSearch(); + console.log(this.state.searchPhase); } handleRedirect() { @@ -41,16 +42,16 @@ export class Search extends Component { render() { let resultMarkup = this.state.searchResult ? ( - this.state.searchResult.map(result => ( - -
- {result} -
-
- )) + + + ) : ( // console.log(this.state.searchResult) -

searching...

+

No result

); return ( @@ -65,6 +66,11 @@ export class Search extends Component { onChange={event => this.handleInput(event)} /> + + + {resultMarkup} ); From e8110d643feaae370be1bef3e4f8f4c61abf6752 Mon Sep 17 00:00:00 2001 From: Aditya Sankaran Date: Thu, 21 Nov 2019 15:42:43 -0500 Subject: [PATCH 15/28] finished quoting microblogs --- functions/handlers/post.js | 97 ++++++++++++++++++++++- functions/index.js | 7 +- twistter-frontend/src/pages/Home.js | 115 ++++++++++++++++++++++++++++ 3 files changed, 217 insertions(+), 2 deletions(-) diff --git a/functions/handlers/post.js b/functions/handlers/post.js index b431e0a..407a85c 100644 --- a/functions/handlers/post.js +++ b/functions/handlers/post.js @@ -67,6 +67,102 @@ exports.getallPosts = (req, res) => { }); }; +exports.quoteWithPost = (req, res) => { + + let quoteData; + const quoteDoc = admin.firestore().collection('quote'). + where('userHandle', '==', req.user.handle). + where('postId', '==', req.params.postId).limit(1); + + const postDoc = db.doc(`/posts/${req.params.postId}`); + + postDoc.get() + .then((doc) => { + if(doc.exists) { + quoteData = doc.data(); + return quoteDoc.get(); + } + else + { + return res.status(404).json({error: 'Post not found'}); + } + }) + .then((data) => { + if(data.empty) { + return admin.firestore().collection('quote').add({ + postId : req.params.postId, + userHandle : req.user.handle, + quotePost : req.body.quotePost + }) + .then(() => { + return admin.firestore().collection('posts').add({ + quoteData, + quoteUser : req.user.handle, + quotePost : req.body.quotePost, + quotedAt : new Date().toISOString() + + }) + }) + } + else { + console.log("Post has already been quoted."); + return res.status(400).json({ error: 'Post has already been quoted.' }); + } + }) + .catch((err) => { + return res.status(500).json({error: 'Something is wrong'}); + + }) + +} + +exports.quoteWithoutPost = (req, res) => { + let quoteData; + const quoteDoc = admin.firestore().collection('quote'). + where('userHandle', '==', req.user.handle). + where('postId', '==', req.params.postId).limit(1); + + const postDoc = db.doc(`/posts/${req.params.postId}`); + + postDoc.get() + .then((doc) => { + if(doc.exists) { + quoteData = doc.data(); + return quoteDoc.get(); + } + else + { + return res.status(404).json({error: 'Post not found'}); + } + }) + .then((data) => { + if(data.empty) { + return admin.firestore().collection('quote').add({ + postId : req.params.postId, + userHandle : req.user.handle, + }) + .then(() => { + return admin.firestore().collection('posts').add({ + quoteData, + quoteUser : req.user.handle, + quotedAt : new Date().toISOString() + + }) + }) + } + else { + console.log("Post has already been quoted."); + return res.status(400).json({ error: 'Post has already been quoted.' }); + } + }) + .catch((err) => { + return res.status(500).json({error: 'Something is wrong'}); + + }) + +} + + exports.likePost = (req, res) => { let postData; const likeDoc = admin.firestore().collection('likes').where('userHandle', '==', req.user.handle) @@ -146,7 +242,6 @@ exports.unlikePost = (req, res) => { } - exports.getFilteredPosts = (req, res) => { admin.firestore().collection('posts').where('userHandle', '==', 'new user').where('microBlogTopics', '==') }; diff --git a/functions/index.js b/functions/index.js index 2dd66ae..cdea2d1 100644 --- a/functions/index.js +++ b/functions/index.js @@ -58,7 +58,7 @@ app.get("/getUserHandles", fbAuth, getUserHandles); /*------------------------------------------------------------------* * handlers/post.js * *------------------------------------------------------------------*/ -const { getallPostsforUser, getallPosts, putPost, likePost, unlikePost} = require("./handlers/post"); +const { getallPostsforUser, getallPosts, putPost, likePost, unlikePost, quoteWithPost, quoteWithoutPost} = require("./handlers/post"); app.get("/getallPostsforUser", fbAuth, getallPostsforUser); @@ -70,6 +70,11 @@ app.post("/putPost", fbAuth, putPost); app.get("/like/:postId", fbAuth, likePost); app.get("/unlike/:postId", fbAuth, unlikePost); +app.post("/quoteWithPost/:postId", fbAuth, quoteWithPost); +app.post("/quoteWithoutPost/:postId", fbAuth, quoteWithoutPost); + + + /*------------------------------------------------------------------* * handlers/topic.js * *------------------------------------------------------------------*/ diff --git a/twistter-frontend/src/pages/Home.js b/twistter-frontend/src/pages/Home.js index fb28409..ff90580 100644 --- a/twistter-frontend/src/pages/Home.js +++ b/twistter-frontend/src/pages/Home.js @@ -15,6 +15,8 @@ import '../App.css'; import logo from '../images/twistter-logo.png'; import noImage from '../images/no-img.png'; import Writing_Microblogs from '../Writing_Microblogs'; +import ReactModal from 'react-modal'; + class Home extends Component { state = { @@ -58,6 +60,7 @@ class Home extends Component {
Likes {post.likeCount} Comments {post.commentCount} + ) @@ -110,6 +113,118 @@ Home.propTypes = { user: PropTypes.object.isRequired } +class Quote extends Component { + constructor(props) { + super(props); + this.state = { + characterCount: 250, + showModal: false, + value: "" + } + + this.handleSubmitWithoutPost = this.handleSubmitWithoutPost.bind(this); + this.handleOpenModal = this.handleOpenModal.bind(this); + this.handleCloseModal = this.handleCloseModal.bind(this); + this.handleSubmit = this.handleSubmit.bind(this); + } + + handleSubmitWithoutPost(event) { + const headers = { + headers: { "Content-Type": "application/json" } + }; + axios.post(`/quoteWithoutPost/${this.props.microblog}`, headers) + .then((res) => { + + console.log(res.data); + }) + .catch(err => { + + console.error(err); + }); + event.preventDefault(); + } + + handleOpenModal() { + this.setState({ showModal: true }); + } + + handleCloseModal() { + this.setState({ showModal: false }); + } + + handleChangeforPost(event) { + this.setState({ value: event.target.value }); + } + + handleChangeforCharacterCount(event) { + const charCount = event.target.value.length; + const charRemaining = 250 - charCount; + this.setState({ characterCount: charRemaining }); + } + + handleSubmit(event) { + const quotedPost = { + quotePost: this.state.value, + }; + const headers = { + headers: { "Content-Type": "application/json" } + }; + axios.post(`/quoteWithPost/${this.props.microblog}`, quotedPost, headers) + .then((res) => { + + console.log(res.data); + }) + .catch(err => { + + console.error(err); + }); + event.preventDefault(); + this.setState({ showModal: false, characterCount: 250, value: "" }); + } + + render() { + return ( +
+ + +
+
+