diff --git a/flow-typed/Comment.js b/flow-typed/Comment.js index 15c6d3038..fb30d4c5e 100644 --- a/flow-typed/Comment.js +++ b/flow-typed/Comment.js @@ -71,12 +71,33 @@ declare type CommentReactParams = { remove?: boolean, }; -declare type CommentReactListParams = { - comment_ids?: string, +declare type ReactionReactParams = { + comment_ids: string, + signature?: string, + signing_ts?: string, + remove?: boolean, + clear_types?: string, + type: string, + channel_id: string, + channel_name: string, +}; + +declare type ReactionReactResponse = { + Reactions: { [string]: { [string]: number} }, +}; + +declare type ReactionListParams = { + comment_ids: string, // CSV of IDs channel_id?: string, channel_name?: string, - wallet_id?: string, - react_types?: string, + signature?: string, + signing_ts?: string, + types?: string, +}; + +declare type ReactionListResponse = { + my_reactions: Array, + others_reactions: Array, }; declare type CommentListParams = { @@ -113,6 +134,28 @@ declare type CommentByIdResponse = { ancestors: Array, } +declare type CommentPinParams = { + comment_id: string, + channel_id: string, + channel_name: string, + remove?: boolean, + signature: string, + signing_ts: string, +} + +declare type CommentPinResponse = { + items: Comment, // "items" is an inherited typo to match SDK. Will be "item" in a new version. +} + +declare type CommentEditParams = { + comment: string, + comment_id: string, + signature: string, + signing_ts: string, +} + +declare type CommentEditResponse = Comment + declare type CommentAbandonParams = { comment_id: string, creator_channel_id?: string, diff --git a/static/app-strings.json b/static/app-strings.json index e3849098f..b8b6816a0 100644 --- a/static/app-strings.json +++ b/static/app-strings.json @@ -1458,6 +1458,8 @@ "Your channel is still being setup, try again in a few moments.": "Your channel is still being setup, try again in a few moments.", "Unable to delete this comment, please try again later.": "Unable to delete this comment, please try again later.", "Unable to edit this comment, please try again later.": "Unable to edit this comment, please try again later.", + "No active channel selected.": "No active channel selected.", + "Unable to verify your channel. Please try again.": "Unable to verify your channel. Please try again.", "Channel cannot be anonymous, please select a channel and try again.": "Channel cannot be anonymous, please select a channel and try again.", "Change to list layout": "Change to list layout", "Change to tile layout": "Change to tile layout", diff --git a/ui/comments.js b/ui/comments.js index 885d54715..8e9c5121e 100644 --- a/ui/comments.js +++ b/ui/comments.js @@ -18,6 +18,10 @@ const Comments = { comment_abandon: (params: CommentAbandonParams) => fetchCommentsApi('comment.Abandon', params), comment_create: (params: CommentCreateParams) => fetchCommentsApi('comment.Create', params), comment_by_id: (params: CommentByIdParams) => fetchCommentsApi('comment.ByID', params), + comment_pin: (params: CommentPinParams) => fetchCommentsApi('comment.Pin', params), + comment_edit: (params: CommentEditParams) => fetchCommentsApi('comment.Edit', params), + reaction_list: (params: ReactionListParams) => fetchCommentsApi('reaction.List', params), + reaction_react: (params: ReactionReactParams) => fetchCommentsApi('reaction.React', params), setting_list: (params: SettingsParams) => fetchCommentsApi('setting.List', params), setting_block_word: (params: BlockWordParams) => fetchCommentsApi('setting.BlockWord', params), setting_unblock_word: (params: BlockWordParams) => fetchCommentsApi('setting.UnBlockWord', params), diff --git a/ui/redux/actions/comments.js b/ui/redux/actions/comments.js index 301fc2011..75fb0aa27 100644 --- a/ui/redux/actions/comments.js +++ b/ui/redux/actions/comments.js @@ -206,7 +206,7 @@ export function doSuperChatList(uri: string) { } export function doCommentReactList(commentIds: Array) { - return (dispatch: Dispatch, getState: GetState) => { + return async (dispatch: Dispatch, getState: GetState) => { const state = getState(); const activeChannelClaim = selectActiveChannelClaim(state); @@ -214,17 +214,24 @@ export function doCommentReactList(commentIds: Array) { type: ACTIONS.COMMENT_REACTION_LIST_STARTED, }); - const params: CommentReactListParams = { + const params: ReactionListParams = { comment_ids: commentIds.join(','), }; if (activeChannelClaim) { - params['channel_name'] = activeChannelClaim.name; - params['channel_id'] = activeChannelClaim.claim_id; + const signatureData = await channelSignName(activeChannelClaim.claim_id, activeChannelClaim.name); + if (!signatureData) { + return dispatch(doToast({ isError: true, message: __('Unable to verify your channel. Please try again.') })); + } + + params.channel_name = activeChannelClaim.name; + params.channel_id = activeChannelClaim.claim_id; + params.signature = signatureData.signature; + params.signing_ts = signatureData.signing_ts; } - return Lbry.comment_react_list(params) - .then((result: CommentReactListResponse) => { + return Comments.reaction_list(params) + .then((result: ReactionListResponse) => { const { my_reactions: myReactions, others_reactions: othersReactions } = result; dispatch({ type: ACTIONS.COMMENT_REACTION_LIST_COMPLETED, @@ -246,7 +253,7 @@ export function doCommentReactList(commentIds: Array) { } export function doCommentReact(commentId: string, type: string) { - return (dispatch: Dispatch, getState: GetState) => { + return async (dispatch: Dispatch, getState: GetState) => { const state = getState(); const activeChannelClaim = selectActiveChannelClaim(state); const pendingReacts = selectPendingCommentReacts(state); @@ -274,11 +281,19 @@ export function doCommentReact(commentId: string, type: string) { const reactKey = `${commentId}:${activeChannelClaim.claim_id}`; const myReacts = makeSelectMyReactionsForComment(reactKey)(state); const othersReacts = makeSelectOthersReactionsForComment(reactKey)(state); - const params: CommentReactParams = { + + const signatureData = await channelSignName(activeChannelClaim.claim_id, activeChannelClaim.name); + if (!signatureData) { + return dispatch(doToast({ isError: true, message: __('Unable to verify your channel. Please try again.') })); + } + + const params: ReactionReactParams = { comment_ids: commentId, channel_name: activeChannelClaim.name, channel_id: activeChannelClaim.claim_id, - react_type: type, + signature: signatureData.signature, + signing_ts: signatureData.signing_ts, + type: type, }; if (myReacts.includes(type)) { @@ -293,6 +308,7 @@ export function doCommentReact(commentId: string, type: string) { } } } + dispatch({ type: ACTIONS.COMMENT_REACT_STARTED, data: commentId + type, @@ -312,8 +328,8 @@ export function doCommentReact(commentId: string, type: string) { }, }); - Lbry.comment_react(params) - .then((result: CommentReactListResponse) => { + Comments.reaction_react(params) + .then((result: ReactionReactResponse) => { dispatch({ type: ACTIONS.COMMENT_REACT_COMPLETED, data: commentId + type, @@ -462,7 +478,7 @@ export function doCommentCreate( } export function doCommentPin(commentId: string, claimId: string, remove: boolean) { - return (dispatch: Dispatch, getState: GetState) => { + return async (dispatch: Dispatch, getState: GetState) => { const state = getState(); const activeChannel = selectActiveChannelClaim(state); @@ -471,16 +487,25 @@ export function doCommentPin(commentId: string, claimId: string, remove: boolean return; } + const signedCommentId = await channelSignData(activeChannel.claim_id, commentId); + if (!signedCommentId) { + return dispatch(doToast({ isError: true, message: __('Unable to verify your channel. Please try again.') })); + } + dispatch({ type: ACTIONS.COMMENT_PIN_STARTED, }); - return Lbry.comment_pin({ + const params: CommentPinParams = { comment_id: commentId, - channel_name: activeChannel.name, channel_id: activeChannel.claim_id, - ...(remove ? { remove: true } : {}), - }) + channel_name: activeChannel.name, + remove: remove, + signature: signedCommentId.signature, + signing_ts: signedCommentId.signing_ts, + }; + + return Comments.comment_pin(params) .then((result: CommentPinResponse) => { dispatch({ type: ACTIONS.COMMENT_PIN_COMPLETED, @@ -577,15 +602,30 @@ export function doCommentUpdate(comment_id: string, comment: string) { if (comment === '') { return doCommentAbandon(comment_id); } else { - return (dispatch: Dispatch) => { + return async (dispatch: Dispatch, getState: GetState) => { + const state = getState(); + + const activeChannelClaim = selectActiveChannelClaim(state); + if (!activeChannelClaim) { + return dispatch(doToast({ isError: true, message: __('No active channel selected.') })); + } + + const signedComment = await channelSignData(activeChannelClaim.claim_id, comment); + if (!signedComment) { + return dispatch(doToast({ isError: true, message: __('Unable to verify your channel. Please try again.') })); + } + dispatch({ type: ACTIONS.COMMENT_UPDATE_STARTED, }); - return Lbry.comment_update({ + + return Comments.comment_edit({ comment_id: comment_id, comment: comment, + signature: signedComment.signature, + signing_ts: signedComment.signing_ts, }) - .then((result: CommentUpdateResponse) => { + .then((result: CommentEditResponse) => { if (result != null) { dispatch({ type: ACTIONS.COMMENT_UPDATE_COMPLETED, @@ -638,6 +678,19 @@ async function channelSignName(channelClaimId: string, channelName: string) { return signedObject; } +async function channelSignData(channelClaimId: string, data: string) { + let signedObject; + + try { + signedObject = await Lbry.channel_sign({ + channel_id: channelClaimId, + hexdata: toHex(data), + }); + } catch (e) {} + + return signedObject; +} + // Hides a users comments from all creator's claims and prevent them from commenting in the future function doCommentModToggleBlock( unblock: boolean,