From d45c160b10f9e6f9e56fe724105e7381fdb5375f Mon Sep 17 00:00:00 2001 From: Oleg Silkin Date: Tue, 7 Jan 2020 00:21:13 -0500 Subject: [PATCH 01/17] Adds proper comment type --- flow-typed/Comment.js | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/flow-typed/Comment.js b/flow-typed/Comment.js index f2f35b7..4619430 100644 --- a/flow-typed/Comment.js +++ b/flow-typed/Comment.js @@ -1,19 +1,22 @@ declare type Comment = { - author?: string, - author_url?: string, - claim_index?: number, - comment_id?: number, - downvotes?: number, - message: string, - omitted?: number, - reply_count?: number, - time_posted?: number, - upvotes?: number, - parent_id?: number, + comment: string, // comment body + comment_id: string, // sha256 digest + timestamp: number, // integer representing unix-time + is_hidden: boolean, // claim owner may enable/disable this + channel_id?: string, // claimId of channel signing this comment + channel_name?: string, // name of channel claim + channel_url?: string, // full lbry url to signing channel + signature?: string, // signature of comment by originating channel + signing_ts?: string, // timestamp used when signing this comment + is_channel_signature_valid?: boolean, // whether or not the signature could be validated + parent_id?: number, // comment_id of comment this is in reply to + claim_id?: string, // id linking to the claim this comment }; +// todo: relate individual comments to their commentId declare type CommentsState = { - byId: {}, - isLoading: boolean, commentsByUri: { [string]: string }, -} + byId: { [string]: Array }, + isLoading: boolean, + myComments: ?Set, +}; From 7c55a9e0f51b4efa2b01983b86d265e6282d2546 Mon Sep 17 00:00:00 2001 From: Oleg Silkin Date: Tue, 7 Jan 2020 00:22:57 -0500 Subject: [PATCH 02/17] Adds edit, hide, and abandon action types from new lbry sdk api --- dist/bundle.es.js | 9 +++++++++ src/constants/action_types.js | 9 +++++++++ src/redux/reducers/comments.js | 37 ++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/dist/bundle.es.js b/dist/bundle.es.js index 035f91c..da208a5 100644 --- a/dist/bundle.es.js +++ b/dist/bundle.es.js @@ -377,6 +377,15 @@ var action_types = /*#__PURE__*/Object.freeze({ COMMENT_CREATE_STARTED: COMMENT_CREATE_STARTED, COMMENT_CREATE_COMPLETED: COMMENT_CREATE_COMPLETED, COMMENT_CREATE_FAILED: COMMENT_CREATE_FAILED, + COMMENT_ABANDON_STARTED: COMMENT_ABANDON_STARTED, + COMMENT_ABANDON_COMPLETED: COMMENT_ABANDON_COMPLETED, + COMMENT_ABANDON_FAILED: COMMENT_ABANDON_FAILED, + COMMENT_HIDE_STARTED: COMMENT_HIDE_STARTED, + COMMENT_HIDE_COMPLETED: COMMENT_HIDE_COMPLETED, + COMMENT_HIDE_FAILED: COMMENT_HIDE_FAILED, + COMMENT_EDIT_STARTED: COMMENT_EDIT_STARTED, + COMMENT_EDIT_COMPLETED: COMMENT_EDIT_COMPLETED, + COMMENT_EDIT_FAILED: COMMENT_EDIT_FAILED, FILE_LIST_STARTED: FILE_LIST_STARTED, FILE_LIST_SUCCEEDED: FILE_LIST_SUCCEEDED, FETCH_FILE_INFO_STARTED: FETCH_FILE_INFO_STARTED, diff --git a/src/constants/action_types.js b/src/constants/action_types.js index ac23374..097b2cd 100644 --- a/src/constants/action_types.js +++ b/src/constants/action_types.js @@ -111,6 +111,15 @@ export const COMMENT_LIST_FAILED = 'COMMENT_LIST_FAILED'; export const COMMENT_CREATE_STARTED = 'COMMENT_CREATE_STARTED'; export const COMMENT_CREATE_COMPLETED = 'COMMENT_CREATE_COMPLETED'; export const COMMENT_CREATE_FAILED = 'COMMENT_CREATE_FAILED'; +export const COMMENT_ABANDON_STARTED = 'COMMENT_ABANDON_STARTED'; +export const COMMENT_ABANDON_COMPLETED = 'COMMENT_ABANDON_COMPLETED'; +export const COMMENT_ABANDON_FAILED = 'COMMENT_ABANDON_FAILED'; +export const COMMENT_EDIT_STARTED = 'COMMENT_EDIT_STARTED'; +export const COMMENT_EDIT_COMPLETED = 'COMMENT_EDIT_COMPLETED'; +export const COMMENT_EDIT_FAILED = 'COMMENT_EDIT_FAILED'; +export const COMMENT_HIDE_STARTED = 'COMMENT_HIDE_STARTED'; +export const COMMENT_HIDE_COMPLETED = 'COMMENT_HIDE_COMPLETED'; +export const COMMENT_HIDE_FAILED = 'COMMENT_HIDE_FAILED'; // Files export const FILE_LIST_STARTED = 'FILE_LIST_STARTED'; diff --git a/src/redux/reducers/comments.js b/src/redux/reducers/comments.js index 3d1738b..c3b9bd4 100644 --- a/src/redux/reducers/comments.js +++ b/src/redux/reducers/comments.js @@ -6,6 +6,7 @@ const defaultState: CommentsState = { byId: {}, commentsByUri: {}, isLoading: false, + myComments: undefined, }; export const commentReducer = handleActions( @@ -58,6 +59,42 @@ export const commentReducer = handleActions( ...state, isLoading: false, }), + [ACTIONS.COMMENT_ABANDON_STARTED]: (state: CommentsState, action: any) => ({ + ...state, + isLoading: true, + }), + [ACTIONS.COMMENT_ABANDON_COMPLETED]: (state: CommentsState, action: any) => ({ + ...state, + isLoading: false, + }), + [ACTIONS.COMMENT_ABANDON_FAILED]: (state: CommentsState, action: any) => ({ + ...state, + isLoading: false, + }), + [ACTIONS.COMMENT_EDIT_STARTED]: (state: CommentsState, action: any) => ({ + ...state, + isLoading: true, + }), + [ACTIONS.COMMENT_EDIT_COMPLETED]: (state: CommentsState, action: any) => ({ + ...state, + isLoading: false, + }), + [ACTIONS.COMMENT_EDIT_FAILED]: (state: CommentsState, action: any) => ({ + ...state, + isLoading: false, + }), + [ACTIONS.COMMENT_HIDE_STARTED]: (state: CommentsState, action: any) => ({ + ...state, + isLoading: true, + }), + [ACTIONS.COMMENT_HIDE_COMPLETED]: (state: CommentsState, action: any) => ({ + ...state, + isLoading: false, + }), + [ACTIONS.COMMENT_HIDE_FAILED]: (state: CommentsState, action: any) => ({ + ...state, + isLoading: false, + }), }, defaultState ); From 78ec8c3c16cd350068151233c3780d980bba54a7 Mon Sep 17 00:00:00 2001 From: Oleg Silkin Date: Thu, 9 Jan 2020 17:09:37 -0500 Subject: [PATCH 03/17] Adds edit, hide, and abandon action types from new lbry sdk api --- flow-typed/Lbry.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/flow-typed/Lbry.js b/flow-typed/Lbry.js index cc8a4ac..92ce9dd 100644 --- a/flow-typed/Lbry.js +++ b/flow-typed/Lbry.js @@ -125,6 +125,8 @@ declare type ChannelUpdateResponse = GenericTxResponse & { }; declare type CommentCreateResponse = Comment; +declare type CommentEditResponse = Comment; + declare type CommentListResponse = { items: Array, page: number, @@ -133,6 +135,16 @@ declare type CommentListResponse = { total_pages: number, }; +declare type CommentHideResponse = { + // keyed by the CommentIds entered + [string]: { hidden: boolean }, +}; + +declare type CommentAbandonResponse = { + // keyed by the CommentId given + [string]: { abandoned: boolean }, +}; + declare type ChannelListResponse = { items: Array, page: number, From ba9e68b1da73b067c38fa7693e569aeba45f9c1a Mon Sep 17 00:00:00 2001 From: Oleg Silkin Date: Thu, 9 Jan 2020 17:10:54 -0500 Subject: [PATCH 04/17] claim_id is now guaranteed --- flow-typed/Comment.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flow-typed/Comment.js b/flow-typed/Comment.js index 4619430..368164c 100644 --- a/flow-typed/Comment.js +++ b/flow-typed/Comment.js @@ -1,6 +1,7 @@ declare type Comment = { comment: string, // comment body comment_id: string, // sha256 digest + claim_id: string, // id linking to the claim this comment timestamp: number, // integer representing unix-time is_hidden: boolean, // claim owner may enable/disable this channel_id?: string, // claimId of channel signing this comment @@ -10,7 +11,6 @@ declare type Comment = { signing_ts?: string, // timestamp used when signing this comment is_channel_signature_valid?: boolean, // whether or not the signature could be validated parent_id?: number, // comment_id of comment this is in reply to - claim_id?: string, // id linking to the claim this comment }; // todo: relate individual comments to their commentId From 586b07a2431c75f4fb30c370f270fad8a5c79ac5 Mon Sep 17 00:00:00 2001 From: Oleg Silkin Date: Thu, 9 Jan 2020 17:11:37 -0500 Subject: [PATCH 05/17] adds edit, hide, and abandon methods to call the API with --- src/lbry.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lbry.js b/src/lbry.js index ceb96f3..9fdd033 100644 --- a/src/lbry.js +++ b/src/lbry.js @@ -119,6 +119,11 @@ const Lbry: LbryTypes = { // Comments comment_list: (params = {}) => daemonCallWithResult('comment_list', params), comment_create: (params = {}) => daemonCallWithResult('comment_create', params), + // todo: implement these in reducers + comment_hide: (params = {}) => daemonCallWithResult('comment_hide', params), + comment_abandon: (params = {}) => daemonCallWithResult('comment_abandon', params), + comment_edit: (params = {}) => daemonCallWithResult('comment_hide', params), + // Connect to the sdk connect: () => { if (Lbry.connectPromise === null) { From 28440873346a21d4a5daf2524ff70104844b3c8c Mon Sep 17 00:00:00 2001 From: Oleg Silkin Date: Thu, 9 Jan 2020 21:27:23 -0500 Subject: [PATCH 06/17] Schema change for `CommentsState` --- flow-typed/Comment.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/flow-typed/Comment.js b/flow-typed/Comment.js index 368164c..64ea974 100644 --- a/flow-typed/Comment.js +++ b/flow-typed/Comment.js @@ -16,7 +16,8 @@ declare type Comment = { // todo: relate individual comments to their commentId declare type CommentsState = { commentsByUri: { [string]: string }, - byId: { [string]: Array }, + byId: { [string]: Array }, + commentById: { [string]: Comment }, isLoading: boolean, - myComments: ?Set, + myComments: ?Set, }; From c171a38067ec9d3e806df9afb4d08e51065d307d Mon Sep 17 00:00:00 2001 From: Oleg Silkin Date: Thu, 9 Jan 2020 21:27:59 -0500 Subject: [PATCH 07/17] Adds parent_id param to `comment_create` to allow replies --- src/redux/actions/comments.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/redux/actions/comments.js b/src/redux/actions/comments.js index dbf1d94..64b5344 100644 --- a/src/redux/actions/comments.js +++ b/src/redux/actions/comments.js @@ -55,9 +55,10 @@ export function doCommentCreate( myChannels && myChannels.find(myChannel => myChannel.name === channel); const channel_id = namedChannelClaim ? namedChannelClaim.claim_id : null; return Lbry.comment_create({ - comment, - claim_id, - channel_id, + comment: comment, + claim_id: claim_id, + channel_id: channel_id, + parent_id: parent_id, }) .then((result: Comment) => { dispatch({ From b65149fe003e301505b8c6a9bc9b95428dad62a7 Mon Sep 17 00:00:00 2001 From: Oleg Silkin Date: Thu, 9 Jan 2020 21:28:35 -0500 Subject: [PATCH 08/17] Refactors old reducers to be compliant with new schema --- src/redux/reducers/comments.js | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/redux/reducers/comments.js b/src/redux/reducers/comments.js index c3b9bd4..0513625 100644 --- a/src/redux/reducers/comments.js +++ b/src/redux/reducers/comments.js @@ -3,8 +3,9 @@ import * as ACTIONS from 'constants/action_types'; import { handleActions } from 'util/redux-utils'; const defaultState: CommentsState = { - byId: {}, - commentsByUri: {}, + commentById: {}, // commentId -> Comment + byId: {}, // ClaimID -> list of comments + commentsByUri: {}, // URI -> claimId isLoading: false, myComments: undefined, }; @@ -23,16 +24,23 @@ export const commentReducer = handleActions( [ACTIONS.COMMENT_CREATE_COMPLETED]: (state: CommentsState, action: any): CommentsState => { const { comment, claimId }: any = action.data; + const commentById = Object.assign({}, state.commentById); const byId = Object.assign({}, state.byId); const comments = byId[claimId]; - const newComments = comments.slice(); + const newCommentIds = comments.slice(); - newComments.unshift(comment); - byId[claimId] = newComments; + // add the comment by its ID + commentById[comment.comment_id] = comment; + + // push the comment_id to the top of ID list + newCommentIds.unshift(comment.comment_id); + byId[claimId] = newCommentIds; return { ...state, + commentById, byId, + isLoading: false, }; }, @@ -40,16 +48,26 @@ export const commentReducer = handleActions( [ACTIONS.COMMENT_LIST_COMPLETED]: (state: CommentsState, action: any) => { const { comments, claimId, uri } = action.data; + + const commentById = Object.assign({}, state.commentById); const byId = Object.assign({}, state.byId); const commentsByUri = Object.assign({}, state.commentsByUri); if (comments) { - byId[claimId] = comments; + const commentIds = Array(comments.length); + // map the comment_ids to the new comments + for (let i = 0; i < comments.length; i++) { + commentIds[i] = comments[i].comment_id; + commentById[commentIds[i]] = comments[i]; + } + + byId[claimId] = commentIds; commentsByUri[uri] = claimId; } return { ...state, byId, + commentById, commentsByUri, isLoading: false, }; From 1038ff16c87d69b192b914773a5d07dbee8eb1e6 Mon Sep 17 00:00:00 2001 From: Oleg Silkin Date: Thu, 9 Jan 2020 21:29:16 -0500 Subject: [PATCH 09/17] Creates new selectors that are more consistent with the rest of the redux selectors for claims --- src/redux/selectors/comments.js | 37 +++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/src/redux/selectors/comments.js b/src/redux/selectors/comments.js index c391a5c..048e7f2 100644 --- a/src/redux/selectors/comments.js +++ b/src/redux/selectors/comments.js @@ -5,9 +5,37 @@ const selectState = state => state.comments || {}; export const selectCommentsById = createSelector( selectState, - state => state.byId || {} + state => state.commentById || {} ); +export const selectCommentsByClaimId = createSelector( + selectState, + selectCommentsById, + (state, byId) => { + const byClaimId = state.byId || {}; + const comments = {}; + + // for every claimId -> commentId, put comments in the object + Object.keys(byClaimId).forEach(claimId => { + // get all the commentIds that commented on this ClaimId + const commentIds = byClaimId[claimId]; + + // map a new array of comments by the claimId + comments[claimId] = Array(commentIds === null ? 0 : commentIds.length); + for (let i = 0; i < commentIds.length; i++) { + comments[claimId][i] = byId[commentIds[i]]; + } + }); + + return comments; + } +); + +// previously this used a mapping from claimId -> Array +/* export const selectCommentsById = createSelector( + selectState, + state => state.byId || {} +); */ export const selectCommentsByUri = createSelector( selectState, state => { @@ -21,16 +49,17 @@ export const selectCommentsByUri = createSelector( comments[uri] = claimId; } }); + return comments; } ); export const makeSelectCommentsForUri = (uri: string) => createSelector( - selectCommentsById, + selectCommentsByClaimId, selectCommentsByUri, - (byId, byUri) => { + (byClaimId, byUri) => { const claimId = byUri[uri]; - return byId && byId[claimId]; + return byClaimId && byClaimId[claimId]; } ); From ff9493f33494ddd9b69a7f95a04ac14919990dc8 Mon Sep 17 00:00:00 2001 From: Oleg Silkin Date: Thu, 9 Jan 2020 21:29:54 -0500 Subject: [PATCH 10/17] Reflect changes in dist code --- dist/bundle.es.js | 132 ++++++++++++++++++++++++++++++++----- dist/flow-typed/Comment.js | 32 +++++---- dist/flow-typed/Lbry.js | 12 ++++ 3 files changed, 145 insertions(+), 31 deletions(-) diff --git a/dist/bundle.es.js b/dist/bundle.es.js index da208a5..e8b420a 100644 --- a/dist/bundle.es.js +++ b/dist/bundle.es.js @@ -134,6 +134,15 @@ const COMMENT_LIST_FAILED = 'COMMENT_LIST_FAILED'; const COMMENT_CREATE_STARTED = 'COMMENT_CREATE_STARTED'; const COMMENT_CREATE_COMPLETED = 'COMMENT_CREATE_COMPLETED'; const COMMENT_CREATE_FAILED = 'COMMENT_CREATE_FAILED'; +const COMMENT_ABANDON_STARTED = 'COMMENT_ABANDON_STARTED'; +const COMMENT_ABANDON_COMPLETED = 'COMMENT_ABANDON_COMPLETED'; +const COMMENT_ABANDON_FAILED = 'COMMENT_ABANDON_FAILED'; +const COMMENT_EDIT_STARTED = 'COMMENT_EDIT_STARTED'; +const COMMENT_EDIT_COMPLETED = 'COMMENT_EDIT_COMPLETED'; +const COMMENT_EDIT_FAILED = 'COMMENT_EDIT_FAILED'; +const COMMENT_HIDE_STARTED = 'COMMENT_HIDE_STARTED'; +const COMMENT_HIDE_COMPLETED = 'COMMENT_HIDE_COMPLETED'; +const COMMENT_HIDE_FAILED = 'COMMENT_HIDE_FAILED'; // Files const FILE_LIST_STARTED = 'FILE_LIST_STARTED'; @@ -380,12 +389,12 @@ var action_types = /*#__PURE__*/Object.freeze({ COMMENT_ABANDON_STARTED: COMMENT_ABANDON_STARTED, COMMENT_ABANDON_COMPLETED: COMMENT_ABANDON_COMPLETED, COMMENT_ABANDON_FAILED: COMMENT_ABANDON_FAILED, - COMMENT_HIDE_STARTED: COMMENT_HIDE_STARTED, - COMMENT_HIDE_COMPLETED: COMMENT_HIDE_COMPLETED, - COMMENT_HIDE_FAILED: COMMENT_HIDE_FAILED, COMMENT_EDIT_STARTED: COMMENT_EDIT_STARTED, COMMENT_EDIT_COMPLETED: COMMENT_EDIT_COMPLETED, COMMENT_EDIT_FAILED: COMMENT_EDIT_FAILED, + COMMENT_HIDE_STARTED: COMMENT_HIDE_STARTED, + COMMENT_HIDE_COMPLETED: COMMENT_HIDE_COMPLETED, + COMMENT_HIDE_FAILED: COMMENT_HIDE_FAILED, FILE_LIST_STARTED: FILE_LIST_STARTED, FILE_LIST_SUCCEEDED: FILE_LIST_SUCCEEDED, FETCH_FILE_INFO_STARTED: FETCH_FILE_INFO_STARTED, @@ -925,6 +934,11 @@ const Lbry = { // Comments comment_list: (params = {}) => daemonCallWithResult('comment_list', params), comment_create: (params = {}) => daemonCallWithResult('comment_create', params), + // todo: implement these in reducers + comment_hide: (params = {}) => daemonCallWithResult('comment_hide', params), + comment_abandon: (params = {}) => daemonCallWithResult('comment_abandon', params), + comment_edit: (params = {}) => daemonCallWithResult('comment_hide', params), + // Connect to the sdk connect: () => { if (Lbry.connectPromise === null) { @@ -1232,6 +1246,18 @@ function buildURI(UrlObj, includeProto = true, protoDefault = 'lbry://') { deprecatedParts = _objectWithoutProperties(UrlObj, ['streamName', 'streamClaimId', 'channelName', 'channelClaimId', 'primaryClaimSequence', 'primaryBidPosition', 'secondaryClaimSequence', 'secondaryBidPosition']); const { claimId, claimName, contentName } = deprecatedParts; + { + if (claimId) { + console.error(__("'claimId' should no longer be used. Use 'streamClaimId' or 'channelClaimId' instead")); + } + if (claimName) { + console.error(__("'claimName' should no longer be used. Use 'streamClaimName' or 'channelClaimName' instead")); + } + if (contentName) { + console.error(__("'contentName' should no longer be used. Use 'streamName' instead")); + } + } + if (!claimName && !channelName && !streamName) { console.error(__("'claimName', 'channelName', and 'streamName' are all empty. One must be present to build a url.")); } @@ -4104,9 +4130,10 @@ function doCommentCreate(comment = '', claim_id = '', channel, parent_id) { const namedChannelClaim = myChannels && myChannels.find(myChannel => myChannel.name === channel); const channel_id = namedChannelClaim ? namedChannelClaim.claim_id : null; return lbryProxy.comment_create({ - comment, - claim_id, - channel_id + comment: comment, + claim_id: claim_id, + channel_id: channel_id, + parent_id: parent_id }).then(result => { dispatch({ type: COMMENT_CREATE_COMPLETED, @@ -4542,9 +4569,11 @@ const handleActions = (actionMap, defaultState) => (state = defaultState, action var _extends$7 = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; const defaultState$1 = { - byId: {}, - commentsByUri: {}, - isLoading: false + commentById: {}, // commentId -> Comment + byId: {}, // ClaimID -> list of comments + commentsByUri: {}, // URI -> claimId + isLoading: false, + myComments: undefined }; const commentReducer = handleActions({ @@ -4558,15 +4587,22 @@ const commentReducer = handleActions({ [COMMENT_CREATE_COMPLETED]: (state, action) => { const { comment, claimId } = action.data; + const commentById = Object.assign({}, state.commentById); const byId = Object.assign({}, state.byId); const comments = byId[claimId]; - const newComments = comments.slice(); + const newCommentIds = comments.slice(); - newComments.unshift(comment); - byId[claimId] = newComments; + // add the comment by its ID + commentById[comment.comment_id] = comment; + + // push the comment_id to the top of ID list + newCommentIds.unshift(comment.comment_id); + byId[claimId] = newCommentIds; return _extends$7({}, state, { - byId + commentById, + byId, + isLoading: false }); }, @@ -4574,15 +4610,25 @@ const commentReducer = handleActions({ [COMMENT_LIST_COMPLETED]: (state, action) => { const { comments, claimId, uri } = action.data; + + const commentById = Object.assign({}, state.commentById); const byId = Object.assign({}, state.byId); const commentsByUri = Object.assign({}, state.commentsByUri); if (comments) { - byId[claimId] = comments; + const commentIds = Array(comments.length); + // map the comment_ids to the new comments + for (let i = 0; i < comments.length; i++) { + commentIds[i] = comments[i].comment_id; + commentById[commentIds[i]] = comments[i]; + } + + byId[claimId] = commentIds; commentsByUri[uri] = claimId; } return _extends$7({}, state, { byId, + commentById, commentsByUri, isLoading: false }); @@ -4590,6 +4636,33 @@ const commentReducer = handleActions({ [COMMENT_LIST_FAILED]: (state, action) => _extends$7({}, state, { isLoading: false + }), + [COMMENT_ABANDON_STARTED]: (state, action) => _extends$7({}, state, { + isLoading: true + }), + [COMMENT_ABANDON_COMPLETED]: (state, action) => _extends$7({}, state, { + isLoading: false + }), + [COMMENT_ABANDON_FAILED]: (state, action) => _extends$7({}, state, { + isLoading: false + }), + [COMMENT_EDIT_STARTED]: (state, action) => _extends$7({}, state, { + isLoading: true + }), + [COMMENT_EDIT_COMPLETED]: (state, action) => _extends$7({}, state, { + isLoading: false + }), + [COMMENT_EDIT_FAILED]: (state, action) => _extends$7({}, state, { + isLoading: false + }), + [COMMENT_HIDE_STARTED]: (state, action) => _extends$7({}, state, { + isLoading: true + }), + [COMMENT_HIDE_COMPLETED]: (state, action) => _extends$7({}, state, { + isLoading: false + }), + [COMMENT_HIDE_FAILED]: (state, action) => _extends$7({}, state, { + isLoading: false }) }, defaultState$1); @@ -5470,8 +5543,32 @@ const selectError = reselect.createSelector(selectState$7, state => { const selectState$8 = state => state.comments || {}; -const selectCommentsById = reselect.createSelector(selectState$8, state => state.byId || {}); +const selectCommentsById = reselect.createSelector(selectState$8, state => state.commentById || {}); +const selectCommentsByClaimId = reselect.createSelector(selectState$8, selectCommentsById, (state, byId) => { + const byClaimId = state.byId || {}; + const comments = {}; + + // for every claimId -> commentId, put comments in the object + Object.keys(byClaimId).forEach(claimId => { + // get all the commentIds that commented on this ClaimId + const commentIds = byClaimId[claimId]; + + // map a new array of comments by the claimId + comments[claimId] = Array(commentIds === null ? 0 : commentIds.length); + for (let i = 0; i < commentIds.length; i++) { + comments[claimId][i] = byId[commentIds[i]]; + } + }); + + return comments; +}); + +// previously this used a mapping from claimId -> Array +/* export const selectCommentsById = createSelector( + selectState, + state => state.byId || {} +); */ const selectCommentsByUri = reselect.createSelector(selectState$8, state => { const byUri = state.commentsByUri || {}; const comments = {}; @@ -5483,12 +5580,13 @@ const selectCommentsByUri = reselect.createSelector(selectState$8, state => { comments[uri] = claimId; } }); + return comments; }); -const makeSelectCommentsForUri = uri => reselect.createSelector(selectCommentsById, selectCommentsByUri, (byId, byUri) => { +const makeSelectCommentsForUri = uri => reselect.createSelector(selectCommentsByClaimId, selectCommentsByUri, (byClaimId, byUri) => { const claimId = byUri[uri]; - return byId && byId[claimId]; + return byClaimId && byClaimId[claimId]; }); // diff --git a/dist/flow-typed/Comment.js b/dist/flow-typed/Comment.js index f2f35b7..64ea974 100644 --- a/dist/flow-typed/Comment.js +++ b/dist/flow-typed/Comment.js @@ -1,19 +1,23 @@ declare type Comment = { - author?: string, - author_url?: string, - claim_index?: number, - comment_id?: number, - downvotes?: number, - message: string, - omitted?: number, - reply_count?: number, - time_posted?: number, - upvotes?: number, - parent_id?: number, + comment: string, // comment body + comment_id: string, // sha256 digest + claim_id: string, // id linking to the claim this comment + timestamp: number, // integer representing unix-time + is_hidden: boolean, // claim owner may enable/disable this + channel_id?: string, // claimId of channel signing this comment + channel_name?: string, // name of channel claim + channel_url?: string, // full lbry url to signing channel + signature?: string, // signature of comment by originating channel + signing_ts?: string, // timestamp used when signing this comment + is_channel_signature_valid?: boolean, // whether or not the signature could be validated + parent_id?: number, // comment_id of comment this is in reply to }; +// todo: relate individual comments to their commentId declare type CommentsState = { - byId: {}, - isLoading: boolean, commentsByUri: { [string]: string }, -} + byId: { [string]: Array }, + commentById: { [string]: Comment }, + isLoading: boolean, + myComments: ?Set, +}; diff --git a/dist/flow-typed/Lbry.js b/dist/flow-typed/Lbry.js index cc8a4ac..92ce9dd 100644 --- a/dist/flow-typed/Lbry.js +++ b/dist/flow-typed/Lbry.js @@ -125,6 +125,8 @@ declare type ChannelUpdateResponse = GenericTxResponse & { }; declare type CommentCreateResponse = Comment; +declare type CommentEditResponse = Comment; + declare type CommentListResponse = { items: Array, page: number, @@ -133,6 +135,16 @@ declare type CommentListResponse = { total_pages: number, }; +declare type CommentHideResponse = { + // keyed by the CommentIds entered + [string]: { hidden: boolean }, +}; + +declare type CommentAbandonResponse = { + // keyed by the CommentId given + [string]: { abandoned: boolean }, +}; + declare type ChannelListResponse = { items: Array, page: number, From 24707eea5b8c5095edf0ba981c3220fedb5bcb41 Mon Sep 17 00:00:00 2001 From: Oleg Silkin Date: Mon, 13 Jan 2020 11:53:35 -0500 Subject: [PATCH 11/17] fix type on parent_id param --- src/redux/actions/comments.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/redux/actions/comments.js b/src/redux/actions/comments.js index 64b5344..a56b5a1 100644 --- a/src/redux/actions/comments.js +++ b/src/redux/actions/comments.js @@ -43,7 +43,7 @@ export function doCommentCreate( comment: string = '', claim_id: string = '', channel: ?string, - parent_id?: number + parent_id?: string, ) { return (dispatch: Dispatch, getState: GetState) => { const state = getState(); From 13cd56dabccfb359913786687a2d79f4cdf88fec Mon Sep 17 00:00:00 2001 From: Oleg Silkin Date: Mon, 13 Jan 2020 16:52:23 -0500 Subject: [PATCH 12/17] "comment edit" -> "comment update" --- flow-typed/Lbry.js | 2 +- src/constants/action_types.js | 6 ++-- src/lbry.js | 4 +-- src/redux/reducers/comments.js | 65 +++++++++++++++++++++++++++------- 4 files changed, 59 insertions(+), 18 deletions(-) diff --git a/flow-typed/Lbry.js b/flow-typed/Lbry.js index 92ce9dd..8afe66f 100644 --- a/flow-typed/Lbry.js +++ b/flow-typed/Lbry.js @@ -125,7 +125,7 @@ declare type ChannelUpdateResponse = GenericTxResponse & { }; declare type CommentCreateResponse = Comment; -declare type CommentEditResponse = Comment; +declare type CommentUpdateResponse = Comment; declare type CommentListResponse = { items: Array, diff --git a/src/constants/action_types.js b/src/constants/action_types.js index 097b2cd..0c31338 100644 --- a/src/constants/action_types.js +++ b/src/constants/action_types.js @@ -114,9 +114,9 @@ export const COMMENT_CREATE_FAILED = 'COMMENT_CREATE_FAILED'; export const COMMENT_ABANDON_STARTED = 'COMMENT_ABANDON_STARTED'; export const COMMENT_ABANDON_COMPLETED = 'COMMENT_ABANDON_COMPLETED'; export const COMMENT_ABANDON_FAILED = 'COMMENT_ABANDON_FAILED'; -export const COMMENT_EDIT_STARTED = 'COMMENT_EDIT_STARTED'; -export const COMMENT_EDIT_COMPLETED = 'COMMENT_EDIT_COMPLETED'; -export const COMMENT_EDIT_FAILED = 'COMMENT_EDIT_FAILED'; +export const COMMENT_UPDATE_STARTED = 'COMMENT_UPDATE_STARTED'; +export const COMMENT_UPDATE_COMPLETED = 'COMMENT_UPDATE_COMPLETED'; +export const COMMENT_UPDATE_FAILED = 'COMMENT_UPDATE_FAILED'; export const COMMENT_HIDE_STARTED = 'COMMENT_HIDE_STARTED'; export const COMMENT_HIDE_COMPLETED = 'COMMENT_HIDE_COMPLETED'; export const COMMENT_HIDE_FAILED = 'COMMENT_HIDE_FAILED'; diff --git a/src/lbry.js b/src/lbry.js index 9fdd033..f6cc88c 100644 --- a/src/lbry.js +++ b/src/lbry.js @@ -119,10 +119,10 @@ const Lbry: LbryTypes = { // Comments comment_list: (params = {}) => daemonCallWithResult('comment_list', params), comment_create: (params = {}) => daemonCallWithResult('comment_create', params), - // todo: implement these in reducers comment_hide: (params = {}) => daemonCallWithResult('comment_hide', params), comment_abandon: (params = {}) => daemonCallWithResult('comment_abandon', params), - comment_edit: (params = {}) => daemonCallWithResult('comment_hide', params), + // requires SDK ver. 0.53.0 + comment_update: (params = {}) => daemonCallWithResult('comment_update', params), // Connect to the sdk connect: () => { diff --git a/src/redux/reducers/comments.js b/src/redux/reducers/comments.js index 0513625..8876b0e 100644 --- a/src/redux/reducers/comments.js +++ b/src/redux/reducers/comments.js @@ -23,7 +23,7 @@ export const commentReducer = handleActions( }), [ACTIONS.COMMENT_CREATE_COMPLETED]: (state: CommentsState, action: any): CommentsState => { - const { comment, claimId }: any = action.data; + const { comment, claimId }: { comment: Comment, claimId: string } = action.data; const commentById = Object.assign({}, state.commentById); const byId = Object.assign({}, state.byId); const comments = byId[claimId]; @@ -54,7 +54,11 @@ export const commentReducer = handleActions( const commentsByUri = Object.assign({}, state.commentsByUri); if (comments) { + // we use an Array to preserve order of listing + // in reality this doesn't matter and we can just + // sort comments by their timestamp const commentIds = Array(comments.length); + // map the comment_ids to the new comments for (let i = 0; i < comments.length; i++) { commentIds[i] = comments[i].comment_id; @@ -81,34 +85,71 @@ export const commentReducer = handleActions( ...state, isLoading: true, }), - [ACTIONS.COMMENT_ABANDON_COMPLETED]: (state: CommentsState, action: any) => ({ - ...state, - isLoading: false, - }), + // remove the existing comment from the id -> comment list and claim -> commentIds + [ACTIONS.COMMENT_ABANDON_COMPLETED]: (state: CommentsState, action: any) => { + const { comment_id, abandoned } = action.data; + const commentById = Object.assign({}, state.commentById); + const byId = Object.assign({}, state.byId); + + if (abandoned && comment_id in abandoned) { + // messy but necessary for the time being + const comment: Comment = commentById[comment_id]; + const commentIds = byId[comment.claim_id]; + byId[comment.claim_id] = commentIds.filter(commentId => commentId !== comment_id); + + Object.keys(commentById).forEach(commentId => { + if (commentId === comment_id) { + delete commentById[commentId]; + } + }); + } + return { + ...state, + commentById, + byId, + isLoading: false, + }; + }, + // do nothing [ACTIONS.COMMENT_ABANDON_FAILED]: (state: CommentsState, action: any) => ({ ...state, isLoading: false, }), - [ACTIONS.COMMENT_EDIT_STARTED]: (state: CommentsState, action: any) => ({ + // do nothing + [ACTIONS.COMMENT_UPDATE_STARTED]: (state: CommentsState, action: any) => ({ ...state, isLoading: true, }), - [ACTIONS.COMMENT_EDIT_COMPLETED]: (state: CommentsState, action: any) => ({ - ...state, - isLoading: false, - }), - [ACTIONS.COMMENT_EDIT_FAILED]: (state: CommentsState, action: any) => ({ + // replace existing comment with comment returned here under its comment_id + [ACTIONS.COMMENT_UPDATE_COMPLETED]: (state: CommentsState, action: any) => { + const { comment } = action.data; + const commentById = Object.assign({}, state.commentById); + + if (comment) { + commentById[comment.comment_id] = comment; + } + + return { + ...state, + commentById, + isLoading: false, + }; + }, + // nothing can be done here + [ACTIONS.COMMENT_UPDATE_FAILED]: (state: CommentsState, action: any) => ({ ...state, isLoading: false, }), + // nothing can really be done here [ACTIONS.COMMENT_HIDE_STARTED]: (state: CommentsState, action: any) => ({ ...state, isLoading: true, }), [ACTIONS.COMMENT_HIDE_COMPLETED]: (state: CommentsState, action: any) => ({ - ...state, + ...state, // todo: add HiddenComments state & create selectors isLoading: false, }), + // nothing can be done here [ACTIONS.COMMENT_HIDE_FAILED]: (state: CommentsState, action: any) => ({ ...state, isLoading: false, From c0db949a53dee4b06acfcc96d7f2a874d3f6e468 Mon Sep 17 00:00:00 2001 From: Oleg Silkin Date: Mon, 13 Jan 2020 16:55:28 -0500 Subject: [PATCH 13/17] lint --- src/constants/shared_preferences.js | 12 ++++---- src/redux/actions/content.js | 3 +- src/redux/actions/sync.js | 7 +++-- src/redux/reducers/blocked.js | 3 +- src/redux/reducers/claims.js | 3 +- src/redux/selectors/content.js | 19 +++++++----- src/redux/selectors/notifications.js | 44 ++++++++++++++++------------ 7 files changed, 53 insertions(+), 38 deletions(-) diff --git a/src/constants/shared_preferences.js b/src/constants/shared_preferences.js index 56faf8f..e508e83 100644 --- a/src/constants/shared_preferences.js +++ b/src/constants/shared_preferences.js @@ -1,10 +1,10 @@ /* -* How to use this file: -* Settings exported from here will trigger the setting to be -* sent to the preference middleware when set using the -* usual setDaemonSettings and clearDaemonSettings methods. -* -* See redux/settings/actions in the app for where this is used. + * How to use this file: + * Settings exported from here will trigger the setting to be + * sent to the preference middleware when set using the + * usual setDaemonSettings and clearDaemonSettings methods. + * + * See redux/settings/actions in the app for where this is used. */ import * as DAEMON_SETTINGS from './daemon_settings'; diff --git a/src/redux/actions/content.js b/src/redux/actions/content.js index fff0f11..2654f74 100644 --- a/src/redux/actions/content.js +++ b/src/redux/actions/content.js @@ -1,7 +1,8 @@ +// @flow import * as ACTIONS from 'constants/action_types'; export function savePosition(claimId: string, outpoint: string, position: number) { - return dispatch => { + return (dispatch: Dispatch) => { dispatch({ type: ACTIONS.SET_CONTENT_POSITION, data: { claimId, outpoint, position }, diff --git a/src/redux/actions/sync.js b/src/redux/actions/sync.js index 41a25ea..7f80d33 100644 --- a/src/redux/actions/sync.js +++ b/src/redux/actions/sync.js @@ -14,7 +14,7 @@ type SharedData = { function extractUserState(rawObj: SharedData) { if (rawObj && rawObj.version === '0.1' && rawObj.value) { - const { subscriptions, tags, blocked, settings} = rawObj.value; + const { subscriptions, tags, blocked, settings } = rawObj.value; return { ...(subscriptions ? { subscriptions } : {}), @@ -30,7 +30,10 @@ function extractUserState(rawObj: SharedData) { export function doPopulateSharedUserState(sharedSettings: any) { return (dispatch: Dispatch) => { const { subscriptions, tags, blocked, settings } = extractUserState(sharedSettings); - dispatch({ type: ACTIONS.USER_STATE_POPULATE, data: { subscriptions, tags, blocked, settings } }); + dispatch({ + type: ACTIONS.USER_STATE_POPULATE, + data: { subscriptions, tags, blocked, settings }, + }); }; } diff --git a/src/redux/reducers/blocked.js b/src/redux/reducers/blocked.js index b69d9c9..c688936 100644 --- a/src/redux/reducers/blocked.js +++ b/src/redux/reducers/blocked.js @@ -33,8 +33,7 @@ export const blockedReducer = handleActions( const { blocked } = action.data; return { ...state, - blockedChannels: - blocked && blocked.length ? blocked : state.blockedChannels, + blockedChannels: blocked && blocked.length ? blocked : state.blockedChannels, }; }, }, diff --git a/src/redux/reducers/claims.js b/src/redux/reducers/claims.js index 8e6b121..56a4383 100644 --- a/src/redux/reducers/claims.js +++ b/src/redux/reducers/claims.js @@ -265,7 +265,8 @@ reducers[ACTIONS.FETCH_CHANNEL_CLAIMS_COMPLETED] = (state: State, action: any): const paginatedClaimsByChannel = Object.assign({}, state.paginatedClaimsByChannel); // check if count has changed - that means cached pagination will be wrong, so clear it const previousCount = paginatedClaimsByChannel[uri] && paginatedClaimsByChannel[uri]['itemCount']; - const byChannel = (claimsInChannel === previousCount) ? Object.assign({}, paginatedClaimsByChannel[uri]) : {}; + const byChannel = + claimsInChannel === previousCount ? Object.assign({}, paginatedClaimsByChannel[uri]) : {}; const allClaimIds = new Set(byChannel.all); const currentPageClaimIds = []; const byId = Object.assign({}, state.byId); diff --git a/src/redux/selectors/content.js b/src/redux/selectors/content.js index f494fab..2daac6e 100644 --- a/src/redux/selectors/content.js +++ b/src/redux/selectors/content.js @@ -1,14 +1,19 @@ +// @flow import { createSelector } from 'reselect'; import { makeSelectClaimForUri } from 'redux/selectors/claims'; export const selectState = (state: any) => state.content || {}; export const makeSelectContentPositionForUri = (uri: string) => - createSelector(selectState, makeSelectClaimForUri(uri), (state, claim) => { - if (!claim) { - return null; + createSelector( + selectState, + makeSelectClaimForUri(uri), + (state, claim) => { + if (!claim) { + return null; + } + const outpoint = `${claim.txid}:${claim.nout}`; + const id = claim.claim_id; + return state.positions[id] ? state.positions[id][outpoint] : null; } - const outpoint = `${claim.txid}:${claim.nout}`; - const id = claim.claim_id; - return state.positions[id] ? state.positions[id][outpoint] : null; - }); + ); diff --git a/src/redux/selectors/notifications.js b/src/redux/selectors/notifications.js index 00844be..8894442 100644 --- a/src/redux/selectors/notifications.js +++ b/src/redux/selectors/notifications.js @@ -1,26 +1,32 @@ import { createSelector } from 'reselect'; -export const selectState = (state) => state.notifications || {}; +export const selectState = state => state.notifications || {}; -export const selectToast = createSelector(selectState, (state) => { - if (state.toasts.length) { - const { id, params } = state.toasts[0]; - return { - id, - ...params, - }; +export const selectToast = createSelector( + selectState, + state => { + if (state.toasts.length) { + const { id, params } = state.toasts[0]; + return { + id, + ...params, + }; + } + + return null; } +); - return null; -}); +export const selectError = createSelector( + selectState, + state => { + if (state.errors.length) { + const { error } = state.errors[0]; + return { + error, + }; + } -export const selectError = createSelector(selectState, (state) => { - if (state.errors.length) { - const { error } = state.errors[0]; - return { - error, - }; + return null; } - - return null; -}); +); From 2bdbf3d645baabaf59fdaf08c7cf735118cead24 Mon Sep 17 00:00:00 2001 From: Oleg Silkin Date: Mon, 13 Jan 2020 17:00:04 -0500 Subject: [PATCH 14/17] Implements reducers for COMMENT_UPDATE, COMMENT_HIDE, and COMMENT_ABANDON --- dist/bundle.es.js | 194 +++++++++++++++++++++++++++------- dist/flow-typed/Lbry.js | 6 +- flow-typed/Lbry.js | 4 + src/index.js | 8 +- src/redux/actions/comments.js | 99 ++++++++++++++++- 5 files changed, 271 insertions(+), 40 deletions(-) diff --git a/dist/bundle.es.js b/dist/bundle.es.js index e8b420a..9e3c5e6 100644 --- a/dist/bundle.es.js +++ b/dist/bundle.es.js @@ -137,9 +137,9 @@ const COMMENT_CREATE_FAILED = 'COMMENT_CREATE_FAILED'; const COMMENT_ABANDON_STARTED = 'COMMENT_ABANDON_STARTED'; const COMMENT_ABANDON_COMPLETED = 'COMMENT_ABANDON_COMPLETED'; const COMMENT_ABANDON_FAILED = 'COMMENT_ABANDON_FAILED'; -const COMMENT_EDIT_STARTED = 'COMMENT_EDIT_STARTED'; -const COMMENT_EDIT_COMPLETED = 'COMMENT_EDIT_COMPLETED'; -const COMMENT_EDIT_FAILED = 'COMMENT_EDIT_FAILED'; +const COMMENT_UPDATE_STARTED = 'COMMENT_UPDATE_STARTED'; +const COMMENT_UPDATE_COMPLETED = 'COMMENT_UPDATE_COMPLETED'; +const COMMENT_UPDATE_FAILED = 'COMMENT_UPDATE_FAILED'; const COMMENT_HIDE_STARTED = 'COMMENT_HIDE_STARTED'; const COMMENT_HIDE_COMPLETED = 'COMMENT_HIDE_COMPLETED'; const COMMENT_HIDE_FAILED = 'COMMENT_HIDE_FAILED'; @@ -389,9 +389,9 @@ var action_types = /*#__PURE__*/Object.freeze({ COMMENT_ABANDON_STARTED: COMMENT_ABANDON_STARTED, COMMENT_ABANDON_COMPLETED: COMMENT_ABANDON_COMPLETED, COMMENT_ABANDON_FAILED: COMMENT_ABANDON_FAILED, - COMMENT_EDIT_STARTED: COMMENT_EDIT_STARTED, - COMMENT_EDIT_COMPLETED: COMMENT_EDIT_COMPLETED, - COMMENT_EDIT_FAILED: COMMENT_EDIT_FAILED, + COMMENT_UPDATE_STARTED: COMMENT_UPDATE_STARTED, + COMMENT_UPDATE_COMPLETED: COMMENT_UPDATE_COMPLETED, + COMMENT_UPDATE_FAILED: COMMENT_UPDATE_FAILED, COMMENT_HIDE_STARTED: COMMENT_HIDE_STARTED, COMMENT_HIDE_COMPLETED: COMMENT_HIDE_COMPLETED, COMMENT_HIDE_FAILED: COMMENT_HIDE_FAILED, @@ -783,12 +783,12 @@ var daemon_settings = /*#__PURE__*/Object.freeze({ }); /* -* How to use this file: -* Settings exported from here will trigger the setting to be -* sent to the preference middleware when set using the -* usual setDaemonSettings and clearDaemonSettings methods. -* -* See redux/settings/actions in the app for where this is used. + * How to use this file: + * Settings exported from here will trigger the setting to be + * sent to the preference middleware when set using the + * usual setDaemonSettings and clearDaemonSettings methods. + * + * See redux/settings/actions in the app for where this is used. */ const WALLET_SERVERS = LBRYUM_SERVERS; @@ -934,10 +934,10 @@ const Lbry = { // Comments comment_list: (params = {}) => daemonCallWithResult('comment_list', params), comment_create: (params = {}) => daemonCallWithResult('comment_create', params), - // todo: implement these in reducers comment_hide: (params = {}) => daemonCallWithResult('comment_hide', params), comment_abandon: (params = {}) => daemonCallWithResult('comment_abandon', params), - comment_edit: (params = {}) => daemonCallWithResult('comment_hide', params), + // requires SDK ver. 0.53.0 + comment_update: (params = {}) => daemonCallWithResult('comment_update', params), // Connect to the sdk connect: () => { @@ -1246,18 +1246,6 @@ function buildURI(UrlObj, includeProto = true, protoDefault = 'lbry://') { deprecatedParts = _objectWithoutProperties(UrlObj, ['streamName', 'streamClaimId', 'channelName', 'channelClaimId', 'primaryClaimSequence', 'primaryBidPosition', 'secondaryClaimSequence', 'secondaryBidPosition']); const { claimId, claimName, contentName } = deprecatedParts; - { - if (claimId) { - console.error(__("'claimId' should no longer be used. Use 'streamClaimId' or 'channelClaimId' instead")); - } - if (claimName) { - console.error(__("'claimName' should no longer be used. Use 'streamClaimName' or 'channelClaimName' instead")); - } - if (contentName) { - console.error(__("'contentName' should no longer be used. Use 'streamName' instead")); - } - } - if (!claimName && !channelName && !streamName) { console.error(__("'claimName', 'channelName', and 'streamName' are all empty. One must be present to build a url.")); } @@ -1570,7 +1558,10 @@ function extractUserState(rawObj) { function doPopulateSharedUserState(sharedSettings) { return dispatch => { const { subscriptions, tags, blocked, settings } = extractUserState(sharedSettings); - dispatch({ type: USER_STATE_POPULATE, data: { subscriptions, tags, blocked, settings } }); + dispatch({ + type: USER_STATE_POPULATE, + data: { subscriptions, tags, blocked, settings } + }); }; } @@ -4053,6 +4044,8 @@ const doUpdateSearchOptions = newOptions => (dispatch, getState) => { } }; +// + function savePosition(claimId, outpoint, position) { return dispatch => { dispatch({ @@ -4155,6 +4148,89 @@ function doCommentCreate(comment = '', claim_id = '', channel, parent_id) { }; } +function doCommentHide(comment_id) { + return dispatch => { + dispatch({ + type: COMMENT_HIDE_STARTED + }); + return lbryProxy.comment_hide({ + comment_ids: [comment_id] + }).then(result => { + dispatch({ + type: COMMENT_HIDE_COMPLETED, + data: result + }); + }).catch(error => { + dispatch({ + type: COMMENT_HIDE_FAILED, + data: error + }); + dispatch(doToast({ + message: `SDK Errored when trying to hide Comment with comment_id: "${comment_id}"`, + isError: true + })); + }); + }; +} + +function doCommentAbandon(comment_id) { + return dispatch => { + dispatch({ + type: COMMENT_ABANDON_STARTED + }); + return lbryProxy.comment_abandon({ + comment_id: comment_id + }).then(result => { + dispatch({ + type: COMMENT_ABANDON_COMPLETED, + data: { + comment_id: comment_id, + abandoned: result + } + }); + }).catch(error => { + dispatch({ + type: COMMENT_ABANDON_FAILED, + data: error + }); + dispatch(doToast({ + message: `SDK Errored during abandon on Comment w/ ID = "${comment_id}"`, + isError: true + })); + }); + }; +} + +function doCommentUpdate(comment_id, comment) { + // if they provided an empty string, they must have wanted to abandon + if (comment === '') { + return doCommentAbandon(comment_id); + } else { + return dispatch => { + dispatch({ + type: COMMENT_UPDATE_STARTED + }); + return lbryProxy.comment_update({ + comment_id: comment_id, + comment: comment + }).then(result => { + dispatch({ + type: COMMENT_UPDATE_COMPLETED, + data: { + comment: result + } + }); + }).catch(error => { + dispatch({ type: COMMENT_UPDATE_FAILED, data: error }); + dispatch(doToast({ + message: `SDK Errored during update on Comment w/ ID = ${comment_id}`, + isError: true + })); + }); + }; + } +} + // const doToggleBlockChannel = uri => ({ @@ -4616,7 +4692,11 @@ const commentReducer = handleActions({ const commentsByUri = Object.assign({}, state.commentsByUri); if (comments) { + // we use an Array to preserve order of listing + // in reality this doesn't matter and we can just + // sort comments by their timestamp const commentIds = Array(comments.length); + // map the comment_ids to the new comments for (let i = 0; i < comments.length; i++) { commentIds[i] = comments[i].comment_id; @@ -4640,27 +4720,64 @@ const commentReducer = handleActions({ [COMMENT_ABANDON_STARTED]: (state, action) => _extends$7({}, state, { isLoading: true }), - [COMMENT_ABANDON_COMPLETED]: (state, action) => _extends$7({}, state, { - isLoading: false - }), + // remove the existing comment from the id -> comment list and claim -> commentIds + [COMMENT_ABANDON_COMPLETED]: (state, action) => { + const { comment_id, abandoned } = action.data; + const commentById = Object.assign({}, state.commentById); + const byId = Object.assign({}, state.byId); + + if (abandoned && comment_id in abandoned) { + // messy but necessary for the time being + const comment = commentById[comment_id]; + const commentIds = byId[comment.claim_id]; + byId[comment.claim_id] = commentIds.filter(commentId => commentId !== comment_id); + + Object.keys(commentById).forEach(commentId => { + if (commentId === comment_id) { + delete commentById[commentId]; + } + }); + } + return _extends$7({}, state, { + commentById, + byId, + isLoading: false + }); + }, + // do nothing [COMMENT_ABANDON_FAILED]: (state, action) => _extends$7({}, state, { isLoading: false }), - [COMMENT_EDIT_STARTED]: (state, action) => _extends$7({}, state, { + // do nothing + [COMMENT_UPDATE_STARTED]: (state, action) => _extends$7({}, state, { isLoading: true }), - [COMMENT_EDIT_COMPLETED]: (state, action) => _extends$7({}, state, { - isLoading: false - }), - [COMMENT_EDIT_FAILED]: (state, action) => _extends$7({}, state, { + // replace existing comment with comment returned here under its comment_id + [COMMENT_UPDATE_COMPLETED]: (state, action) => { + const { comment } = action.data; + const commentById = Object.assign({}, state.commentById); + + if (comment) { + commentById[comment.comment_id] = comment; + } + + return _extends$7({}, state, { + commentById, + isLoading: false + }); + }, + // nothing can be done here + [COMMENT_UPDATE_FAILED]: (state, action) => _extends$7({}, state, { isLoading: false }), + // nothing can really be done here [COMMENT_HIDE_STARTED]: (state, action) => _extends$7({}, state, { isLoading: true }), - [COMMENT_HIDE_COMPLETED]: (state, action) => _extends$7({}, state, { + [COMMENT_HIDE_COMPLETED]: (state, action) => _extends$7({}, state, { // todo: add HiddenComments state & create selectors isLoading: false }), + // nothing can be done here [COMMENT_HIDE_FAILED]: (state, action) => _extends$7({}, state, { isLoading: false }) @@ -5502,6 +5619,8 @@ const walletReducer = handleActions({ }) }, defaultState$a); +// + const selectState$6 = state => state.content || {}; const makeSelectContentPositionForUri = uri => reselect.createSelector(selectState$6, makeSelectClaimForUri(uri), (state, claim) => { @@ -5666,8 +5785,11 @@ exports.doCheckPendingPublishes = doCheckPendingPublishes; exports.doClaimSearch = doClaimSearch; exports.doClearPublish = doClearPublish; exports.doClearSupport = doClearSupport; +exports.doCommentAbandon = doCommentAbandon; exports.doCommentCreate = doCommentCreate; +exports.doCommentHide = doCommentHide; exports.doCommentList = doCommentList; +exports.doCommentUpdate = doCommentUpdate; exports.doCreateChannel = doCreateChannel; exports.doDeletePurchasedUri = doDeletePurchasedUri; exports.doDeleteTag = doDeleteTag; diff --git a/dist/flow-typed/Lbry.js b/dist/flow-typed/Lbry.js index 92ce9dd..408beb0 100644 --- a/dist/flow-typed/Lbry.js +++ b/dist/flow-typed/Lbry.js @@ -125,7 +125,7 @@ declare type ChannelUpdateResponse = GenericTxResponse & { }; declare type CommentCreateResponse = Comment; -declare type CommentEditResponse = Comment; +declare type CommentUpdateResponse = Comment; declare type CommentListResponse = { items: Array, @@ -254,6 +254,10 @@ declare type LbryTypes = { // Commenting comment_list: (params: {}) => Promise, comment_create: (params: {}) => Promise, + comment_update: (params: {}) => Promise, + comment_hide: (params: {}) => Promise, + comment_abandon: (params: {}) => Promise, + // Wallet utilities wallet_balance: (params: {}) => Promise, wallet_decrypt: (prams: {}) => Promise, diff --git a/flow-typed/Lbry.js b/flow-typed/Lbry.js index 8afe66f..408beb0 100644 --- a/flow-typed/Lbry.js +++ b/flow-typed/Lbry.js @@ -254,6 +254,10 @@ declare type LbryTypes = { // Commenting comment_list: (params: {}) => Promise, comment_create: (params: {}) => Promise, + comment_update: (params: {}) => Promise, + comment_hide: (params: {}) => Promise, + comment_abandon: (params: {}) => Promise, + // Wallet utilities wallet_balance: (params: {}) => Promise, wallet_decrypt: (prams: {}) => Promise, diff --git a/src/index.js b/src/index.js index 65f1db9..98c6e8a 100644 --- a/src/index.js +++ b/src/index.js @@ -121,7 +121,13 @@ export { export { doToggleTagFollow, doAddTag, doDeleteTag } from 'redux/actions/tags'; -export { doCommentList, doCommentCreate } from 'redux/actions/comments'; +export { + doCommentList, + doCommentCreate, + doCommentAbandon, + doCommentHide, + doCommentUpdate, +} from 'redux/actions/comments'; export { doToggleBlockChannel } from 'redux/actions/blocked'; diff --git a/src/redux/actions/comments.js b/src/redux/actions/comments.js index a56b5a1..48f4bad 100644 --- a/src/redux/actions/comments.js +++ b/src/redux/actions/comments.js @@ -43,7 +43,7 @@ export function doCommentCreate( comment: string = '', claim_id: string = '', channel: ?string, - parent_id?: string, + parent_id?: string ) { return (dispatch: Dispatch, getState: GetState) => { const state = getState(); @@ -60,7 +60,7 @@ export function doCommentCreate( channel_id: channel_id, parent_id: parent_id, }) - .then((result: Comment) => { + .then((result: CommentCreateResponse) => { dispatch({ type: ACTIONS.COMMENT_CREATE_COMPLETED, data: { @@ -83,3 +83,98 @@ export function doCommentCreate( }); }; } + +export function doCommentHide(comment_id: string) { + return (dispatch: Dispatch) => { + dispatch({ + type: ACTIONS.COMMENT_HIDE_STARTED, + }); + return Lbry.comment_hide({ + comment_ids: [comment_id], + }) + .then((result: CommentHideResponse) => { + dispatch({ + type: ACTIONS.COMMENT_HIDE_COMPLETED, + data: result, + }); + }) + .catch(error => { + dispatch({ + type: ACTIONS.COMMENT_HIDE_FAILED, + data: error, + }); + dispatch( + doToast({ + message: `SDK Errored when trying to hide Comment with comment_id: "${comment_id}"`, + isError: true, + }) + ); + }); + }; +} + +export function doCommentAbandon(comment_id: string) { + return (dispatch: Dispatch) => { + dispatch({ + type: ACTIONS.COMMENT_ABANDON_STARTED, + }); + return Lbry.comment_abandon({ + comment_id: comment_id, + }) + .then((result: CommentAbandonResponse) => { + dispatch({ + type: ACTIONS.COMMENT_ABANDON_COMPLETED, + data: { + comment_id: comment_id, + abandoned: result, + }, + }); + }) + .catch(error => { + dispatch({ + type: ACTIONS.COMMENT_ABANDON_FAILED, + data: error, + }); + dispatch( + doToast({ + message: `SDK Errored during abandon on Comment w/ ID = "${comment_id}"`, + isError: true, + }) + ); + }); + }; +} + +export function doCommentUpdate(comment_id: string, comment: string) { + // if they provided an empty string, they must have wanted to abandon + if (comment === '') { + return doCommentAbandon(comment_id); + } else { + return (dispatch: Dispatch) => { + dispatch({ + type: ACTIONS.COMMENT_UPDATE_STARTED, + }); + return Lbry.comment_update({ + comment_id: comment_id, + comment: comment, + }) + .then((result: CommentUpdateResponse) => { + dispatch({ + type: ACTIONS.COMMENT_UPDATE_COMPLETED, + data: { + comment: result, + }, + }); + }) + .catch(error => { + dispatch({ type: ACTIONS.COMMENT_UPDATE_FAILED, data: error }); + dispatch( + doToast({ + message: `SDK Errored during update on Comment w/ ID = ${comment_id}`, + isError: true, + }) + ); + }); + }; + } +} From c30a8ec66c0d5692cc93ef8adeb95ba830c857f8 Mon Sep 17 00:00:00 2001 From: Oleg Silkin Date: Mon, 13 Jan 2020 17:04:41 -0500 Subject: [PATCH 15/17] Add todos --- src/redux/selectors/comments.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/redux/selectors/comments.js b/src/redux/selectors/comments.js index 048e7f2..86286ea 100644 --- a/src/redux/selectors/comments.js +++ b/src/redux/selectors/comments.js @@ -63,3 +63,6 @@ export const makeSelectCommentsForUri = (uri: string) => return byClaimId && byClaimId[claimId]; } ); + +// todo: allow SDK to retrieve user comments through comment_list +// todo: implement selectors for selecting comments owned by user From fd0d097c91fb6132ba614114ad5ee268c9f05951 Mon Sep 17 00:00:00 2001 From: Oleg Silkin Date: Sun, 19 Jan 2020 21:27:36 -0500 Subject: [PATCH 16/17] Cleanup --- dist/bundle.es.js | 31 ++++++++++++++++++++----------- src/redux/actions/comments.js | 6 +++--- src/redux/reducers/comments.js | 6 +----- src/redux/selectors/comments.js | 4 +--- 4 files changed, 25 insertions(+), 22 deletions(-) diff --git a/dist/bundle.es.js b/dist/bundle.es.js index 9e3c5e6..c80a7e8 100644 --- a/dist/bundle.es.js +++ b/dist/bundle.es.js @@ -1246,6 +1246,18 @@ function buildURI(UrlObj, includeProto = true, protoDefault = 'lbry://') { deprecatedParts = _objectWithoutProperties(UrlObj, ['streamName', 'streamClaimId', 'channelName', 'channelClaimId', 'primaryClaimSequence', 'primaryBidPosition', 'secondaryClaimSequence', 'secondaryBidPosition']); const { claimId, claimName, contentName } = deprecatedParts; + { + if (claimId) { + console.error(__("'claimId' should no longer be used. Use 'streamClaimId' or 'channelClaimId' instead")); + } + if (claimName) { + console.error(__("'claimName' should no longer be used. Use 'streamClaimName' or 'channelClaimName' instead")); + } + if (contentName) { + console.error(__("'contentName' should no longer be used. Use 'streamName' instead")); + } + } + if (!claimName && !channelName && !streamName) { console.error(__("'claimName', 'channelName', and 'streamName' are all empty. One must be present to build a url.")); } @@ -4166,7 +4178,7 @@ function doCommentHide(comment_id) { data: error }); dispatch(doToast({ - message: `SDK Errored when trying to hide Comment with comment_id: "${comment_id}"`, + message: 'There was an error hiding this comment. Please try again later.', isError: true })); }); @@ -4194,7 +4206,7 @@ function doCommentAbandon(comment_id) { data: error }); dispatch(doToast({ - message: `SDK Errored during abandon on Comment w/ ID = "${comment_id}"`, + message: 'There was an error hiding this comment. Please try again later.', isError: true })); }); @@ -4223,7 +4235,7 @@ function doCommentUpdate(comment_id, comment) { }).catch(error => { dispatch({ type: COMMENT_UPDATE_FAILED, data: error }); dispatch(doToast({ - message: `SDK Errored during update on Comment w/ ID = ${comment_id}`, + message: 'There was an error hiding this comment. Please try again later.', isError: true })); }); @@ -4732,11 +4744,7 @@ const commentReducer = handleActions({ const commentIds = byId[comment.claim_id]; byId[comment.claim_id] = commentIds.filter(commentId => commentId !== comment_id); - Object.keys(commentById).forEach(commentId => { - if (commentId === comment_id) { - delete commentById[commentId]; - } - }); + delete commentById[comment_id]; } return _extends$7({}, state, { commentById, @@ -5668,12 +5676,10 @@ const selectCommentsByClaimId = reselect.createSelector(selectState$8, selectCom const byClaimId = state.byId || {}; const comments = {}; - // for every claimId -> commentId, put comments in the object + // replace every comment_id in the list with the actual comment object Object.keys(byClaimId).forEach(claimId => { - // get all the commentIds that commented on this ClaimId const commentIds = byClaimId[claimId]; - // map a new array of comments by the claimId comments[claimId] = Array(commentIds === null ? 0 : commentIds.length); for (let i = 0; i < commentIds.length; i++) { comments[claimId][i] = byId[commentIds[i]]; @@ -5708,6 +5714,9 @@ const makeSelectCommentsForUri = uri => reselect.createSelector(selectCommentsBy return byClaimId && byClaimId[claimId]; }); +// todo: allow SDK to retrieve user comments through comment_list +// todo: implement selectors for selecting comments owned by user + // const selectState$9 = state => state.tags || {}; diff --git a/src/redux/actions/comments.js b/src/redux/actions/comments.js index 48f4bad..01c79b2 100644 --- a/src/redux/actions/comments.js +++ b/src/redux/actions/comments.js @@ -105,7 +105,7 @@ export function doCommentHide(comment_id: string) { }); dispatch( doToast({ - message: `SDK Errored when trying to hide Comment with comment_id: "${comment_id}"`, + message: 'There was an error hiding this comment. Please try again later.', isError: true, }) ); @@ -137,7 +137,7 @@ export function doCommentAbandon(comment_id: string) { }); dispatch( doToast({ - message: `SDK Errored during abandon on Comment w/ ID = "${comment_id}"`, + message: 'There was an error hiding this comment. Please try again later.', isError: true, }) ); @@ -170,7 +170,7 @@ export function doCommentUpdate(comment_id: string, comment: string) { dispatch({ type: ACTIONS.COMMENT_UPDATE_FAILED, data: error }); dispatch( doToast({ - message: `SDK Errored during update on Comment w/ ID = ${comment_id}`, + message: 'There was an error hiding this comment. Please try again later.', isError: true, }) ); diff --git a/src/redux/reducers/comments.js b/src/redux/reducers/comments.js index 8876b0e..c45edd0 100644 --- a/src/redux/reducers/comments.js +++ b/src/redux/reducers/comments.js @@ -97,11 +97,7 @@ export const commentReducer = handleActions( const commentIds = byId[comment.claim_id]; byId[comment.claim_id] = commentIds.filter(commentId => commentId !== comment_id); - Object.keys(commentById).forEach(commentId => { - if (commentId === comment_id) { - delete commentById[commentId]; - } - }); + delete commentById[comment_id]; } return { ...state, diff --git a/src/redux/selectors/comments.js b/src/redux/selectors/comments.js index 86286ea..c3a2f2d 100644 --- a/src/redux/selectors/comments.js +++ b/src/redux/selectors/comments.js @@ -15,12 +15,10 @@ export const selectCommentsByClaimId = createSelector( const byClaimId = state.byId || {}; const comments = {}; - // for every claimId -> commentId, put comments in the object + // replace every comment_id in the list with the actual comment object Object.keys(byClaimId).forEach(claimId => { - // get all the commentIds that commented on this ClaimId const commentIds = byClaimId[claimId]; - // map a new array of comments by the claimId comments[claimId] = Array(commentIds === null ? 0 : commentIds.length); for (let i = 0; i < commentIds.length; i++) { comments[claimId][i] = byId[commentIds[i]]; From bc2436dce28eec09a268afb4b896421ec718443f Mon Sep 17 00:00:00 2001 From: Oleg Silkin Date: Tue, 21 Jan 2020 00:46:14 -0500 Subject: [PATCH 17/17] Corrects abandon return type & fixes issue where redux wasn't being changed --- dist/bundle.es.js | 62 +++++++++++++++++++--------------- dist/flow-typed/Lbry.js | 2 +- flow-typed/Lbry.js | 2 +- src/redux/actions/comments.js | 21 ++++++++---- src/redux/reducers/comments.js | 19 ++++++----- 5 files changed, 61 insertions(+), 45 deletions(-) diff --git a/dist/bundle.es.js b/dist/bundle.es.js index c915b8b..16deb2d 100644 --- a/dist/bundle.es.js +++ b/dist/bundle.es.js @@ -4067,7 +4067,7 @@ const doUpdateSearchOptions = newOptions => (dispatch, getState) => { } }; -// +// function savePosition(claimId, outpoint, position) { return dispatch => { @@ -4204,13 +4204,20 @@ function doCommentAbandon(comment_id) { return lbryProxy.comment_abandon({ comment_id: comment_id }).then(result => { - dispatch({ - type: COMMENT_ABANDON_COMPLETED, - data: { - comment_id: comment_id, - abandoned: result - } - }); + // Comment may not be deleted if the signing channel can't be signed. + // This will happen if the channel was recently created or abandoned. + if (result.abandoned) { + dispatch({ + type: COMMENT_ABANDON_COMPLETED, + data: { + comment_id: comment_id + } + }); + } else { + dispatch({ + type: COMMENT_ABANDON_FAILED + }); + } }).catch(error => { dispatch({ type: COMMENT_ABANDON_FAILED, @@ -4740,35 +4747,36 @@ const commentReducer = handleActions({ [COMMENT_LIST_FAILED]: (state, action) => _extends$8({}, state, { isLoading: false }), - [COMMENT_ABANDON_STARTED]: (state, action) => _extends$7({}, state, { + [COMMENT_ABANDON_STARTED]: (state, action) => _extends$8({}, state, { isLoading: true }), - // remove the existing comment from the id -> comment list and claim -> commentIds [COMMENT_ABANDON_COMPLETED]: (state, action) => { - const { comment_id, abandoned } = action.data; + const { comment_id } = action.data; const commentById = Object.assign({}, state.commentById); const byId = Object.assign({}, state.byId); - if (abandoned && comment_id in abandoned) { - // messy but necessary for the time being - const comment = commentById[comment_id]; - const commentIds = byId[comment.claim_id]; - byId[comment.claim_id] = commentIds.filter(commentId => commentId !== comment_id); - - delete commentById[comment_id]; + // to remove the comment and its references + const claimId = commentById[comment_id].claim_id; + for (let i = 0; i < byId[claimId].length; i++) { + if (byId[claimId][i] === comment_id) { + byId[claimId].splice(i, 1); + break; + } } - return _extends$7({}, state, { + delete commentById[comment_id]; + + return _extends$8({}, state, { commentById, byId, isLoading: false }); }, // do nothing - [COMMENT_ABANDON_FAILED]: (state, action) => _extends$7({}, state, { + [COMMENT_ABANDON_FAILED]: (state, action) => _extends$8({}, state, { isLoading: false }), // do nothing - [COMMENT_UPDATE_STARTED]: (state, action) => _extends$7({}, state, { + [COMMENT_UPDATE_STARTED]: (state, action) => _extends$8({}, state, { isLoading: true }), // replace existing comment with comment returned here under its comment_id @@ -4780,24 +4788,24 @@ const commentReducer = handleActions({ commentById[comment.comment_id] = comment; } - return _extends$7({}, state, { + return _extends$8({}, state, { commentById, isLoading: false }); }, // nothing can be done here - [COMMENT_UPDATE_FAILED]: (state, action) => _extends$7({}, state, { + [COMMENT_UPDATE_FAILED]: (state, action) => _extends$8({}, state, { isLoading: false }), // nothing can really be done here - [COMMENT_HIDE_STARTED]: (state, action) => _extends$7({}, state, { + [COMMENT_HIDE_STARTED]: (state, action) => _extends$8({}, state, { isLoading: true }), - [COMMENT_HIDE_COMPLETED]: (state, action) => _extends$7({}, state, { // todo: add HiddenComments state & create selectors + [COMMENT_HIDE_COMPLETED]: (state, action) => _extends$8({}, state, { // todo: add HiddenComments state & create selectors isLoading: false }), // nothing can be done here - [COMMENT_HIDE_FAILED]: (state, action) => _extends$7({}, state, { + [COMMENT_HIDE_FAILED]: (state, action) => _extends$8({}, state, { isLoading: false }) }, defaultState$1); @@ -5638,7 +5646,7 @@ const walletReducer = handleActions({ }) }, defaultState$a); -// +// const selectState$6 = state => state.content || {}; diff --git a/dist/flow-typed/Lbry.js b/dist/flow-typed/Lbry.js index 408beb0..d2657cb 100644 --- a/dist/flow-typed/Lbry.js +++ b/dist/flow-typed/Lbry.js @@ -142,7 +142,7 @@ declare type CommentHideResponse = { declare type CommentAbandonResponse = { // keyed by the CommentId given - [string]: { abandoned: boolean }, + abandoned: boolean, }; declare type ChannelListResponse = { diff --git a/flow-typed/Lbry.js b/flow-typed/Lbry.js index 408beb0..d2657cb 100644 --- a/flow-typed/Lbry.js +++ b/flow-typed/Lbry.js @@ -142,7 +142,7 @@ declare type CommentHideResponse = { declare type CommentAbandonResponse = { // keyed by the CommentId given - [string]: { abandoned: boolean }, + abandoned: boolean, }; declare type ChannelListResponse = { diff --git a/src/redux/actions/comments.js b/src/redux/actions/comments.js index 01c79b2..4441651 100644 --- a/src/redux/actions/comments.js +++ b/src/redux/actions/comments.js @@ -122,13 +122,20 @@ export function doCommentAbandon(comment_id: string) { comment_id: comment_id, }) .then((result: CommentAbandonResponse) => { - dispatch({ - type: ACTIONS.COMMENT_ABANDON_COMPLETED, - data: { - comment_id: comment_id, - abandoned: result, - }, - }); + // Comment may not be deleted if the signing channel can't be signed. + // This will happen if the channel was recently created or abandoned. + if (result.abandoned) { + dispatch({ + type: ACTIONS.COMMENT_ABANDON_COMPLETED, + data: { + comment_id: comment_id, + }, + }); + } else { + dispatch({ + type: ACTIONS.COMMENT_ABANDON_FAILED, + }); + } }) .catch(error => { dispatch({ diff --git a/src/redux/reducers/comments.js b/src/redux/reducers/comments.js index c45edd0..493d225 100644 --- a/src/redux/reducers/comments.js +++ b/src/redux/reducers/comments.js @@ -85,20 +85,21 @@ export const commentReducer = handleActions( ...state, isLoading: true, }), - // remove the existing comment from the id -> comment list and claim -> commentIds [ACTIONS.COMMENT_ABANDON_COMPLETED]: (state: CommentsState, action: any) => { - const { comment_id, abandoned } = action.data; + const { comment_id } = action.data; const commentById = Object.assign({}, state.commentById); const byId = Object.assign({}, state.byId); - if (abandoned && comment_id in abandoned) { - // messy but necessary for the time being - const comment: Comment = commentById[comment_id]; - const commentIds = byId[comment.claim_id]; - byId[comment.claim_id] = commentIds.filter(commentId => commentId !== comment_id); - - delete commentById[comment_id]; + // to remove the comment and its references + const claimId = commentById[comment_id].claim_id; + for (let i = 0; i < byId[claimId].length; i++) { + if (byId[claimId][i] === comment_id) { + byId[claimId].splice(i, 1); + break; + } } + delete commentById[comment_id]; + return { ...state, commentById,