disable while reacting, dont call api twice
This commit is contained in:
parent
63ce107cc1
commit
ad88f7de7f
7 changed files with 92 additions and 21 deletions
1
flow-typed/Comment.js
vendored
1
flow-typed/Comment.js
vendored
|
@ -25,6 +25,7 @@ declare type CommentsState = {
|
||||||
isFetchingReacts: boolean,
|
isFetchingReacts: boolean,
|
||||||
myReactsByCommentId: any,
|
myReactsByCommentId: any,
|
||||||
othersReactsByCommentId: any,
|
othersReactsByCommentId: any,
|
||||||
|
typesReacting: Array<string>,
|
||||||
};
|
};
|
||||||
|
|
||||||
declare type CommentReactParams = {
|
declare type CommentReactParams = {
|
||||||
|
|
|
@ -1297,5 +1297,6 @@
|
||||||
"Credit Details": "Credit Details",
|
"Credit Details": "Credit Details",
|
||||||
"Sign In": "Sign In",
|
"Sign In": "Sign In",
|
||||||
"Change to tile layout": "Change to tile layout",
|
"Change to tile layout": "Change to tile layout",
|
||||||
|
"%total_comments% comments": "%total_comments% comments",
|
||||||
"--end--": "--end--"
|
"--end--": "--end--"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import Comment from './view';
|
import Comment from './view';
|
||||||
import { makeSelectMyReactionsForComment, makeSelectOthersReactionsForComment } from 'redux/selectors/comments';
|
import {
|
||||||
|
makeSelectMyReactionsForComment,
|
||||||
|
makeSelectOthersReactionsForComment,
|
||||||
|
selectTypesReacting,
|
||||||
|
} from 'redux/selectors/comments';
|
||||||
import { doCommentReact } from 'redux/actions/comments';
|
import { doCommentReact } from 'redux/actions/comments';
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
myReacts: makeSelectMyReactionsForComment(props.commentId)(state),
|
myReacts: makeSelectMyReactionsForComment(props.commentId)(state),
|
||||||
othersReacts: makeSelectOthersReactionsForComment(props.commentId)(state),
|
othersReacts: makeSelectOthersReactionsForComment(props.commentId)(state),
|
||||||
|
typesReacting: selectTypesReacting(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
|
|
|
@ -11,10 +11,11 @@ type Props = {
|
||||||
othersReacts: any,
|
othersReacts: any,
|
||||||
react: (string, string) => void,
|
react: (string, string) => void,
|
||||||
commentId: string,
|
commentId: string,
|
||||||
|
typesReacting: Array<string>,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function CommentReactions(props: Props) {
|
export default function CommentReactions(props: Props) {
|
||||||
const { myReacts, othersReacts, commentId, react } = props;
|
const { myReacts, othersReacts, commentId, react, typesReacting } = props;
|
||||||
const [activeChannel] = usePersistedState('comment-channel');
|
const [activeChannel] = usePersistedState('comment-channel');
|
||||||
|
|
||||||
const getCountForReact = type => {
|
const getCountForReact = type => {
|
||||||
|
@ -36,7 +37,7 @@ export default function CommentReactions(props: Props) {
|
||||||
className={classnames('comment__action', {
|
className={classnames('comment__action', {
|
||||||
'comment__action--active': myReacts && myReacts.includes(REACTION_TYPES.LIKE),
|
'comment__action--active': myReacts && myReacts.includes(REACTION_TYPES.LIKE),
|
||||||
})}
|
})}
|
||||||
disabled={!activeChannel}
|
disabled={!activeChannel || typesReacting.includes(REACTION_TYPES.LIKE)}
|
||||||
onClick={() => react(commentId, REACTION_TYPES.LIKE)}
|
onClick={() => react(commentId, REACTION_TYPES.LIKE)}
|
||||||
label={getCountForReact(REACTION_TYPES.LIKE)}
|
label={getCountForReact(REACTION_TYPES.LIKE)}
|
||||||
/>
|
/>
|
||||||
|
@ -46,6 +47,7 @@ export default function CommentReactions(props: Props) {
|
||||||
className={classnames('comment__action', {
|
className={classnames('comment__action', {
|
||||||
'comment__action--active': myReacts && myReacts.includes(REACTION_TYPES.DISLIKE),
|
'comment__action--active': myReacts && myReacts.includes(REACTION_TYPES.DISLIKE),
|
||||||
})}
|
})}
|
||||||
|
disabled={!activeChannel || typesReacting.includes(REACTION_TYPES.DISLIKE)}
|
||||||
onClick={() => react(commentId, REACTION_TYPES.DISLIKE)}
|
onClick={() => react(commentId, REACTION_TYPES.DISLIKE)}
|
||||||
label={getCountForReact(REACTION_TYPES.DISLIKE)}
|
label={getCountForReact(REACTION_TYPES.DISLIKE)}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -3,7 +3,11 @@ import * as ACTIONS from 'constants/action_types';
|
||||||
import * as REACTION_TYPES from 'constants/reactions';
|
import * as REACTION_TYPES from 'constants/reactions';
|
||||||
import { Lbry, selectClaimsByUri, selectMyChannelClaims } from 'lbry-redux';
|
import { Lbry, selectClaimsByUri, selectMyChannelClaims } from 'lbry-redux';
|
||||||
import { doToast } from 'redux/actions/notifications';
|
import { doToast } from 'redux/actions/notifications';
|
||||||
import { makeSelectCommentIdsForUri, makeSelectMyReactionsForComment } from 'redux/selectors/comments';
|
import {
|
||||||
|
makeSelectCommentIdsForUri,
|
||||||
|
makeSelectMyReactionsForComment,
|
||||||
|
makeSelectOthersReactionsForComment,
|
||||||
|
} from 'redux/selectors/comments';
|
||||||
|
|
||||||
export function doCommentList(uri: string, page: number = 1, pageSize: number = 99999) {
|
export function doCommentList(uri: string, page: number = 1, pageSize: number = 99999) {
|
||||||
return (dispatch: Dispatch, getState: GetState) => {
|
return (dispatch: Dispatch, getState: GetState) => {
|
||||||
|
@ -45,7 +49,6 @@ export function doCommentReactList(uri: string | null, commentId?: string) {
|
||||||
return (dispatch: Dispatch, getState: GetState) => {
|
return (dispatch: Dispatch, getState: GetState) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const channel = localStorage.getItem('comment-channel');
|
const channel = localStorage.getItem('comment-channel');
|
||||||
// if not channel, fail?
|
|
||||||
if (!channel) {
|
if (!channel) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ACTIONS.COMMENT_REACTION_LIST_FAILED,
|
type: ACTIONS.COMMENT_REACTION_LIST_FAILED,
|
||||||
|
@ -88,7 +91,6 @@ export function doCommentReact(commentId: string, type: string) {
|
||||||
return (dispatch: Dispatch, getState: GetState) => {
|
return (dispatch: Dispatch, getState: GetState) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const channel = localStorage.getItem('comment-channel');
|
const channel = localStorage.getItem('comment-channel');
|
||||||
// if not channel, fail?
|
|
||||||
if (!channel) {
|
if (!channel) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ACTIONS.COMMENT_REACTION_LIST_FAILED,
|
type: ACTIONS.COMMENT_REACTION_LIST_FAILED,
|
||||||
|
@ -97,7 +99,9 @@ export function doCommentReact(commentId: string, type: string) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const myChannels = selectMyChannelClaims(state);
|
const myChannels = selectMyChannelClaims(state);
|
||||||
const myReacts = makeSelectMyReactionsForComment(commentId)(state);
|
let myReacts = makeSelectMyReactionsForComment(commentId)(state);
|
||||||
|
let reactingTypes = [];
|
||||||
|
const othersReacts = makeSelectOthersReactionsForComment(commentId)(state);
|
||||||
const claimForChannelName = myChannels.find(chan => chan.name === channel);
|
const claimForChannelName = myChannels.find(chan => chan.name === channel);
|
||||||
const channelId = claimForChannelName && claimForChannelName.claim_id;
|
const channelId = claimForChannelName && claimForChannelName.claim_id;
|
||||||
const exclusiveTypes = {
|
const exclusiveTypes = {
|
||||||
|
@ -105,33 +109,55 @@ export function doCommentReact(commentId: string, type: string) {
|
||||||
[REACTION_TYPES.DISLIKE]: REACTION_TYPES.LIKE,
|
[REACTION_TYPES.DISLIKE]: REACTION_TYPES.LIKE,
|
||||||
};
|
};
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.COMMENT_REACT_STARTED,
|
|
||||||
});
|
|
||||||
const params: CommentReactParams = {
|
const params: CommentReactParams = {
|
||||||
comment_ids: commentId,
|
comment_ids: commentId,
|
||||||
channel_name: channel,
|
channel_name: channel,
|
||||||
channel_id: channelId,
|
channel_id: channelId,
|
||||||
react_type: type,
|
react_type: type,
|
||||||
};
|
};
|
||||||
if (Object.keys(exclusiveTypes).includes(type)) {
|
|
||||||
params['clear_types'] = exclusiveTypes[type];
|
|
||||||
}
|
|
||||||
if (myReacts.includes(type)) {
|
if (myReacts.includes(type)) {
|
||||||
params['remove'] = true;
|
params['remove'] = true;
|
||||||
|
myReacts.splice(myReacts.indexOf(type), 1);
|
||||||
|
reactingTypes.push(type);
|
||||||
|
} else {
|
||||||
|
myReacts.push(type);
|
||||||
|
reactingTypes.push(type);
|
||||||
|
if (Object.keys(exclusiveTypes).includes(type)) {
|
||||||
|
params['clear_types'] = exclusiveTypes[type];
|
||||||
|
reactingTypes.push(exclusiveTypes[type]);
|
||||||
|
if (myReacts.indexOf(exclusiveTypes[type]) !== -1) {
|
||||||
|
myReacts.splice(myReacts.indexOf(exclusiveTypes[type]), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.COMMENT_REACT_STARTED,
|
||||||
|
data: reactingTypes,
|
||||||
|
});
|
||||||
|
// simulate api return shape: ['like'] -> { 'like': 1 }
|
||||||
|
const myReactsObj = myReacts.reduce((acc, el) => {
|
||||||
|
acc[el] = 1;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
Lbry.comment_react(params)
|
Lbry.comment_react(params)
|
||||||
.then((result: CommentReactListResponse) => {
|
.then((result: CommentReactListResponse) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ACTIONS.COMMENT_REACT_COMPLETED,
|
type: ACTIONS.COMMENT_REACT_COMPLETED,
|
||||||
|
data: reactingTypes,
|
||||||
|
});
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.COMMENT_REACTION_LIST_COMPLETED,
|
||||||
|
data: {
|
||||||
|
myReactions: { [commentId]: myReactsObj },
|
||||||
|
othersReactions: { [commentId]: othersReacts },
|
||||||
|
},
|
||||||
});
|
});
|
||||||
dispatch(doCommentReactList(null, commentId));
|
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ACTIONS.COMMENT_REACT_FAILED,
|
type: ACTIONS.COMMENT_REACT_FAILED,
|
||||||
data: error,
|
data: reactingTypes,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,6 +12,7 @@ const defaultState: CommentsState = {
|
||||||
isCommenting: false,
|
isCommenting: false,
|
||||||
myComments: undefined,
|
myComments: undefined,
|
||||||
isFetchingReacts: false,
|
isFetchingReacts: false,
|
||||||
|
typesReacting: [],
|
||||||
myReactsByCommentId: {},
|
myReactsByCommentId: {},
|
||||||
othersReactsByCommentId: {},
|
othersReactsByCommentId: {},
|
||||||
};
|
};
|
||||||
|
@ -81,23 +82,56 @@ export default handleActions(
|
||||||
isFetchingReacts: false,
|
isFetchingReacts: false,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
[ACTIONS.COMMENT_REACT_FAILED]: (state: CommentsState, action: any): CommentsState => {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
typesReacting: [],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
[ACTIONS.COMMENT_REACT_STARTED]: (state: CommentsState, action: any): CommentsState => {
|
||||||
|
const reactingTypes = action.data;
|
||||||
|
const newReactingTypes = new Set(state.typesReacting);
|
||||||
|
reactingTypes.forEach(type => {
|
||||||
|
newReactingTypes.add(type);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
typesReacting: Array.from(newReactingTypes),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
[ACTIONS.COMMENT_REACT_COMPLETED]: (state: CommentsState, action: any): CommentsState => {
|
||||||
|
const reactingTypes = action.data;
|
||||||
|
const newReactingTypes = new Set(state.typesReacting);
|
||||||
|
reactingTypes.forEach(type => {
|
||||||
|
newReactingTypes.delete(type);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
typesReacting: Array.from(newReactingTypes),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
[ACTIONS.COMMENT_REACTION_LIST_COMPLETED]: (state: CommentsState, action: any): CommentsState => {
|
[ACTIONS.COMMENT_REACTION_LIST_COMPLETED]: (state: CommentsState, action: any): CommentsState => {
|
||||||
const { myReactions, othersReactions } = action.data;
|
const { myReactions, othersReactions } = action.data;
|
||||||
const myReacts = Object.assign({}, state.myReactsByCommentId);
|
const myReacts = Object.assign({}, state.myReactsByCommentId);
|
||||||
const othersReacts = Object.assign({}, state.othersReactsByCommentId);
|
const othersReacts = Object.assign({}, state.othersReactsByCommentId);
|
||||||
if (myReactions) {
|
if (myReactions) {
|
||||||
Object.entries(myReactions).forEach(e => {
|
Object.entries(myReactions).forEach(([commentId, reactions]) => {
|
||||||
myReacts[e[0]] = Object.entries(e[1]).reduce((acc, el) => {
|
myReacts[commentId] = Object.entries(reactions).reduce((acc, [name, count]) => {
|
||||||
if (el[1] === 1) {
|
if (count === 1) {
|
||||||
acc.push(el[0]);
|
acc.push(name);
|
||||||
}
|
}
|
||||||
return acc;
|
return acc;
|
||||||
}, []);
|
}, []);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (othersReactions) {
|
if (othersReactions) {
|
||||||
Object.entries(othersReactions).forEach(e => {
|
Object.entries(othersReactions).forEach(([commentId, reactions]) => {
|
||||||
othersReacts[e[0]] = e[1];
|
othersReacts[commentId] = reactions;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -104,6 +104,8 @@ export const makeSelectOthersReactionsForComment = (commentId: string) =>
|
||||||
return state.othersReactsByCommentId[commentId];
|
return state.othersReactsByCommentId[commentId];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const selectTypesReacting = createSelector(selectState, state => state.typesReacting);
|
||||||
|
|
||||||
export const makeSelectCommentsForUri = (uri: string) =>
|
export const makeSelectCommentsForUri = (uri: string) =>
|
||||||
createSelector(
|
createSelector(
|
||||||
selectCommentsByClaimId,
|
selectCommentsByClaimId,
|
||||||
|
|
Loading…
Add table
Reference in a new issue