diff --git a/src/core/server/graph/tenant/mutators/comment.ts b/src/core/server/graph/tenant/mutators/comment.ts index ba0211d1b..734e724b5 100644 --- a/src/core/server/graph/tenant/mutators/comment.ts +++ b/src/core/server/graph/tenant/mutators/comment.ts @@ -1,13 +1,17 @@ import TenantContext from "talk-server/graph/tenant/context"; import { + GQLCreateCommentFlagInput, GQLCreateCommentInput, GQLCreateCommentReactionInput, + GQLDeleteCommentFlagInput, GQLDeleteCommentReactionInput, GQLEditCommentInput, } from "talk-server/graph/tenant/schema/__generated__/types"; import { create, + createFlag, createReaction, + deleteFlag, deleteReaction, edit, } from "talk-server/services/comments"; @@ -45,4 +49,13 @@ export default (ctx: TenantContext) => ({ deleteReaction(ctx.mongo, ctx.tenant, ctx.user!, { item_id: input.commentID, }), + createFlag: (input: GQLCreateCommentFlagInput) => + createFlag(ctx.mongo, ctx.tenant, ctx.user!, { + item_id: input.commentID, + reason: input.reason, + }), + deleteFlag: (input: GQLDeleteCommentFlagInput) => + deleteFlag(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 25746017f..9074ccfc8 100644 --- a/src/core/server/graph/tenant/resolvers/mutation.ts +++ b/src/core/server/graph/tenant/resolvers/mutation.ts @@ -31,6 +31,14 @@ const Mutation: GQLMutationTypeResolver = { comment: await ctx.mutators.Comment.deleteReaction(input), clientMutationId: input.clientMutationId, }), + createCommentFlag: async (source, { input }, ctx) => ({ + comment: await ctx.mutators.Comment.createFlag(input), + clientMutationId: input.clientMutationId, + }), + deleteCommentFlag: async (source, { input }, ctx) => ({ + comment: await ctx.mutators.Comment.deleteFlag(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 6a4b05051..7ca1ec4dc 100644 --- a/src/core/server/graph/tenant/schema/schema.graphql +++ b/src/core/server/graph/tenant/schema/schema.graphql @@ -1530,6 +1530,67 @@ type DeleteCommentReactionPayload { clientMutationId: String! } +################## +## createCommentFlag +################## + +input CreateCommentFlagInput { + """ + commentID is the Comment's ID that we want to create a Flag on. + """ + commentID: ID! + + """ + reason is the selected reason why the Flag is being created. + """ + reason: COMMENT_FLAG_REPORTED_REASON! + + """ + clientMutationId is required for Relay support. + """ + clientMutationId: String! +} + +type CreateCommentFlagPayload { + """ + comment is the Comment that the Flag was created on. + """ + comment: Comment + + """ + clientMutationId is required for Relay support. + """ + clientMutationId: String! +} + +################## +## deleteCommentFlag +################## + +input DeleteCommentFlagInput { + """ + commentID is the Comment's ID that we want to delete a Flag on. + """ + commentID: ID! + + """ + clientMutationId is required for Relay support. + """ + clientMutationId: String! +} + +type DeleteCommentFlagPayload { + """ + comment is the Comment that the Flag was deleted on. + """ + comment: Comment + + """ + clientMutationId is required for Relay support. + """ + clientMutationId: String! +} + ################## ## Mutation ################## @@ -1554,7 +1615,7 @@ type Mutation { """ createCommentReaction will create a Reaction authored by the current logged in - user on a Comment. + User on a Comment. """ createCommentReaction( input: CreateCommentReactionInput! @@ -1562,11 +1623,25 @@ type Mutation { """ deleteCommentReaction will delete a Reaction authored by the current logged in - user on a Comment if it exists. + User on a Comment if it exists. """ deleteCommentReaction( input: CreateCommentReactionInput! ): CreateCommentReactionPayload @auth + + """ + createCommentFlag will create a Flag authored by the current logged in User on + a given Comment. + """ + createCommentFlag(input: CreateCommentFlagInput!): CreateCommentFlagPayload + @auth + + """ + deleteCommentFlag will create a Flag authored by the current logged in User on + a given Comment. + """ + deleteCommentFlag(input: DeleteCommentFlagInput!): DeleteCommentFlagPayload + @auth } ################################################################################ diff --git a/src/core/server/models/action.ts b/src/core/server/models/action.ts index c5b8c36d9..bcc1c4fd3 100644 --- a/src/core/server/models/action.ts +++ b/src/core/server/models/action.ts @@ -7,7 +7,9 @@ import { Omit, Sub } from "talk-common/types"; import { GQLActionCounts, GQLActionPresence, + GQLCOMMENT_FLAG_DETECTED_REASON, GQLCOMMENT_FLAG_REASON, + GQLCOMMENT_FLAG_REPORTED_REASON, } from "talk-server/graph/tenant/schema/__generated__/types"; import { FilterQuery } from "talk-server/models/query"; import { TenantResource } from "talk-server/models/tenant"; @@ -45,12 +47,20 @@ export enum ACTION_ITEM_TYPE { COMMENTS = "COMMENTS", } +/** + * FLAG_REASON is the reason that a given Flag has been created. + */ +export type FLAG_REASON = + | GQLCOMMENT_FLAG_DETECTED_REASON + | GQLCOMMENT_FLAG_REPORTED_REASON + | GQLCOMMENT_FLAG_REASON; + export interface Action extends TenantResource { readonly id: string; action_type: ACTION_TYPE; item_type: ACTION_ITEM_TYPE; item_id: string; - reason?: GQLCOMMENT_FLAG_REASON; + reason?: FLAG_REASON; user_id?: string; created_at: Date; metadata?: Record; @@ -274,10 +284,15 @@ export async function deleteAction( action_type: input.action_type, item_type: input.item_type, item_id: input.item_id, - reason: input.reason, user_id: input.user_id, }; + // Only add the reason to the filter if it's been specified, otherwise we'll + // never match a Flag that has an unspecified reason. + if (input.reason) { + filter.reason = input.reason; + } + // Remove the action from the database, returning the action that was deleted. const result = await collection(mongo).findOneAndDelete(filter); return { diff --git a/src/core/server/services/comments/index.ts b/src/core/server/services/comments/index.ts index dde8826af..947ac4ae9 100644 --- a/src/core/server/services/comments/index.ts +++ b/src/core/server/services/comments/index.ts @@ -1,6 +1,7 @@ import { Db } from "mongodb"; import { Omit } from "talk-common/types"; +import { GQLCOMMENT_FLAG_REPORTED_REASON } from "talk-server/graph/tenant/schema/__generated__/types"; import { ACTION_ITEM_TYPE, ACTION_TYPE, @@ -319,3 +320,60 @@ export async function deleteReaction( return comment; } + +export type CreateCommentFlag = Pick & { + reason: GQLCOMMENT_FLAG_REPORTED_REASON; +}; + +export async function createFlag( + mongo: Db, + tenant: Tenant, + author: User, + input: CreateCommentFlag +) { + // 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 addCommentActions(mongo, tenant, comment, [ + { + action_type: ACTION_TYPE.FLAG, + reason: input.reason, + item_type: ACTION_ITEM_TYPE.COMMENTS, + item_id: input.item_id, + user_id: author.id, + }, + ]); + + return comment; +} + +export type DeleteCommentFlag = Pick; + +export async function deleteFlag( + mongo: Db, + tenant: Tenant, + author: User, + input: DeleteCommentFlag +) { + // 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.FLAG, + item_type: ACTION_ITEM_TYPE.COMMENTS, + item_id: input.item_id, + user_id: author.id, + }); + + return comment; +}