diff --git a/functions/handlers/post.js b/functions/handlers/post.js index 1db0144..831dba9 100644 --- a/functions/handlers/post.js +++ b/functions/handlers/post.js @@ -1,5 +1,7 @@ /* eslint-disable promise/always-return */ const admin = require('firebase-admin'); +const { db } = require('../util/admin'); + exports.putPost = (req, res) => { const newPost = { @@ -27,6 +29,106 @@ exports.putPost = (req, res) => { }); }; + +exports.getPost = (req, res) => { + let postData = {}; + db.doc(`/posts/${req.params.postId}`) + .get() + .then((doc) => { + if (!doc.exists) { + return res.status(404).json({error: 'Post is not found'}); + + } + postData = doc.data(); + postData.postId = doc.id; + return res.status(200).json(postData); + }) + .catch((err) => { + console.error(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.userData.handle) + .where('postId', '==', req.params.postId).limit(1); + + const postDoc = db.doc(`/posts/${req.params.postId}`); + + likeDoc.get() + .then((data) => { + if (data.empty) { + return admin.firestore().collection('likes').add({ + postId : req.params.postId, + userHandle: req.userData.handle + }) + .then(() => { + postData.likeCount++; + return postDoc.update({likeCount : postData.likeCount}) + }) + .then(() => { + return res.status(200).json(postData); + }) + } + else { + return res.status(400).json({error: 'Post has already been liked.'}) + } + }) + .catch((err) => { + console.error(err); + return res.status(500).json({error: 'Something is wrong'}); + }) + +} + +exports.unlikePost = (re, res) => { + + let postData; + const likeDoc = admin.firestore().collection('likes').where('userHandle', '==', req.userData.handle) + .where('postId', '==', req.params.postId).limit(1); + + const postDoc = db.doc(`/posts/${req.params.postId}`); + + likeDoc.get() + .then((data) => { + if (data.empty) { + return res.status(400).json({ error: 'Post cannot be unliked because it is not liked at the moment.' }); + } else { + 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.getallPostsforFeed = (req, res) => { + admin.firestore().collection('posts').get() + .then((data) => { + let posts = []; + data.forEach(function(doc) { + posts.push(doc.data()); + }); + return res.status(200).json(posts); + }) + .catch((err) => { + console.error(err); + return res.status(500).json({error: 'Failed to fetch all posts written by all other users.'}) + }) +}; + exports.getallPostsforUser = (req, res) => { admin.firestore().collection('posts').where('userHandle', '==', req.userData.handle ).get() .then((data) => { diff --git a/functions/index.js b/functions/index.js index 3be20a4..0a8587e 100644 --- a/functions/index.js +++ b/functions/index.js @@ -44,10 +44,17 @@ app.get("/user", fbAuth, getAuthenticatedUser); /*------------------------------------------------------------------* * handlers/post.js * *------------------------------------------------------------------*/ -const { getallPostsforUser, putPost +const { getallPostsforUser, putPost, getPost, getallPostsforFeed, likePost, unlikePost } = require("./handlers/post"); -app.get("/getallPostsforUser", getallPostsforUser); +app.get("/getallPostsforUser", fbAuth, getallPostsforUser); + +app.get("/getallPostsforFeed", fbAuth, getallPostsforFeed); + +app.get("/putPost/:postId", fbAuth, getPost); +app.get("/putPost/:postId/like", fbAuth, likePost); +app.get("/putPost/:postId/unlike", fbAuth, unlikePost); + // Adds one post to the database app.post("/putPost", fbAuth, putPost); diff --git a/twistter-frontend/package-lock.json b/twistter-frontend/package-lock.json index c14d5b6..b112d85 100644 --- a/twistter-frontend/package-lock.json +++ b/twistter-frontend/package-lock.json @@ -121,6 +121,16 @@ "react-is": "^16.8.6" } }, + "@restart/context": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@restart/context/-/context-2.1.4.tgz", + "integrity": "sha512-INJYZQJP7g+IoDUh/475NlGiTeMfwTXUEr3tmRneckHIxNolGOW9CTq83S8cxq0CgJwwcMzMJFchxvlwe7Rk8Q==" + }, + "@restart/hooks": { + "version": "0.3.15", + "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.3.15.tgz", + "integrity": "sha512-rVNba1A2oMzKBg16fCrrHmCf4JjOzFhT9TWR8J+Y8iOcY4zffxtP3ke7mEsakvghHZT+9//uDOPSSeuBDW41GQ==" + }, "@types/prop-types": { "version": "15.7.3", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", @@ -1419,6 +1429,11 @@ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" }, + "bootstrap": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.3.1.tgz", + "integrity": "sha512-rXqOmH1VilAt2DyPzluTi2blhk17bO7ef+zLLPlWvG494pDxcM234pJ8wTc/6R40UWizAIIMgxjvxZg5kmsbag==" + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1704,6 +1719,11 @@ } } }, + "classnames": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz", + "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==" + }, "clean-css": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz", @@ -2228,6 +2248,15 @@ } } }, + "create-react-context": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.3.0.tgz", + "integrity": "sha512-dNldIoSuNSvlTJ7slIKC/ZFGKexBMBrrcc+TTe1NdmROnaASuLPvqpwj9v4XS4uXZ8+YPu0sNmShX2rXI5LNsw==", + "requires": { + "gud": "^1.0.0", + "warning": "^4.0.3" + } + }, "cross-spawn": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", @@ -5290,6 +5319,11 @@ "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-2.2.0.tgz", "integrity": "sha1-fYa9VmefWM5qhHBKZX3TkruoGnk=" }, + "keycode": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.0.tgz", + "integrity": "sha1-PQr1bce4uOXLqNCpfxByBO7CKwQ=" + }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -6998,6 +7032,25 @@ "react-is": "^16.8.1" } }, + "prop-types-extra": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.0.tgz", + "integrity": "sha512-QFyuDxvMipmIVKD2TwxLVPzMnO4e5oOf1vr3tJIomL8E7d0lr6phTHd5nkPhFIzTD1idBLLEPeylL9g+rrTzRg==", + "requires": { + "react-is": "^16.3.2", + "warning": "^3.0.0" + }, + "dependencies": { + "warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", + "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", + "requires": { + "loose-envify": "^1.0.0" + } + } + } + }, "proxy-addr": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", @@ -7134,6 +7187,43 @@ "prop-types": "^15.6.2" } }, + "react-bootstrap": { + "version": "1.0.0-beta.14", + "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-1.0.0-beta.14.tgz", + "integrity": "sha512-UGK5f78FE8wAei1YL/oSwFlJZLqxJ/h4S8DCwHyY8hQjFCrjEW5PoEBTOOhQ6PQL6WOsZe1jkiOJG7L5TZWu+w==", + "requires": { + "@babel/runtime": "^7.4.2", + "@restart/context": "^2.1.4", + "@restart/hooks": "^0.3.11", + "@types/react": "^16.8.23", + "classnames": "^2.2.6", + "dom-helpers": "^3.4.0", + "invariant": "^2.2.4", + "keycode": "^2.2.0", + "popper.js": "^1.14.7", + "prop-types": "^15.7.2", + "prop-types-extra": "^1.1.0", + "react-overlays": "^1.2.0", + "react-transition-group": "^4.0.0", + "uncontrollable": "^7.0.0", + "warning": "^4.0.3" + }, + "dependencies": { + "dom-helpers": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", + "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", + "requires": { + "@babel/runtime": "^7.1.2" + } + } + } + }, + "react-context-toolbox": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/react-context-toolbox/-/react-context-toolbox-2.0.2.tgz", + "integrity": "sha512-tY4j0imkYC3n5ZlYSgFkaw7fmlCp3IoQQ6DxpqeNHzcD0hf+6V+/HeJxviLUZ1Rv1Yn3N3xyO2EhkkZwHn0m1A==" + }, "react-dev-utils": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-0.5.2.tgz", @@ -7162,11 +7252,71 @@ "scheduler": "^0.15.0" } }, + "react-dropdown": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/react-dropdown/-/react-dropdown-1.6.4.tgz", + "integrity": "sha512-zTlNRZ6vzjEPsodBNgh6Xjp9IempEx9sReH3crT2Jw4S6KW2wS/BRIH3d/grYf/iXARadDRD91//uUCs9yjoLg==", + "requires": { + "classnames": "^2.2.3" + } + }, "react-is": { "version": "16.9.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.9.0.tgz", "integrity": "sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw==" }, + "react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "react-overlays": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-1.2.0.tgz", + "integrity": "sha512-i/FCV8wR6aRaI+Kz/dpJhOdyx+ah2tN1RhT9InPrexyC4uzf3N4bNayFTGtUeQVacj57j1Mqh1CwV60/5153Iw==", + "requires": { + "classnames": "^2.2.6", + "dom-helpers": "^3.4.0", + "prop-types": "^15.6.2", + "prop-types-extra": "^1.1.0", + "react-context-toolbox": "^2.0.2", + "react-popper": "^1.3.2", + "uncontrollable": "^6.0.0", + "warning": "^4.0.2" + }, + "dependencies": { + "dom-helpers": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", + "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", + "requires": { + "@babel/runtime": "^7.1.2" + } + }, + "uncontrollable": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-6.2.3.tgz", + "integrity": "sha512-VgOAoBU2ptCL2bfTG2Mra0I8i1u6Aq84AFonD5tmCAYSfs3hWvr2Rlw0q2ntoxXTHjcQOmZOh3FKaN+UZVyREQ==", + "requires": { + "@babel/runtime": "^7.4.5", + "invariant": "^2.2.4" + } + } + } + }, + "react-popper": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-1.3.4.tgz", + "integrity": "sha512-9AcQB29V+WrBKk6X7p0eojd1f25/oJajVdMZkywIoAV6Ag7hzE1Mhyeup2Q1QnvFRtGQFQvtqfhlEoDAPfKAVA==", + "requires": { + "@babel/runtime": "^7.1.2", + "create-react-context": "^0.3.0", + "popper.js": "^1.14.4", + "prop-types": "^15.6.1", + "typed-styles": "^0.0.7", + "warning": "^4.0.2" + } + }, "react-redux": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.1.1.tgz", @@ -8155,6 +8305,15 @@ } } }, + "react-simple-dropdown": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/react-simple-dropdown/-/react-simple-dropdown-3.2.3.tgz", + "integrity": "sha512-NmyyvA0D4wph5ctzkn8U4wmblOacavJMl9gTOhQR3v8I997mc1FL1NFKkj3Mx+HNysBKRD/HI+kpxXCAgXumPw==", + "requires": { + "classnames": "^2.1.2", + "prop-types": "^15.5.8" + } + }, "react-transition-group": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.3.0.tgz", @@ -9720,6 +9879,11 @@ "mime-types": "~2.1.24" } }, + "typed-styles": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/typed-styles/-/typed-styles-0.0.7.tgz", + "integrity": "sha512-pzP0PWoZUhsECYjABgCGQlRGL1n7tOHsgwYv3oIiEpJwGhFTuty/YNeduxQYzXXa3Ge5BdT6sHYIQYpl4uJ+5Q==" + }, "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -9761,6 +9925,16 @@ "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz", "integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=" }, + "uncontrollable": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.0.2.tgz", + "integrity": "sha512-7fa8OBQ5+X4VAcp0os6BD74bCeUPQSHmr4Rqy75Me98NnlD5kNShCqqx4xWo4OmlAMiT2/YSMklLFC4FCuoGYg==", + "requires": { + "@babel/runtime": "^7.4.5", + "invariant": "^2.2.4", + "react-lifecycles-compat": "^3.0.4" + } + }, "union-value": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", @@ -9991,6 +10165,14 @@ "makeerror": "1.0.x" } }, + "warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "requires": { + "loose-envify": "^1.0.0" + } + }, "watch": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/watch/-/watch-0.10.0.tgz", diff --git a/twistter-frontend/package.json b/twistter-frontend/package.json index f7dd12f..4f782a4 100644 --- a/twistter-frontend/package.json +++ b/twistter-frontend/package.json @@ -8,16 +8,20 @@ "@material-ui/styles": "^4.5.0", "@material-ui/system": "^4.5.0", "axios": "^0.19.0", + "bootstrap": "^4.3.1", "clsx": "^1.0.4", "create-react-app": "^3.1.2", "install": "^0.13.0", "jwt-decode": "^2.2.0", "node-pre-gyp": "^0.13.0", "react": "^16.9.0", + "react-bootstrap": "^1.0.0-beta.14", "react-dom": "^16.9.0", + "react-dropdown": "^1.6.4", "react-redux": "^7.1.1", "react-router-dom": "^5.1.0", "react-scripts": "0.9.5", + "react-simple-dropdown": "^3.2.3", "redux": "^4.0.4", "redux-thunk": "^2.3.0", "typeface-roboto": "0.0.75" diff --git a/twistter-frontend/redux/actions/dataActions.js b/twistter-frontend/redux/actions/dataActions.js index e69de29..e0198d4 100644 --- a/twistter-frontend/redux/actions/dataActions.js +++ b/twistter-frontend/redux/actions/dataActions.js @@ -0,0 +1,55 @@ +import {LIKE_POST, UNLIKE_POST, SET_POST, SET_POSTS} from '../types'; +import axios from 'axios'; + +export const getPosts = () => (dispatch) => { + axios + .get('/posts') + .then((res) => { + dispatch({ + type: SET_POSTS, + payload: res.data + }); + }) + .catch((err) => { + dispatch({ + type: SET_POSTS, + payload: [] + }); + }); + }; + +export const likePost = (postId) => (dispatch) => { + axios + .get(`/posts/${postId}/like`) + .then((res) => { + dispatch({ + type: LIKE_POST, + payload: res.data + }); + }) + .catch((err) => console.log(err)); + }; + + export const unlikePost = (postId) => (dispatch) => { + axios + .get(`/posts/${postId}/unlike`) + .then((res) => { + dispatch({ + type: UNLIKE_POST, + payload: res.data + }); + }) + .catch((err) => console.log(err)); + }; + + export const getPost = (postId) => (dispatch) => { + axios + .get(`/posts/${postId}`) + .then((res) => { + dispatch({ + type: SET_POST, + payload: res.data + }); + }) + .catch((err) => console.log(err)); + }; \ No newline at end of file diff --git a/twistter-frontend/redux/reducers/userReducer.js b/twistter-frontend/redux/reducers/userReducer.js index e69de29..4e6f681 100644 --- a/twistter-frontend/redux/reducers/userReducer.js +++ b/twistter-frontend/redux/reducers/userReducer.js @@ -0,0 +1,31 @@ +import { LIKE_POST, UNLIKE_POST} from '../types'; + +const initialState = { + likes: [], + credentials: {} +} + +export default function(state = initialState, action) { + switch (action.type) { + case LIKE_POST: + return { + ...state, + likes: [ + ...state.likes, + { + userHandle: state.credentials.handle, + postId: action.payload.postId + } + ] + } + case UNLIKE_POST: + return { + ...state, + likes: state.likes.filter( + (like) => like.postId === action.payload.postId + ) + }; + default: + return state; + } +} \ No newline at end of file diff --git a/twistter-frontend/redux/store.js b/twistter-frontend/redux/store.js index 4536cfe..50eee80 100644 --- a/twistter-frontend/redux/store.js +++ b/twistter-frontend/redux/store.js @@ -15,4 +15,14 @@ const reducers = combineReducers({ UI: uiReducer }); -//const store = createStore(reducers, ) \ No newline at end of file +const store = createStore( + reducers, + initialState, + compose( + applyMiddleware(...middleware), + window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() + + ) +) + +export default store; \ No newline at end of file diff --git a/twistter-frontend/redux/types.js b/twistter-frontend/redux/types.js index e69de29..3cd2bd7 100644 --- a/twistter-frontend/redux/types.js +++ b/twistter-frontend/redux/types.js @@ -0,0 +1,3 @@ +export const SET_POST = 'SET_POST'; +export const LIKE_POST = 'LIKE_POST'; +export const UNLIKE_POST = 'UNLIKE_POST'; diff --git a/twistter-frontend/src/App.css b/twistter-frontend/src/App.css index 940f1fc..7d1cd68 100644 --- a/twistter-frontend/src/App.css +++ b/twistter-frontend/src/App.css @@ -12,6 +12,12 @@ body { height: 200px; } +.userhome{ + display:flex; + flex-direction:row; +} + + .authButtons { border-radius: 100px; box-shadow: none; diff --git a/twistter-frontend/src/App.js b/twistter-frontend/src/App.js index bec4ac6..b4659ae 100644 --- a/twistter-frontend/src/App.js +++ b/twistter-frontend/src/App.js @@ -6,6 +6,8 @@ import axios from "axios"; import { BrowserRouter as Router, Route, Switch } from "react-router-dom"; import Navbar from "./components/layout/NavBar"; import jwtDecode from "jwt-decode"; +import 'bootstrap/dist/css/bootstrap.min.css'; +import { Button } from 'react-bootstrap'; // Redux import { Provider } from "react-redux"; @@ -31,6 +33,9 @@ import Delete from './pages/Delete'; import writeMicroblog from './Writing_Microblogs.js'; import editProfile from './pages/editProfile'; import userLine from './Userline.js'; +import like from './Like.js'; +import quote from './Quote.js'; +import feed from './Feed.js'; const theme = createMuiTheme(themeObject); @@ -70,11 +75,16 @@ class App extends Component { - - +
+ + +
+ - {/* */} + + + diff --git a/twistter-frontend/src/Feed.js b/twistter-frontend/src/Feed.js new file mode 100644 index 0000000..d624c65 --- /dev/null +++ b/twistter-frontend/src/Feed.js @@ -0,0 +1,75 @@ +import React, { Component } from "react"; +import { BrowserRouter as Router } from 'react-router-dom'; +import Route from 'react-router-dom/Route'; +import axios from 'axios'; +import Box from '@material-ui/core/Box' +import {connect} from 'react-redux'; +import {getPosts} from '../redux/actions/dataActions'; +import PropTypes from 'prop-types'; +import { likePost, unlikePost} from '../redux/actions/dataActions'; + +class Feed extends Component { + + + constructor(props) { + super(props); + this.state = { + microBlogs: [] + }; + } + + componentDidMount() { + + axios.get('/getallPostsforFeed') + .then(res => { + const post = res.data; + this.setState({microBlogs : post}) + + }) + this.props.getPosts(); + + } + + render() { + + const {posts} = this.props.data; + + const sortedPosts = (this.state.microBlogs).sort((a,b) => + -a.createdAt.localeCompare(b.createdAt) + ) + return( +
+
+

Feed

+
+ + +
+

+ {sortedPosts.map((microBlog) =>

Microblog Title: {microBlog.microBlogTitle} +

When post was created: {microBlog.createdAt.substring(0,10) + + " " + microBlog.createdAt.substring(11,19)} +

Who wrote the microBlog: {microBlog.userHandle} +

Body of post: {microBlog.body} +

Tagged topics: {microBlog.microBlogTopics.join("," + " ")} +













+ +

)} +

+
+
+
+ + ) + } +} + +home.propTypes = { + getPosts: PropTypes.func.isRequired, + data: PropTypes.object.isRequired +} +const mapStateToProps = state => ({ + data: state.data +}) + +export default connect(mapStateToProps, { getPosts })(Feed); \ No newline at end of file diff --git a/twistter-frontend/src/Like.js b/twistter-frontend/src/Like.js new file mode 100644 index 0000000..879e2a7 --- /dev/null +++ b/twistter-frontend/src/Like.js @@ -0,0 +1,37 @@ +import React, { Component } from "react"; +import { BrowserRouter as Router } from 'react-router-dom'; +import Route from 'react-router-dom/Route'; +import axios from 'axios'; + +class Like extends Component { + + constructor(props) { + super(props); + this.state = { + like : false, + count : 0 + }; + + + this.handleSubmit = this.handleSubmit.bind(this); + } + + handleSubmit() { + this.setState({ + like: !this.state.like + }); + const LikedPost = { + + } + + } + + render() { + const label = this.state.like ? 'Unlike' : 'Like' + return ( + + ) + } +} + +export default Like; \ No newline at end of file diff --git a/twistter-frontend/src/Quote.js b/twistter-frontend/src/Quote.js new file mode 100644 index 0000000..3d9e045 --- /dev/null +++ b/twistter-frontend/src/Quote.js @@ -0,0 +1,42 @@ +import React, { Component } from "react"; +import { BrowserRouter as Router } from 'react-router-dom'; +import Route from 'react-router-dom/Route'; +import axios from 'axios'; +import DropdownButton from 'react-bootstrap/DropdownButton' +import Dropdown from 'react-bootstrap/Dropdown' +import Modal from 'react-bootstrap/Modal' +import Button from 'react-bootstrap/Button' + + + +class Quote extends Component { + + constructor(props) { + super(props); + this.state = { + value : '' + }; + this.handleSubmit = this.handleSubmit.bind(this); + this.handleSubmit2 = this.handleSubmit2.bind(this); + + + } + + handleSubmit() { + + } + + handleSubmit2() { + + } + render() { + return( + + Quote with comment + Quote without comment + + ) + } +} + +export default Quote \ No newline at end of file diff --git a/twistter-frontend/src/Writing_Microblogs.js b/twistter-frontend/src/Writing_Microblogs.js index 6ddb974..9a189cf 100644 --- a/twistter-frontend/src/Writing_Microblogs.js +++ b/twistter-frontend/src/Writing_Microblogs.js @@ -4,6 +4,7 @@ import Route from 'react-router-dom/Route'; import axios from 'axios'; + class Writing_Microblogs extends Component { constructor(props) { @@ -70,8 +71,11 @@ class Writing_Microblogs extends Component { } render() { + + return (
+