diff --git a/src/core/server/graph/tenant/mutators/comment.ts b/src/core/server/graph/tenant/mutators/comment.ts index 8a8e86066..ba0211d1b 100644 --- a/src/core/server/graph/tenant/mutators/comment.ts +++ b/src/core/server/graph/tenant/mutators/comment.ts @@ -2,11 +2,13 @@ import TenantContext from "talk-server/graph/tenant/context"; import { GQLCreateCommentInput, GQLCreateCommentReactionInput, + GQLDeleteCommentReactionInput, GQLEditCommentInput, } from "talk-server/graph/tenant/schema/__generated__/types"; import { create, - createCommentReaction, + createReaction, + deleteReaction, edit, } from "talk-server/services/comments"; @@ -36,7 +38,11 @@ export default (ctx: TenantContext) => ({ ctx.req ), createReaction: (input: GQLCreateCommentReactionInput) => - createCommentReaction(ctx.mongo, ctx.tenant, ctx.user!, { + createReaction(ctx.mongo, ctx.tenant, ctx.user!, { + item_id: input.commentID, + }), + deleteReaction: (input: GQLDeleteCommentReactionInput) => + deleteReaction(ctx.mongo, ctx.tenant, ctx.user!, { item_id: input.commentID, }), }); diff --git a/src/core/server/graph/tenant/resolvers/mutation.ts b/src/core/server/graph/tenant/resolvers/mutation.ts index 2ff79b8d2..25746017f 100644 --- a/src/core/server/graph/tenant/resolvers/mutation.ts +++ b/src/core/server/graph/tenant/resolvers/mutation.ts @@ -27,6 +27,10 @@ const Mutation: GQLMutationTypeResolver = { comment: await ctx.mutators.Comment.createReaction(input), clientMutationId: input.clientMutationId, }), + deleteCommentReaction: async (source, { input }, ctx) => ({ + comment: await ctx.mutators.Comment.deleteReaction(input), + clientMutationId: input.clientMutationId, + }), }; export default Mutation; diff --git a/src/core/server/graph/tenant/schema/schema.graphql b/src/core/server/graph/tenant/schema/schema.graphql index f03de4ffc..6a4b05051 100644 --- a/src/core/server/graph/tenant/schema/schema.graphql +++ b/src/core/server/graph/tenant/schema/schema.graphql @@ -1475,7 +1475,7 @@ type UpdateSettingsPayload { } ################## -## Mutation +## createCommentReaction ################## input CreateCommentReactionInput { @@ -1502,6 +1502,34 @@ type CreateCommentReactionPayload { clientMutationId: String! } +################## +## deleteCommentReaction +################## + +input DeleteCommentReactionInput { + """ + commentID is the Comment's ID that we want to delete a Reaction on. + """ + commentID: ID! + + """ + clientMutationId is required for Relay support. + """ + clientMutationId: String! +} + +type DeleteCommentReactionPayload { + """ + comment is the Comment that the Reaction was deleted on. + """ + comment: Comment + + """ + clientMutationId is required for Relay support. + """ + clientMutationId: String! +} + ################## ## Mutation ################## @@ -1531,6 +1559,14 @@ type Mutation { createCommentReaction( input: CreateCommentReactionInput! ): CreateCommentReactionPayload @auth + + """ + deleteCommentReaction will delete a Reaction authored by the current logged in + user on a Comment if it exists. + """ + deleteCommentReaction( + input: CreateCommentReactionInput! + ): CreateCommentReactionPayload @auth } ################################################################################ diff --git a/src/core/server/models/action.ts b/src/core/server/models/action.ts index 2c1834c4e..c5b8c36d9 100644 --- a/src/core/server/models/action.ts +++ b/src/core/server/models/action.ts @@ -315,6 +315,27 @@ export function encodeActionCounts(...actions: Action[]): EncodedActionCounts { return actionCounts; } +/** + * invertEncodedActionCounts will allow inverting of the action count object. + * + * @param actionCounts the encoded action counts to invert + */ +export function invertEncodedActionCounts( + actionCounts: EncodedActionCounts +): EncodedActionCounts { + for (const key in actionCounts) { + if (!actionCounts.hasOwnProperty(key)) { + continue; + } + + if (actionCounts[key] > 0) { + actionCounts[key] = -actionCounts[key]; + } + } + + return actionCounts; +} + /** * encodeActionCountKeys encodes the action into string keys which represents * the groupings as seen in `EncodedActionCounts`. diff --git a/src/core/server/services/comments/index.ts b/src/core/server/services/comments/index.ts index ee1fb969d..dde8826af 100644 --- a/src/core/server/services/comments/index.ts +++ b/src/core/server/services/comments/index.ts @@ -6,7 +6,10 @@ import { ACTION_TYPE, CreateActionInput, createActions, + deleteAction, + DeleteActionInput, encodeActionCounts, + invertEncodedActionCounts, } from "talk-server/models/action"; import { retrieveAsset, @@ -222,9 +225,50 @@ async function addCommentActions( return comment; } +async function deleteCommentAction( + mongo: Db, + tenant: Tenant, + comment: Readonly, + input: DeleteActionInput +): Promise> { + // Create each of the actions, returning each of the action results. + const { wasDeleted, action } = await deleteAction(mongo, tenant.id, input); + if (wasDeleted) { + // Compute the action counts, and invert them (because we're deleting an + // action). + const actionCounts = invertEncodedActionCounts(encodeActionCounts(action!)); + + // Update the comment action counts here. + const updatedComment = await updateCommentActionCounts( + mongo, + tenant.id, + comment.id, + actionCounts + ); + + // Update the Asset with the updated action counts. + await updateAssetActionCounts( + mongo, + tenant.id, + comment.asset_id, + actionCounts + ); + + // Check to see if there was an actual comment returned. + if (!updatedComment) { + // TODO: (wyattjoh) return a better error. + throw new Error("could not update comment action counts"); + } + + return updatedComment; + } + + return comment; +} + export type CreateCommentReaction = Pick; -export async function createCommentReaction( +export async function createReaction( mongo: Db, tenant: Tenant, author: User, @@ -249,3 +293,29 @@ export async function createCommentReaction( return comment; } + +export type DeleteCommentReaction = Pick; + +export async function deleteReaction( + mongo: Db, + tenant: Tenant, + author: User, + input: DeleteCommentReaction +) { + // Get the Comment that we are leaving the Action on. + let comment = await retrieveComment(mongo, tenant.id, input.item_id); + if (!comment) { + // TODO: replace to match error returned by the models/comments.ts + throw new Error("comment not found"); + } + + // Add the comment actions, and return the Comment that we just updated. + comment = await deleteCommentAction(mongo, tenant, comment, { + action_type: ACTION_TYPE.REACTION, + item_type: ACTION_ITEM_TYPE.COMMENTS, + item_id: input.item_id, + user_id: author.id, + }); + + return comment; +}