mirror of
https://github.com/wassname/talk.git
synced 2026-07-08 04:19:15 +08:00
[CORL-623] add live updates for comments moving from premod to approved (#2571)
* add live updates for comments moving from premod to approved * rename disposables * check if comment is system withheld as well as premod to publish event
This commit is contained in:
committed by
Wyatt Johnson
parent
e4bfbbc1f8
commit
b3064c89de
+12
-2
@@ -25,6 +25,7 @@ import IgnoredTombstoneOrHideContainer from "../../IgnoredTombstoneOrHideContain
|
||||
import { ReplyListContainer } from "../../ReplyList";
|
||||
import AllCommentsTabViewNewMutation from "./AllCommentsTabViewNewMutation";
|
||||
import CommentCreatedSubscription from "./CommentCreatedSubscription";
|
||||
import CommentReleasedSubscription from "./CommentReleasedSubscription";
|
||||
|
||||
import styles from "./AllCommentsTabContainer.css";
|
||||
|
||||
@@ -54,6 +55,9 @@ export const AllCommentsTabContainer: FunctionComponent<Props> = props => {
|
||||
`
|
||||
);
|
||||
const subscribeToCommentCreated = useSubscription(CommentCreatedSubscription);
|
||||
const subscribeToCommentReleased = useSubscription(
|
||||
CommentReleasedSubscription
|
||||
);
|
||||
useEffect(() => {
|
||||
if (!props.story.settings.live.enabled) {
|
||||
return;
|
||||
@@ -78,16 +82,22 @@ export const AllCommentsTabContainer: FunctionComponent<Props> = props => {
|
||||
// Only chronological sort supports top level live updates of incoming comments.
|
||||
return;
|
||||
}
|
||||
const disposable = subscribeToCommentCreated({
|
||||
const newCommentDisposable = subscribeToCommentCreated({
|
||||
storyID: props.story.id,
|
||||
orderBy: commentsOrderBy,
|
||||
});
|
||||
const releasedCommentDisposable = subscribeToCommentReleased({
|
||||
storyID: props.story.id,
|
||||
orderBy: commentsOrderBy,
|
||||
});
|
||||
return () => {
|
||||
disposable.dispose();
|
||||
newCommentDisposable.dispose();
|
||||
releasedCommentDisposable.dispose();
|
||||
};
|
||||
}, [
|
||||
commentsOrderBy,
|
||||
subscribeToCommentCreated,
|
||||
subscribeToCommentReleased,
|
||||
props.story.id,
|
||||
props.relay.hasMore(),
|
||||
props.story.settings.live.enabled,
|
||||
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
import { graphql, requestSubscription } from "react-relay";
|
||||
import {
|
||||
ConnectionHandler,
|
||||
Environment,
|
||||
RecordProxy,
|
||||
RecordSourceSelectorProxy,
|
||||
} from "relay-runtime";
|
||||
|
||||
import {
|
||||
createSubscription,
|
||||
SubscriptionVariables,
|
||||
} from "coral-framework/lib/relay";
|
||||
import { GQLCOMMENT_SORT, GQLCOMMENT_SORT_RL } from "coral-framework/schema";
|
||||
import { CommentReleasedSubscription } from "coral-stream/__generated__/CommentReleasedSubscription.graphql";
|
||||
|
||||
function updateForNewestFirst(
|
||||
store: RecordSourceSelectorProxy,
|
||||
storyID: string
|
||||
) {
|
||||
const rootField = store.getRootField("commentReleased");
|
||||
if (!rootField) {
|
||||
return;
|
||||
}
|
||||
const comment = rootField.getLinkedRecord("comment")!;
|
||||
comment.setValue(true, "enteredLive");
|
||||
const commentsEdge = store.create(
|
||||
`edge-${comment.getValue("id")!}`,
|
||||
"CommentsEdge"
|
||||
);
|
||||
commentsEdge.setValue(comment.getValue("createdAt"), "cursor");
|
||||
commentsEdge.setLinkedRecord(comment, "node");
|
||||
const story = store.get(storyID)!;
|
||||
const connection = ConnectionHandler.getConnection(story, "Stream_comments", {
|
||||
orderBy: GQLCOMMENT_SORT.CREATED_AT_DESC,
|
||||
})!;
|
||||
const linked = connection.getLinkedRecords("viewNewEdges") || [];
|
||||
connection.setLinkedRecords(linked.concat(commentsEdge), "viewNewEdges");
|
||||
}
|
||||
|
||||
function updateForOldestFirst(
|
||||
store: RecordSourceSelectorProxy,
|
||||
storyID: string
|
||||
) {
|
||||
const story = store.get(storyID)!;
|
||||
const connection = ConnectionHandler.getConnection(story, "Stream_comments", {
|
||||
orderBy: GQLCOMMENT_SORT.CREATED_AT_ASC,
|
||||
})!;
|
||||
const pageInfo = connection.getLinkedRecord("pageInfo") as RecordProxy;
|
||||
pageInfo.setValue(true, "hasNextPage");
|
||||
}
|
||||
|
||||
const CommentReleasedSubscription = createSubscription(
|
||||
"subscribeToCommentReleased",
|
||||
(
|
||||
environment: Environment,
|
||||
variables: SubscriptionVariables<CommentReleasedSubscription> & {
|
||||
orderBy: GQLCOMMENT_SORT_RL;
|
||||
}
|
||||
) =>
|
||||
requestSubscription(environment, {
|
||||
subscription: graphql`
|
||||
subscription CommentReleasedSubscription($storyID: ID!) {
|
||||
commentReleased(storyID: $storyID) {
|
||||
comment {
|
||||
id
|
||||
createdAt
|
||||
...AllCommentsTabContainer_comment
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables,
|
||||
updater: store => {
|
||||
if (variables.orderBy === GQLCOMMENT_SORT.CREATED_AT_DESC) {
|
||||
updateForNewestFirst(store, variables.storyID);
|
||||
return;
|
||||
}
|
||||
if (variables.orderBy === GQLCOMMENT_SORT.CREATED_AT_ASC) {
|
||||
updateForOldestFirst(store, variables.storyID);
|
||||
return;
|
||||
}
|
||||
throw new Error(
|
||||
`Unsupport new top level comment live updates for sort ${
|
||||
variables.orderBy
|
||||
}`
|
||||
);
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
export default CommentReleasedSubscription;
|
||||
@@ -0,0 +1,11 @@
|
||||
import { GQLCommentReleasedPayloadTypeResolver } from "coral-server/graph/tenant/schema/__generated__/types";
|
||||
|
||||
import { maybeLoadOnlyID } from "./Comment";
|
||||
import { CommentReleasedInput } from "./Subscription/commentReleased";
|
||||
|
||||
export const CommentReleasedPayload: GQLCommentReleasedPayloadTypeResolver<
|
||||
CommentReleasedInput
|
||||
> = {
|
||||
comment: ({ commentID }, args, ctx, info) =>
|
||||
maybeLoadOnlyID(ctx, info, commentID),
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
import { SubscriptionToCommentReleasedResolver } from "coral-server/graph/tenant/schema/__generated__/types";
|
||||
|
||||
import { createIterator } from "./helpers";
|
||||
import {
|
||||
SUBSCRIPTION_CHANNELS,
|
||||
SubscriptionPayload,
|
||||
SubscriptionType,
|
||||
} from "./types";
|
||||
|
||||
export interface CommentReleasedInput extends SubscriptionPayload {
|
||||
storyID: string;
|
||||
commentID: string;
|
||||
}
|
||||
|
||||
export type CommentReleasedSubscription = SubscriptionType<
|
||||
SUBSCRIPTION_CHANNELS.COMMENT_RELEASED,
|
||||
CommentReleasedInput
|
||||
>;
|
||||
|
||||
export const commentReleased: SubscriptionToCommentReleasedResolver<
|
||||
CommentReleasedInput
|
||||
> = createIterator(SUBSCRIPTION_CHANNELS.COMMENT_RELEASED, {
|
||||
filter: (source, { storyID }) => {
|
||||
if (source.storyID !== storyID) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
});
|
||||
@@ -4,6 +4,7 @@ import { commentCreated } from "./commentCreated";
|
||||
import { commentEnteredModerationQueue } from "./commentEnteredModerationQueue";
|
||||
import { commentFeatured } from "./commentFeatured";
|
||||
import { commentLeftModerationQueue } from "./commentLeftModerationQueue";
|
||||
import { commentReleased } from "./commentReleased";
|
||||
import { commentReplyCreated } from "./commentReplyCreated";
|
||||
import { commentStatusUpdated } from "./commentStatusUpdated";
|
||||
|
||||
@@ -14,4 +15,5 @@ export const Subscription: GQLSubscriptionTypeResolver = {
|
||||
commentReplyCreated,
|
||||
commentStatusUpdated,
|
||||
commentFeatured,
|
||||
commentReleased,
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@ import { CommentCreatedSubscription } from "./commentCreated";
|
||||
import { CommentEnteredModerationQueueSubscription } from "./commentEnteredModerationQueue";
|
||||
import { CommentFeaturedSubscription } from "./commentFeatured";
|
||||
import { CommentLeftModerationQueueSubscription } from "./commentLeftModerationQueue";
|
||||
import { CommentReleasedSubscription } from "./commentReleased";
|
||||
import { CommentReplyCreatedSubscription } from "./commentReplyCreated";
|
||||
import { CommentStatusUpdatedSubscription } from "./commentStatusUpdated";
|
||||
|
||||
@@ -12,6 +13,7 @@ export enum SUBSCRIPTION_CHANNELS {
|
||||
COMMENT_REPLY_CREATED = "COMMENT_REPLY_CREATED",
|
||||
COMMENT_CREATED = "COMMENT_CREATED",
|
||||
COMMENT_FEATURED = "COMMENT_FEATURED",
|
||||
COMMENT_RELEASED = "COMMENT_RELEASED",
|
||||
}
|
||||
|
||||
export interface SubscriptionPayload {
|
||||
@@ -32,4 +34,5 @@ export type SUBSCRIPTION_INPUT =
|
||||
| CommentStatusUpdatedSubscription
|
||||
| CommentReplyCreatedSubscription
|
||||
| CommentCreatedSubscription
|
||||
| CommentFeaturedSubscription;
|
||||
| CommentFeaturedSubscription
|
||||
| CommentReleasedSubscription;
|
||||
|
||||
@@ -14,6 +14,7 @@ import { CommentCreatedPayload } from "./CommentCreatedPayload";
|
||||
import { CommentEnteredModerationQueuePayload } from "./CommentEnteredModerationQueuePayload";
|
||||
import { CommentLeftModerationQueuePayload } from "./CommentLeftModerationQueuePayload";
|
||||
import { CommentModerationAction } from "./CommentModerationAction";
|
||||
import { CommentReleasedPayload } from "./CommentReleasedPayload";
|
||||
import { CommentReplyCreatedPayload } from "./CommentReplyCreatedPayload";
|
||||
import { CommentRevision } from "./CommentRevision";
|
||||
import { CommentStatusUpdatedPayload } from "./CommentStatusUpdatedPayload";
|
||||
@@ -52,6 +53,7 @@ const Resolvers: GQLResolver = {
|
||||
Comment,
|
||||
CommentCounts,
|
||||
CommentCreatedPayload,
|
||||
CommentReleasedPayload,
|
||||
CommentEnteredModerationQueuePayload,
|
||||
CommentLeftModerationQueuePayload,
|
||||
CommentModerationAction,
|
||||
|
||||
@@ -5321,6 +5321,16 @@ type CommentFeaturedPayload {
|
||||
comment: Comment!
|
||||
}
|
||||
|
||||
"""
|
||||
CommentReleasedPayload is returned when a new top level Comment is approved from premod stream
|
||||
"""
|
||||
type CommentReleasedPayload {
|
||||
"""
|
||||
comment is the new top level Comment that was approved on the Story.
|
||||
"""
|
||||
comment: Comment!
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
"""
|
||||
commentEnteredModerationQueue returns when a Comment enters a ModerationQueue.
|
||||
@@ -5353,6 +5363,11 @@ type Subscription {
|
||||
"""
|
||||
commentCreated(storyID: ID!): CommentCreatedPayload!
|
||||
|
||||
"""
|
||||
commentReleased returns when a Comment on a premoderated stream is approved
|
||||
"""
|
||||
commentReleased(storyID: ID!): CommentReleasedPayload!
|
||||
|
||||
"""
|
||||
commentReplyCreated returns when a Comment is posted in the ancestor chain of
|
||||
comments.
|
||||
|
||||
@@ -13,6 +13,7 @@ import { updateCommentStatus } from "coral-server/models/comment";
|
||||
import { updateStoryCounts } from "coral-server/models/story";
|
||||
import { Tenant } from "coral-server/models/tenant";
|
||||
import {
|
||||
publishCommentReleased,
|
||||
publishCommentStatusChanges,
|
||||
publishModerationQueueChanges,
|
||||
} from "coral-server/services/events";
|
||||
@@ -114,6 +115,14 @@ const moderate = (
|
||||
result.comment.id,
|
||||
input.moderatorID
|
||||
);
|
||||
if (
|
||||
[GQLCOMMENT_STATUS.PREMOD, GQLCOMMENT_STATUS.SYSTEM_WITHHELD].includes(
|
||||
result.oldStatus
|
||||
) &&
|
||||
status === "APPROVED"
|
||||
) {
|
||||
publishCommentReleased(publish, result.comment);
|
||||
}
|
||||
|
||||
log.trace({ oldStatus: result.oldStatus }, "adjusted story comment counts");
|
||||
|
||||
|
||||
@@ -57,6 +57,21 @@ export function publishCommentCreated(
|
||||
}
|
||||
}
|
||||
|
||||
export function publishCommentReleased(
|
||||
publish: Publisher,
|
||||
comment: Pick<Comment, "id" | "storyID" | "parentID" | "status">
|
||||
) {
|
||||
if (!comment.parentID && hasPublishedStatus(comment)) {
|
||||
publish({
|
||||
channel: SUBSCRIPTION_CHANNELS.COMMENT_RELEASED,
|
||||
payload: {
|
||||
commentID: comment.id,
|
||||
storyID: comment.storyID,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function publishCommentFeatured(
|
||||
publish: Publisher,
|
||||
comment: Pick<Comment, "id" | "status" | "storyID">
|
||||
|
||||
Reference in New Issue
Block a user