Skip to content

Commit

Permalink
Add Redux and use it for state management
Browse files Browse the repository at this point in the history
  • Loading branch information
riggraz committed Sep 11, 2019
1 parent db4c144 commit 8d9ab0f
Show file tree
Hide file tree
Showing 22 changed files with 657 additions and 247 deletions.
3 changes: 1 addition & 2 deletions app/controllers/posts_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ class PostsController < ApplicationController

def index
posts = Post
.left_outer_joins(:post_status)
.select('posts.title, posts.description, post_statuses.name as post_status_name, post_statuses.color as post_status_color')
.select(:title, :description, :post_status_id)
.where(filter_params)
.search_by_name_or_description(params[:search])
.page(params[:page])
Expand Down
27 changes: 27 additions & 0 deletions app/javascript/actions/changeFilters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
export const SET_SEARCH_FILTER = 'SET_SEARCH_FILTER';
interface SetSearchFilterAction {
type: typeof SET_SEARCH_FILTER;
searchQuery: string;
}

export const SET_POST_STATUS_FILTER = 'SET_POST_STATUS_FILTER';
interface SetPostStatusFilterAction {
type: typeof SET_POST_STATUS_FILTER;
postStatusId: number;
}


export const setSearchFilter = (searchQuery: string): SetSearchFilterAction => ({
type: SET_SEARCH_FILTER,
searchQuery,
});

export const setPostStatusFilter = (postStatusId: number): SetPostStatusFilterAction => ({
type: SET_POST_STATUS_FILTER,
postStatusId,
});


export type ChangeFiltersActionTypes =
SetSearchFilterAction |
SetPostStatusFilterAction;
59 changes: 59 additions & 0 deletions app/javascript/actions/requestPostStatuses.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { Action } from 'redux';
import { ThunkAction } from 'redux-thunk';

import IPostStatus from '../interfaces/IPostStatus';

import { State } from '../reducers/rootReducer';

export const POST_STATUSES_REQUEST_START = 'POST_STATUSES_REQUEST_START';
interface PostStatusesRequestStartAction {
type: typeof POST_STATUSES_REQUEST_START;
}

export const POST_STATUSES_REQUEST_SUCCESS = 'POST_STATUSES_REQUEST_SUCCESS';
interface PostStatusesRequestSuccessAction {
type: typeof POST_STATUSES_REQUEST_SUCCESS;
postStatuses: Array<IPostStatus>;
}

export const POST_STATUSES_REQUEST_FAILURE = 'POST_STATUSES_REQUEST_FAILURE';
interface PostStatusesRequestFailureAction {
type: typeof POST_STATUSES_REQUEST_FAILURE;
error: string;
}

export type PostStatusesRequestActionTypes =
PostStatusesRequestStartAction |
PostStatusesRequestSuccessAction |
PostStatusesRequestFailureAction


const postStatusesRequestStart = (): PostStatusesRequestActionTypes => ({
type: POST_STATUSES_REQUEST_START,
});

const postStatusesRequestSuccess = (
postStatuses: Array<IPostStatus>
): PostStatusesRequestActionTypes => ({
type: POST_STATUSES_REQUEST_SUCCESS,
postStatuses,
});

const postStatusesRequestFailure = (error: string): PostStatusesRequestActionTypes => ({
type: POST_STATUSES_REQUEST_FAILURE,
error,
});

export const requestPostStatuses = (): ThunkAction<void, State, null, Action<string>> => (
async (dispatch) => {
dispatch(postStatusesRequestStart());

try {
const response = await fetch('/post_statuses');
const json = await response.json();
dispatch(postStatusesRequestSuccess(json));
} catch (e) {
dispatch(postStatusesRequestFailure(e));
}
}
)
68 changes: 68 additions & 0 deletions app/javascript/actions/requestPosts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { Action } from 'redux';
import { ThunkAction } from 'redux-thunk';

import IPostJSON from '../interfaces/json/IPost';

import { State } from '../reducers/rootReducer';

export const POSTS_REQUEST_START = 'POSTS_REQUEST_START';
interface PostsRequestStartAction {
type: typeof POSTS_REQUEST_START;
}

export const POSTS_REQUEST_SUCCESS = 'POSTS_REQUEST_SUCCESS';
interface PostsRequestSuccessAction {
type: typeof POSTS_REQUEST_SUCCESS;
posts: Array<IPostJSON>;
page: number;
}

export const POSTS_REQUEST_FAILURE = 'POSTS_REQUEST_FAILURE';
interface PostsRequestFailureAction {
type: typeof POSTS_REQUEST_FAILURE;
error: string;
}

export type PostsRequestActionTypes =
PostsRequestStartAction |
PostsRequestSuccessAction |
PostsRequestFailureAction;


const postsRequestStart = (): PostsRequestActionTypes => ({
type: POSTS_REQUEST_START,
});

const postsRequestSuccess = (posts: Array<IPostJSON>, page: number): PostsRequestActionTypes => ({
type: POSTS_REQUEST_SUCCESS,
posts,
page,
});

const postsRequestFailure = (error: string): PostsRequestActionTypes => ({
type: POSTS_REQUEST_FAILURE,
error,
});

export const requestPosts = (
boardId: number,
page: number,
searchQuery: string,
postStatusId: number,
): ThunkAction<void, State, null, Action<string>> => async (dispatch) => {
dispatch(postsRequestStart());

try {
let params = '';
params += `page=${page}`;
params += `&board_id=${boardId}`;
if (searchQuery) params += `&search=${searchQuery}`;
if (postStatusId) params += `&post_status_id=${postStatusId}`;

const response = await fetch(`/posts?${params}`);
const json = await response.json();
dispatch(postsRequestSuccess(json, page));
} catch (e) {
dispatch(postsRequestFailure(e));
}
}
112 changes: 112 additions & 0 deletions app/javascript/components/Board/BoardP.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import * as React from 'react';

import NewPost from './NewPost';
import SearchFilter from './SearchFilter';
import PostStatusFilter from './PostStatusFilter';
import PostList from './PostList';

import IBoard from '../../interfaces/IBoard';
import IPost from '../../interfaces/IPost';

import { PostsState } from '../../reducers/postsReducer';
import { PostStatusesState } from '../../reducers/postStatusesReducer';

interface Props {
board: IBoard;
isLoggedIn: boolean;
authenticityToken: string;
posts: PostsState;
postStatuses: PostStatusesState;

requestPosts(
boardId: number,
page?: number,
searchQuery?: string,
postStatusId?: number,
): void;
requestPostStatuses(): void;
handleSearchFilterChange(searchQuery: string): void;
handlePostStatusFilterChange(postStatusId: number): void;
}

class BoardP extends React.Component<Props> {
searchFilterTimeoutId: ReturnType<typeof setTimeout>;

componentDidMount() {
this.props.requestPosts(this.props.board.id);
this.props.requestPostStatuses();
}

componentDidUpdate(prevProps) {
const { searchQuery } = this.props.posts.filters;
const prevSearchQuery = prevProps.posts.filters.searchQuery;

const { postStatusId } = this.props.posts.filters;
const prevPostStatusId = prevProps.posts.filters.postStatusId;

// search filter changed
if (searchQuery !== prevSearchQuery) {
if (this.searchFilterTimeoutId) clearInterval(this.searchFilterTimeoutId);

this.searchFilterTimeoutId = setTimeout(() => (
this.props.requestPosts(this.props.board.id, 1, searchQuery, postStatusId)
), 500);
}

// post status filter changed
if (postStatusId !== prevPostStatusId) {
this.props.requestPosts(this.props.board.id, 1, searchQuery, postStatusId);
}
}

render() {
const {
board,
isLoggedIn,
authenticityToken,
posts,
postStatuses,

requestPosts,
handleSearchFilterChange,
handlePostStatusFilterChange,
} = this.props;

return (
<div className="boardContainer">
<div className="sidebar">
<NewPost
board={board}
isLoggedIn={isLoggedIn}
authenticityToken={authenticityToken}
/>
<SearchFilter
searchQuery={posts.filters.searchQuery}
handleChange={handleSearchFilterChange}
/>
<PostStatusFilter
postStatuses={postStatuses.items}
areLoading={postStatuses.areLoading}
error={postStatuses.error}

currentFilter={posts.filters.postStatusId}
handleFilterClick={handlePostStatusFilterChange}
/>
</div>

<PostList
posts={posts.items}
postStatuses={postStatuses.items}
page={posts.page}
hasMore={posts.haveMore}
areLoading={posts.areLoading}
error={posts.error}

handleLoadMore={() => requestPosts(board.id, posts.page + 1)}
/>
</div>
);
}
}

export default BoardP;
14 changes: 12 additions & 2 deletions app/javascript/components/Board/PostList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ import PostListItem from './PostListItem';
import Spinner from '../shared/Spinner';

import IPost from '../../interfaces/IPost';
import IPostStatus from '../../interfaces/IPostStatus';

interface Props {
posts: Array<IPost>;
postStatuses: Array<IPostStatus>;
areLoading: boolean;
error: string;

Expand All @@ -17,7 +19,15 @@ interface Props {
hasMore: boolean;
}

const PostList = ({ posts, areLoading, error, handleLoadMore, page, hasMore }: Props) => (
const PostList = ({
posts,
postStatuses,
areLoading,
error,
handleLoadMore,
page,
hasMore
}: Props) => (
<div className="box postList">
{ error ? <span className="error">{error}</span> : null }
<InfiniteScroll
Expand All @@ -34,7 +44,7 @@ const PostList = ({ posts, areLoading, error, handleLoadMore, page, hasMore }: P
<PostListItem
title={post.title}
description={post.description}
postStatus={post.postStatus}
postStatus={postStatuses.find(postStatus => postStatus.id === post.postStatusId)}

key={i}
/>
Expand Down
17 changes: 12 additions & 5 deletions app/javascript/components/Board/PostListItem.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import * as React from 'react';

import IPostStatus from '../../interfaces/IPostStatus';

interface Props {
title: string;
description?: string;
postStatus: {name: string, color: string};
postStatus: IPostStatus;
}

const PostListItem = ({ title, description, postStatus}: Props) => (
Expand All @@ -23,10 +25,15 @@ const PostListItem = ({ title, description, postStatus}: Props) => (
<span className="comment icon"></span>
<span>0 comments</span>
</div>
<div className="postDetailsStatus">
<div className="dot" style={{backgroundColor: postStatus.color}}></div>
<span className="postStatusName">{postStatus.name}</span>
</div>
{
postStatus ?
<div className="postDetailsStatus">
<div className="dot" style={{backgroundColor: postStatus.color}}></div>
<span className="postStatusName">{postStatus.name}</span>
</div>
:
null
}
</div>
</div>
</a>
Expand Down
Loading

0 comments on commit 8d9ab0f

Please sign in to comment.