1
0
Fork 0

Merge branch 'fix_loading_cookies' of Asara/sudoscientist-js-frontend into master

This commit is contained in:
Amarpreet Minhas 2019-10-26 12:25:33 -04:00 committed by Gogs
commit d4651a2315
13 changed files with 3256 additions and 2582 deletions

View file

@ -1,5 +1,5 @@
# TODO # TODO
1. Add filtering posts by tags 1. Fix up UX, return errors to screen instead of doing nothing.
2. Auth 2. Add filtering posts by tags
3. Comments 3. Comments

5622
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -6,15 +6,15 @@
"axios": "^0.18.1", "axios": "^0.18.1",
"github-markdown-css": "^3.0.1", "github-markdown-css": "^3.0.1",
"react": "^16.10.2", "react": "^16.10.2",
"react-cookie": "^4.0.1",
"react-dom": "^16.10.2", "react-dom": "^16.10.2",
"react-markdown": "^4.2.2", "react-markdown": "^4.2.2",
"react-mde": "^7.6.2",
"react-redux": "^7.1.1", "react-redux": "^7.1.1",
"react-router-dom": "^5.1.2", "react-router-dom": "^5.1.2",
"react-scripts": "3.0.0", "react-scripts": "3.0.0",
"redux": "^4.0.4", "redux": "^4.0.4",
"redux-thunk": "^2.3.0", "redux-thunk": "^2.3.0",
"semantic-ui-react": "^0.87.3" "universal-cookie": "^4.0.2"
}, },
"scripts": { "scripts": {
"start": "react-scripts start", "start": "react-scripts start",

View file

@ -4,7 +4,6 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" /> <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#EEEEEE" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.css">
<!-- <!--
manifest.json provides metadata used when your web app is installed on a manifest.json provides metadata used when your web app is installed on a

2
src/actions/history.js Normal file
View file

@ -0,0 +1,2 @@
import { createBrowserHistory } from 'history'
export default createBrowserHistory()

View file

@ -1,4 +1,5 @@
import sudoscientist from '../apis/sudoscientist'; import sudoscientist from '../apis/sudoscientist';
import history from './history'
export const fetchPosts = () => async (dispatch) => { export const fetchPosts = () => async (dispatch) => {
const response = await sudoscientist.get('/blog'); const response = await sudoscientist.get('/blog');
@ -25,9 +26,21 @@ export const userLogin = (username, password) => async (dispatch) => {
} }
) )
if (response.status === 401) { if (response.status === 401) {
console.log("Please check your credentials") dispatch({ type: 'USER_LOGIN', payload: null })
} }
if (response.status === 200) { if (response.status === 200) {
dispatch({ type: 'USER_LOGIN', payload: response }) dispatch({ type: 'USER_LOGIN', payload: response })
} }
}; };
export const loadCookieToState = (data) => async (dispatch) => {
dispatch({ type: 'LOAD_COOKIE', data })
};
export const newBlogPost = (payload) => async (dispatch) => {
const response = await sudoscientist.post('/blog', payload)
if (response.status === 201) {
history.push('/posts/' + response.data.slug)
}
};

View file

@ -1,6 +1,6 @@
import axios from 'axios'; import axios from 'axios';
export default axios.create({ export default axios.create({
baseURL: 'http://api.sudosci.test:8080/v1/api/', baseURL: 'https://api.sudoscientist.com/v1/api/',
withCredentials: true withCredentials: true
}); });

View file

@ -4,12 +4,9 @@ import ReactMarkdown from 'react-markdown';
import AboutMarkdown from '../static/about.md'; import AboutMarkdown from '../static/about.md';
class About extends React.Component { class About extends React.Component {
constructor () { state = { about: '' };
super();
this.state = { about: '' };
}
componentWillMount() { componentDidMount() {
fetch(AboutMarkdown).then(res => res.text()).then(text => this.setState({ markdown: text })); fetch(AboutMarkdown).then(res => res.text()).then(text => this.setState({ markdown: text }));
} }

View file

@ -1,21 +1,25 @@
import React from 'react'; import React from 'react';
import { BrowserRouter as Router, Route, Switch } from "react-router-dom"; import { Router, Route, Switch } from "react-router-dom";
import NavBar from './NavBar'; import NavBar from './NavBar';
import Footer from './Footer'; import Footer from './Footer';
import PostList from './PostList'; import PostList from './PostList';
import Post from './Post'; import Post from './Post';
import User from './User'; import User from './User';
import About from './About' import About from './About'
import NewPost from './NewPost'
import history from '../actions/history';
function App() { function App() {
return ( return (
<> <>
<Router> <Router history={history}>
<NavBar/> <NavBar/>
<Switch> <Switch>
<Route exact path="/" component={PostList}/> <Route exact path="/" component={PostList}/>
<Route exact path="/about/" component={About}/> <Route exact path="/about/" component={About}/>
<Route exact path="/posts/" component={PostList}/> <Route exact path="/posts/" component={PostList}/>
<Route exact path="/newpost/" component={NewPost}/>
<Route path="/posts/:slug" component={Post}/> <Route path="/posts/:slug" component={Post}/>
<Route path="/users/:user" component={User}/> <Route path="/users/:user" component={User}/>
</Switch> </Switch>

View file

@ -2,20 +2,48 @@ import React, { Component } from 'react';
import Cookies from 'universal-cookie'; import Cookies from 'universal-cookie';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { userLogin } from '../actions'; import { userLogin, loadCookieToState } from '../actions';
class AuthMenu extends Component { class AuthMenu extends Component {
constructor (props) { constructor (props) {
super(props) super(props)
this.state = { const cookies = new Cookies();
user_authed: false, const datacookie = cookies.get('DataCookie');
where_in_auth_menu: "requestUsername",
username: "", if (datacookie) {
password: "", const data = JSON.parse(atob(datacookie.split('.')[1]))
email: "" if (data.exp > Math.floor(Date.now() / 1000)) {
this.state = {
user_authed: true,
where_in_auth_menu: "loggedIn",
username: data.username,
exp: data.exp,
}
}
else {
this.state = {
user_authed: false,
where_in_auth_menu: "requestUsername",
username: "",
password: "",
email: "",
exp: null,
}
}
} else {
this.state = {
user_authed: false,
where_in_auth_menu: "requestUsername",
username: "",
password: "",
email: "",
exp: null
}
} }
this.props.loadCookieToState(this.state)
this.handleInputChange = this.handleInputChange.bind(this) this.handleInputChange = this.handleInputChange.bind(this)
this.handleEmailRequestForAccountCreation = this.handleEmailRequestForAccountCreation.bind(this) this.handleEmailRequestForAccountCreation = this.handleEmailRequestForAccountCreation.bind(this)
@ -27,7 +55,6 @@ class AuthMenu extends Component {
this.handleCreateAccount = this.handleCreateAccount.bind(this) this.handleCreateAccount = this.handleCreateAccount.bind(this)
this.authMenu = this.authMenu.bind(this) this.authMenu = this.authMenu.bind(this)
} }
@ -63,16 +90,15 @@ class AuthMenu extends Component {
} }
handleLogin() { handleLogin() {
const cookies = new Cookies();
this.props.userLogin(this.state.username, this.state.password) this.props.userLogin(this.state.username, this.state.password)
.then(res => { .then(res => {
this.setState(state => ({ this.setState(state => ({
auth_menu_visible: false auth_menu_visible: false,
where_in_auth_menu: "loggedIn"
})) }))
this.props.close() this.props.close()
} }
) )
.then(console.log(cookies.getAll()))
.catch(err => { .catch(err => {
console.log(err); console.log(err);
}) })
@ -159,6 +185,15 @@ class AuthMenu extends Component {
<button onClick={this.handleCreateAccount} className="fluid ui positive button">Create Account!</button> <button onClick={this.handleCreateAccount} className="fluid ui positive button">Create Account!</button>
</div> </div>
) )
case 'loggedIn':
return (
<div className="ui menu dropdown" style={{display: "inline"}}>
<div className="ui left icon input">
<i className="edit icon"></i>
<button onClick={() => { document.location.href = "/newpost/"; }} className="fluid ui positive button">Create Post!</button>
</div>
</div>
)
default: default:
return null; return null;
} }
@ -171,12 +206,7 @@ class AuthMenu extends Component {
} }
} }
const mapStateToProps = (state) => {
return {
username: state.username,
};
}
export default connect( export default connect(
mapStateToProps, null,
{ userLogin } { userLogin, loadCookieToState }
)(AuthMenu); )(AuthMenu);

View file

@ -7,8 +7,9 @@ class NavBar extends Component {
constructor (props) { constructor (props) {
super(props) super(props)
this.state = { this.state = {
logged_in: false, user_authed: false,
auth_menu_visible: false, auth_menu_visible: false,
user_or_login: 'Login'
} }
this.handleLoginDropdown = this.handleLoginDropdown.bind(this) this.handleLoginDropdown = this.handleLoginDropdown.bind(this)
} }
@ -20,6 +21,7 @@ class NavBar extends Component {
} }
render() { render() {
const { user } = this.props;
return ( return (
<div> <div>
<div className="ui four item secondary menu"> <div className="ui four item secondary menu">
@ -27,7 +29,7 @@ class NavBar extends Component {
<NavLink to="/posts/" className='item' activeClassName='active'>Posts</NavLink> <NavLink to="/posts/" className='item' activeClassName='active'>Posts</NavLink>
<NavLink to="/about/" className='item' activeClassName='active'>About</NavLink> <NavLink to="/about/" className='item' activeClassName='active'>About</NavLink>
<div onClick={this.handleLoginDropdown} className='item ui button dropdown'> <div onClick={this.handleLoginDropdown} className='item ui button dropdown'>
Login {user.user_authed ? user.username : 'Login'}
<i className="dropdown icon"></i> <i className="dropdown icon"></i>
<AuthMenu auth_menu_visible={this.state.auth_menu_visible} close={ () => this.setState({auth_menu_visible: false})}></AuthMenu> <AuthMenu auth_menu_visible={this.state.auth_menu_visible} close={ () => this.setState({auth_menu_visible: false})}></AuthMenu>
</div> </div>
@ -39,7 +41,7 @@ class NavBar extends Component {
const mapStateToProps = (state) => { const mapStateToProps = (state) => {
return { return {
auth: state.auth user: state.auth,
}; };
} }

67
src/components/NewPost.js Normal file
View file

@ -0,0 +1,67 @@
import React from 'react';
import { useSelector } from "react-redux";
import ReactMde from "react-mde";
import ReactMarkdown from 'react-markdown';
import { connect } from 'react-redux';
import 'github-markdown-css'
import "react-mde/lib/styles/css/react-mde-all.css"
import { newBlogPost } from '../actions';
const NewPost = (props) => {
const [title, setTitle] = React.useState("");
const [content, setContent] = React.useState("");
const [tags, setTags] = React.useState("");
const [selectedTab, setSelectedTab] = React.useState("write");
const username = useSelector(state => state.auth.username);
const submitPost = () => {
const payload = {
title: title,
content: content,
tags: tags,
author: username
}
props.newBlogPost(payload)
}
return(
<div className="container">
<input
value={title}
placeholder="Title..."
onChange={e => setTitle(e.target.value)}
/>
<div className="markdown-body">
<ReactMde
value={content}
onChange={setContent}
selectedTab={selectedTab}
onTabChange={setSelectedTab}
generateMarkdownPreview={(markdown) =>
Promise.resolve(<ReactMarkdown source={markdown} />)}
/>
</div>
<input
value={tags}
placeholder="Comma,Seperated,Tags..."
onChange={e => setTags(e.target.value)}
/>
<div>
<button onClick={submitPost}>Submit Post</button>
</div>
</div>
)
}
const mapStateToProps = (state) => {
return {
user: state.auth,
};
}
export default connect(
mapStateToProps,
{ newBlogPost }
)(NewPost);

View file

@ -1,18 +1,24 @@
const initialState = { const initialState = {
user_logged_in: false, user_authed: false,
username: '', where_in_auth_menu: "requestUsername",
email: "",
exp: null
}; };
export default (state = initialState, action) => { export default (state = initialState, action) => {
switch (action.type) { switch (action.type) {
case 'USER_LOGIN': case 'LOAD_COOKIE':
return {...state, ...{ return {...state, ...action.data}
username: action.payload.data.username, case 'USER_LOGIN':
user_logged_in: true if (action.payload) {
}} var data = JSON.parse(atob(action.payload.data.split('.')[1]))
default: return {...state, ...{
return state; user_authed: true,
} username: data.username,
}}
}
else return;
default:
return state
}
}; };