Pulled latest version from master and fixed all conflicts

This commit is contained in:
Aaron Sun
2019-11-24 17:08:13 -08:00
parent ad0b21e629
commit 1482830131
15 changed files with 1306 additions and 383 deletions

View File

@@ -2417,6 +2417,11 @@
"resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
"integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs="
},
"dayjs": {
"version": "1.8.17",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.17.tgz",
"integrity": "sha512-47VY/htqYqr9GHd7HW/h56PpQzRBSJcxIQFwqL3P20bMF/3az5c3PWdVY3LmPXFl6cQCYHL7c79b9ov+2bOBbw=="
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",

View File

@@ -10,6 +10,7 @@
"axios": "^0.19.0",
"clsx": "^1.0.4",
"create-react-app": "^3.1.2",
"dayjs": "^1.8.17",
"install": "^0.13.0",
"jwt-decode": "^2.2.0",
"node-pre-gyp": "^0.13.0",

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,21 +53,21 @@ 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} />
<AuthRoute exact path="/" component={home}/>
<AuthRoute exact path="/" component={home} />
<Route exact path="/logout" component={logout} />
<Route exact path="/delete" component={Delete} />
@@ -75,6 +75,11 @@ 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} />
<Route exact path="/user/:userhandle" component={otherUser} />
<AuthRoute exact path="/" component={home} />
</Switch>
</div>
</Router>

View File

@@ -1,107 +1,128 @@
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 { 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
};
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);
}
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 });
}
handleChange(event) {
this.setState( {title: event.target.value });
}
handleChangeforTopics(event) {
this.setState({ topics: 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" }
};
handleSubmit(event) {
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: "" });
}
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 });
}
handleChangeforPost(event) {
this.setState({value: event.target.value })
}
handleChangeforCharacterCount(event) {
const charCount = event.target.value.length;
const charRemaining = 250 - charCount;
this.setState({ characterCount: charRemaining });
}
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>
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 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={{ 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>
);
}
<div style={{ marginRight: "-100px" }}>
<button onClick>Share Post</button>
</div>
</form>
</div>
</div>
);
}
}
export default Writing_Microblogs;
export default Writing_Microblogs;

View File

@@ -1,71 +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='/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='/logout'>
Logout
</Button>}
</ToolBar>
</AppBar>
)
}
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 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));

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,9 +109,12 @@ 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="p" 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"

View File

@@ -1,17 +1,10 @@
import React, { Component } from "react";
// import props
import { TextField, Paper } from "@material-ui/core";
import { TextField, Button } from "@material-ui/core";
import Grid from "@material-ui/core/Grid";
import Axios from "axios";
import user from "./user.js";
import {
BrowserRouter as Router,
Switch,
Route,
Link,
useRouteMatch
} from "react-router-dom";
import { BrowserRouter as Router } from "react-router-dom";
export class Search extends Component {
state = {
@@ -19,20 +12,28 @@ export class Search extends Component {
searchResult: null
};
handleSearch(event) {
Axios.get("/getUserHandles").then(res => {
this.setState({
searchResult: res.data
});
});
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);
});
};
handleInput(event) {
this.setState({
searchPhase: event.target.value
});
this.handleSearch();
console.log(this.state.searchPhase);
}
handleRedirect() {
@@ -41,16 +42,16 @@ export class Search extends Component {
render() {
let resultMarkup = this.state.searchResult ? (
this.state.searchResult.map(result => (
<Router>
<div>
<Link to={`/user`}>{result}</Link>
</div>
</Router>
))
<Router>
<div>
<a href={`/user/${this.state.searchResult}`}>
{this.state.searchResult}
</a>
</div>
</Router>
) : (
// console.log(this.state.searchResult)
<p> searching... </p>
<p> No result </p>
);
return (
@@ -65,6 +66,11 @@ export class Search extends Component {
onChange={event => this.handleInput(event)}
/>
</Grid>
<Grid>
<Button color="primary" onClick={this.handleSearch}>
Search
</Button>
</Grid>
<Grid>{resultMarkup}</Grid>
</Grid>
);

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"
@@ -146,6 +161,8 @@ export class Signup extends Component {
onChange={this.handleChange}
fullWidth
/>
<br></br>
<br></br>
<Button
type="submit"
variant="contained"

View File

@@ -0,0 +1,158 @@
/* 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 { 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";
// 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"
});
class user extends Component {
state = {
profile: window.location.pathname.split("/").pop(),
imageUrl: null,
topics: null,
user: null,
following: 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)
});
})
.catch(err => console.log(err));
}
render() {
let profileMarkup = this.state.profile ? (
<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 => <MyChip label={topic} key={{ topic }.topic.id} /> // console.log({ topic }.topic.id)
)
) : (
<p> loading topics...</p>
);
let imageMarkup = this.state.imageUrl ? (
<img src={this.state.imageUrl} height="150" width="150" />
) : (
<img src={noImage} height="150" width="150" />
);
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>
);
console.log(this.state.following);
return (
<Grid container spacing={24}>
<Grid item sm={4} xs={8}>
{imageMarkup}
{profileMarkup}
{followMarkup}
{topicsMarkup}
<br />
</Grid>
</Grid>
);
}
}
const mapStateToProps = state => ({
user: state.user
});
user.propTypes = {
user: PropTypes.object.isRequired
};
export default connect(mapStateToProps)(user);

View File

@@ -29,28 +29,36 @@ const MyChip = styled(Chip)({
color: 'primary'
});
class user extends Component {
class user extends Component {
state = {
profile: null,
imageUrl: null,
topics: null,
newTopic: null
};
handleDelete = (topic) => {
alert(`Delete topic: ${topic}!`);
axios.post(`/deleteTopic`, {
unfollow: topic
})
.then(function() {
location.reload();
})
.catch(function(err) {
console.log(err);
});
}
handleAddCircle = () => {
axios.post('/putTopic', {
topic: this.state.newTopic
})
.then(function () {
location.reload();
})
.catch(function (err) {
console.log(err);
});
following: this.state.newTopic
})
.then(function () {
location.reload();
})
.catch(function (err) {
console.log(err);
});
}
handleChange(event) {
@@ -65,12 +73,14 @@ 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
axios
.get("/getAllTopics")
.then(res => {
this.setState({