diff --git a/packages/client/components/DiscussionThreadInput.tsx b/packages/client/components/DiscussionThreadInput.tsx index fc4ff6e8a7e..33d358a4d88 100644 --- a/packages/client/components/DiscussionThreadInput.tsx +++ b/packages/client/components/DiscussionThreadInput.tsx @@ -51,7 +51,6 @@ interface Props { getMaxSortOrder: () => number discussion: DiscussionThreadInput_discussion$key viewer: DiscussionThreadInput_viewer$key - threadParentId?: string isReply?: boolean isDisabled?: boolean isCreatingPoll?: boolean @@ -62,7 +61,6 @@ const DiscussionThreadInput = (props: Props) => { allowedThreadables, getMaxSortOrder, discussion: discussionRef, - threadParentId, viewer: viewerRef, isCreatingPoll } = props @@ -186,6 +184,7 @@ const DiscussionThreadInput = (props: Props) => { const addComment = (rawContent: string) => { submitMutation() + const threadParentId = replyingTo?.threadParentId ?? replyingTo?.id const comment = { content: rawContent, isAnonymous: isAnonymousComment, @@ -234,6 +233,7 @@ const DiscussionThreadInput = (props: Props) => { const addTask = () => { const {viewerId} = atmosphere + const threadParentId = replyingTo?.threadParentId ?? replyingTo?.id const newTask = { status: 'active', sortOrder: dndNoise(), diff --git a/packages/client/components/ThreadedCommentBase.tsx b/packages/client/components/ThreadedCommentBase.tsx index 1c687672a3c..93cec7894d8 100644 --- a/packages/client/components/ThreadedCommentBase.tsx +++ b/packages/client/components/ThreadedCommentBase.tsx @@ -14,36 +14,36 @@ import anonymousAvatar from '../styles/theme/images/anonymous-avatar.svg' import deletedAvatar from '../styles/theme/images/deleted-avatar-placeholder.svg' import {PARABOL_AI_USER_ID} from '../utils/constants' import SendClientSideEvent from '../utils/SendClientSideEvent' +import DiscussionThreadInput from './DiscussionThreadInput' import {DiscussionThreadables} from './DiscussionThreadList' import {TipTapEditor} from './promptResponse/TipTapEditor' import ThreadedAvatarColumn from './ThreadedAvatarColumn' import ThreadedCommentFooter from './ThreadedCommentFooter' import ThreadedCommentHeader from './ThreadedCommentHeader' -import ThreadedItemReply from './ThreadedItemReply' import ThreadedItemWrapper from './ThreadedItemWrapper' interface Props { allowedThreadables: DiscussionThreadables[] comment: ThreadedCommentBase_comment$key - children?: ReactNode // the replies, listed here to avoid a circular reference discussion: ThreadedCommentBase_discussion$key - isReply?: boolean // this comment is a reply & should be indented - viewer: ThreadedCommentBase_viewer$key + repliesList?: ReactNode + getMaxSortOrder: () => number } const ThreadedCommentBase = (props: Props) => { const { allowedThreadables, - children, comment: commentRef, discussion: discussionRef, - viewer: viewerRef + viewer: viewerRef, + repliesList, + getMaxSortOrder } = props const viewer = useFragment( graphql` fragment ThreadedCommentBase_viewer on User { - ...ThreadedItemReply_viewer + ...DiscussionThreadInput_viewer billingTier } `, @@ -53,11 +53,13 @@ const ThreadedCommentBase = (props: Props) => { graphql` fragment ThreadedCommentBase_discussion on Discussion { ...DiscussionThreadInput_discussion - ...ThreadedItemReply_discussion id meetingId teamId discussionTopicId + replyingTo { + id + } } `, discussionRef @@ -66,7 +68,6 @@ const ThreadedCommentBase = (props: Props) => { graphql` fragment ThreadedCommentBase_comment on Comment { ...ThreadedCommentHeader_comment - ...ThreadedItemReply_threadable id isActive content @@ -80,21 +81,13 @@ const ThreadedCommentBase = (props: Props) => { id isViewerReactji } - threadParentId } `, commentRef ) - const isReply = !!props.isReply - const {id: discussionId, meetingId, teamId, discussionTopicId} = discussion - const { - id: commentId, - content, - createdByUserNullable, - isActive, - reactjis, - threadParentId - } = comment + const isReply = !repliesList + const {id: discussionId, meetingId, teamId, discussionTopicId, replyingTo} = discussion + const {id: commentId, content, createdByUserNullable, isActive, reactjis} = comment const picture = isActive ? (createdByUserNullable?.picture ?? anonymousAvatar) : deletedAvatar const {submitMutation, submitting, onError, onCompleted} = useMutationProps() const atmosphere = useAtmosphere() @@ -143,7 +136,6 @@ const ThreadedCommentBase = (props: Props) => { commitLocalUpdate(atmosphere, (store) => { const comment = store.get(commentId) if (!comment) return - comment.setValue(threadParentId, 'threadParentId') store .getRoot() .getLinkedRecord('viewer') @@ -193,13 +185,16 @@ const ThreadedCommentBase = (props: Props) => { onReply={onReply} /> )} - {children} - + {repliesList} + {replyingTo?.id === comment.id && ( + + )} ) diff --git a/packages/client/components/ThreadedItem.tsx b/packages/client/components/ThreadedItem.tsx index 9bad832498a..39c07240995 100644 --- a/packages/client/components/ThreadedItem.tsx +++ b/packages/client/components/ThreadedItem.tsx @@ -60,13 +60,17 @@ export const ThreadedItem = (props: Props) => { __typename replies { ...ThreadedRepliesList_replies + threadSortOrder } } `, threadableRef ) const {__typename, replies} = threadable - const child = ( + const getMaxSortOrder = () => { + return replies ? Math.max(0, ...replies.map((reply) => reply.threadSortOrder || 0)) : 0 + } + const repliesList = ( { task={threadable} discussion={discussion} viewer={viewer} - > - {child} - + repliesList={repliesList} + getMaxSortOrder={getMaxSortOrder} + /> ) } if (__typename === 'Poll') { @@ -101,9 +105,9 @@ export const ThreadedItem = (props: Props) => { comment={threadable} discussion={discussion} viewer={viewer} - > - {child} - + repliesList={repliesList} + getMaxSortOrder={getMaxSortOrder} + /> ) } diff --git a/packages/client/components/ThreadedItemReply.tsx b/packages/client/components/ThreadedItemReply.tsx deleted file mode 100644 index ca282ec963b..00000000000 --- a/packages/client/components/ThreadedItemReply.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import graphql from 'babel-plugin-relay/macro' -import {useFragment} from 'react-relay' -import {ThreadedItemReply_discussion$key} from '~/__generated__/ThreadedItemReply_discussion.graphql' -import {ThreadedItemReply_threadable$key} from '~/__generated__/ThreadedItemReply_threadable.graphql' -import {ThreadedItemReply_viewer$key} from '~/__generated__/ThreadedItemReply_viewer.graphql' -import DiscussionThreadInput from './DiscussionThreadInput' -import {DiscussionThreadables} from './DiscussionThreadList' - -interface Props { - allowedThreadables: DiscussionThreadables[] - threadable: ThreadedItemReply_threadable$key - discussion: ThreadedItemReply_discussion$key - viewer: ThreadedItemReply_viewer$key -} - -const ThreadedItemReply = (props: Props) => { - const { - allowedThreadables, - threadable: threadableRef, - discussion: discussionRef, - viewer: viewerRef - } = props - const viewer = useFragment( - graphql` - fragment ThreadedItemReply_viewer on User { - ...DiscussionThreadInput_viewer - } - `, - viewerRef - ) - const threadable = useFragment( - graphql` - fragment ThreadedItemReply_threadable on Threadable { - id - replies { - id - threadSortOrder - } - } - `, - threadableRef - ) - const discussion = useFragment( - graphql` - fragment ThreadedItemReply_discussion on Discussion { - ...DiscussionThreadInput_discussion - replyingTo { - id - } - } - `, - discussionRef - ) - const {id: threadableId, replies} = threadable - const {replyingTo} = discussion - const isReplying = replyingTo?.id === threadableId - if (!isReplying) return null - const getMaxSortOrder = () => { - return replies ? Math.max(0, ...replies.map((reply) => reply.threadSortOrder || 0)) : 0 - } - return ( - - ) -} - -export default ThreadedItemReply diff --git a/packages/client/components/ThreadedRepliesList.tsx b/packages/client/components/ThreadedRepliesList.tsx index cdfc65286b5..573807f938b 100644 --- a/packages/client/components/ThreadedRepliesList.tsx +++ b/packages/client/components/ThreadedRepliesList.tsx @@ -46,16 +46,17 @@ const ThreadedRepliesList = (props: Props) => { ...ThreadedCommentBase_comment __typename id + threadSortOrder } `, repliesRef ) - // https://sentry.io/organizations/parabol/issues/1569570376/?project=107196&query=is%3Aunresolved - // not sure why this is required addComment and createTask but request replies - if (!replies) return null + const getMaxSortOrder = () => { + return replies ? Math.max(0, ...replies.map((reply) => reply.threadSortOrder || 0)) : 0 + } return ( <> - {replies.map((reply) => { + {replies?.map((reply) => { const {__typename, id} = reply return __typename === 'Task' ? ( { task={reply} discussion={discussion} viewer={viewer} + getMaxSortOrder={getMaxSortOrder} /> ) : ( ) })} diff --git a/packages/client/components/ThreadedTaskBase.tsx b/packages/client/components/ThreadedTaskBase.tsx index 193e20cf09e..6185efee089 100644 --- a/packages/client/components/ThreadedTaskBase.tsx +++ b/packages/client/components/ThreadedTaskBase.tsx @@ -7,11 +7,11 @@ import {ThreadedTaskBase_task$key} from '~/__generated__/ThreadedTaskBase_task.g import {ThreadedTaskBase_viewer$key} from '~/__generated__/ThreadedTaskBase_viewer.graphql' import useAtmosphere from '~/hooks/useAtmosphere' import {PALETTE} from '~/styles/paletteV3' +import DiscussionThreadInput from './DiscussionThreadInput' import {DiscussionThreadables} from './DiscussionThreadList' import NullableTask from './NullableTask/NullableTask' import ThreadedAvatarColumn from './ThreadedAvatarColumn' import ThreadedItemHeaderDescription from './ThreadedItemHeaderDescription' -import ThreadedItemReply from './ThreadedItemReply' import ThreadedItemWrapper from './ThreadedItemWrapper' import ThreadedReplyButton from './ThreadedReplyButton' @@ -35,15 +35,17 @@ const StyledNullableTask = styled(NullableTask)({ interface Props { allowedThreadables: DiscussionThreadables[] task: ThreadedTaskBase_task$key - children?: ReactNode + repliesList?: ReactNode discussion: ThreadedTaskBase_discussion$key viewer: ThreadedTaskBase_viewer$key + getMaxSortOrder: () => number } const ThreadedTaskBase = (props: Props) => { const { allowedThreadables, - children, + repliesList, + getMaxSortOrder, discussion: discussionRef, task: taskRef, viewer: viewerRef @@ -51,7 +53,7 @@ const ThreadedTaskBase = (props: Props) => { const viewer = useFragment( graphql` fragment ThreadedTaskBase_viewer on User { - ...ThreadedItemReply_viewer + ...DiscussionThreadInput_viewer } `, viewerRef @@ -59,7 +61,7 @@ const ThreadedTaskBase = (props: Props) => { const discussion = useFragment( graphql` fragment ThreadedTaskBase_discussion on Discussion { - ...ThreadedItemReply_discussion + ...DiscussionThreadInput_discussion id replyingTo { id @@ -72,7 +74,6 @@ const ThreadedTaskBase = (props: Props) => { graphql` fragment ThreadedTaskBase_task on Task { ...NullableTask_task - ...ThreadedItemReply_threadable id content createdByUser { @@ -85,7 +86,7 @@ const ThreadedTaskBase = (props: Props) => { taskRef ) const {id: discussionId, replyingTo} = discussion - const isReply = !!replyingTo + const isReply = !repliesList const {id: taskId, createdByUser, threadParentId} = task const {picture, preferredName} = createdByUser const atmosphere = useAtmosphere() @@ -108,13 +109,16 @@ const ThreadedTaskBase = (props: Props) => { - {children} - + {repliesList} + {replyingTo?.id === task.id && ( + + )} ) diff --git a/packages/client/mutations/CreateTaskMutation.ts b/packages/client/mutations/CreateTaskMutation.ts index 4e238bd6647..29ef91a1c8f 100644 --- a/packages/client/mutations/CreateTaskMutation.ts +++ b/packages/client/mutations/CreateTaskMutation.ts @@ -32,7 +32,6 @@ graphql` fragment CreateTaskMutation_task on CreateTaskPayload { task { ...CompleteTaskFrag @relay(mask: false) - ...ThreadedItemReply_threadable ...ThreadedItem_threadable discussionId threadSortOrder diff --git a/packages/client/mutations/EditCommentingMutation.ts b/packages/client/mutations/EditCommentingMutation.ts index 058d1b0258c..ba007c2c88f 100644 --- a/packages/client/mutations/EditCommentingMutation.ts +++ b/packages/client/mutations/EditCommentingMutation.ts @@ -19,6 +19,11 @@ const mutation = graphql` mutation EditCommentingMutation($isCommenting: Boolean!, $discussionId: ID!) { editCommenting(isCommenting: $isCommenting, discussionId: $discussionId) { ...EditCommentingMutation_meeting @relay(mask: false) + ... on ErrorPayload { + error { + message + } + } } } `