Merge pull request #106 from ClaytonWWilson/likes

Likes
This commit is contained in:
asankaran35 2019-12-04 17:42:03 -05:00 committed by GitHub
commit 35cec7abf3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 379 additions and 136 deletions

View File

@ -136,25 +136,23 @@ exports.getOtherUsersPosts = (req, res) => {
}; };
exports.quoteWithPost = (req, res) => { exports.quoteWithPost = (req, res) => {
let quoteData; let quoteData;
const quoteDoc = admin const quoteDoc = admin.firestore().collection('quote').
.firestore() where('userHandle', '==', req.user.handle).
.collection("quote") where('quoteId', '==', req.params.postId).limit(1);
.where("userHandle", "==", req.user.handle)
.where("postId", "==", req.params.postId)
.limit(1);
const postDoc = db.doc(`/posts/${req.params.postId}`); const postDoc = db.doc(`/posts/${req.params.postId}`);
postDoc postDoc.get()
.get() .then((doc) => {
.then(doc => { if(doc.exists) {
if (doc.exists) { quoteData = doc.data();
quoteData = doc.data(); return quoteDoc.get();
return quoteDoc.get(); }
} else { else
return res.status(404).json({ error: "Post not found" }); {
} return res.status(404).json({error: 'Post not found'});
}
}) })
.then(data => { .then(data => {
if (data.empty) { if (data.empty) {
@ -202,25 +200,23 @@ exports.quoteWithPost = (req, res) => {
}; };
exports.quoteWithoutPost = (req, res) => { exports.quoteWithoutPost = (req, res) => {
let quoteData; let quoteData;
const quoteDoc = admin const quoteDoc = admin.firestore().collection('quote').
.firestore() where('userHandle', '==', req.user.handle).
.collection("quote") where('quoteId', '==', req.params.postId).limit(1);
.where("userHandle", "==", req.user.handle)
.where("postId", "==", req.params.postId)
.limit(1);
const postDoc = db.doc(`/posts/${req.params.postId}`); const postDoc = db.doc(`/posts/${req.params.postId}`);
postDoc postDoc.get()
.get() .then((doc) => {
.then(doc => { if(doc.exists) {
if (doc.exists) { quoteData = doc.data();
quoteData = doc.data(); return quoteDoc.get();
return quoteDoc.get(); }
} else { else
return res.status(404).json({ error: "Post not found" }); {
} return res.status(404).json({error: 'Post not found'});
}
}) })
.then(data => { .then(data => {
if (data.empty) { if (data.empty) {
@ -292,94 +288,186 @@ exports.checkforLikePost = (req, res) => {
}; };
exports.likePost = (req, res) => { 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}`); const postId = req.params.postId;
let likedPostDoc;
db.doc(`/users/${req.userData.handle}`)
.get()
.then((userDoc) => {
let likes = userDoc.data().likes;
if (likes === undefined || likes === null) {
likes = [];
}
postDoc if (likes.includes(postId)) {
.get() return res.status(400).json({error: "This user has already liked this post"});
.then(doc => { }
if (doc.exists) {
postData = doc.data();
return likeDoc.get();
} else {
return res.status(404).json({ error: "Post not found" });
}
})
.then(data => { likes.push(postId);
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" });
return res.status(500).json({ error: err });
});
};
exports.unlikePost = (req, res) => { return userDoc.ref.update({likes})
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(() => { .then(() => {
res.status(200).json(postData); return db.doc(`/posts/${postId}`).get()
});
}) })
.catch(err => { .then((postDoc) => {
console.error(err); let postData = postDoc.data();
return res.status(500).json({ error: "Something is wrong" }); postData.likeCount++;
}); likedPostDoc = postData;
}; return postDoc.ref.update({likeCount : postData.likeCount})
})
.then(() => {
return res.status(201).json(likedPostDoc);
})
.catch((err) => {
console.log(err);
return res.status(500).json({error: err});
})
// 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) => {
const postId = req.params.postId;
let likedPostDoc;
db.doc(`/users/${req.userData.handle}`)
.get()
.then((userDoc) => {
let likes = userDoc.data().likes;
if (likes === undefined || likes === null) {
likes = [];
}
if (!likes.includes(postId)) {
return res.status(400).json({error: "This user hasn't liked this post yet"});
}
let i;
for (i = 0; i < likes.length; i++) {
if (likes[i] === postId) {
likes.splice(i, 1);
}
}
return userDoc.ref.update({likes})
})
.then(() => {
return db.doc(`/posts/${postId}`).get()
})
.then((postDoc) => {
let postData = postDoc.data();
postData.likeCount--;
likedPostDoc = postData;
return postDoc.ref.update({likeCount : postData.likeCount})
})
.then(() => {
return res.status(201).json(likedPostDoc);
})
.catch((err) => {
console.log(err);
return res.status(500).json({error: err});
})
// 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.getLikes = (req, res) => {
db.doc(`/users/${req.userData.handle}`)
.get()
.then((doc) => {
let likes = doc.data().likes;
if (likes === undefined || likes === null) {
likes = [];
}
return res.status(200).json({likes});
})
.catch((err) => {
console.log(err);
return res.status(500).json({error: err});
})
}
exports.getFilteredPosts = (req, res) => { exports.getFilteredPosts = (req, res) => {
admin admin
.firestore() .firestore()
.collection("posts") .collection("posts")
.where("userHandle", "==", "new user") .where("userHandle", "==", "new user")
.where("microBlogTopics", "=="); .where("microBlogTopics", "==");
}; };

View File

@ -81,17 +81,9 @@ app.post("/removeSub", fbAuth, removeSub);
/*------------------------------------------------------------------* /*------------------------------------------------------------------*
* handlers/post.js * * handlers/post.js *
*------------------------------------------------------------------*/ *------------------------------------------------------------------*/
const {
getallPostsforUser, const { getallPostsforUser, getallPosts, putPost, likePost, unlikePost, getLikes, quoteWithPost, quoteWithoutPost, checkforLikePost} = require("./handlers/post");
getallPosts,
putPost,
likePost,
unlikePost,
quoteWithPost,
quoteWithoutPost,
checkforLikePost,
getOtherUsersPosts
} = require("./handlers/post");
app.get("/getallPostsforUser", fbAuth, getallPostsforUser); app.get("/getallPostsforUser", fbAuth, getallPostsforUser);
@ -100,6 +92,7 @@ app.get("/getallPosts", getallPosts);
// Adds one post to the database // Adds one post to the database
app.post("/putPost", fbAuth, putPost); app.post("/putPost", fbAuth, putPost);
app.get("/likes", fbAuth, getLikes);
app.get("/like/:postId", fbAuth, likePost); app.get("/like/:postId", fbAuth, likePost);
app.get("/unlike/:postId", fbAuth, unlikePost); app.get("/unlike/:postId", fbAuth, unlikePost);
app.get("/checkforLikePost/:postId", fbAuth, checkforLikePost); app.get("/checkforLikePost/:postId", fbAuth, checkforLikePost);

View File

@ -5,10 +5,14 @@ import { connect } from "react-redux";
import axios from "axios"; import axios from "axios";
// Material UI and React Router // Material UI and React Router
import CircularProgress from '@material-ui/core/CircularProgress'; import CircularProgress from '@material-ui/core/CircularProgress';
import Button from '@material-ui/core/Button';
import Grid from "@material-ui/core/Grid"; import Grid from "@material-ui/core/Grid";
import Card from "@material-ui/core/Card"; import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent"; import CardContent from "@material-ui/core/CardContent";
import TextField from '@material-ui/core/TextField';
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
import withStyles from '@material-ui/styles/withStyles'; import withStyles from '@material-ui/styles/withStyles';
@ -19,6 +23,9 @@ import noImage from '../images/no-img.png';
import Writing_Microblogs from '../Writing_Microblogs'; import Writing_Microblogs from '../Writing_Microblogs';
import ReactModal from 'react-modal'; import ReactModal from 'react-modal';
// Redux
import { likePost, unlikePost, getLikes } from '../redux/actions/userActions';
const styles = { const styles = {
card: { card: {
@ -28,7 +35,7 @@ const styles = {
class Home extends Component { class Home extends Component {
state = { state = {
likes: []
}; };
@ -42,6 +49,34 @@ class Home extends Component {
}); });
}) })
.catch(err => console.log(err)); .catch(err => console.log(err));
this.props.getLikes();
}
componentWillReceiveProps(nextProps) {
this.setState({
likes: nextProps.user.likes
})
}
handleClickLikeButton = (event) => {
// Need the ternary if statement because the user can click on the text or body of the
// Button and they are two different html elements
let postId = event.target.dataset.key ? event.target.dataset.key : event.target.parentNode.dataset.key;
console.log(postId)
let doc = document.getElementById(postId);
// console.log(postId);
if (this.state.likes.includes(postId)) {
this.props.unlikePost(postId, this.state.likes)
doc.dataset.likes--;
} else {
this.props.likePost(postId, this.state.likes)
doc.dataset.likes++;
}
doc.innerHTML = "Likes " + doc.dataset.likes;
} }
formatDate(dateString) { formatDate(dateString) {
@ -50,6 +85,7 @@ class Home extends Component {
} }
render() { render() {
const { UI:{ loading } } = this.props; const { UI:{ loading } } = this.props;
let authenticated = this.props.user.authenticated; let authenticated = this.props.user.authenticated;
let {classes} = this.props; let {classes} = this.props;
@ -79,9 +115,21 @@ class Home extends Component {
<br /> <br />
<Typography variant="body2"><b>Topics:</b> {post.microBlogTopics}</Typography> <Typography variant="body2"><b>Topics:</b> {post.microBlogTopics}</Typography>
<br /> <br />
{/* <Typography variant="body2" color={"textSecondary"}>Likes {post.likeCount}</Typography> */} <Typography id={post.postId} data-likes={post.likeCount} variant="body2" color={"textSecondary"}>Likes {post.likeCount}</Typography>
<Like microBlog = {post.postId} count = {post.likeCount} name = {username}></Like> {/* <Like microBlog = {post.postId} count = {post.likeCount} name = {username}></Like> */}
<Quote microblog = {post.postId}></Quote> <Button
onClick={this.handleClickLikeButton}
data-key={post.postId}
disabled={loading}
variant="outlined"
color="primary"
>{
this.state.likes && this.state.likes.includes(post.postId) ? 'Unlike' : 'Like'
}</Button>
<Quote microblog = {post.postId}></Quote>
{/* <button>Quote</button> */}
</CardContent> </CardContent>
</Card> </Card>
) )
@ -207,14 +255,14 @@ class Quote extends Component {
render() { render() {
return ( return (
<div> <div>
<button onClick={this.handleOpenModal}>Quote with Post</button> <Button variant="outlined" color="primary" onClick={this.handleOpenModal}>Quote with Post</Button>
<ReactModal <ReactModal
isOpen={this.state.showModal} isOpen={this.state.showModal}
style={{content: {height: "50%", width: "25%", marginTop: "auto", marginLeft: "auto", marginRight: "auto", marginBottom : "auto"}}} style={{content: {height: "50%", width: "25%", marginTop: "auto", marginLeft: "auto", marginRight: "auto", marginBottom : "auto"}}}
> >
<div style={{ width: "200px", marginLeft: "50px" }}> <div style={{ width: "200px", marginLeft: "50px" }}>
<form> <form style={{ width: "350px"}}>
<textarea {/* <textarea
value={this.state.value} value={this.state.value}
required required
maxLength="250" maxLength="250"
@ -226,21 +274,39 @@ class Quote extends Component {
}} }}
cols={40} cols={40}
rows={20} rows={20}
/> /> */}
<TextField
style={{width: 300}}
value={this.state.value}
label="Write Quoted Post here..."
required
multiline
color="primary"
rows="14"
variant="outlined"
inputProps={{
maxLength: 250
}}
onChange={e => {
this.handleChangeforPost(e);
this.handleChangeforCharacterCount(e);
}}
autoComplete='off'
></TextField>
<div style={{ fontSize: "14px", marginRight: "-100px" }}> <div style={{ fontSize: "14px", marginRight: "-100px" }}>
<p2>Characters Left: {this.state.characterCount}</p2> <p2>Characters Left: {this.state.characterCount}</p2>
</div> </div>
<button onClick={this.handleSubmit}>Share Quoted Post</button> <Button variant="outlined" color="primary" onClick={this.handleSubmit}>Share Quoted Post</Button>
<button onClick={this.handleCloseModal}>Cancel</button> <Button variant="outlined" color="primary" onClick={this.handleCloseModal}>Cancel</Button>
</form> </form>
</div> </div>
</ReactModal> </ReactModal>
<button onClick={this.handleSubmitWithoutPost}>Quote without Post</button> <Button variant="outlined" color="primary" onClick={this.handleSubmitWithoutPost}>Quote without Post</Button>
</div> </div>
) )
@ -343,10 +409,20 @@ class Like extends Component {
const mapStateToProps = (state) => ({ const mapStateToProps = (state) => ({
user: state.user, user: state.user,
UI: state.UI UI: state.UI
}); })
const mapActionsToProps = {
likePost,
unlikePost,
getLikes
}
Home.propTypes = { Home.propTypes = {
user: PropTypes.object.isRequired, user: PropTypes.object.isRequired,
likePost: PropTypes.func.isRequired,
unlikePost: PropTypes.func.isRequired,
getLikes: PropTypes.func.isRequired,
classes: PropTypes.object.isRequired, classes: PropTypes.object.isRequired,
UI: PropTypes.object.isRequired UI: PropTypes.object.isRequired
} }
@ -359,4 +435,6 @@ Quote.propTypes = {
user: PropTypes.object.isRequired user: PropTypes.object.isRequired
} }
export default connect(mapStateToProps)(withStyles(styles)(Home, Like, Quote));
export default connect(mapStateToProps, mapActionsToProps)(Home, Like, Quote);

View File

@ -101,6 +101,7 @@ export class Search extends Component {
) )
return ( return (
this.state.loading this.state.loading
? ?
<CircularProgress size={60} style={{marginTop: "300px"}}></CircularProgress> <CircularProgress size={60} style={{marginTop: "300px"}}></CircularProgress>

View File

@ -1,3 +1,4 @@
import { import {
SET_USER, SET_USER,
SET_ERRORS, SET_ERRORS,
@ -5,6 +6,9 @@ import {
LOADING_UI, LOADING_UI,
// SET_AUTHENTICATED, // SET_AUTHENTICATED,
SET_UNAUTHENTICATED, SET_UNAUTHENTICATED,
LIKE_POST,
UNLIKE_POST,
SET_LIKES,
LOADING_USER LOADING_USER
} from '../types'; } from '../types';
import axios from 'axios'; import axios from 'axios';
@ -102,6 +106,60 @@ export const deleteUser = () => (dispatch) => {
dispatch({ type: SET_UNAUTHENTICATED }); dispatch({ type: SET_UNAUTHENTICATED });
} }
export const getLikes = () => (dispatch) => {
axios.get('/likes')
.then((res) => {
dispatch({
type: SET_LIKES,
payload: res.data
})
})
}
export const likePost = (postId, postArray) => (dispatch) => {
postArray.push(postId);
dispatch({
type: LIKE_POST,
payload: {
likes: postArray
}
})
axios.get(`/like/${postId}`)
.then((res) => {
getLikes();
})
}
export const unlikePost = (postId, postArray) => (dispatch) => {
let i;
for (i = 0; i < postArray.length; i++) {
if (postArray[i] === postId) {
postArray.splice(i, 1);
break;
}
}
dispatch({
type: UNLIKE_POST,
payload: {
likes: postArray
}
})
axios.get(`/unlike/${postId}`)
.then((res) => {
getLikes();
})
}
const setAuthorizationHeader = (token) => {
const FBIdToken = `Bearer ${token}`;
localStorage.setItem('FBIdToken', FBIdToken);
axios.defaults.headers.common['Authorization'] = FBIdToken;
}
// Sends an image data form to firebase to be uploaded to the user profile // Sends an image data form to firebase to be uploaded to the user profile
export const uploadImage = (formData) => (dispatch) => { export const uploadImage = (formData) => (dispatch) => {
dispatch({ type: LOADING_UI }); dispatch({ type: LOADING_UI });
@ -114,3 +172,4 @@ export const uploadImage = (formData) => (dispatch) => {
console.log(err); console.log(err);
}) })
} }

View File

@ -1,3 +1,4 @@
import { import {
SET_USER, SET_USER,
// SET_ERRORS, // SET_ERRORS,
@ -5,9 +6,13 @@ import {
// LOADING_UI, // LOADING_UI,
SET_AUTHENTICATED, SET_AUTHENTICATED,
SET_UNAUTHENTICATED, SET_UNAUTHENTICATED,
LOADING_USER LOADING_USER,
LIKE_POST,
UNLIKE_POST,
SET_LIKES
} from '../types'; } from '../types';
const initialState = { const initialState = {
authenticated: false, authenticated: false,
credentials: {}, credentials: {},
@ -27,10 +32,26 @@ export default function(state = initialState, action) {
return initialState; return initialState;
case SET_USER: case SET_USER:
return { return {
...state,
authenticated: true, authenticated: true,
loading: false, loading: false,
...action.payload, ...action.payload,
}; };
case LIKE_POST:
return {
...state,
...action.payload
}
case UNLIKE_POST:
return {
...state,
...action.payload
}
case SET_LIKES:
return {
...state,
...action.payload
case LOADING_USER: case LOADING_USER:
return { return {
...state, ...state,

View File

@ -3,6 +3,9 @@ export const SET_AUTHENTICATED = 'SET_AUTHENTICATED';
export const SET_UNAUTHENTICATED = 'SET_UNAUTHENTICATED'; export const SET_UNAUTHENTICATED = 'SET_UNAUTHENTICATED';
export const SET_USER = 'SET_USER'; export const SET_USER = 'SET_USER';
export const LOADING_USER = 'LOADING_USER'; export const LOADING_USER = 'LOADING_USER';
export const LIKE_POST = 'LIKE_POST';
export const UNLIKE_POST = 'UNLIKE_POST';
export const SET_LIKES = 'SET_LIKES';
// UI reducer types // UI reducer types
export const SET_ERRORS = 'SET_ERRORS'; export const SET_ERRORS = 'SET_ERRORS';