1
0
Fork 0

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

This commit is contained in:
Amarpreet Minhas 2020-02-09 19:37:31 -05:00 committed by Gogs
commit b53cd23708
7 changed files with 184 additions and 85 deletions

View file

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

View file

@ -7,8 +7,11 @@ export const fetchPosts = () => async (dispatch) => {
}; };
export const fetchPost = (slug) => async (dispatch) => { export const fetchPost = (slug) => async (dispatch) => {
const response = await sudoscientist.get('/blog/posts/by-slug/' + slug); const post = await sudoscientist.get('/blog/posts/by-slug/' + slug)
dispatch({ type: 'FETCH_POST', payload: response.data }) const comments = await sudoscientist.get('/blog/comments/' + post.data.id);
const response = { post: post.data }
response.post.comments = comments.data
dispatch({ type: 'FETCH_POST', payload: response })
}; };
@ -61,3 +64,10 @@ export const newBlogPost = (payload) => async (dispatch) => {
history.push('/posts/' + response.data.slug) history.push('/posts/' + response.data.slug)
} }
}; };
export const newComment = (payload, parent_id) => async (dispatch) => {
const response = await sudoscientist.post('/blog/comments/' + parent_id, payload)
if (response.status === 201) {
window.location.reload()
}
};

View file

@ -0,0 +1,49 @@
import React from 'react';
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 { newComment } from '../actions';
const Comment = (props) => {
const [content, setContent] = React.useState("");
const [selectedTab, setSelectedTab] = React.useState("write");
const submitComment = () => {
const payload = {
content: content
}
props.newComment(payload, props.post.currentId)
}
return (
<div>
<div className="comment">
<h3><b>Leave a comment!</b></h3>
</div>
<div className="markdown-body">
<ReactMde
value={content}
onChange={setContent}
selectedTab={selectedTab}
onTabChange={setSelectedTab}
generateMarkdownPreview={(markdown) =>
Promise.resolve(<ReactMarkdown source={markdown} />)}
/>
</div>
<div>
<button onClick={submitComment}>Submit Comment</button>
</div>
</div>
)
}
const mapStateToProps = (state) => {
return {
post: state.posts,
user: state.auth,
};
}
export default connect(
mapStateToProps,
{ newComment },
)(Comment);

View file

@ -2,57 +2,96 @@ import React from 'react';
import ReactMarkdown from 'react-markdown'; import ReactMarkdown from 'react-markdown';
import 'github-markdown-css' import 'github-markdown-css'
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { fetchPost } from '../actions'; import { fetchPost,newComment } from '../actions';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import Comment from './Comments';
class Post extends React.Component { class Post extends React.Component {
constructor (props) { constructor (props) {
super(props) super(props)
this.state = { this.state = {
isLoading: true isLoading: true
}
}
componentDidMount() {
const { slug } = this.props.match.params
this.props.fetchPost(slug)
.then(() => this.setState({isLoading: false}))
} }
}
render () { componentDidMount() {
const {posts} = this.props const { slug } = this.props.match.params
const {isLoading} = this.state this.props.fetchPost(slug)
.then(() => this.setState({isLoading: false, slug: slug}))
}
if (isLoading) { renderComments(comments, parent_id) {
return <p>Loading</p> if (comments) {
} else { return comments.map(comment => {
const post = posts.entities[posts.currentId] return (
return ( <div key={comment.id}>
<div className="item" key={post.id}> <div className="item" style={{outlineOffset: 2, outline: '1px solid', outlineColor: '#DADADA'}}>
<div className="content"> <h5><b>Comment by <Link to={"/users/"+ comment.author}>{comment.author}</Link></b><br></br>
<div className="description"> Posted: {comment.time_published}
<h1><b><u><Link to={"/posts/" + post.slug}>{post.title}</Link></u></b></h1> </h5>
<h3><b>By <Link to={"/users/"+ post.author}>{post.author}</Link></b></h3> <hr></hr>
<h4>Posted {post.time_published}</h4> <div className="content">
<div className="markdown-body"> <div className="markdown-body">
<ReactMarkdown source={post.content} /> <ReactMarkdown source={comment.content} />
</div> </div>
</div>
</div>
</div> </div>
); </div>
} <br></br>
</div>
);
});
} }
}
renderPost() {
const {posts} = this.props
const post = posts.entities[posts.currentId]
return(
<div className="container">
<div className="item" key={post.id}>
<div className="content">
<div className="description">
<h4><b><u><Link to={"/posts/" + post.slug}>{post.title}</Link></u></b></h4>
<h3><b>By <Link to={"/users/"+ post.author}>{post.author}</Link></b></h3>
<h4>Posted {post.time_published}</h4>
<div className="markdown-body">
<ReactMarkdown source={post.content} />
</div>
</div>
</div>
{ this.props.auth.verified &&
<Comment></Comment>
}
</div>
<h3>Comments:</h3>
{ this.renderComments(post.comments,post.id) }
</div>
)
}
render () {
const {isLoading} = this.state
if (isLoading) {
return <p>Loading</p>
} else {
return (
<div>
{ this.renderPost() }
</div>
);
}
}
} }
const mapStateToProps = (state) => { const mapStateToProps = (state) => {
return { return {
posts: state.posts, posts: state.posts,
slug: state.slug, slug: state.slug,
comments: state.comments auth: state.auth
}; };
} }
export default connect( export default connect(
mapStateToProps, mapStateToProps,
{ fetchPost } { fetchPost, newComment },
)(Post); )(Post);

View file

@ -7,52 +7,52 @@ import { Link } from 'react-router-dom';
class PostList extends React.Component { class PostList extends React.Component {
componentDidMount() { componentDidMount() {
this.props.fetchPosts(); this.props.fetchPosts();
} }
renderList() { renderList() {
const {posts} = this.props const {posts} = this.props
const postKeys = Object.keys(posts.entities) const postKeys = Object.keys(posts.entities)
return postKeys.map(id => { return postKeys.map(id => {
const post = posts.entities[id] const post = posts.entities[id]
return ( return (
<div className="item" key={post.id}> <div className="item" key={post.id}>
<div className="content"> <div className="content">
<div className="description"> <div className="description">
<h1><b><u><Link to={"/posts/" + post.slug}>{post.title}</Link></u></b></h1> <h1><b><u><Link to={"/posts/" + post.slug}>{post.title}</Link></u></b></h1>
<h3><b>By <Link to={"/users/"+ post.author}>{post.author}</Link></b></h3> <h3><b>By <Link to={"/users/"+ post.author}>{post.author}</Link></b></h3>
<h4>Posted {post.time_published}</h4> <h4>Posted {post.time_published}</h4>
<div className="markdown-body"> <div className="markdown-body">
<ReactMarkdown source={post.content} /> <ReactMarkdown source={post.content} />
</div> </div>
</div>
</div>
</div>
);
}).reverse();
}
render () {
return (
<div>
<h1>Blog Posts</h1>
<div className="ui relaxed divided list">
{this.renderList()}
</div>
</div> </div>
); </div>
} </div>
);
}).reverse();
}
render () {
return (
<div>
<h1>Blog Posts</h1>
<div className="ui relaxed divided list">
{this.renderList()}
</div>
</div>
);
}
} }
const mapStateToProps = (state) => { const mapStateToProps = (state) => {
return { return {
posts: state.posts, posts: state.posts,
slug: state.slug slug: state.slug
}; };
} }
export default connect( export default connect(
mapStateToProps, mapStateToProps,
{ fetchPosts } { fetchPosts }
)(PostList); )(PostList);

View file

@ -17,8 +17,8 @@ export default (state = initialState, action) => {
return {...state, ...{entities: mergedEntities}} return {...state, ...{entities: mergedEntities}}
case 'FETCH_POST': case 'FETCH_POST':
mergedEntities = normalizeEntities(state.entities, [action.payload]) mergedEntities = normalizeEntities(state.entities, [action.payload.post])
return {...state, ...{entities: mergedEntities, currentId: action.payload.id}} return {...state, ...{entities: mergedEntities, currentId: action.payload.post.id}}
default: default:
return state; return state;
} }

View file

@ -12,6 +12,8 @@ The frontend for this blog is written using react-redux and can be [found here](
### Communication ### Communication
Matrix: `@Asara:devvul.com` Matrix: `@Asara:devvul.com`
Fediverse: https://social.devvul.com/Asara
Email: `amarpreet@minhas.io` Email: `amarpreet@minhas.io`
### Lightning Network ### Lightning Network