Merge branch 'fix_loading_cookies' of Asara/sudoscientist-js-frontend into master
This commit is contained in:
commit
d4651a2315
13 changed files with 3256 additions and 2582 deletions
4
TODO.md
4
TODO.md
|
@ -1,5 +1,5 @@
|
|||
# TODO
|
||||
|
||||
1. Add filtering posts by tags
|
||||
2. Auth
|
||||
1. Fix up UX, return errors to screen instead of doing nothing.
|
||||
2. Add filtering posts by tags
|
||||
3. Comments
|
||||
|
|
5622
package-lock.json
generated
5622
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -6,15 +6,15 @@
|
|||
"axios": "^0.18.1",
|
||||
"github-markdown-css": "^3.0.1",
|
||||
"react": "^16.10.2",
|
||||
"react-cookie": "^4.0.1",
|
||||
"react-dom": "^16.10.2",
|
||||
"react-markdown": "^4.2.2",
|
||||
"react-mde": "^7.6.2",
|
||||
"react-redux": "^7.1.1",
|
||||
"react-router-dom": "^5.1.2",
|
||||
"react-scripts": "3.0.0",
|
||||
"redux": "^4.0.4",
|
||||
"redux-thunk": "^2.3.0",
|
||||
"semantic-ui-react": "^0.87.3"
|
||||
"universal-cookie": "^4.0.2"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
<meta charset="utf-8" />
|
||||
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<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">
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
|
|
2
src/actions/history.js
Normal file
2
src/actions/history.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
import { createBrowserHistory } from 'history'
|
||||
export default createBrowserHistory()
|
|
@ -1,4 +1,5 @@
|
|||
import sudoscientist from '../apis/sudoscientist';
|
||||
import history from './history'
|
||||
|
||||
export const fetchPosts = () => async (dispatch) => {
|
||||
const response = await sudoscientist.get('/blog');
|
||||
|
@ -25,9 +26,21 @@ export const userLogin = (username, password) => async (dispatch) => {
|
|||
}
|
||||
)
|
||||
if (response.status === 401) {
|
||||
console.log("Please check your credentials")
|
||||
dispatch({ type: 'USER_LOGIN', payload: null })
|
||||
}
|
||||
if (response.status === 200) {
|
||||
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)
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import axios from 'axios';
|
||||
|
||||
export default axios.create({
|
||||
baseURL: 'http://api.sudosci.test:8080/v1/api/',
|
||||
baseURL: 'https://api.sudoscientist.com/v1/api/',
|
||||
withCredentials: true
|
||||
});
|
||||
|
|
|
@ -4,12 +4,9 @@ import ReactMarkdown from 'react-markdown';
|
|||
import AboutMarkdown from '../static/about.md';
|
||||
|
||||
class About extends React.Component {
|
||||
constructor () {
|
||||
super();
|
||||
this.state = { about: '' };
|
||||
}
|
||||
state = { about: '' };
|
||||
|
||||
componentWillMount() {
|
||||
componentDidMount() {
|
||||
fetch(AboutMarkdown).then(res => res.text()).then(text => this.setState({ markdown: text }));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,21 +1,25 @@
|
|||
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 Footer from './Footer';
|
||||
import PostList from './PostList';
|
||||
import Post from './Post';
|
||||
import User from './User';
|
||||
import About from './About'
|
||||
import NewPost from './NewPost'
|
||||
import history from '../actions/history';
|
||||
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<>
|
||||
<Router>
|
||||
<Router history={history}>
|
||||
<NavBar/>
|
||||
<Switch>
|
||||
<Route exact path="/" component={PostList}/>
|
||||
<Route exact path="/about/" component={About}/>
|
||||
<Route exact path="/posts/" component={PostList}/>
|
||||
<Route exact path="/newpost/" component={NewPost}/>
|
||||
<Route path="/posts/:slug" component={Post}/>
|
||||
<Route path="/users/:user" component={User}/>
|
||||
</Switch>
|
||||
|
|
|
@ -2,20 +2,48 @@ import React, { Component } from 'react';
|
|||
import Cookies from 'universal-cookie';
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
import { userLogin } from '../actions';
|
||||
import { userLogin, loadCookieToState } from '../actions';
|
||||
|
||||
class AuthMenu extends Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
user_authed: false,
|
||||
where_in_auth_menu: "requestUsername",
|
||||
username: "",
|
||||
password: "",
|
||||
email: ""
|
||||
const cookies = new Cookies();
|
||||
const datacookie = cookies.get('DataCookie');
|
||||
|
||||
if (datacookie) {
|
||||
const data = JSON.parse(atob(datacookie.split('.')[1]))
|
||||
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.handleEmailRequestForAccountCreation = this.handleEmailRequestForAccountCreation.bind(this)
|
||||
|
@ -27,7 +55,6 @@ class AuthMenu extends Component {
|
|||
this.handleCreateAccount = this.handleCreateAccount.bind(this)
|
||||
|
||||
this.authMenu = this.authMenu.bind(this)
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -63,16 +90,15 @@ class AuthMenu extends Component {
|
|||
}
|
||||
|
||||
handleLogin() {
|
||||
const cookies = new Cookies();
|
||||
this.props.userLogin(this.state.username, this.state.password)
|
||||
.then(res => {
|
||||
this.setState(state => ({
|
||||
auth_menu_visible: false
|
||||
auth_menu_visible: false,
|
||||
where_in_auth_menu: "loggedIn"
|
||||
}))
|
||||
this.props.close()
|
||||
}
|
||||
)
|
||||
.then(console.log(cookies.getAll()))
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
})
|
||||
|
@ -159,6 +185,15 @@ class AuthMenu extends Component {
|
|||
<button onClick={this.handleCreateAccount} className="fluid ui positive button">Create Account!</button>
|
||||
</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:
|
||||
return null;
|
||||
}
|
||||
|
@ -171,12 +206,7 @@ class AuthMenu extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
return {
|
||||
username: state.username,
|
||||
};
|
||||
}
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
{ userLogin }
|
||||
null,
|
||||
{ userLogin, loadCookieToState }
|
||||
)(AuthMenu);
|
||||
|
|
|
@ -7,8 +7,9 @@ class NavBar extends Component {
|
|||
constructor (props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
logged_in: false,
|
||||
user_authed: false,
|
||||
auth_menu_visible: false,
|
||||
user_or_login: 'Login'
|
||||
}
|
||||
this.handleLoginDropdown = this.handleLoginDropdown.bind(this)
|
||||
}
|
||||
|
@ -20,6 +21,7 @@ class NavBar extends Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const { user } = this.props;
|
||||
return (
|
||||
<div>
|
||||
<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="/about/" className='item' activeClassName='active'>About</NavLink>
|
||||
<div onClick={this.handleLoginDropdown} className='item ui button dropdown'>
|
||||
Login
|
||||
{user.user_authed ? user.username : 'Login'}
|
||||
<i className="dropdown icon"></i>
|
||||
<AuthMenu auth_menu_visible={this.state.auth_menu_visible} close={ () => this.setState({auth_menu_visible: false})}></AuthMenu>
|
||||
</div>
|
||||
|
@ -39,7 +41,7 @@ class NavBar extends Component {
|
|||
|
||||
const mapStateToProps = (state) => {
|
||||
return {
|
||||
auth: state.auth
|
||||
user: state.auth,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
67
src/components/NewPost.js
Normal file
67
src/components/NewPost.js
Normal 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);
|
||||
|
|
@ -1,18 +1,24 @@
|
|||
const initialState = {
|
||||
user_logged_in: false,
|
||||
username: '',
|
||||
user_authed: false,
|
||||
where_in_auth_menu: "requestUsername",
|
||||
email: "",
|
||||
exp: null
|
||||
};
|
||||
|
||||
|
||||
|
||||
export default (state = initialState, action) => {
|
||||
switch (action.type) {
|
||||
case 'USER_LOGIN':
|
||||
return {...state, ...{
|
||||
username: action.payload.data.username,
|
||||
user_logged_in: true
|
||||
}}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
switch (action.type) {
|
||||
case 'LOAD_COOKIE':
|
||||
return {...state, ...action.data}
|
||||
case 'USER_LOGIN':
|
||||
if (action.payload) {
|
||||
var data = JSON.parse(atob(action.payload.data.split('.')[1]))
|
||||
return {...state, ...{
|
||||
user_authed: true,
|
||||
username: data.username,
|
||||
}}
|
||||
}
|
||||
else return;
|
||||
default:
|
||||
return state
|
||||
}
|
||||
};
|
||||
|
|
Reference in a new issue