From f0ddaa30b1b6753e295298ea46c81e3b9e6f1dcf Mon Sep 17 00:00:00 2001 From: Leon Liang Date: Mon, 2 Dec 2019 18:31:33 -0500 Subject: [PATCH 1/2] add topics when publish new post --- twistter-frontend/src/Writing_Microblogs.js | 17 +++- twistter-frontend/src/pages/Home.js | 102 ++++++++++++-------- 2 files changed, 75 insertions(+), 44 deletions(-) diff --git a/twistter-frontend/src/Writing_Microblogs.js b/twistter-frontend/src/Writing_Microblogs.js index 9cac7b7..4d7484d 100644 --- a/twistter-frontend/src/Writing_Microblogs.js +++ b/twistter-frontend/src/Writing_Microblogs.js @@ -40,15 +40,28 @@ class Writing_Microblogs extends Component { }; axios - .post("/putPost", postData, headers) + .post("/putPost", postData, headers) // TODO: add topics .then(res => { - alert("Post was shared successfully!"); + // alert("Post was shared successfully!"); console.log(res.data); }) .catch(err => { alert("An error occured."); console.error(err); }); + console.log(postData.microBlogTopics); + postData.microBlogTopics.forEach(topic => { + axios + .post("/putTopic", { + following: topic + }) + .then(res => { + console.log(res.data); + }) + .catch(err => { + console.error(err); + }); + }); event.preventDefault(); this.setState({ value: "", title: "", characterCount: 250, topics: "" }); } diff --git a/twistter-frontend/src/pages/Home.js b/twistter-frontend/src/pages/Home.js index cee0a93..956ccf0 100644 --- a/twistter-frontend/src/pages/Home.js +++ b/twistter-frontend/src/pages/Home.js @@ -1,20 +1,20 @@ /* eslint-disable */ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import axios from 'axios'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import { connect } from "react-redux"; +import axios from "axios"; // Material UI and React Router -import Grid from '@material-ui/core/Grid'; -import Card from '@material-ui/core/Card'; -import CardContent from '@material-ui/core/CardContent'; +import Grid from "@material-ui/core/Grid"; +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; import Typography from "@material-ui/core/Typography"; // component -import '../App.css'; -import logo from '../images/twistter-logo.png'; -import noImage from '../images/no-img.png'; -import Writing_Microblogs from '../Writing_Microblogs'; +import "../App.css"; +import logo from "../images/twistter-logo.png"; +import noImage from "../images/no-img.png"; +import Writing_Microblogs from "../Writing_Microblogs"; class Home extends Component { state = {}; @@ -26,7 +26,7 @@ class Home extends Component { console.log(res.data); this.setState({ posts: res.data - }) + }); }) .catch(err => console.log(err)); } @@ -35,31 +35,43 @@ class Home extends Component { let authenticated = this.props.user.authenticated; let postMarkup = this.state.posts ? ( - this.state.posts.map(post => + this.state.posts.map(post => ( - { - this.state.imageUrl ? () : - () - } + {this.state.imageUrl ? ( + + ) : ( + + )} + + + {post.userHandle} + + + {post.createdAt} - {post.userHandle} - {post.createdAt}
- {post.microBlogTitle} + + {post.microBlogTitle} + {post.body}
- Topics: {post.microBlogTopics} + + Topics: {post.microBlogTopics} +
- Likes {post.likeCount} Comments {post.commentCount} + + Likes {post.likeCount} Comments {post.commentCount} +
- ) - ) : (

My Posts

); + )) + ) : ( +

Loading post...

+ ); - return ( - authenticated ? + return authenticated ? ( @@ -67,26 +79,32 @@ class Home extends Component { {postMarkup} - - : + + ) : (
logo -

- Welcome to Twistter! -

- See the most interesting topics people are following right now. +
+
+ Welcome to Twistter! +
+
+ See the most interesting topics people are following right now.
-



+
+
+
+
-
- Join today or sign in if you already have an account. -

+
+ Join today or sign in if you already have an account. +
+
- +
-
+
@@ -96,12 +114,12 @@ class Home extends Component { } } -const mapStateToProps = (state) => ({ +const mapStateToProps = state => ({ user: state.user -}) +}); Home.propTypes = { user: PropTypes.object.isRequired -} +}; -export default connect(mapStateToProps)(Home); \ No newline at end of file +export default connect(mapStateToProps)(Home); From 1a02b53d0f1c1618f57bad9930721c75ad44a561 Mon Sep 17 00:00:00 2001 From: Clayton Wilson Date: Tue, 3 Dec 2019 00:54:06 -0500 Subject: [PATCH 2/2] Improved speed of search and made it fuzzy search --- functions/handlers/users.js | 18 ++++ functions/index.js | 5 + twistter-frontend/package.json | 2 + twistter-frontend/src/pages/Search.js | 148 +++++++++++++++++--------- 4 files changed, 123 insertions(+), 50 deletions(-) diff --git a/functions/handlers/users.js b/functions/handlers/users.js index 5350d19..3430fff 100644 --- a/functions/handlers/users.js +++ b/functions/handlers/users.js @@ -331,6 +331,24 @@ exports.getUserDetails = (req, res) => { }); }; +exports.getAllHandles = (req, res) => { + var user_query = admin.firestore().collection("users"); + user_query.get() + .then((allUsers) => { + let users = []; + allUsers.forEach((user) => { + users.push(user.data().handle); + }); + return res.status(200).json(users); + }) + .catch((err) => { + return res.status(500).json({ + message:"Failed to retrieve posts from database.", + error: err + }); + }); +}; + exports.getAuthenticatedUser = (req, res) => { let credentials = {}; db.doc(`/users/${req.user.handle}`) diff --git a/functions/index.js b/functions/index.js index ee203eb..eab8aa9 100644 --- a/functions/index.js +++ b/functions/index.js @@ -11,6 +11,7 @@ app.use(cors()); *------------------------------------------------------------------*/ const { getAuthenticatedUser, + getAllHandles, getUserDetails, getProfileInfo, login, @@ -39,6 +40,10 @@ app.delete("/delete", fbAuth, deleteUser); app.post("/getUserDetails", fbAuth, getUserDetails); +// Returns a list of all usernames +// Used for searching +app.get("/getAllHandles", fbAuth, getAllHandles); + // Returns all profile data of the currently logged in user app.get("/getProfileInfo", fbAuth, getProfileInfo); diff --git a/twistter-frontend/package.json b/twistter-frontend/package.json index a0cec16..52e50f3 100644 --- a/twistter-frontend/package.json +++ b/twistter-frontend/package.json @@ -10,11 +10,13 @@ "axios": "^0.19.0", "clsx": "^1.0.4", "create-react-app": "^3.1.2", + "fuse.js": "^3.4.6", "install": "^0.13.0", "jwt-decode": "^2.2.0", "node-pre-gyp": "^0.13.0", "react": "^16.9.0", "react-dom": "^16.9.0", + "react-modal": "^3.11.1", "react-redux": "^7.1.1", "react-router-dom": "^5.1.0", "react-scripts": "0.9.5", diff --git a/twistter-frontend/src/pages/Search.js b/twistter-frontend/src/pages/Search.js index 14cbc52..c8409f5 100644 --- a/twistter-frontend/src/pages/Search.js +++ b/twistter-frontend/src/pages/Search.js @@ -2,38 +2,75 @@ import React, { Component } from "react"; // import props import { TextField, Button } from "@material-ui/core"; import Grid from "@material-ui/core/Grid"; -import Axios from "axios"; +import axios from "axios"; +import Fuse from "fuse.js"; import { BrowserRouter as Router } from "react-router-dom"; +import CircularProgress from "@material-ui/core/CircularProgress"; + +const fuseOptions = { + shouldSort: true, + threshold: 0.6, + location: 0, + distance: 100, + maxPatternLength: 32, + minMatchCharLength: 1, + keys: [] +}; + +let fuse; + + export class Search extends Component { state = { - searchPhase: null, - searchResult: null + handles: [], + // searchPhrase: null, + searchResult: null, + loading: false }; - handleSearch = () => { - console.log(this.state.searchPhase); - Axios.post("/getUserHandles", { - userHandle: this.state.searchPhase - }) - .then(res => { - console.log(res); - - this.setState({ - searchResult: res.data - }); + componentDidMount() { + this.setState({loading: true}); + axios.get("/getAllHandles") + .then((res) => { + this.setState({ + handles: res.data, + loading: false + }, () => { + console.log(res.data); + fuse = new Fuse(this.state.handles, fuseOptions); // "list" is the item array }) - .catch(err => { - console.log(err); - }); - }; + }) + } - handleInput(event) { + // 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); + // }); + // }; + + handleChange = (event) => { + let result = fuse.search(event.target.value); + let parsed = []; + result.forEach((res) => { + console.log(res) + parsed.push(this.state.handles[res]) + }) this.setState({ - searchPhase: event.target.value - }); - console.log(this.state.searchPhase); + searchResult: parsed.length !== 0 ? parsed : "No Results" + }) } handleRedirect() { @@ -41,38 +78,49 @@ export class Search extends Component { } render() { - let resultMarkup = this.state.searchResult ? ( - - - - ) : ( - // console.log(this.state.searchResult) -

No result

- ); + let resultMarkup = this.state.searchResult && this.state.searchResult !== "No Results" ? ( + this.state.searchResult.map(res => + + + + ) + ) + : + this.state.searchResult === "No Results" ? + ( +

No results

+ ) + : + ( + null + ) return ( - + this.state.loading + ? + + : - this.handleInput(event)} - /> + + + + + {/* */} + + {resultMarkup} - - - - {resultMarkup} - ); } }