allow reaction list without channel
uncomment reacts requireauth, commentReact handles missing channels enable config, better track pending reacts
This commit is contained in:
parent
5f9fda0e7c
commit
e954bce821
10 changed files with 62 additions and 55 deletions
|
@ -29,13 +29,13 @@ const config = {
|
|||
DEFAULT_LANGUAGE: process.env.DEFAULT_LANGUAGE,
|
||||
AUTO_FOLLOW_CHANNELS: process.env.AUTO_FOLLOW_CHANNELS,
|
||||
UNSYNCED_SETTINGS: process.env.UNSYNCED_SETTINGS,
|
||||
ENABLE_COMMENT_REACTIONS: process.env.ENABLE_COMMENT_REACTIONS === 'true',
|
||||
SIMPLE_SITE: process.env.SIMPLE_SITE === 'true',
|
||||
SHOW_ADS: process.env.SHOW_ADS === 'true',
|
||||
PINNED_URI_1: process.env.PINNED_URI_1,
|
||||
PINNED_LABEL_1: process.env.PINNED_LABEL_1,
|
||||
PINNED_URI_2: process.env.PINNED_URI_2,
|
||||
PINNED_LABEL_2: process.env.PINNED_LABEL_2,
|
||||
ENABLE_COMMENT_REACTIONS: process.env.ENABLE_COMMENT_REACTIONS === 'true',
|
||||
};
|
||||
|
||||
config.URL_LOCAL = `http://localhost:${config.WEB_SERVER_PORT}`;
|
||||
|
|
2
flow-typed/Comment.js
vendored
2
flow-typed/Comment.js
vendored
|
@ -25,7 +25,7 @@ declare type CommentsState = {
|
|||
isFetchingReacts: boolean,
|
||||
myReactsByCommentId: any,
|
||||
othersReactsByCommentId: any,
|
||||
typesReacting: Array<string>,
|
||||
pendingCommentReactions: Array<string>,
|
||||
};
|
||||
|
||||
declare type CommentReactParams = {
|
||||
|
|
|
@ -1303,5 +1303,9 @@
|
|||
"Create a channel": "Create a channel",
|
||||
"Credit Details": "Credit Details",
|
||||
"LBRY Credits": "LBRY Credits",
|
||||
"Sign In": "Sign In",
|
||||
"Change to tile layout": "Change to tile layout",
|
||||
"%total_comments% comments": "%total_comments% comments",
|
||||
"Sync my YouTube channel": "Sync my YouTube channel",
|
||||
"--end--": "--end--"
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import * as ICONS from 'constants/icons';
|
||||
import * as PAGES from 'constants/pages';
|
||||
import { FF_MAX_CHARS_IN_COMMENT } from 'constants/form-field';
|
||||
import { SITE_NAME, SIMPLE_SITE } from 'config';
|
||||
import { SITE_NAME, SIMPLE_SITE, ENABLE_COMMENT_REACTIONS } from 'config';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { isEmpty } from 'util/object';
|
||||
import DateTime from 'component/dateTime';
|
||||
|
@ -255,7 +255,7 @@ function Comment(props: Props) {
|
|||
icon={ICONS.REPLY}
|
||||
/>
|
||||
)}
|
||||
<CommentReactions commentId={commentId} />
|
||||
{ENABLE_COMMENT_REACTIONS && <CommentReactions commentId={commentId} />}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
|
|
@ -3,14 +3,14 @@ import Comment from './view';
|
|||
import {
|
||||
makeSelectMyReactionsForComment,
|
||||
makeSelectOthersReactionsForComment,
|
||||
selectTypesReacting,
|
||||
selectPendingCommentReacts,
|
||||
} from 'redux/selectors/comments';
|
||||
import { doCommentReact } from 'redux/actions/comments';
|
||||
|
||||
const select = (state, props) => ({
|
||||
myReacts: makeSelectMyReactionsForComment(props.commentId)(state),
|
||||
othersReacts: makeSelectOthersReactionsForComment(props.commentId)(state),
|
||||
typesReacting: selectTypesReacting(state),
|
||||
pendingCommentReacts: selectPendingCommentReacts(state),
|
||||
});
|
||||
|
||||
const perform = dispatch => ({
|
||||
|
|
|
@ -12,10 +12,11 @@ type Props = {
|
|||
othersReacts: any,
|
||||
react: (string, string) => void,
|
||||
commentId: string,
|
||||
pendingCommentReacts: Array<string>,
|
||||
};
|
||||
|
||||
export default function CommentReactions(props: Props) {
|
||||
const { myReacts, othersReacts, commentId, react } = props;
|
||||
const { myReacts, othersReacts, commentId, react, pendingCommentReacts } = props;
|
||||
const [activeChannel] = usePersistedState('comment-channel');
|
||||
|
||||
const getCountForReact = type => {
|
||||
|
@ -29,29 +30,29 @@ export default function CommentReactions(props: Props) {
|
|||
return count;
|
||||
};
|
||||
|
||||
if (!ENABLE_COMMENT_REACTIONS) {
|
||||
return null;
|
||||
}
|
||||
// return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
requiresAuth={IS_WEB}
|
||||
title={__('Upvote')}
|
||||
icon={ICONS.UPVOTE}
|
||||
className={classnames('comment__action', {
|
||||
'comment__action--active': myReacts && myReacts.includes(REACTION_TYPES.LIKE),
|
||||
})}
|
||||
disabled={!activeChannel}
|
||||
disabled={!activeChannel || pendingCommentReacts.includes(commentId + REACTION_TYPES.LIKE)}
|
||||
onClick={() => react(commentId, REACTION_TYPES.LIKE)}
|
||||
label={getCountForReact(REACTION_TYPES.LIKE)}
|
||||
/>
|
||||
<Button
|
||||
requiresAuth={IS_WEB}
|
||||
title={__('Downvote')}
|
||||
icon={ICONS.DOWNVOTE}
|
||||
className={classnames('comment__action', {
|
||||
'comment__action--active': myReacts && myReacts.includes(REACTION_TYPES.DISLIKE),
|
||||
})}
|
||||
disabled={!activeChannel}
|
||||
disabled={!activeChannel || pendingCommentReacts.includes(commentId + REACTION_TYPES.DISLIKE)}
|
||||
onClick={() => react(commentId, REACTION_TYPES.DISLIKE)}
|
||||
label={getCountForReact(REACTION_TYPES.DISLIKE)}
|
||||
/>
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
// @flow
|
||||
import { ENABLE_COMMENT_REACTIONS } from 'config';
|
||||
import * as ICONS from 'constants/icons';
|
||||
import React, { useEffect } from 'react';
|
||||
import Comment from 'component/comment';
|
||||
|
@ -8,6 +7,7 @@ import Button from 'component/button';
|
|||
import Card from 'component/common/card';
|
||||
import CommentCreate from 'component/commentCreate';
|
||||
import usePersistedState from 'effects/use-persisted-state';
|
||||
import { ENABLE_COMMENT_REACTIONS } from 'config';
|
||||
|
||||
type Props = {
|
||||
comments: Array<any>,
|
||||
|
@ -33,6 +33,7 @@ function CommentList(props: Props) {
|
|||
linkedComment,
|
||||
totalComments,
|
||||
} = props;
|
||||
|
||||
const commentRef = React.useRef();
|
||||
const [activeChannel] = usePersistedState('comment-channel', '');
|
||||
const [start] = React.useState(0);
|
||||
|
@ -67,7 +68,7 @@ function CommentList(props: Props) {
|
|||
if (totalComments && ENABLE_COMMENT_REACTIONS) {
|
||||
fetchReacts(uri);
|
||||
}
|
||||
}, [fetchReacts, uri, totalComments, activeChannel]);
|
||||
}, [fetchReacts, uri, totalComments, activeChannel, ENABLE_COMMENT_REACTIONS]);
|
||||
|
||||
useEffect(() => {
|
||||
if (linkedCommentId && commentRef && commentRef.current) {
|
||||
|
|
|
@ -7,6 +7,7 @@ import {
|
|||
makeSelectCommentIdsForUri,
|
||||
makeSelectMyReactionsForComment,
|
||||
makeSelectOthersReactionsForComment,
|
||||
selectPendingCommentReacts,
|
||||
} from 'redux/selectors/comments';
|
||||
|
||||
export function doCommentList(uri: string, page: number = 1, pageSize: number = 99999) {
|
||||
|
@ -49,31 +50,29 @@ export function doCommentReactList(uri: string | null, commentId?: string) {
|
|||
return (dispatch: Dispatch, getState: GetState) => {
|
||||
const state = getState();
|
||||
const channel = localStorage.getItem('comment-channel');
|
||||
if (!channel) {
|
||||
dispatch({
|
||||
type: ACTIONS.COMMENT_REACTION_LIST_FAILED,
|
||||
data: 'No active channel found',
|
||||
});
|
||||
return;
|
||||
}
|
||||
const commentIds = uri ? makeSelectCommentIdsForUri(uri)(state) : [commentId];
|
||||
const myChannels = selectMyChannelClaims(state);
|
||||
const claimForChannelName = myChannels.find(chan => chan.name === channel);
|
||||
const channelId = claimForChannelName && claimForChannelName.claim_id;
|
||||
|
||||
dispatch({
|
||||
type: ACTIONS.COMMENT_REACTION_LIST_STARTED,
|
||||
});
|
||||
Lbry.comment_react_list({
|
||||
const params: { comment_ids: string, channel_name?: string, channel_id?: string } = {
|
||||
comment_ids: commentIds.join(','),
|
||||
channel_name: channel,
|
||||
channel_id: channelId,
|
||||
})
|
||||
};
|
||||
|
||||
if (channel && myChannels) {
|
||||
const claimForChannelName = myChannels && myChannels.find(chan => chan.name === channel);
|
||||
const channelId = claimForChannelName && claimForChannelName.claim_id;
|
||||
params['channel_name'] = channel;
|
||||
params['channel_id'] = channelId;
|
||||
}
|
||||
Lbry.comment_react_list(params)
|
||||
.then((result: CommentReactListResponse) => {
|
||||
const { my_reactions: myReactions, others_reactions: othersReactions } = result;
|
||||
dispatch({
|
||||
type: ACTIONS.COMMENT_REACTION_LIST_COMPLETED,
|
||||
data: {
|
||||
myReactions,
|
||||
myReactions: myReactions || {},
|
||||
othersReactions,
|
||||
},
|
||||
});
|
||||
|
@ -91,23 +90,27 @@ export function doCommentReact(commentId: string, type: string) {
|
|||
return (dispatch: Dispatch, getState: GetState) => {
|
||||
const state = getState();
|
||||
const channel = localStorage.getItem('comment-channel');
|
||||
if (!channel) {
|
||||
const pendingReacts = selectPendingCommentReacts(state);
|
||||
const myChannels = selectMyChannelClaims(state);
|
||||
const exclusiveTypes = {
|
||||
[REACTION_TYPES.LIKE]: REACTION_TYPES.DISLIKE,
|
||||
[REACTION_TYPES.DISLIKE]: REACTION_TYPES.LIKE,
|
||||
};
|
||||
if (!channel || !myChannels) {
|
||||
dispatch({
|
||||
type: ACTIONS.COMMENT_REACTION_LIST_FAILED,
|
||||
data: 'No active channel found',
|
||||
});
|
||||
return;
|
||||
}
|
||||
const myChannels = selectMyChannelClaims(state);
|
||||
if (pendingReacts.includes(commentId + exclusiveTypes[type])) {
|
||||
// ignore dislikes during likes, for example
|
||||
return;
|
||||
}
|
||||
let myReacts = makeSelectMyReactionsForComment(commentId)(state);
|
||||
let reactingTypes = [];
|
||||
const othersReacts = makeSelectOthersReactionsForComment(commentId)(state);
|
||||
const claimForChannelName = myChannels.find(chan => chan.name === channel);
|
||||
const channelId = claimForChannelName && claimForChannelName.claim_id;
|
||||
const exclusiveTypes = {
|
||||
[REACTION_TYPES.LIKE]: REACTION_TYPES.DISLIKE,
|
||||
[REACTION_TYPES.DISLIKE]: REACTION_TYPES.LIKE,
|
||||
};
|
||||
|
||||
const params: CommentReactParams = {
|
||||
comment_ids: commentId,
|
||||
|
@ -118,13 +121,10 @@ export function doCommentReact(commentId: string, type: string) {
|
|||
if (myReacts.includes(type)) {
|
||||
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);
|
||||
}
|
||||
|
@ -132,7 +132,7 @@ export function doCommentReact(commentId: string, type: string) {
|
|||
}
|
||||
dispatch({
|
||||
type: ACTIONS.COMMENT_REACT_STARTED,
|
||||
data: reactingTypes,
|
||||
data: commentId + type,
|
||||
});
|
||||
// simulate api return shape: ['like'] -> { 'like': 1 }
|
||||
const myReactsObj = myReacts.reduce((acc, el) => {
|
||||
|
@ -144,7 +144,7 @@ export function doCommentReact(commentId: string, type: string) {
|
|||
.then((result: CommentReactListResponse) => {
|
||||
dispatch({
|
||||
type: ACTIONS.COMMENT_REACT_COMPLETED,
|
||||
data: reactingTypes,
|
||||
data: commentId + type,
|
||||
});
|
||||
dispatch({
|
||||
type: ACTIONS.COMMENT_REACTION_LIST_COMPLETED,
|
||||
|
@ -157,7 +157,7 @@ export function doCommentReact(commentId: string, type: string) {
|
|||
.catch(error => {
|
||||
dispatch({
|
||||
type: ACTIONS.COMMENT_REACT_FAILED,
|
||||
data: reactingTypes,
|
||||
data: commentId + type,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
|
@ -12,6 +12,7 @@ const defaultState: CommentsState = {
|
|||
isCommenting: false,
|
||||
myComments: undefined,
|
||||
isFetchingReacts: false,
|
||||
pendingCommentReactions: [],
|
||||
typesReacting: [],
|
||||
myReactsByCommentId: {},
|
||||
othersReactsByCommentId: {},
|
||||
|
@ -83,35 +84,35 @@ export default handleActions(
|
|||
}),
|
||||
|
||||
[ACTIONS.COMMENT_REACT_FAILED]: (state: CommentsState, action: any): CommentsState => {
|
||||
const commentReaction = action.data; // String: reactionHash + type
|
||||
const newReactingTypes = new Set(state.pendingCommentReactions);
|
||||
newReactingTypes.delete(commentReaction);
|
||||
|
||||
return {
|
||||
...state,
|
||||
typesReacting: [],
|
||||
pendingCommentReactions: Array.from(newReactingTypes),
|
||||
};
|
||||
},
|
||||
|
||||
[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);
|
||||
});
|
||||
const commentReaction = action.data;
|
||||
const newReactingTypes = new Set(state.pendingCommentReactions);
|
||||
newReactingTypes.add(commentReaction);
|
||||
|
||||
return {
|
||||
...state,
|
||||
typesReacting: Array.from(newReactingTypes),
|
||||
pendingCommentReactions: 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);
|
||||
});
|
||||
const commentReaction = action.data; // String: reactionHash + type
|
||||
const newReactingTypes = new Set(state.pendingCommentReactions);
|
||||
newReactingTypes.delete(commentReaction);
|
||||
|
||||
return {
|
||||
...state,
|
||||
typesReacting: Array.from(newReactingTypes),
|
||||
pendingCommentReactions: Array.from(newReactingTypes),
|
||||
};
|
||||
},
|
||||
|
||||
|
|
|
@ -104,7 +104,7 @@ export const makeSelectOthersReactionsForComment = (commentId: string) =>
|
|||
return state.othersReactsByCommentId[commentId];
|
||||
});
|
||||
|
||||
export const selectTypesReacting = createSelector(selectState, state => state.typesReacting);
|
||||
export const selectPendingCommentReacts = createSelector(selectState, state => state.pendingCommentReactions);
|
||||
|
||||
export const makeSelectCommentsForUri = (uri: string) =>
|
||||
createSelector(
|
||||
|
|
Loading…
Add table
Reference in a new issue