zcash-grant-system/frontend/client/modules/proposals/reducers.ts

241 lines
6.1 KiB
TypeScript
Raw Normal View History

2018-09-10 09:55:26 -07:00
import types from './types';
import { findComment } from 'utils/helpers';
2019-01-09 12:48:41 -08:00
import { Proposal, ProposalComments, ProposalUpdates, Comment, ProposalContributions } from 'types';
2018-09-10 09:55:26 -07:00
export interface ProposalState {
proposals: Proposal[];
2018-09-10 09:55:26 -07:00
proposalsError: null | string;
isFetchingProposals: boolean;
proposalComments: { [id: string]: ProposalComments };
commentsError: null | string;
isFetchingComments: boolean;
proposalUpdates: { [id: string]: ProposalUpdates };
updatesError: null | string;
isFetchingUpdates: boolean;
2019-01-09 12:48:41 -08:00
proposalContributions: { [id: string]: ProposalContributions };
fetchContributionsError: null | string;
isFetchingContributions: boolean;
isPostCommentPending: boolean;
postCommentError: null | string;
2018-09-10 09:55:26 -07:00
}
export const INITIAL_STATE: ProposalState = {
proposals: [],
proposalsError: null,
isFetchingProposals: false,
proposalComments: {},
commentsError: null,
isFetchingComments: false,
proposalUpdates: {},
updatesError: null,
isFetchingUpdates: false,
2019-01-09 12:48:41 -08:00
proposalContributions: {},
fetchContributionsError: null,
isFetchingContributions: false,
isPostCommentPending: false,
postCommentError: null,
2018-09-10 09:55:26 -07:00
};
function addProposal(state: ProposalState, payload: Proposal) {
2018-09-10 09:55:26 -07:00
let proposals = state.proposals;
const existingProposal = state.proposals.find(
(p: Proposal) => p.proposalId === payload.proposalId,
2018-09-10 09:55:26 -07:00
);
if (!existingProposal) {
proposals = proposals.concat(payload);
} else {
proposals = [...proposals];
const index = proposals.indexOf(existingProposal);
proposals[index] = payload;
}
return {
...state,
...{
proposals,
},
};
}
function addProposals(state: ProposalState, payload: Proposal[]) {
2018-09-10 09:55:26 -07:00
return {
...state,
proposals: payload,
isFetchingProposals: false,
};
}
function addComments(state: ProposalState, payload: { data: ProposalComments }) {
return {
...state,
proposalComments: {
...state.proposalComments,
[payload.data.proposalId]: payload.data,
},
isFetchingComments: false,
};
}
function addUpdates(state: ProposalState, payload: ProposalUpdates) {
2018-09-10 09:55:26 -07:00
return {
...state,
proposalUpdates: {
...state.proposalUpdates,
[payload.proposalId]: payload,
2018-09-10 09:55:26 -07:00
},
isFetchingUpdates: false,
};
}
2019-01-09 12:48:41 -08:00
function addContributions(state: ProposalState, payload: ProposalContributions) {
return {
...state,
proposalContributions: {
...state.proposalContributions,
[payload.proposalId]: payload,
},
isFetchingContributions: false,
};
}
interface PostCommentPayload {
proposalId: Proposal['proposalId'];
comment: Comment;
parentCommentId?: Comment['id'];
}
function addPostedComment(state: ProposalState, payload: PostCommentPayload) {
const { proposalId, comment, parentCommentId } = payload;
const newComments = state.proposalComments[proposalId]
? {
...state.proposalComments[proposalId],
totalComments: state.proposalComments[proposalId].totalComments + 1,
comments: [...state.proposalComments[proposalId].comments],
}
: {
proposalId: payload.proposalId,
totalComments: 1,
comments: [],
};
if (parentCommentId) {
const parentComment = findComment(parentCommentId, newComments.comments);
if (parentComment) {
// FIXME: Object mutation because I'm lazy, but this probably shouldnt
// exist once API hookup is done. We'll just re-request from server.
parentComment.replies.unshift(comment);
}
} else {
newComments.comments.unshift(comment);
}
return {
...state,
isPostCommentPending: false,
proposalComments: {
...state.proposalComments,
[payload.proposalId]: newComments,
},
};
}
2018-09-10 09:55:26 -07:00
export default (state = INITIAL_STATE, action: any) => {
const { payload } = action;
switch (action.type) {
case types.PROPOSALS_DATA_PENDING:
return {
...state,
proposals: [],
proposalsError: null,
isFetchingProposals: true,
};
case types.PROPOSALS_DATA_FULFILLED:
return addProposals(state, payload);
case types.PROPOSALS_DATA_REJECTED:
return {
...state,
// TODO: Get action to send real error
proposalsError: 'Failed to fetch proposal',
isFetchingProposals: false,
};
case types.PROPOSAL_DATA_FULFILLED:
return addProposal(state, payload);
case types.PROPOSAL_COMMENTS_PENDING:
return {
...state,
commentsError: null,
isFetchingComments: true,
};
case types.PROPOSAL_COMMENTS_FULFILLED:
return addComments(state, payload);
case types.PROPOSAL_COMMENTS_REJECTED:
return {
...state,
// TODO: Get action to send real error
commentsError: 'Failed to fetch comments',
isFetchingComments: false,
};
case types.PROPOSAL_UPDATES_PENDING:
return {
...state,
updatesError: null,
isFetchingUpdates: true,
};
case types.PROPOSAL_UPDATES_FULFILLED:
return addUpdates(state, payload);
case types.PROPOSAL_UPDATES_REJECTED:
return {
...state,
// TODO: Get action to send real error
updatesError: 'Failed to fetch updates',
isFetchingUpdates: false,
};
2019-01-09 12:48:41 -08:00
case types.PROPOSAL_CONTRIBUTIONS_PENDING:
return {
...state,
fetchContributionsError: null,
isFetchingContributions: true,
};
case types.PROPOSAL_CONTRIBUTIONS_FULFILLED:
return addContributions(state, payload);
case types.PROPOSAL_CONTRIBUTIONS_REJECTED:
return {
...state,
// TODO: Get action to send real error
fetchContributionsError: 'Failed to fetch updates',
isFetchingContributions: false,
};
case types.POST_PROPOSAL_COMMENT_PENDING:
return {
...state,
isPostCommentPending: true,
postCommentError: null,
};
case types.POST_PROPOSAL_COMMENT_FULFILLED:
return addPostedComment(state, payload);
case types.POST_PROPOSAL_COMMENT_REJECTED:
return {
...state,
isPostCommentPending: false,
postCommentError: payload,
};
2018-09-10 09:55:26 -07:00
default:
return state;
}
};