diff --git a/src/components/widgets/Comments/Comment/Comment.js b/src/components/widgets/Comments/Comment/Comment.js index 9d7245a02..1a2e08f33 100644 --- a/src/components/widgets/Comments/Comment/Comment.js +++ b/src/components/widgets/Comments/Comment/Comment.js @@ -23,8 +23,8 @@ const Comment = ({ isPrivate = false, text, repost, - isToggling = false, - togglePrivacy, + isUpdating = false, + setPrivacy, deleteComment, additionalPublicSwitchNote = null, links: { USER_URI_FACTORY }, @@ -66,7 +66,7 @@ const Comment = ({ onClick={() => deleteComment(id)} /> )} - {isFromCurrentUser && togglePrivacy && ( + {isFromCurrentUser && setPrivacy && ( }> togglePrivacy(id)} + icon={isUpdating ? 'circle-notch' : isPrivate ? 'eye-slash' : 'eye'} + onClick={() => setPrivacy(id, !isPrivate)} className={classnames({ 'float-right': true, [styles.iconButton]: true, [styles.iconButtonLock]: !isPrivate, [styles.iconButtonUnlock]: isPrivate, })} - spin={isToggling} + spin={isUpdating} /> )} @@ -126,8 +126,8 @@ Comment.propTypes = { text: PropTypes.string.isRequired, repost: PropTypes.func, isPrivate: PropTypes.bool, - isToggling: PropTypes.bool, - togglePrivacy: PropTypes.func, + isUpdating: PropTypes.bool, + setPrivacy: PropTypes.func, deleteComment: PropTypes.func, additionalPublicSwitchNote: PropTypes.oneOfType([PropTypes.string, PropTypes.element]), links: PropTypes.object, diff --git a/src/components/widgets/Comments/CommentThread/CommentThread.js b/src/components/widgets/Comments/CommentThread/CommentThread.js index 86d207a3a..87c70fe7f 100644 --- a/src/components/widgets/Comments/CommentThread/CommentThread.js +++ b/src/components/widgets/Comments/CommentThread/CommentThread.js @@ -14,7 +14,7 @@ const CommentThread = ({ currentUserId, addComment, repostComment, - togglePrivacy, + setPrivacy, refresh, deleteComment, inModal = false, @@ -33,7 +33,7 @@ const CommentThread = ({ {...comment} key={comment.id} repost={repostComment} - togglePrivacy={togglePrivacy} + setPrivacy={setPrivacy} deleteComment={deleteComment} additionalPublicSwitchNote={additionalPublicSwitchNote} /> @@ -73,7 +73,7 @@ CommentThread.propTypes = { currentUserId: PropTypes.string.isRequired, addComment: PropTypes.func, repostComment: PropTypes.func, - togglePrivacy: PropTypes.func, + setPrivacy: PropTypes.func, refresh: PropTypes.func, deleteComment: PropTypes.func, inModal: PropTypes.bool, diff --git a/src/containers/CommentThreadContainer/CommentThreadContainer.js b/src/containers/CommentThreadContainer/CommentThreadContainer.js index f917fdbdf..dc6bc49b1 100644 --- a/src/containers/CommentThreadContainer/CommentThreadContainer.js +++ b/src/containers/CommentThreadContainer/CommentThreadContainer.js @@ -6,7 +6,7 @@ import { FormattedMessage } from 'react-intl'; import { postComment, repostComment, - togglePrivacy, + setPrivacy, fetchThreadIfNeeded, updateThread, deleteComment, @@ -43,7 +43,7 @@ class CommentThreadContainer extends Component { user, addComment, repostComment, - togglePrivacy, + setPrivacy, refresh, deleteComment, inModal = false, @@ -58,7 +58,7 @@ class CommentThreadContainer extends Component { comments={thread.comments.sort((a, b) => a.postedAt - b.postedAt)} currentUserId={user.id} addComment={(text, isPrivate) => addComment(user, text, isPrivate)} - togglePrivacy={togglePrivacy} + setPrivacy={setPrivacy} repostComment={repostComment} refresh={refresh} deleteComment={deleteComment} @@ -79,7 +79,7 @@ CommentThreadContainer.propTypes = { user: PropTypes.object, addComment: PropTypes.func.isRequired, repostComment: PropTypes.func, - togglePrivacy: PropTypes.func, + setPrivacy: PropTypes.func, refresh: PropTypes.func, deleteComment: PropTypes.func, }; @@ -92,7 +92,7 @@ export default connect( (dispatch, { threadId }) => ({ addComment: (user, text, isPrivate) => dispatch(postComment(user, threadId, text, isPrivate)), repostComment: tmpId => dispatch(repostComment(threadId, tmpId)), - togglePrivacy: id => dispatch(togglePrivacy(threadId, id)), + setPrivacy: (id, isPrivate) => dispatch(setPrivacy(threadId, id, isPrivate)), loadThreadIfNeeded: () => dispatch(fetchThreadIfNeeded(threadId)), refresh: () => dispatch(updateThread(threadId)), deleteComment: id => dispatch(deleteComment(threadId, id)), diff --git a/src/redux/modules/comments.js b/src/redux/modules/comments.js index 5e53d7b00..b4e276640 100644 --- a/src/redux/modules/comments.js +++ b/src/redux/modules/comments.js @@ -2,11 +2,15 @@ import { handleActions } from 'redux-actions'; import { fromJS } from 'immutable'; import { createApiAction } from '../middleware/apiMiddleware'; -import factory, { initialState } from '../helpers/resourceManager'; +import factory, { initialState, createActionsWithPostfixes } from '../helpers/resourceManager'; import { commentsSelector } from '../selectors/comments'; const resourceName = 'comments'; -const { actions, actionTypes: resourceActionTypes, reduceActions } = factory({ +const { + actions, + actionTypes: resourceActionTypes, + reduceActions, +} = factory({ resourceName, needsRefetching: () => true, // always look for newer comments }); @@ -18,18 +22,10 @@ const { actions, actionTypes: resourceActionTypes, reduceActions } = factory({ export const actionTypes = { UPDATE: 'redux/comments/UPDATE', UPDATE_FULFILLED: 'redux/comments/UPDATE_FULFILLED', - POST_COMMENT: 'redux/comments/POST_COMMENT', - POST_COMMENT_PENDING: 'redux/comments/POST_COMMENT_PENDING', - POST_COMMENT_REJECTED: 'redux/comments/POST_COMMENT_REJECTED', - POST_COMMENT_FULFILLED: 'redux/comments/POST_COMMENT_FULFILLED', - TOGGLE_PRIVACY: 'redux/comment/TOGGLE_PRIVACY', - TOGGLE_PRIVACY_PENDING: 'redux/comment/TOGGLE_PRIVACY_PENDING', - TOGGLE_PRIVACY_FULFILLED: 'redux/comment/TOGGLE_PRIVACY_FULFILLED', - TOGGLE_PRIVACY_REJECTED: 'redux/comment/TOGGLE_PRIVACY_REJECTED', - DELETE_COMMENT: 'redux/comment/DELETE_COMMENT', - DELETE_COMMENT_PENDING: 'redux/comment/DELETE_COMMENT_PENDING', - DELETE_COMMENT_FULFILLED: 'redux/comment/DELETE_COMMENT_FULFILLED', - DELETE_COMMENT_REJECTED: 'redux/comment/DELETE_COMMENT_REJECTED', + ...createActionsWithPostfixes('POST_COMMENT'), + ...createActionsWithPostfixes('TOGGLE_PRIVACY'), + ...createActionsWithPostfixes('SET_PRIVACY'), + ...createActionsWithPostfixes('DELETE_COMMENT'), }; export const fetchThreadIfNeeded = actions.fetchOneIfNeeded; @@ -46,7 +42,7 @@ export const postComment = (user, threadId, text, isPrivate) => type: actionTypes.POST_COMMENT, endpoint: `/comments/${threadId}`, method: 'POST', - body: { text, isPrivate: isPrivate ? 'yes' : 'no' }, + body: { text, isPrivate }, meta: { threadId, user, tmpId: Math.random().toString() }, }); @@ -61,6 +57,7 @@ export const repostComment = (threadId, tmpId) => (dispatch, getState) => { } }; +// DEPRECATED export const togglePrivacy = (threadId, commentId) => createApiAction({ type: actionTypes.TOGGLE_PRIVACY, @@ -69,6 +66,15 @@ export const togglePrivacy = (threadId, commentId) => meta: { threadId, commentId }, }); +export const setPrivacy = (threadId, commentId, isPrivate) => + createApiAction({ + type: actionTypes.SET_PRIVACY, + endpoint: `/comments/${threadId}/comment/${commentId}/private`, + method: 'POST', + meta: { threadId, commentId }, + body: { isPrivate }, + }); + export const deleteComment = (threadId, commentId) => createApiAction({ type: actionTypes.DELETE_COMMENT, @@ -94,7 +100,7 @@ const reducer = handleActions( status: 'pending', text, user: correctUserData, - isPrivate: isPrivate === 'yes', + isPrivate, }); return state.updateIn(['resources', threadId, 'data', 'comments'], comments => comments.push(resource)); }, @@ -129,6 +135,24 @@ const reducer = handleActions( ); }, + [actionTypes.SET_PRIVACY_PENDING]: (state, { meta: { threadId, commentId } }) => { + return state.updateIn(['resources', threadId, 'data', 'comments'], comments => + comments.map(comment => (comment.get('id') === commentId ? comment.set('isUpdating', true) : comment)) + ); + }, + + [actionTypes.SET_PRIVACY_FULFILLED]: (state, { payload, meta: { threadId, commentId } }) => { + return state.updateIn(['resources', threadId, 'data', 'comments'], comments => + comments.map(comment => (comment.get('id') === commentId ? fromJS(payload) : comment)) + ); + }, + + [actionTypes.SET_PRIVACY_REJECTED]: (state, { meta: { threadId, commentId } }) => { + return state.updateIn(['resources', threadId, 'data', 'comments'], comments => + comments.map(comment => (comment.get('id') === commentId ? comment.set('isUpdating', false) : comment)) + ); + }, + [actionTypes.DELETE_COMMENT_FULFILLED]: (state, { meta: { threadId, commentId } }) => { return state.updateIn(['resources', threadId, 'data', 'comments'], comments => comments.filter(cmt => cmt.get('id') !== commentId)