Implement basic version of likes

This commit is contained in:
riggraz
2019-09-27 16:57:23 +02:00
parent 5ee6a4afae
commit 80164178c2
11 changed files with 189 additions and 18 deletions

View File

@@ -0,0 +1,41 @@
import { Action } from "redux";
import { ThunkAction } from "redux-thunk";
import { State } from "../reducers/rootReducer";
export const LIKE_SUBMIT_SUCCESS = 'LIKE_SUBMIT_SUCCESS';
interface LikeSubmitSuccessAction {
type: typeof LIKE_SUBMIT_SUCCESS,
postId: number;
isLike: boolean;
}
export type LikeActionTypes = LikeSubmitSuccessAction;
const likeSubmitSuccess = (postId: number, isLike: boolean): LikeSubmitSuccessAction => ({
type: LIKE_SUBMIT_SUCCESS,
postId,
isLike,
});
export const submitLike = (
postId: number,
isLike: boolean,
authenticityToken: string,
): ThunkAction<void, State, null, Action<string>> => async (dispatch) => {
try {
const res = await fetch(`/posts/${postId}/likes`, {
method: isLike ? 'POST' : 'DELETE',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
'X-CSRF-Token': authenticityToken,
},
});
if (res.status === 201 || res.status === 202)
dispatch(likeSubmitSuccess(postId, isLike));
} catch (e) {
console.log('An error occurred while liking a post');
}
}

View File

@@ -97,16 +97,19 @@ class BoardP extends React.Component<Props> {
<PostList
posts={posts.items}
postStatuses={postStatuses.items}
hasMore={posts.haveMore}
areLoading={posts.areLoading}
error={posts.error}
hasMore={posts.haveMore}
handleLoadMore={() =>
posts.areLoading ?
null
:
requestPosts(board.id, posts.page + 1, filters.searchQuery, filters.postStatusId)
}
isLoggedIn={isLoggedIn}
authenticityToken={authenticityToken}
/>
</div>
);

View File

@@ -18,8 +18,11 @@ interface Props {
areLoading: boolean;
error: string;
handleLoadMore(): void;
hasMore: boolean;
handleLoadMore(): void;
isLoggedIn: boolean;
authenticityToken: string;
}
const PostList = ({
@@ -27,8 +30,10 @@ const PostList = ({
postStatuses,
areLoading,
error,
hasMore,
handleLoadMore,
hasMore
isLoggedIn,
authenticityToken,
}: Props) => (
<div className="postList">
{ error ? <DangerText>{error}</DangerText> : null }
@@ -48,8 +53,13 @@ const PostList = ({
title={post.title}
description={post.description}
postStatus={postStatuses.find(postStatus => postStatus.id === post.postStatusId)}
likesCount={post.likesCount}
liked={post.liked}
commentsCount={post.commentsCount}
isLoggedIn={isLoggedIn}
authenticityToken={authenticityToken}
key={i}
/>
))

View File

@@ -1,5 +1,6 @@
import * as React from 'react';
import LikeButton from '../../containers/LikeButton';
import CommentsNumber from '../shared/CommentsNumber';
import PostStatusLabel from '../shared/PostStatusLabel';
import { DescriptionText } from '../shared/CustomTexts';
@@ -11,21 +12,46 @@ interface Props {
title: string;
description?: string;
postStatus: IPostStatus;
likesCount: number;
liked: number;
commentsCount: number;
isLoggedIn: boolean;
authenticityToken: string;
}
const PostListItem = ({ id, title, description, postStatus, commentsCount }: Props) => (
<a href={`/posts/${id}`} className="postLink">
<div className="postListItem">
<span className="postTitle">{title}</span>
<DescriptionText limit={120}>{description}</DescriptionText>
const PostListItem = ({
id,
title,
description,
postStatus,
likesCount,
liked,
commentsCount,
<div className="postDetails">
<CommentsNumber number={commentsCount} />
{ postStatus ? <PostStatusLabel {...postStatus} /> : null }
isLoggedIn,
authenticityToken,
}: Props) => (
<React.Fragment>
<LikeButton
postId={id}
likesCount={likesCount}
liked={liked}
isLoggedIn={isLoggedIn}
authenticityToken={authenticityToken}
/>
<a href={`/posts/${id}`} className="postLink">
<div className="postListItem">
<span className="postTitle">{title}</span>
<DescriptionText limit={120}>{description}</DescriptionText>
<div className="postDetails">
<CommentsNumber number={commentsCount} />
{ postStatus ? <PostStatusLabel {...postStatus} /> : null }
</div>
</div>
</div>
</a>
</a>
</React.Fragment>
);
export default PostListItem;

View File

@@ -0,0 +1,37 @@
import * as React from 'react';
interface Props {
postId: number;
likesCount: number;
liked: number;
handleLikeSubmit(
postId: number,
isLike: boolean,
authenticityToken: string,
): void;
authenticityToken: string;
isLoggedIn: boolean;
}
const LikeButtonP = ({
postId,
likesCount,
liked,
handleLikeSubmit,
authenticityToken,
isLoggedIn,
}: Props) => (
<div className="likeButtonContainer">
<button onClick={() =>
isLoggedIn ?
handleLikeSubmit(postId, !liked, authenticityToken)
:
window.location.href = `/users/sign_in`
}>
{ liked ? 'down' : 'up' }
</button>
<span className="likesCountLabel">{likesCount}</span>
</div>
);
export default LikeButtonP;

View File

@@ -0,0 +1,16 @@
import { connect } from 'react-redux';
import LikeButtonP from '../components/LikeButton/LikeButtonP';
import { submitLike } from '../actions/submitLike';
const mapDispatchToProps = dispatch => ({
handleLikeSubmit(postId: number, isLike: boolean, authenticityToken: string) {
dispatch(submitLike(postId, isLike, authenticityToken));
},
});
export default connect(
null,
mapDispatchToProps,
)(LikeButtonP);

View File

@@ -4,7 +4,10 @@ interface IPost {
description?: string;
boardId: number;
postStatusId?: number;
likesCount: number;
liked: number;
commentsCount: number;
hotness: number;
userId: number;
createdAt: string;
}

View File

@@ -4,7 +4,10 @@ interface IPostJSON {
description?: string;
board_id: number;
post_status_id?: number;
likes_count: number;
liked: number;
comments_count: number;
hotness: number;
user_id: number;
created_at: string;
}

View File

@@ -21,7 +21,10 @@ const initialState: IPost = {
description: null,
boardId: 0,
postStatusId: null,
likesCount: 0,
liked: 0,
commentsCount: 0,
hotness: 0,
userId: 0,
createdAt: '',
};
@@ -41,7 +44,10 @@ const postReducer = (
description: action.post.description,
boardId: action.post.board_id,
postStatusId: action.post.post_status_id,
likesCount: action.post.likes_count,
liked: action.post.liked,
commentsCount: action.post.comments_count,
hotness: action.post.hotness,
userId: action.post.user_id,
createdAt: action.post.created_at,
};

View File

@@ -22,6 +22,11 @@ import {
SET_POST_STATUS_FILTER,
} from '../actions/changeFilters';
import {
LikeActionTypes,
LIKE_SUBMIT_SUCCESS,
} from '../actions/submitLike';
export interface PostsState {
items: Array<IPost>;
page: number;
@@ -42,7 +47,10 @@ const initialState: PostsState = {
const postsReducer = (
state = initialState,
action: PostsRequestActionTypes | ChangeFiltersActionTypes,
action:
PostsRequestActionTypes |
ChangeFiltersActionTypes |
LikeActionTypes,
): PostsState => {
switch (action.type) {
case POSTS_REQUEST_START:
@@ -78,6 +86,19 @@ const postsReducer = (
filters: filtersReducer(state.filters, action),
};
case LIKE_SUBMIT_SUCCESS:
return {
...state,
items: state.items.map(post => {
if (action.postId === post.id) {
return action.isLike ?
{ ...post, likesCount: post.likesCount + 1, liked: 1 }
:
{ ...post, likesCount: post.likesCount - 1, liked: 0 }
} else return post;
}),
};
default:
return state;
}