Merge branch 'master' into edit-profile-image-upload

This commit is contained in:
2019-12-04 00:02:03 -05:00
committed by GitHub
20 changed files with 2701 additions and 510 deletions

View File

@@ -10,33 +10,33 @@ import jwtDecode from "jwt-decode";
// Redux
import { Provider } from "react-redux";
import store from "./redux/store";
import MuiThemeProvider from '@material-ui/core/styles/MuiThemeProvider';
import createMuiTheme from '@material-ui/core/styles/createMuiTheme';
import themeObject from './util/theme';
import { SET_AUTHENTICATED } from './redux/types';
import { logoutUser, getUserData } from './redux/actions/userActions';
import MuiThemeProvider from "@material-ui/core/styles/MuiThemeProvider";
import createMuiTheme from "@material-ui/core/styles/createMuiTheme";
import themeObject from "./util/theme";
import { SET_AUTHENTICATED } from "./redux/types";
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';
import login from './pages/Login';
import user from './pages/user';
import logout from './pages/Logout';
import Delete from './pages/Delete';
import writeMicroblog from './Writing_Microblogs.js';
import editProfile from './pages/editProfile';
import userLine from './Userline.js';
import home from "./pages/Home";
import signup from "./pages/Signup";
import login from "./pages/Login";
import user from "./pages/user";
import logout from "./pages/Logout";
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";
import otherUser from "./pages/otherUser";
const theme = createMuiTheme(themeObject);
const token = localStorage.FBIdToken;
if (token) {
try {
const decodedToken = jwtDecode(token);
if (decodedToken.exp * 1000 < Date.now()) {
@@ -44,7 +44,7 @@ if (token) {
window.location.href = "/login";
} else {
store.dispatch({ type: SET_AUTHENTICATED });
axios.defaults.headers.common['Authorization'] = token;
axios.defaults.headers.common["Authorization"] = token;
store.dispatch(getUserData());
}
} catch (invalidTokenError) {
@@ -53,34 +53,35 @@ if (token) {
}
}
class App extends Component {
render() {
return (
<MuiThemeProvider theme={theme}>
<Provider store={store}>
<Router>
<div className='container' >
<div className="container">
<Navbar />
</div>
<div className="app">
<Switch>
{/* AuthRoute checks if the user is logged in and if they are it redirects them to /home */}
<AuthRoute exact path="/signup" component={signup} />
<AuthRoute exact path="/login" component={login} />
<Route exact path="/logout" component={logout} />
<Route exact path="/delete" component={Delete} />
{/* AuthRoute checks if the user is logged in and if they are it redirects them to /home */}
<AuthRoute exact path="/signup" component={signup} />
<AuthRoute exact path="/login" component={login} />
<AuthRoute exact path="/" component={home} />
<Route exact path="/user" component={user} />
<Route exact path="/home" component={writeMicroblog} />
<Route exact path="/edit" component={editProfile} />
{/* <Route exact path="/user" component={userLine} /> */}
<Route exact path="/logout" component={logout} />
<Route exact path="/delete" component={Delete} />
<AuthRoute exact path="/" component={home}/>
<Route exact path="/home" component={home} />
<Route exact path="/user" component={user} />
<Route exact path="/user/edit" component={editProfile} />
<Route exact path="/verify" component={verify} />
<Route exact path="/search" component={Search} />
<Route exact path="/user/:userhandle" component={otherUser} />
<AuthRoute exact path="/" component={home} />
</Switch>
</div>
</Router>
</Provider>
</MuiThemeProvider>

View File

@@ -1,10 +1,10 @@
import React, { Component } from "react";
import { BrowserRouter as Router } from 'react-router-dom';
import Route from 'react-router-dom/Route';
// 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 {borders} from '@material-ui/system';
import { sizing } from '@material-ui/system';
// import {borders} from '@material-ui/system';
// import { sizing } from '@material-ui/system';
// var moment = require('moment');
@@ -41,7 +41,7 @@ class Userline extends Component {
<div style={{fontsize: "13px", textAlign: "left", marginLeft: "14px"}}>
<p>Userline</p>
</div>
<Box border={1} width="25%" flex="1" height="auto" m={2} fontSize="13px" textAlign= "left" padding="5px" flexWrap= "wrap" flexDirection= "row" >
<Box border={1} width="25%" flex="1" height="auto" m={2} fontSize="13px" textAlign="left" padding="5px" flexWrap="wrap" flexDirection="row" >
<div style={{flexWrap: "wrap", flex: "1", flexDirection: "row", wordBreak: "break-word"}}>
<p>
{sortedPosts.map((microBlog) => <p>Microblog Title: {microBlog.microBlogTitle}
@@ -50,7 +50,7 @@ class Userline extends Component {
<br></br>Number of comments: {microBlog.commentCount}
<br></br>Number of likes: {microBlog.likeCount}
<br></br>Body of post: {microBlog.body}
<br></br>Tagged topics: {microBlog.microBlogTopics.join("," + " ")}
<br></br>Tagged topics: {microBlog.microBlogTopics.join(", ")}
</p>)}
</p>
</div>

View File

@@ -1,107 +1,165 @@
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 Writing_Microblogs extends Component {
constructor(props) {
super(props);
this.state = {
value: '',
title: '',
topics: '',
characterCount: 250
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChangeforPost = this.handleChangeforPost.bind(this);
this.handleChangeforTopics = this.handleChangeforTopics.bind(this);
}
handleChange(event) {
this.setState( {title: event.target.value });
}
handleChangeforTopics(event) {
this.setState( {topics: event.target.value});
}
handleSubmit(event) {
// alert('A title for the microblog was inputted: ' + this.state.title + '\nA microblog was posted: ' + this.state.value);
const postData = {
body: this.state.value,
userImage: "bing-url",
microBlogTitle: this.state.title,
microBlogTopics: this.state.topics.split(', ')
}
const headers = {
headers: { 'Content-Type': 'application/json'}
}
axios
.post("/putPost", postData, headers)
.then((res) =>{
alert('Post was shared successfully!')
console.log(res.data);
})
.catch((err) => {
alert('An error occured.');
console.error(err);
})
event.preventDefault();
this.setState({value: '', title: '',characterCount: 250, topics: ''})
}
handleChangeforPost(event) {
this.setState({value: event.target.value })
}
handleChangeforCharacterCount(event) {
const charCount = event.target.value.length
const charRemaining = 250 - charCount
this.setState({characterCount: charRemaining })
}
render() {
return (
<div>
<div style={{ width: "200px", height: "50px", marginTop: "180px", marginLeft: "50px" }}>
<form>
<textarea placeholder="Enter Microblog Title" value={this.state.title} required onChange={this.handleChange} cols={30} rows={1} />
</form>
</div>
<div style={{ width: "200px", height: "50px", marginLeft: "50px"}} >
<form>
<textarea placeholder="Enter topics seperated by a comma" value={this.state.topics} required onChange={this.handleChangeforTopics} cols={40} rows={1} />
</form>
</div>
<div style={{ width: "200px", marginLeft: "50px"}}>
<form onSubmit={this.handleSubmit}>
<textarea value={this.state.value} required maxLength="250" placeholder= "Write Microblog here..."
onChange = { (e) => { this.handleChangeforPost(e); this.handleChangeforCharacterCount(e) } } cols={40} rows={20} />
<div style={{ fontSize: "14px", marginRight: "-100px"}} >
<p2>Characters Left: {this.state.characterCount}</p2>
</div>
<div style={{ marginRight: "-100px" }}>
<button onClick>Share Post</button>
</div>
</form>
</div>
</div>
);
}
// import { BrowserRouter as Router } from "react-router-dom";
// import Route from "react-router-dom/Route";
import axios from "axios";
// Material-UI
import TextField from '@material-ui/core/TextField';
// import Typography from '@material-ui/core/Typography';
import Button from '@material-ui/core/Button';
import withStyles from "@material-ui/styles/withStyles";
const styles = {
container: {
position: "fixed"
},
form: {
width: "300px",
height: "50px",
marginTop: "180px",
marginLeft: "50px"
},
textField: {
marginBottom: 15
}
}
export default Writing_Microblogs;
class Writing_Microblogs extends Component {
constructor(props) {
super(props);
this.state = {
value: "",
title: "",
topics: "",
characterCount: 250
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChangeforPost = this.handleChangeforPost.bind(this);
this.handleChangeforTopics = this.handleChangeforTopics.bind(this);
}
handleChange(event) {
this.setState({ title: event.target.value });
}
handleChangeforTopics(event) {
this.setState({ topics: event.target.value });
}
handleSubmit = (event) => {
// alert('A title for the microblog was inputted: ' + this.state.title + '\nA microblog was posted: ' + this.state.value);
const postData = {
body: this.state.value,
userImage: "bing-url",
microBlogTitle: this.state.title,
microBlogTopics: this.state.topics.split(", ")
};
const headers = {
headers: { "Content-Type": "application/json" }
};
axios
.post("/putPost", postData, headers) // TODO: add topics
.then(res => {
// 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: "" });
}
handleChangeforPost(event) {
this.setState({ value: event.target.value });
}
handleChangeforCharacterCount(event) {
const charCount = event.target.value.length;
const charRemaining = 250 - charCount;
this.setState({ characterCount: charRemaining });
}
render() {
const { classes } = this.props;
return (
<div className={classes.container}>
<form noValidate className={classes.form}>
<TextField
id="title"
name="title"
label="Title"
className={classes.textField}
value={this.state.title}
variant="outlined"
onChange={this.handleChange}
fullWidth
autoComplete='off'
/>
<TextField
id="topics"
name="topics"
label="Topics"
className={classes.textField}
value={this.state.topics}
variant="outlined"
onChange={this.handleChangeforTopics}
color="primary"
fullWidth
autoComplete='off'
/>
<TextField
id="content"
name="content"
label="Content"
color="primary"
className={classes.textField}
value={this.state.value}
helperText={`${this.state.characterCount} characters left`}
multiline
rows="9"
variant="outlined"
inputProps={{
maxLength: 250
}}
onChange={(e) => {
this.handleChangeforPost(e);
this.handleChangeforCharacterCount(e);
}}
fullWidth
autoComplete='off'
/>
<Button
onClick={this.handleSubmit}
// disabled={loading}
variant="outlined"
color="primary"
>
Share Post
</Button>
</form>
</div>
);
}
}
export default withStyles(styles)(Writing_Microblogs);

View File

@@ -1,81 +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 (
<AppBar>
<ToolBar>
<Button component={ Link } to='/'>
Home
</Button>
{!authenticated && <Button component={ Link } to='/login'>
Login
</Button>}
{!authenticated && <Button component={ Link } to='/signup'>
Sign Up
</Button>}
{authenticated && <Button component={ Link } to='/logout'>
Logout
</Button>}
{/* Commented out the delete button, because it should probably go on
the profile or editProfile page instead of the NavBar */}
{/* <Button component={ Link } to='/delete'>
Delete Account
</Button> */}
</ToolBar>
</AppBar>
)
}
export class Navbar extends Component {
render() {
const authenticated = this.props.user.authenticated;
return (
<AppBar>
<ToolBar>
<Button component={Link} to="/">
Home
</Button>
{authenticated && (
<Button component={Link} to="/user">
Profile
</Button>
)}
{!authenticated && (
<Button component={Link} to="/login">
Login
</Button>
)}
{!authenticated && (
<Button component={Link} to="/signup">
Sign Up
</Button>
)}
{authenticated && (
<Button component={Link} to="/search">
Search
</Button>
)}
{authenticated && (
<Button component={Link} to="/logout">
Logout
</Button>
)}
</ToolBar>
</AppBar>
);
}
}
const mapStateToProps = (state) => ({
user: state.user
})
// const mapActionsToProps = { logoutUser };
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));
// export default Navbar;

View File

@@ -7,7 +7,8 @@ import Button from "@material-ui/core/Button";
import withStyles from "@material-ui/core/styles/withStyles";
// Redux stuff
import { logoutUser } from "../redux/actions/userActions";
//import { logoutUser } from "../redux/actions/userActions";
import { deleteUser } from "../redux/actions/userActions";
import { connect } from "react-redux";
const styles = {
@@ -32,7 +33,8 @@ const styles = {
export class Delete extends Component {
componentDidMount() {
this.props.logoutUser();
//this.props.logoutUser();
this.props.deleteUser();
this.props.history.push('/');
}
@@ -45,10 +47,12 @@ const mapStateToProps = (state) => ({
user: state.user
});
const mapActionsToProps = { logoutUser };
//const mapActionsToProps = { logoutUser };
const mapActionsToProps = { deleteUser };
Delete.propTypes = {
logoutUser: PropTypes.func.isRequired,
//logoutUser: PropTypes.func.isRequired,
deleteUser: PropTypes.func.isRequired,
user: PropTypes.object.isRequired,
classes: PropTypes.object.isRequired
};

View File

@@ -1,37 +1,358 @@
import React, { Component } from 'react';
/* eslint-disable */
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 CircularProgress from '@material-ui/core/CircularProgress';
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";
import withStyles from '@material-ui/styles/withStyles';
// 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 ReactModal from 'react-modal';
const styles = {
card: {
marginBottom: 5
}
}
class Home extends Component {
state = {
};
componentDidMount() {
axios
.get("/getallPosts")
.then(res => {
// console.log(res.data);
this.setState({
posts: res.data
});
})
.catch(err => console.log(err));
}
formatDate(dateString) {
let newDate = new Date(Date.parse(dateString));
return newDate.toDateString();
}
render() {
const { UI:{ loading } } = this.props;
let authenticated = this.props.user.authenticated;
let {classes} = this.props;
let username = this.props.user.credentials.handle;
let postMarkup = this.state.posts ? (
this.state.posts.map(post =>
<Card className={classes.card} key={post.postId}>
<CardContent>
<Typography>
{
this.state.imageUrl ? (<img src={this.state.imageUrl} height="50" width="50" />) :
(<img src={noImage} height="50" width="50"/>)
}
</Typography>
<Typography variant="h5"><b>{post.userHandle}</b></Typography>
<Typography variant="body2" color={"textSecondary"}>{this.formatDate(post.createdAt)}</Typography>
<br />
<Typography variant="body1"><b>{post.microBlogTitle}</b></Typography>
<Typography variant="body2">{post.quoteBody}</Typography>
<br />
<Typography variant="body2">{post.body}</Typography>
<br />
<Typography variant="body2"><b>Topics:</b> {post.microBlogTopics}</Typography>
<br />
{/* <Typography variant="body2" color={"textSecondary"}>Likes {post.likeCount}</Typography> */}
<Like microBlog = {post.postId} count = {post.likeCount} name = {username}></Like>
<Quote microblog = {post.postId}></Quote>
</CardContent>
</Card>
)
) : (
<p>Loading post...</p>
);
return (
authenticated ? (
<Grid container>
<Grid item sm={4} xs={8}>
<Writing_Microblogs />
</Grid>
<Grid item sm={4} xs={8}>
{postMarkup}
</Grid>
</Grid>
) : loading ?
(<CircularProgress size={60} style={{marginTop: "300px"}}></CircularProgress>)
:
(
<div>
<div>
<img src={logo} className="app-logo" alt="logo" />
<br/><br/>
<b>Welcome to Twistter!</b>
<br/><br/>
<b>See the most interesting topics people are following right now.</b>
</div>
<br/><br/><br/><br/>
<div>
<b>Join today or sign in if you already have an account.</b>
<br/><br/>
<form action="./signup">
<button className="authButtons signup">Sign up</button>
</form>
<br/>
<form action="./login">
<button className="authButtons login">Sign in</button>
</form>
</div>
</div>
));
}
}
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 post = {
userImage: "bing-url",
}
const headers = {
headers: { "Content-Type": "application/json" }
};
axios.post(`/quoteWithoutPost/${this.props.microblog}`, post, 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 = {
quoteBody: this.state.value,
userImage: "bing-url",
};
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 (
<div>
<div>
<img src={logo} className="app-logo" alt="logo" />
<br/><br/>
<b>Welcome to Twistter!</b>
<br/><br/>
<b>See the most interesting topics people are following right now.</b>
</div>
<button onClick={this.handleOpenModal}>Quote with Post</button>
<ReactModal
isOpen={this.state.showModal}
style={{content: {height: "50%", width: "25%", marginTop: "auto", marginLeft: "auto", marginRight: "auto", marginBottom : "auto"}}}
>
<div style={{ width: "200px", marginLeft: "50px" }}>
<form>
<textarea
value={this.state.value}
required
maxLength="250"
placeholder="Write Quoted Post here..."
onChange={e => {
this.handleChangeforPost(e);
this.handleChangeforCharacterCount(e);
}}
cols={40}
rows={20}
/>
<div style={{ fontSize: "14px", marginRight: "-100px" }}>
<p2>Characters Left: {this.state.characterCount}</p2>
</div>
<button onClick={this.handleSubmit}>Share Quoted Post</button>
<button onClick={this.handleCloseModal}>Cancel</button>
<br/><br/><br/><br/>
<div>
<b>Join today or sign in if you already have an account.</b>
<br/><br/>
<form action="./signup">
<button className="authButtons signup">Sign up</button>
</form>
<br/>
<form action="./login">
<button className="authButtons login">Sign in</button>
</form>
</div>
</ReactModal>
<button onClick={this.handleSubmitWithoutPost}>Quote without Post</button>
</div>
);
)
}
}
class Like extends Component {
constructor(props) {
super(props)
this.state = {
num : this.props.count,
}
this.handleClick = this.handleClick.bind(this);
}
export default Home;
componentDidMount() {
this.setState({
like: localStorage.getItem(this.props.microBlog + this.props.name) === "false"
})
}
handleClick(){
this.setState({
like: !this.state.like
});
localStorage.setItem(this.props.microBlog + this.props.name, this.state.like.toString())
if(this.state.like == false)
{
this.setState(() => {
return {num: this.state.num + 1}
});
axios.get(`/like/${this.props.microBlog}`)
.then((res) => {
console.log(res.data);
})
.catch((err) => {
console.log(err);
})
}
else
{
this.setState(() => {
return {num: this.state.num - 1}
});
axios.get(`/unlike/${this.props.microBlog}`)
.then((res) => {
console.log(res.data);
})
.catch((err) => {
console.log(err);
})
}
}
/* componentDidMount() {
axios.get(`/checkforLikePost/${this.props.microBlog}`)
.then((res) => {
this.setState({
like2: res.data
})
console.log(res.data);
})
.catch((err) => {
console.log(err)
})
if (this.state.like2 === this.state.like)
{
this.setState({
like: false
})
}
} */
render() {
const label = this.state.like ? 'Unlike' : 'Like'
return(
<div>
<Typography variant="body2" color={"textSecondary"}>Likes {this.state.num}</Typography>
<button onClick={this.handleClick}>{label}</button>
</div>
)
}
}
const mapStateToProps = (state) => ({
user: state.user,
UI: state.UI
});
Home.propTypes = {
user: PropTypes.object.isRequired,
classes: PropTypes.object.isRequired,
UI: PropTypes.object.isRequired
}
Like.propTypes = {
user: PropTypes.object.isRequired
}
Quote.propTypes = {
user: PropTypes.object.isRequired
}
export default connect(mapStateToProps)(withStyles(styles)(Home, Like, Quote));

View File

@@ -16,13 +16,15 @@ import withStyles from "@material-ui/core/styles/withStyles";
// Redux stuff
import { connect } from 'react-redux';
import { loginUser } from '../redux/actions/userActions';
import { fontFamily } from '@material-ui/system';
//Theme
const styles = {
form: {
textAlign: "center"
},
textField: {
marginBottom: 30
marginBottom: 20
},
pageTitle: {
// marginTop: 20,
@@ -34,6 +36,9 @@ const styles = {
},
progress: {
position: "absolute"
},
p: {
fontFamily: "cursive",
}
};
@@ -104,14 +109,17 @@ export class Login extends Component {
<Grid item sm />
<Grid item sm>
<img src={logo} className="app-logo" alt="logo" />
<Typography variant="h2" className={classes.pageTitle}>
Log in to Twistter
<br></br>
<Typography variant="h6" className={classes.pageTitle} fontFamily = "Georgia, serif">
<b>Log in to Twistter</b>
<br></br>
</Typography>
<br></br>
<form noValidate onSubmit={this.handleSubmit}>
<TextField
id="email"
name="email"
label="Email*"
label="Email or Username*"
className={classes.textField}
value={this.state.email}
helperText={errors.email}
@@ -119,6 +127,7 @@ export class Login extends Component {
variant="outlined"
onChange={this.handleChange}
fullWidth
autoComplete='off'
/>
<TextField
id="password"
@@ -132,6 +141,7 @@ export class Login extends Component {
variant="outlined"
onChange={this.handleChange}
fullWidth
autoComplete='off'
/>
<Button
type="submit"

View File

@@ -0,0 +1,129 @@
import React, { Component } from "react";
// import props
// import { TextField, Button } from "@material-ui/core";
import TextField from "@material-ui/core/TextField"
import Grid from "@material-ui/core/Grid";
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 = {
handles: [],
// searchPhrase: null,
searchResult: null,
loading: false
};
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
})
})
}
// 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({
searchResult: parsed.length !== 0 ? parsed : "No Results"
})
}
handleRedirect() {
location.reload();
}
render() {
let resultMarkup = this.state.searchResult && this.state.searchResult !== "No Results" ? (
this.state.searchResult.map(res =>
<Router key={res}>
<div>
<a href={`/user/${res}`}>
{res}
</a>
</div>
</Router>
)
)
:
this.state.searchResult === "No Results" ?
(
<p> No results </p>
)
:
(
null
)
return (
this.state.loading
?
<CircularProgress size={60} style={{marginTop: "300px"}}></CircularProgress>
:
<Grid>
<Grid>
<TextField
id="standard-required"
label="Username"
margin="normal"
// value={this.state.searchPhrase}
onChange={this.handleChange}
/>
</Grid>
<Grid>
{/* <Button color="primary" onClick={this.handleSearch}>
Search
</Button> */}
</Grid>
<Grid>{resultMarkup}</Grid>
</Grid>
);
}
}
export default Search;

View File

@@ -16,13 +16,17 @@ import withStyles from "@material-ui/core/styles/withStyles";
// Redux stuff
import { connect } from 'react-redux';
import { signupUser } from '../redux/actions/userActions';
import { border } from '@material-ui/system';
const styles = {
form: {
textAlign: "center"
},
textField: {
marginBottom: 30
marginBottom: 20,
//border: "1px solid #234",
display: "inline-block",
boxSizing: "border-box",
},
pageTitle: {
marginBottom: 40
@@ -33,6 +37,14 @@ const styles = {
},
progress: {
position: "absolute"
},
div: {
borderRadius: "5px",
backgroundColor: "grey",
padding: "20px",
},
p: {
fontFamily: "Segoe UI",
}
};
@@ -92,9 +104,12 @@ export class Signup extends Component {
<Grid item sm />
<Grid item sm>
<img src={logo} className="app-logo" alt="logo" />
<Typography variant="h2" className={classes.pageTitle}>
Create a new account
<br></br>
<Typography variant="p" className={classes.pageTitle}>
<b>Create a new account</b>
<br></br>
</Typography>
<br></br>
<form noValidate onSubmit={this.handleSubmit}>
<TextField
id="handle"
@@ -107,6 +122,7 @@ export class Signup extends Component {
variant="outlined"
onChange={this.handleChange}
fullWidth
autoComplete='off'
/>
<TextField
id="email"
@@ -119,6 +135,7 @@ export class Signup extends Component {
variant="outlined"
onChange={this.handleChange}
fullWidth
autoComplete='off'
/>
<TextField
id="password"
@@ -132,6 +149,7 @@ export class Signup extends Component {
variant="outlined"
onChange={this.handleChange}
fullWidth
autoComplete='off'
/>
<TextField
id="confirmPassword"
@@ -145,7 +163,10 @@ export class Signup extends Component {
variant="outlined"
onChange={this.handleChange}
fullWidth
autoComplete='off'
/>
<br></br>
<br></br>
<Button
type="submit"
variant="contained"

View File

@@ -1,16 +1,17 @@
import React, { Component } from "react";
import { Link } from 'react-router-dom';
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
import noImage from '../images/no-img.png';
// Material-UI stuff
import Box from "@material-ui/core/Box"
import Button from "@material-ui/core/Button";
import Box from "@material-ui/core/Box";
import CircularProgress from "@material-ui/core/CircularProgress";
import Grid from "@material-ui/core/Grid";
import Popover from "@material-ui/core/Popover";
import TextField from "@material-ui/core/TextField";
import Typography from "@material-ui/core/Typography";
import withStyles from "@material-ui/core/styles/withStyles";
@@ -40,6 +41,14 @@ const styles = {
box: {
position: "relative"
},
back: {
float: "left",
marginLeft: 15
},
delete: {
float: "right",
marginRight: 15
},
progress: {
position: "absolute"
},
@@ -47,10 +56,18 @@ const styles = {
position: "absolute",
marginLeft: -155,
marginTop: 95
},
popoverBackground: {
marginTop: "-100px",
width: "calc(100vw)",
height: 'calc(100vh + 100px)',
backgroundColor: "gray",
position: "absolute",
opacity: "70%"
}
};
export class edit extends Component {
export class editProfile extends Component {
// mapReduxToState = (credentials) => {
// this.setState({
// imageUrl: credentials.imageUrl ? credentials.imageUrl : noImage,
@@ -62,8 +79,6 @@ export class edit extends Component {
// });
// };
// Runs as soon as the page loads.
// Sets the default values of all the textboxes to the data
// that is stored in the database for the user.
@@ -71,6 +86,8 @@ export class edit extends Component {
// const { credentials } = this.props;
// console.log(this.props.user);
// this.mapReduxToState(credentials);
this.setState({pageLoading: true})
axios
.get("/getProfileInfo")
.then((res) => {
@@ -82,16 +99,15 @@ export class edit extends Component {
lastName: res.data.lastName ? res.data.lastName : "",
email: res.data.email,
handle: res.data.handle,
bio: res.data.bio ? res.data.bio : ""
bio: res.data.bio ? res.data.bio : "",
pageLoading: false
});
})
.catch((err) => {
console.error(err);
if (err.response.status === 403) {
alert("You are not logged in");
// TODO: Redirect them, to the profile they are trying to edit
// If they are on /itsjimmy/edit, they will be redirected to /itsjimmy
this.props.history.push('../');
// This user is not logged in
this.props.history.push('/');
}
});
}
@@ -106,7 +122,9 @@ export class edit extends Component {
email: "",
handle: "",
bio: "",
anchorEl: null,
loading: false,
pageLoading: false,
errors: {}
};
}
@@ -141,8 +159,7 @@ export class edit extends Component {
this.setState({
loading: false
});
// this.props.history.push('/');
// TODO: Need to redirect user to their profile page
this.props.history.push('/user');
})
.catch((err) => {
console.log(err);
@@ -187,18 +204,26 @@ export class edit extends Component {
// this.mapReduxToState(this.props.credentials);
// }
handleOpenConfirmDelete = (event) => {
this.setState({
// anchorEl: event.currentTarget
anchorEl: document.getElementById("container-grid")
});
};
handleCloseConfirmDelete = () => {
this.setState({
anchorEl: null,
createDMUsername: ''
});
};
render() {
const { classes } = this.props;
const uploading = this.props.UI.loading;
const { errors, loading } = this.state;
// let imageMarkup = this.state.imageUrl ? (
// <img
// src={this.state.imageUrl}
// height="250"
// width="250"
// />
// ) : (<img src={noImage}/>);
// <<<<<<< edit-profile-image-upload
let imageMarkup = this.props.user.credentials.imageUrl ? (
<Box
@@ -317,22 +342,199 @@ export class edit extends Component {
onChange={this.handleChange}
fullWidth
/>
// =======
// Used for the delete button
const open = Boolean(this.state.anchorEl);
const id = open ? 'simple-popover' : undefined;
return (
this.state.pageLoading ?
<CircularProgress size={60} style={{marginTop: "300px"}}></CircularProgress>
:
<Grid container className={classes.form} id="container-grid">
<Grid item sm >
// >>>>>>> master
<Button
type="submit"
variant="contained"
variant="outlined"
color="primary"
className={classes.button}
// className={classes.button}
disabled={loading || uploading}
className={classes.back}
component={ Link }
to='/user'
>
Submit
{loading && (
<CircularProgress size={30} className={classes.progress} />
)}
Back to Profile
</Button>
</form>
</Grid>
<Grid item sm>
<Typography variant="h2" className={classes.pageTitle}>
Edit Profile
</Typography>
<form noValidate onSubmit={this.handleSubmit}>
<Grid container className={classes.form} spacing={4}>
<Grid item sm>
<TextField
id="firstName"
name="firstName"
label="First Name"
className={classes.textField}
value={this.state.firstName}
helperText={errors.firstName}
error={errors.firstName ? true : false}
variant="outlined"
onChange={this.handleChange}
fullWidth
autoComplete='off'
/>
</Grid>
<Grid item sm>
<TextField
id="lastName"
name="lastName"
label="Last Name"
className={classes.textField}
value={this.state.lastName}
helperText={errors.lastname}
error={errors.lastName ? true : false}
variant="outlined"
onChange={this.handleChange}
fullWidth
autoComplete='off'
/>
</Grid>
</Grid>
<TextField
id="email"
name="email"
label="Email*"
className={classes.textField}
value={this.state.email}
disabled
helperText="(disabled)"
// INFO: These will be uncommented if changing emails is allowed
// helperText={errors.email}
// error={errors.email ? true : false}
variant="outlined"
onChange={this.handleChange}
fullWidth
autoComplete='off'
/>
<TextField
id="handle"
name="handle"
label="Handle*"
className={classes.textField}
value={"@" + this.state.handle}
disabled
helperText="(disabled)"
// INFO: These will be uncommented if changing usernames is allowed
// helperText={errors.handle}
// error={errors.handle ? true : false}
variant="outlined"
onChange={this.handleChange}
fullWidth
autoComplete='off'
/>
<TextField
id="bio"
name="bio"
label="Bio"
className={classes.textField}
value={this.state.bio}
helperText={errors.bio}
error={errors.bio ? true : false}
multiline
rows="8"
variant="outlined"
onChange={this.handleChange}
fullWidth
autoComplete='off'
/>
<Button
type="submit"
variant="contained"
color="primary"
className={classes.button}
disabled={loading}
//component={ Link }
//to='/user'
>
Submit
{loading && (
<CircularProgress size={30} className={classes.progress} />
)}
</Button>
</form>
</Grid>
<Grid item sm>
<Button
variant="outlined"
color="secondary"
className={classes.delete}
onClick={this.handleOpenConfirmDelete}
>
Delete Account
</Button>
</Grid>
<Box hidden={!Boolean(this.state.anchorEl)} className={classes.popoverBackground}></Box>
<Popover
id={id}
open={open}
anchorEl={this.state.anchorEl}
onClose={this.handleCloseConfirmDelete}
anchorOrigin={{
vertical: 'center',
horizontal: 'center'
}}
transformOrigin={{
vertical: 'top',
horizontal: 'center'
}}
style={{
marginTop: "-200px"
}}
>
<Box
style={{
height: 200,
width: 400
}}
>
<Grid container direction="column" spacing={3}>
<Grid item>
<Typography style={{marginTop: 30, marginLeft: 50, marginRight: 50, textAlign: "center", fontSize: 24}}>Are you sure you want to delete your account?</Typography>
</Grid>
<Grid item>
<Button
color="secondary"
variant="contained"
component={ Link }
to='/delete'
style={{
marginBottom: "-40px",
marginLeft: 10,
width: 90
}}
>
Yes
</Button>
<Button
color="primary"
variant="outlined"
onClick={this.handleCloseConfirmDelete}
style={{
marginBottom: "-40px",
marginLeft: 195
}}
>
Cancel
</Button>
</Grid>
</Grid>
</Box>
</Popover>
</Grid>
<Grid item sm />
</Grid>
);
}
}
@@ -351,4 +553,4 @@ edit.propTypes = {
};
// export default withStyles(styles)(edit);
export default connect(mapStateToProps, mapActionsToProps)(withStyles(styles)(edit));
export default connect(mapStateToProps, mapActionsToProps)(withStyles(styles)(edit));

View File

@@ -0,0 +1,283 @@
/* 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 withStyles from "@material-ui/core/styles/withStyles";
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";
import DoneIcon from "@material-ui/icons/Done";
// 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: 20
},
paper: {
// marginLeft: "10%",
// marginRight: "10%"
},
card: {
marginBottom: 5
},
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 {
constructor() {
super();
this.state = {
profile: window.location.pathname.split("/").pop(),
imageUrl: null,
topics: null,
user: null,
following: null,
posts: null,
myTopics: 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() {
axios
.post("/getUserDetails", {
handle: this.state.profile
})
.then(res => {
this.setState({
imageUrl: res.data.userData.imageUrl,
topics: res.data.userData.followedTopics
});
})
.catch(err => console.log(err));
axios
.get("/user")
.then(res => {
this.setState({
following: res.data.credentials.following.includes(
this.state.profile
),
myTopics: res.data.credentials.followedTopics
});
})
.catch(err => console.log(err));
axios
.post("/getOtherUsersPosts", {
handle: this.state.profile
})
.then(res => {
// console.log(res.data);
this.setState({
posts: res.data
});
})
.catch(err => console.log(err));
}
render() {
const { classes } = this.props;
let followMarkup = this.state.following ? (
<Button variant="contained" color="primary" onClick={this.handleSub}>
unfollow
</Button>
) : (
<Button variant="contained" color="primary" onClick={this.handleSub}>
follow
</Button>
);
let profileMarkup = this.state.profile ? (
<div>
<Typography variant="h5">
@{this.state.profile}{" "}
{this.state.verified ? (
<VerifiedIcon style={{ fill: "#1397D5" }} />
) : null}
</Typography>
{followMarkup}
</div>
) : (
<p>loading username...</p>
);
console.log(this.state.topics);
console.log(this.state.myTopics);
let topicsMarkup = this.state.topics ? (
this.state.topics.map(
topic =>
this.state.myTopics ? (
this.state.myTopics.includes(topic) ? (
<MyChip
label={topic}
key={{ topic }.topic.id}
onDelete
deleteIcon={<DoneIcon />}
/>
) : (
<MyChip
label={topic}
key={{ topic }.topic.id}
color="secondary"
/>
)
) : (
<p></p>
)
// topic => <MyChip label={topic} key={{ topic }.topic.id} /> // console.log({ topic }.topic.id)
)
) : (
<p> no topic yet</p>
);
let imageMarkup = this.state.imageUrl ? (
<img src={this.state.imageUrl} height="150" width="150" />
) : (
<img src={noImage} height="150" width="150" />
);
let postMarkup = this.state.posts ? (
this.state.posts.map(post => (
<Card className={classes.card}>
<CardContent>
<Typography>
{this.state.imageUrl ? (
<img src={this.state.imageUrl} height="50" width="50" />
) : (
<img src={noImage} height="50" width="50" />
)}
</Typography>
<Typography variant="h7">
<b>{post.userHandle}</b>
</Typography>
<Typography variant="body2" color={"textSecondary"}>
{post.createdAt}
</Typography>
<br />
<Typography variant="body1">
<b>{post.microBlogTitle}</b>
</Typography>
<Typography variant="body2">{post.quoteBody}</Typography>
<br />
<Typography variant="body2">{post.body}</Typography>
<br />
<Typography variant="body2">
<b>Topics:</b> {post.microBlogTopics}
</Typography>
<br />
<Typography variant="body2" color={"textSecondary"}>
Likes {post.likeCount}
</Typography>
</CardContent>
</Card>
))
) : (
<p>Posts</p>
);
return (
<Grid container spacing={24}>
<Grid item sm={4} xs={8}>
{imageMarkup}
{profileMarkup}
{/* {followMarkup} */}
{topicsMarkup}
<br />
</Grid>
<Grid item sm={4} xs={8}>
{postMarkup}
<br />
</Grid>
</Grid>
);
}
}
const mapStateToProps = state => ({
user: state.user
});
user.propTypes = {
user: PropTypes.object.isRequired,
classes: PropTypes.object.isRequired
};
export default connect(mapStateToProps)(withStyles(styles)(user));

View File

@@ -1,53 +1,129 @@
/* eslint-disable */
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import axios from 'axios';
import React, { Component } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import axios from "axios";
//import '../App.css';
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';
// Material-UI
import withStyles from "@material-ui/core/styles/withStyles";
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 AddCircle from "@material-ui/icons/AddCircle";
import TextField from "@material-ui/core/TextField";
import VerifiedIcon from "@material-ui/icons/CheckSharp";
import Paper from "@material-ui/core/Paper";
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';
import noImage from '../images/no-img.png';
import "../App.css";
import noImage from "../images/no-img.png";
import Writing_Microblogs from "../Writing_Microblogs";
const MyChip = styled(Chip)({
margin: 2,
color: 'primary'
color: "primary"
});
class user extends Component {
state = {
profile: null,
imageUrl: null,
topics: null,
newTopic: null
const styles = {
button: {
positon: "relative",
float: "left",
marginLeft: 30,
marginTop: 20
},
paper: {
// marginLeft: "10%",
// marginRight: "10%"
},
card: {
marginBottom: 5
},
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 {
constructor() {
super();
this.state = {
profile: null,
imageUrl: null,
topics: null,
newTopic: ""
};
}
handleDelete = topic => {
console.log(topic);
axios
.post(`/deleteTopic`, {
unfollow: topic
})
.then(() => {
let tempTopics = this.state.topics;
tempTopics.forEach((oldTopic, index) => {
if (oldTopic === topic) {
tempTopics.splice(index, 1);
}
});
this.setState({
topics: tempTopics
});
})
.catch(function(err) {
console.log(err);
});
};
handleDelete = (topic) => {
alert(`Delete topic: ${topic}!`);
}
handleAddCircle = () => {
axios.post('/putTopic', {
topic: this.state.newTopic
})
.then(function () {
location.reload();
})
.catch(function (err) {
console.log(err);
});
}
axios
.post("/putTopic", {
following: this.state.newTopic
})
.then(() => {
let tempTopics = this.state.topics;
tempTopics.push(this.state.newTopic);
this.setState({
topics: tempTopics,
newTopic: ""
});
})
.catch(function(err) {
console.log(err);
});
};
handleChange(event) {
this.setState({
newTopic: event.target.value
})
});
}
componentDidMount() {
@@ -56,69 +132,196 @@ 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,
topics: res.data.credentials.followedTopics
});
})
.catch(err => console.log(err));
axios
.get("/getAllTopics")
.get("/getallPostsforUser")
.then(res => {
// console.log(res.data);
this.setState({
topics: res.data
})
posts: res.data
});
})
.catch(err => console.log(err));
}
formatDate(dateString) {
let newDate = new Date(Date.parse(dateString));
return newDate.toDateString();
}
render() {
const classes = this.props;
const { classes } = this.props;
let authenticated = this.props.user.authenticated;
let profileMarkup = this.state.profile ? (
<p>
<Typography variant='h5'>{this.state.profile}</Typography>
</p>) : (<p>loading username...</p>);
<div>
<Typography variant="h5" className={classes.username}>
@{this.state.profile}{" "}
{this.state.verified ? (
<VerifiedIcon style={{ fill: "#1397D5" }} />
) : null}
</Typography>
</div>
) : (
<p className={classes.username}>loading username...</p>
);
let topicsMarkup = this.state.topics ? (
this.state.topics.map(topic => <MyChip
label={{topic}.topic.topic}
key={{topic}.topic.topicId}
onDelete={ (topic) => this.handleDelete(topic)}/>)
) : (<p> loading topics...</p>);
this.state.topics.map(
topic => (
<MyChip
label={topic}
key={topic}
onDelete={key => this.handleDelete(topic)}
/>
) // console.log({ topic }.topic.id)
)
) : (
<p> loading topics...</p>
);
let imageMarkup = this.state.imageUrl ? (
<img
src={this.state.imageUrl}
height="250"
width="250"
className={classes.profileImage}
src={this.state.imageUrl}
height="250"
width="250"
/>
) : (<img src={noImage}/>);
) : (
<img
className={classes.profileImage}
src={noImage}
height="250"
width="250"
/>
);
let postMarkup = this.state.posts ? (
this.state.posts.map(post => (
<Card className={classes.card} key={post.postId}>
<CardContent>
<Typography>
{this.state.imageUrl ? (
<img src={this.state.imageUrl} height="50" width="50" />
) : (
<img src={noImage} height="50" width="50" />
)}
</Typography>
<Typography variant="h6">
<b>{post.userHandle}</b>
</Typography>
<Typography variant="body2" color={"textSecondary"}>
{post.createdAt}
</Typography>
<br />
<Typography variant="body1">
<b>{post.microBlogTitle}</b>
</Typography>
<Typography variant="body2">{post.quoteBody}</Typography>
<br />
<Typography variant="body2">{post.body}</Typography>
<br />
<Typography variant="body2">
<b>Topics:</b> {post.microBlogTopics}
</Typography>
<br />
<Typography variant="body2" color={"textSecondary"}>
Likes {post.likeCount}
</Typography>
</CardContent>
</Card>
))
) : (
<p>My Posts</p>
);
// 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 ? (
<Link to="/user/edit">
<Button className={classes.button} variant="outlined" color="primary">
Edit Profile
</Button>
</Link>
) : null;
return (
<Grid container spacing={16}>
<Grid item sm={8} xs={12}>
<p>Post</p>
<div>
{/* <Paper className={classes.paper}> */}
<Grid container direction="column">
<Grid item>
<Grid container>
<Grid item sm>
{editButtonMarkup}
</Grid>
<Grid item sm>
{/* <Grid container direction="column"> */}
{/* <Grid item sm> */}
{imageMarkup}
{profileMarkup}
{/* </Grid> */}
{/* <Grid item sm> */}
{/* {postMarkup} */}
{/* </Grid> */}
{/* </Grid> */}
</Grid>
<Grid item sm>
<Container className={classes.topicsContainer} maxWidth="xs">
{topicsMarkup}
</Container>
<TextField
id="newTopic"
label="new topic"
// defaultValue=""
margin="normal"
variant="outlined"
value={this.state.newTopic}
onChange={event => this.handleChange(event)}
/>
<AddCircle
className={classes.addCircle}
color="primary"
// iconStyle={classes.addCircle}
clickable="true"
onClick={this.handleAddCircle}
cursor="pointer"
/>
</Grid>
</Grid>
</Grid>
<Grid item>
<Grid container>
<Grid item sm />
<Grid item>{postMarkup}</Grid>
<Grid item sm />
</Grid>
</Grid>
</Grid>
<Grid item sm={4} xs={12}>
{imageMarkup}
{profileMarkup}
{topicsMarkup}
<TextField
id="newTopic"
label="new topic"
defaultValue=""
margin="normal"
variant="outlined"
value={this.state.newTopic}
onChange={ (event) => this.handleChange(event)}
/>
<AddCircle
color="primary"
clickable
onClick={this.handleAddCircle}
/>
</Grid>
</Grid>
</div>
);
}
}
export default user;
const mapStateToProps = state => ({
user: state.user
});
user.propTypes = {
user: PropTypes.object.isRequired,
classes: PropTypes.object.isRequired
};
export default connect(mapStateToProps)(withStyles(styles)(user));

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 { 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);

View File

@@ -1,6 +1,20 @@
import {SET_USER, SET_ERRORS, CLEAR_ERRORS, LOADING_UI, SET_AUTHENTICATED, SET_UNAUTHENTICATED, LOADING_USER} from '../types';
import {
SET_USER,
SET_ERRORS,
CLEAR_ERRORS,
LOADING_UI,
// SET_AUTHENTICATED,
SET_UNAUTHENTICATED,
LOADING_USER
} from '../types';
import axios from 'axios';
const setAuthorizationHeader = (token) => {
const FBIdToken = `Bearer ${token}`;
localStorage.setItem('FBIdToken', FBIdToken);
axios.defaults.headers.common['Authorization'] = FBIdToken;
}
// Gets Database info for the logged in user and sets it in Redux
export const getUserData = () => (dispatch) => {
dispatch({ type: LOADING_USER });
@@ -24,7 +38,7 @@ export const loginUser = (loginData, history) => (dispatch) => {
// Save the login token
setAuthorizationHeader(res.data.token);
dispatch(getUserData());
dispatch({ type: CLEAR_ERRORS })
// dispatch({ type: CLEAR_ERRORS })
// Redirects to home page
history.push('/home');
})
@@ -47,7 +61,7 @@ export const signupUser = (newUserData, history) => (dispatch) => {
// Save the signup token
setAuthorizationHeader(res.data.token);
dispatch(getUserData());
dispatch({ type: CLEAR_ERRORS })
// dispatch({ type: CLEAR_ERRORS })
// Redirects to home page
history.push('/home');
})
@@ -105,4 +119,4 @@ export const uploadImage = (formData) => (dispatch) => {
.catch(err => {
console.log(err);
})
}
}

View File

@@ -1,4 +1,12 @@
import {SET_USER, SET_ERRORS, CLEAR_ERRORS, LOADING_UI, SET_AUTHENTICATED, SET_UNAUTHENTICATED, LOADING_USER} from '../types';
import {
SET_USER,
// SET_ERRORS,
// CLEAR_ERRORS,
// LOADING_UI,
SET_AUTHENTICATED,
SET_UNAUTHENTICATED,
LOADING_USER
} from '../types';
const initialState = {
authenticated: false,