Compare commits

..

12 Commits

Author SHA1 Message Date
shobhitm23
f08db94f0d before npm rebuild 2019-11-12 14:48:58 -05:00
shobhitm23
5ad28dc254 Basic logic 2019-11-12 14:12:18 -05:00
shobhitm23
4958723a42 Filtering just with topics 2019-11-08 18:08:28 -05:00
903ea35662 Fix indents 2019-11-01 16:19:54 -04:00
e644498108 Merge pull request #67 from ClaytonWWilson/verify-profile
Users can be verified and have a check mark displayed on their profiles
2019-11-01 16:11:53 -04:00
97bc9c4fb1 Verify users front-end 2019-11-01 16:11:02 -04:00
Shobhit Makhija
f2dac314e5 Merge pull request #74 from ClaytonWWilson/Beautify
Fixing UI
2019-11-01 15:39:57 -04:00
b01aa92f96 Add post id 2019-11-01 15:39:08 -04:00
f4bedea2c7 Merge branch 'master' into verify-profile 2019-11-01 13:40:30 -04:00
112988c8fb Add '@' to profile handle 2019-10-31 15:46:33 -04:00
325d37f0de Back-end for verifying users 2019-10-31 15:12:22 -04:00
ca1d86acf1 Users can be verified and have a check mark displayed on their profiles 2019-10-31 13:56:40 -04:00
8 changed files with 299 additions and 19 deletions

View File

@@ -17,6 +17,7 @@ exports.putPost = (req, res) => {
admin.firestore().collection('posts').add(newPost)
.then((doc) => {
doc.update({postId: doc.id})
const resPost = newPost;
resPost.postId = doc.id;
return res.status(200).json(resPost);
@@ -67,4 +68,36 @@ exports.getallPosts = (req, res) => {
exports.getFilteredPosts = (req, res) => {
admin.firestore().collection('posts').where('userHandle', '==', 'new user').where('microBlogTopics', '==')
};
};
exports.getFilteredPostsOnTopics = (req, res) => {
// get topics that user follows
// make a set storing all topics
// for each post, make a set of topics and if A and B != 0, you can display post
var queryTopics = admin.firestore().collection('users').where('userHandle', '==', req.userHandle)
var topics = new Set();
topics.add(queryTopics.get().microBlogTopics)
//console.log(topics);
var queryPosts = admin.firestore().collection('posts').where('microBlogTopics','==', topics);
console.log(queryPosts);
queryPosts.get()
.then(function(Posts) {
let posts = [];
Posts.forEach(function(doc){
posts.push(doc.data());
});
return res.status(200).json(posts);
})
.then(function() {
res.status(200).send("Successfully retrieved filtered posts from database.");
return;
})
.catch(function(err) {
res.status(500).send("Failed to retrieve posts from database.", err);
});
}

View File

@@ -77,7 +77,8 @@ exports.signup = (req, res) => {
createdAt: newUser.createdAt,
userId,
followedTopics: [],
imageUrl: defaultImageUrl
imageUrl: defaultImageUrl,
verified: false
};
return db.doc(`/users/${newUser.handle}`).set(userCred);
})
@@ -346,6 +347,59 @@ exports.getAuthenticatedUser = (req, res) => {
});
};
// Verifies the user sent to the request
// 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"});
}
db.doc(`/users/${req.body.user}`)
.get()
.then((doc) => {
if (doc.exists) {
let verifiedUser = doc.data();
verifiedUser.verified = 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`});
}
})
.then(() => {
return res.status(201).json({message: `${req.body.user} is now verified`});
})
.catch((err) => {
console.error(err);
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"});
}
db.doc(`/users/${req.body.user}`)
.get()
.then((doc) => {
if (doc.exists) {
let unverifiedUser = doc.data();
unverifiedUser.verified = false;
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`});
}
})
.then(() => {
return res.status(201).json({message: `${req.body.user} is no longer verified`});
})
.catch((err) => {
console.error(err);
return res.status(500).json({error: err.code});
});
}
exports.getUserHandles = (req, res) => {
admin
.firestore()

View File

@@ -17,6 +17,8 @@ const {
signup,
deleteUser,
updateProfileInfo,
verifyUser,
unverifyUser,
getUserHandles
} = require("./handlers/users");
@@ -42,18 +44,28 @@ app.post("/updateProfileInfo", fbAuth, updateProfileInfo);
app.get("/user", fbAuth, getAuthenticatedUser);
// Verifies the user sent to the request
// Must be run by the Admin user
app.post("/verifyUser", fbAuth, verifyUser);
// Unverifies the user sent to the request
// Must be run by admin
app.post("/unverifyUser", fbAuth, unverifyUser);
// get user handles with search phase
app.get("/getUserHandles", fbAuth, getUserHandles);
/*------------------------------------------------------------------*
* handlers/post.js *
*------------------------------------------------------------------*/
const { getallPostsforUser, getallPosts, putPost } = require("./handlers/post");
const { getallPostsforUser, getallPosts, putPost, getFilteredPostsOnTopics } = require("./handlers/post");
app.get("/getallPostsforUser", fbAuth, getallPostsforUser);
app.get("/getallPosts", getallPosts);
app.get("/getFilteredPostsOnTopics", getFilteredPostsOnTopics);
// Adds one post to the database
app.post("/putPost", fbAuth, putPost);

View File

@@ -31,6 +31,7 @@ import Delete from "./pages/Delete";
import writeMicroblog from "./Writing_Microblogs.js";
import editProfile from "./pages/editProfile";
import userLine from "./Userline.js";
import verify from "./pages/verify";
import Search from "./pages/Search.js";
const theme = createMuiTheme(themeObject);
@@ -76,6 +77,7 @@ class App extends Component {
<Route exact path="/home" component={home} />
<Route exact path="/user" component={user} />
<Route exact path="/edit" component={editProfile} />
<Route exact path="/verify" component={verify}/>
<Route exact path="/search" component={Search} />
<AuthRoute exact path="/" component={home} />

View File

@@ -39,9 +39,6 @@ const styles = {
},
p: {
fontFamily: "cursive",
},
div: {
backgroundColor: "lightgrey",
}
};
@@ -112,14 +109,12 @@ export class Login extends Component {
<Grid item sm />
<Grid item sm>
<img src={logo} className="app-logo" alt="logo" />
<br></br>
<br></br>
<Typography variant="p" className={classes.pageTitle} fontFamily = "Georgia, serif">
<b><font face="Segoe UI">Log in to Twistter</font></b>
<b>Log in to Twistter</b>
<br></br>
</Typography>
<br></br>
<div>
<form noValidate onSubmit={this.handleSubmit}>
<TextField
id="email"
@@ -162,7 +157,6 @@ export class Login extends Component {
<Typography color="error">Invalid username/email or password</Typography>
)}
</form>
</div>
</Grid>
<Grid item sm />
</Grid>

View File

@@ -104,10 +104,9 @@ export class Signup extends Component {
<Grid item sm />
<Grid item sm>
<img src={logo} className="app-logo" alt="logo" />
<br></br>
<br></br>
<Typography variant="p" className={classes.pageTitle}>
<b><font face="Segoe UI">Create a new account</font></b>
<b>Create a new account</b>
<br></br>
</Typography>
<br></br>

View File

@@ -16,8 +16,9 @@ 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 AddCircle from '@material-ui/icons/AddCircle';
import TextField from '@material-ui/core/TextField';
import VerifiedIcon from '@material-ui/icons/CheckSharp';
// component
import '../App.css';
@@ -72,7 +73,8 @@ class user extends Component {
.then(res => {
this.setState({
profile: res.data.credentials.handle,
imageUrl: res.data.credentials.imageUrl
imageUrl: res.data.credentials.imageUrl,
verified: res.data.credentials.verified ? res.data.credentials.verified : false
});
})
.catch(err => console.log(err));
@@ -100,10 +102,12 @@ class user extends Component {
render() {
let authenticated = this.props.user.authenticated;
let classes = this.props;
let profileMarkup = this.state.profile ? (
<p>
<Typography variant='h5'>{this.state.profile}</Typography>
</p>) : (<p>loading username...</p>);
<div>
<Typography variant='h5'>@{this.state.profile} {this.state.verified ? (<VerifiedIcon style={{fill: "#1397D5"}}/>): (null)}</Typography>
</div>) : (<p>loading username...</p>);
let topicsMarkup = this.state.topics ? (
this.state.topics.map(
topic => (
@@ -166,7 +170,36 @@ class user extends Component {
onClick={this.handleAddCircle}
/>
<br />
{authenticated && <Button component={ Link } to='/edit'>Edit Profile Info</Button>}
<Grid container direction="column">
<Grid item>
{
authenticated &&
<Button
style={{width:150, marginBottom: 10, marginTop: 5}}
component={ Link }
to='/edit'
variant="outlined"
color="primary"
>
Edit Profile
</Button>}
</Grid>
<Grid item>
{
authenticated &&
this.state.profile === 'Admin' &&
<Button
style={{width:150}}
component={ Link }
variant="outlined"
color="primary"
to='/verify'
>
Verify Users
</Button>}
</Grid>
</Grid>
</Grid>
<Grid item sm={4} xs={8}>
{postMarkup}

View File

@@ -0,0 +1,153 @@
import React, { Component } from "react";
import axios from "axios";
import PropTypes from "prop-types";
// TODO: Add a read-only '@' in the left side of the handle input
// TODO: Add a cancel button, that takes the user back to their profile page
// Material-UI stuff
import Button from "@material-ui/core/Button";
import { Link } from 'react-router-dom';
import CircularProgress from "@material-ui/core/CircularProgress";
import Grid from "@material-ui/core/Grid";
import TextField from "@material-ui/core/TextField";
import Typography from "@material-ui/core/Typography";
import withStyles from "@material-ui/core/styles/withStyles";
const styles = {
form: {
textAlign: "center"
},
textField: {
marginBottom: 30
},
pageTitle: {
// marginTop: 20,
marginBottom: 40
},
button: {
positon: "relative",
marginBottom: 10
},
progress: {
position: "absolute"
}
};
export class verify extends Component {
// Constructor for the state
constructor() {
super();
this.state = {
handle: "",
loading: false,
errors: {}
};
}
// // Runs whenever the submit button is clicked.
handleSubmit = (event) => {
event.preventDefault();
this.setState({
loading: true
});
const verifyHandle = {
user: this.state.handle
};
axios
.post("/verifyUser", verifyHandle)
.then((res) => {
console.log(res);
this.setState({
loading: false
});
// this.props.history.push('/');
// TODO: Need to redirect user to their profile page
})
.catch((err) => {
console.log(err);
this.setState({
errors: err.response.data,
loading: false
});
});
};
// Updates the state whenever one of the textboxes changes.
// The key is the name of the textbox and the value is the
// value in the text box.
// Also sets errors to null of textboxes that have been edited
handleChange = (event) => {
this.setState({
[event.target.name]: event.target.value,
errors: {
[event.target.name]: null
}
});
};
render() {
const { classes } = this.props;
const { errors, loading } = this.state;
return (
<Grid container className={classes.form}>
<Grid item sm />
<Grid item sm>
<Typography variant="h4" className={classes.pageTitle}>
Verify Users
</Typography>
<form noValidate onSubmit={this.handleSubmit}>
<TextField
id="handle"
name="handle"
label="Username"
className={classes.textField}
value={this.state.handle}
// helperText={errors.handle}
// error={errors.handle ? true : false}
variant="outlined"
onChange={this.handleChange}
fullWidth
/>
<Grid container direction="column">
<Grid item>
<Button
type="submit"
variant="contained"
color="primary"
className={classes.button}
disabled={loading}
>
Submit
{loading && (
<CircularProgress size={30} className={classes.progress} />
)}
</Button>
</Grid>
<Grid item>
<Button
variant="oulined"
color="primary"
// className={classes.button}
component={ Link }
to='/user'
>
Back to Profile
</Button>
</Grid>
</Grid>
</form>
</Grid>
<Grid item sm />
</Grid>
);
}
}
verify.propTypes = {
classes: PropTypes.object.isRequired
};
export default withStyles(styles)(verify);