mirror of
https://github.com/wassname/talk.git
synced 2026-06-27 19:33:06 +08:00
ed92f4916d
* feat: initial implementation
* feat: renamed fields from mutations
* fix: more renaming to streamline {Key,Secret}->SigningSecret
* feat: introduced WrappedInternalError
* feat: enhanced extern payload, more fetch options
- Added tenant.{id,domain} to extern payload
- Added site.id to the extern payload
- Added response size limit to fetch
- Added new SCRAPE_MAX_RESPONSE_SIZE env var for managing the size of
responses for scraping
* fix: fixed bug with scrape invocation
* feat: added more queries + mutations
- Added Query.externalModerationPhase
- Added Mutation.createExternalModerationPhase
- Added Mutation.updateExternalModerationPhase
- Added Mutation.enableExternalModerationPhase
- Added Mutation.disableExternalModerationPhase
- Added Mutation.deleteExternalModerationPhase
- Added Mutation.rotateExternalModerationPhaseSigningSecret
* feat: added secret management
* fix: linting
* fix: merge conflict fix
* feat: added UI
* fix: linting
* fix: linting
* fix: updated snapshots
* fix: improved docs
* fix: improved docs
* fix: added locales
* review: improve naming
* review: some review changes
- Switched /moderation/phase to /moderation/phases
- Fixed scrolling
- Fixed redirection
* fix: added scroll timeout for webhooks
125 lines
4.6 KiB
TypeScript
125 lines
4.6 KiB
TypeScript
import { GraphQLResolveInfo } from "graphql";
|
|
import { defaultTo } from "lodash";
|
|
|
|
import { StoryNotFoundError } from "coral-server/errors";
|
|
import { getRequestedFields } from "coral-server/graph/resolvers/util";
|
|
import {
|
|
ACTION_TYPE,
|
|
decodeActionCounts,
|
|
} from "coral-server/models/action/comment";
|
|
import * as comment from "coral-server/models/comment";
|
|
import {
|
|
getDepth,
|
|
getLatestRevision,
|
|
hasAncestors,
|
|
hasPublishedStatus,
|
|
} from "coral-server/models/comment/helpers";
|
|
import { createConnection } from "coral-server/models/helpers";
|
|
import { getURLWithCommentID } from "coral-server/models/story";
|
|
import { getCommentEditableUntilDate } from "coral-server/services/comments";
|
|
|
|
import {
|
|
GQLComment,
|
|
GQLCommentTypeResolver,
|
|
} from "coral-server/graph/schema/__generated__/types";
|
|
|
|
import GraphContext from "../context";
|
|
|
|
export const maybeLoadOnlyID = (
|
|
ctx: GraphContext,
|
|
info: GraphQLResolveInfo,
|
|
id: string
|
|
) => {
|
|
// Get the field names of the fields being requested, if it's only the ID,
|
|
// we have that, so no need to make a database request.
|
|
const fields = getRequestedFields<GQLComment>(info);
|
|
if (fields.length === 1 && fields[0] === "id") {
|
|
return {
|
|
id,
|
|
};
|
|
}
|
|
|
|
// We want more than the ID! Get the comment!
|
|
// TODO: (wyattjoh) if the parent and the parents (containing the parent) are requested, the parent comment is retrieved from the database twice. Investigate ways of reducing i/o.
|
|
return ctx.loaders.Comments.visible.load(id);
|
|
};
|
|
|
|
export const Comment: GQLCommentTypeResolver<comment.Comment> = {
|
|
body: (c) => (c.revisions.length > 0 ? getLatestRevision(c).body : null),
|
|
// Send the whole comment back when you request revisions. This way, we get to
|
|
// know the comment ID. The field mapping is handled by the CommentRevision
|
|
// resolver.
|
|
revision: (c) =>
|
|
c.revisions.length > 0
|
|
? { revision: getLatestRevision(c), comment: c }
|
|
: null,
|
|
deleted: ({ deletedAt }) => !!deletedAt,
|
|
revisionHistory: (c) =>
|
|
c.revisions.map((revision) => ({ revision, comment: c })),
|
|
editing: ({ revisions, createdAt }, input, ctx) => ({
|
|
// When there is more than one body history, then the comment has been
|
|
// edited.
|
|
edited: revisions.length > 1,
|
|
// The date that the comment is editable until is the tenant's edit window
|
|
// length added to the comment created date.
|
|
editableUntil: getCommentEditableUntilDate(ctx.tenant, createdAt),
|
|
}),
|
|
author: (c, input, ctx) =>
|
|
c.authorID ? ctx.loaders.Users.user.load(c.authorID) : null,
|
|
statusHistory: ({ id }, input, ctx) =>
|
|
ctx.loaders.CommentModerationActions.forComment(input, id),
|
|
replies: (c, input, ctx) =>
|
|
// If there is at least one reply, then use the connection loader, otherwise
|
|
// return a blank connection.
|
|
c.childCount > 0
|
|
? ctx.loaders.Comments.forParent(c.storyID, c.id, input)
|
|
: createConnection(),
|
|
replyCount: async ({ childIDs }, input, ctx) => {
|
|
// TODO: (wyattjoh) the childCount should be used eventually, but it should be managed with the status so it's only a count of published comments
|
|
if (childIDs.length === 0) {
|
|
return 0;
|
|
}
|
|
|
|
const children = await ctx.loaders.Comments.visible.loadMany(childIDs);
|
|
return children.reduce(
|
|
(sum: any, c: any) => (c && hasPublishedStatus(c) ? sum + 1 : sum),
|
|
0
|
|
);
|
|
},
|
|
// Action Counts are encoded, decode them for use with the GraphQL system.
|
|
actionCounts: (c) => decodeActionCounts(c.actionCounts),
|
|
flags: ({ id }, { first, after }, ctx) =>
|
|
ctx.loaders.CommentActions.connection({
|
|
first: defaultTo(first, 10),
|
|
after,
|
|
filter: {
|
|
actionType: ACTION_TYPE.FLAG,
|
|
commentID: id,
|
|
},
|
|
}),
|
|
viewerActionPresence: (c, input, ctx) =>
|
|
ctx.user ? ctx.loaders.Comments.retrieveMyActionPresence.load(c.id) : null,
|
|
parentCount: (c) => getDepth(c),
|
|
depth: (c) => getDepth(c),
|
|
rootParent: (c, input, ctx, info) =>
|
|
hasAncestors(c)
|
|
? maybeLoadOnlyID(ctx, info, c.ancestorIDs[c.ancestorIDs.length - 1])
|
|
: null,
|
|
parent: (c, input, ctx, info) =>
|
|
hasAncestors(c) ? maybeLoadOnlyID(ctx, info, c.parentID) : null,
|
|
parents: (c, input, ctx) =>
|
|
// Some resolver optimization.
|
|
hasAncestors(c)
|
|
? ctx.loaders.Comments.parents(c, input)
|
|
: createConnection(),
|
|
story: (c, input, ctx) => ctx.loaders.Stories.story.load(c.storyID),
|
|
site: (c, input, ctx) => ctx.loaders.Sites.site.load(c.siteID),
|
|
permalink: async ({ id, storyID }, input, ctx) => {
|
|
const story = await ctx.loaders.Stories.story.load(storyID);
|
|
if (!story) {
|
|
throw new StoryNotFoundError(storyID);
|
|
}
|
|
return getURLWithCommentID(story.url, id);
|
|
},
|
|
};
|