diff --git a/src/core/client/stream/local/local.graphql b/src/core/client/stream/local/local.graphql index 6c01257c5..088e86390 100644 --- a/src/core/client/stream/local/local.graphql +++ b/src/core/client/stream/local/local.graphql @@ -21,7 +21,7 @@ type AuthPopup { extend type Comment { pending: Boolean - localReplies: CommentsConnection + localReplies: [Comment!] } type Local { diff --git a/src/core/client/stream/mutations/CreateCommentMutation.ts b/src/core/client/stream/mutations/CreateCommentMutation.ts index b858f04e2..b2f1b78fe 100644 --- a/src/core/client/stream/mutations/CreateCommentMutation.ts +++ b/src/core/client/stream/mutations/CreateCommentMutation.ts @@ -1,5 +1,9 @@ import { graphql } from "react-relay"; -import { Environment, RelayMutationConfig } from "relay-runtime"; +import { + ConnectionHandler, + Environment, + RecordSourceSelectorProxy, +} from "relay-runtime"; import { getMe } from "talk-framework/helpers"; import { TalkContext } from "talk-framework/lib/bootstrap"; @@ -14,7 +18,79 @@ import { CreateCommentMutation as MutationTypes } from "talk-stream/__generated_ export type CreateCommentInput = Omit< MutationTypes["variables"]["input"], "clientMutationId" ->; +> & { local?: boolean }; + +function sharedUpdater( + store: RecordSourceSelectorProxy, + input: CreateCommentInput +) { + if (input.local) { + localUpdate(store, input); + } else { + update(store, input); + } +} + +function update(store: RecordSourceSelectorProxy, input: CreateCommentInput) { + // Get the payload returned from the server. + const payload = store.getRootField("createComment")!; + + // Get the edge of the newly created comment. + const newEdge = payload.getLinkedRecord("edge")!; + + // Get parent proxy. + const parentProxy = input.parentID + ? store.get(input.parentID) + : store.get(input.assetID); + + const connectionKey = input.parentID + ? "ReplyList_replies" + : "Stream_comments"; + + const filters = input.parentID + ? { orderBy: "CREATED_AT_ASC" } + : { orderBy: "CREATED_AT_DESC" }; + + const where = input.parentID ? "append" : "prepend"; + + if (parentProxy) { + const con = ConnectionHandler.getConnection( + parentProxy, + connectionKey, + filters + ); + if (con) { + if (where === "prepend") { + ConnectionHandler.insertEdgeBefore(con, newEdge); + } else { + ConnectionHandler.insertEdgeAfter(con, newEdge); + } + } + } +} + +function localUpdate( + store: RecordSourceSelectorProxy, + input: CreateCommentInput +) { + // Get the payload returned from the server. + const payload = store.getRootField("createComment")!; + + // Get the edge of the newly created comment. + const newEdge = payload.getLinkedRecord("edge")!; + const newComment = newEdge.getLinkedRecord("node"); + + // Get parent proxy. + const parentProxy = store.get(input.parentID!); + + if (parentProxy) { + const localReplies = parentProxy.getLinkedRecords("localReplies"); + const nextLocalReplies = localReplies + ? localReplies.concat(newComment) + : [newComment]; + parentProxy.setLinkedRecords(nextLocalReplies, "localReplies"); + } +} const mutation = graphql` mutation CreateCommentMutation($input: CreateCommentInput!) { @@ -32,39 +108,6 @@ const mutation = graphql` let clientMutationId = 0; -function getConfig(input: CreateCommentInput): RelayMutationConfig[] { - if (!input.parentID) { - return [ - { - type: "RANGE_ADD", - connectionInfo: [ - { - key: "Stream_comments", - rangeBehavior: "prepend", - filters: { orderBy: "CREATED_AT_DESC" }, - }, - ], - parentID: input.assetID, - edgeName: "edge", - }, - ]; - } - return [ - { - type: "RANGE_ADD", - connectionInfo: [ - { - key: "ReplyList_replies", - rangeBehavior: "append", - filters: { orderBy: "CREATED_AT_ASC" }, - }, - ], - parentID: input.parentID, - edgeName: "edge", - }, - ]; -} - function commit( environment: Environment, input: CreateCommentInput, @@ -77,7 +120,9 @@ function commit( mutation, variables: { input: { - ...input, + assetID: input.assetID, + parentID: input.parentID, + body: input.body, clientMutationId: clientMutationId.toString(), }, }, @@ -102,9 +147,13 @@ function commit( }, } as any, // TODO: (cvle) generated types should contain one for the optimistic response. optimisticUpdater: store => { + sharedUpdater(store, input); store.get(id)!.setValue(true, "pending"); }, - configs: getConfig(input), + updater: store => { + sharedUpdater(store, input); + }, + // configs: getConfig(input), }); } diff --git a/src/core/client/stream/tabs/comments/components/Indent.css b/src/core/client/stream/tabs/comments/components/Indent.css index b88e0b642..87f598224 100644 --- a/src/core/client/stream/tabs/comments/components/Indent.css +++ b/src/core/client/stream/tabs/comments/components/Indent.css @@ -30,6 +30,12 @@ border-left: 3px solid var(--palette-grey-lighter); } +.level6 { + padding-left: var(--spacing-unit); + margin-left: calc(5 * var(--spacing-unit)); + border-left: 3px solid var(--palette-grey-lightest); +} + .noBorder { border: 0; } diff --git a/src/core/client/stream/tabs/comments/components/Indent.tsx b/src/core/client/stream/tabs/comments/components/Indent.tsx index e91cdb974..45bac72a5 100644 --- a/src/core/client/stream/tabs/comments/components/Indent.tsx +++ b/src/core/client/stream/tabs/comments/components/Indent.tsx @@ -20,6 +20,7 @@ const Indent: StatelessComponent = props => { [styles.level3]: props.level === 3, [styles.level4]: props.level === 4, [styles.level5]: props.level === 5, + [styles.level6]: props.level === 6, [styles.noBorder]: props.noBorder, })} > diff --git a/src/core/client/stream/tabs/comments/components/ReplyList.tsx b/src/core/client/stream/tabs/comments/components/ReplyList.tsx index 735c49600..896120b91 100644 --- a/src/core/client/stream/tabs/comments/components/ReplyList.tsx +++ b/src/core/client/stream/tabs/comments/components/ReplyList.tsx @@ -22,6 +22,8 @@ export interface ReplyListProps { disableShowAll?: boolean; indentLevel?: number; ReplyListComponent?: React.ComponentType; + localReply?: boolean; + disableReplies?: boolean; } function getReplyListElement( @@ -48,6 +50,8 @@ const ReplyList: StatelessComponent = props => { comment={comment} asset={props.asset} indentLevel={props.indentLevel} + localReply={props.localReply} + disableReplies={props.disableReplies} /> {getReplyListElement(props, comment)} diff --git a/src/core/client/stream/tabs/comments/containers/CommentContainer.tsx b/src/core/client/stream/tabs/comments/containers/CommentContainer.tsx index 24e09fd23..bfeca8995 100644 --- a/src/core/client/stream/tabs/comments/containers/CommentContainer.tsx +++ b/src/core/client/stream/tabs/comments/containers/CommentContainer.tsx @@ -26,6 +26,8 @@ interface InnerProps { asset: AssetData; indentLevel?: number; showAuthPopup: ShowAuthPopupMutation; + localReply?: boolean; + disableReplies?: boolean; } interface State { @@ -107,7 +109,13 @@ export class CommentContainer extends Component { } public render() { - const { comment, asset, indentLevel } = this.props; + const { + comment, + asset, + indentLevel, + localReply, + disableReplies, + } = this.props; const { showReplyDialog, showEditDialog, editable } = this.state; if (showEditDialog) { return ( @@ -144,11 +152,13 @@ export class CommentContainer extends Component { } footer={ <> - + {!disableReplies && ( + + )} } @@ -158,6 +168,7 @@ export class CommentContainer extends Component { comment={comment} asset={asset} onClose={this.closeReplyDialog} + localReply={localReply} /> )} diff --git a/src/core/client/stream/tabs/comments/containers/LocalReplyListContainer.tsx b/src/core/client/stream/tabs/comments/containers/LocalReplyListContainer.tsx index ddafe99a7..2429536cb 100644 --- a/src/core/client/stream/tabs/comments/containers/LocalReplyListContainer.tsx +++ b/src/core/client/stream/tabs/comments/containers/LocalReplyListContainer.tsx @@ -25,9 +25,10 @@ export class LocalReplyListContainer extends Component { e.node)} + comments={this.props.comment.localReplies} asset={this.props.asset} indentLevel={this.props.indentLevel} + disableReplies /> ); } @@ -48,12 +49,8 @@ const enhanced = withFragmentContainer({ fragment LocalReplyListContainer_comment on Comment { id localReplies { - edges { - node { - id - ...CommentContainer_comment - } - } + id + ...CommentContainer_comment } } `, diff --git a/src/core/client/stream/tabs/comments/containers/ReplyCommentFormContainer.spec.tsx b/src/core/client/stream/tabs/comments/containers/ReplyCommentFormContainer.spec.tsx index e189de00d..25a90d517 100644 --- a/src/core/client/stream/tabs/comments/containers/ReplyCommentFormContainer.spec.tsx +++ b/src/core/client/stream/tabs/comments/containers/ReplyCommentFormContainer.spec.tsx @@ -92,7 +92,7 @@ it("save values", async () => { it("creates a comment", async () => { const assetID = "asset-id"; - const input = { body: "Hello World!" }; + const input = { body: "Hello World!", local: false }; const createCommentStub = sinon.stub(); const form = { reset: noop }; const onCloseStub = sinon.stub(); diff --git a/src/core/client/stream/tabs/comments/containers/ReplyCommentFormContainer.tsx b/src/core/client/stream/tabs/comments/containers/ReplyCommentFormContainer.tsx index 6a724b82e..a634d2776 100644 --- a/src/core/client/stream/tabs/comments/containers/ReplyCommentFormContainer.tsx +++ b/src/core/client/stream/tabs/comments/containers/ReplyCommentFormContainer.tsx @@ -25,6 +25,7 @@ interface InnerProps { asset: AssetData; onClose?: () => void; autofocus: boolean; + localReply?: boolean; } interface State { @@ -76,6 +77,7 @@ export class ReplyCommentFormContainer extends Component { await this.props.createComment({ assetID: this.props.asset.id, parentID: this.props.comment.id, + local: this.props.localReply, ...input, }); diff --git a/src/core/client/stream/tabs/comments/containers/ReplyListContainer.spec.tsx b/src/core/client/stream/tabs/comments/containers/ReplyListContainer.spec.tsx index 08508b157..d383a4e7e 100644 --- a/src/core/client/stream/tabs/comments/containers/ReplyListContainer.spec.tsx +++ b/src/core/client/stream/tabs/comments/containers/ReplyListContainer.spec.tsx @@ -29,6 +29,7 @@ it("renders correctly", () => { me: null, indentLevel: 1, ReplyListComponent: () => null, + localReply: false, }; const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); @@ -50,6 +51,7 @@ it("renders correctly when replies are null", () => { me: null, indentLevel: 1, ReplyListComponent: undefined, + localReply: false, }; const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); @@ -75,6 +77,7 @@ describe("when has more replies", () => { me: null, indentLevel: 1, ReplyListComponent: undefined, + localReply: false, }; let wrapper: ShallowWrapper; diff --git a/src/core/client/stream/tabs/comments/containers/ReplyListContainer.tsx b/src/core/client/stream/tabs/comments/containers/ReplyListContainer.tsx index c707cdc94..9edf46d1e 100644 --- a/src/core/client/stream/tabs/comments/containers/ReplyListContainer.tsx +++ b/src/core/client/stream/tabs/comments/containers/ReplyListContainer.tsx @@ -22,6 +22,7 @@ export interface InnerProps { relay: RelayPaginationProp; indentLevel: number; ReplyListComponent: React.ComponentType | undefined; + localReply: boolean | undefined; } // TODO: (cvle) This should be autogenerated. @@ -54,6 +55,7 @@ export class ReplyListContainer extends React.Component { disableShowAll={this.state.disableShowAll} indentLevel={this.props.indentLevel} ReplyListComponent={this.props.ReplyListComponent} + localReply={this.props.localReply} /> ); } @@ -85,9 +87,10 @@ function createReplyListContainer( comment: GraphQLTaggedNode; }, query: GraphQLTaggedNode, - ReplyListComponent?: React.ComponentType + ReplyListComponent?: React.ComponentType, + localReply?: boolean ) { - return withProps({ indentLevel, ReplyListComponent })( + return withProps({ indentLevel, ReplyListComponent, localReply })( withPaginationContainer< InnerProps, ReplyListContainer1PaginationQueryVariables, @@ -170,7 +173,8 @@ const ReplyListContainer5 = createReplyListContainer( `, (props: PropTypesOf) => ( - ) + ), + true ); const ReplyListContainer4 = createReplyListContainer( diff --git a/src/core/client/stream/tabs/comments/containers/__snapshots__/ReplyListContainer.spec.tsx.snap b/src/core/client/stream/tabs/comments/containers/__snapshots__/ReplyListContainer.spec.tsx.snap index ad14ab002..d98e6786a 100644 --- a/src/core/client/stream/tabs/comments/containers/__snapshots__/ReplyListContainer.spec.tsx.snap +++ b/src/core/client/stream/tabs/comments/containers/__snapshots__/ReplyListContainer.spec.tsx.snap @@ -39,6 +39,7 @@ exports[`renders correctly 1`] = ` } disableShowAll={false} indentLevel={1} + localReply={false} me={null} onShowAll={[Function]} /> @@ -85,6 +86,7 @@ exports[`when has more replies renders hasMore 1`] = ` disableShowAll={false} hasMore={true} indentLevel={1} + localReply={false} me={null} onShowAll={[Function]} /> @@ -129,6 +131,7 @@ exports[`when has more replies when showing all disables show all button 1`] = ` disableShowAll={true} hasMore={true} indentLevel={1} + localReply={false} me={null} onShowAll={[Function]} /> @@ -173,6 +176,7 @@ exports[`when has more replies when showing all enable show all button after loa disableShowAll={false} hasMore={true} indentLevel={1} + localReply={false} me={null} onShowAll={[Function]} /> diff --git a/src/core/client/stream/test/comments/__snapshots__/postLocalReply.spec.tsx.snap b/src/core/client/stream/test/comments/__snapshots__/postLocalReply.spec.tsx.snap new file mode 100644 index 000000000..35948074c --- /dev/null +++ b/src/core/client/stream/test/comments/__snapshots__/postLocalReply.spec.tsx.snap @@ -0,0 +1,2939 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`post a reply: open reply form 1`] = ` +
+
+
+
+
+
+ Signed in as + + Markus + + . +
+
+ + Not you?  + + +
+
+
+
+
+
+ +
+
+
+ + + +
+ +
+
+
+
+
+
+ + Powered by + + ⁨The Coral Project⁩ + + +
+ +
+
+ +
+
+
+
+
+
+
+
+ + Markus + +
+ +
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+ + Markus + +
+ +
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+ + Markus + +
+ +
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+ + Markus + +
+ +
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+ + Markus + +
+ +
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+ + Markus + +
+ +
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+ + + +
+ +
+
+
+
+
+ + +
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+`; + +exports[`post a reply: optimistic response 1`] = ` +
+
+
+
+
+
+ Signed in as + + Markus + + . +
+
+ + Not you?  + + +
+
+
+
+
+
+ +
+
+
+ + + +
+ +
+
+
+
+
+
+ + Powered by + + ⁨The Coral Project⁩ + + +
+ +
+
+ +
+
+
+
+
+
+
+
+ + Markus + +
+ +
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+ + Markus + +
+ +
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+ + Markus + +
+ +
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+ + Markus + +
+ +
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+ + Markus + +
+ +
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+ + Markus + +
+ +
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+ + + +
+
Hello world!", + } + } + disabled={true} + id="comments-replyCommentForm-rte-comment-with-deepest-replies-5" + onBlur={[Function]} + onChange={[Function]} + onCut={[Function]} + onFocus={[Function]} + onInput={[Function]} + onKeyDown={[Function]} + onPaste={[Function]} + onSelect={[Function]} + /> +
+
+
+
+ + +
+
+ +
+
+
+
+
+
+
+ + Markus + +
+ +
+
+
+ +
+
+
Hello world!", + } + } + /> +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`; + +exports[`post a reply: server response 1`] = ` +
+
+
+
+
+
+ Signed in as + + Markus + + . +
+
+ + Not you?  + + +
+
+
+
+
+
+ +
+
+
+ + + +
+ +
+
+
+
+
+
+ + Powered by + + ⁨The Coral Project⁩ + + +
+ +
+
+ +
+
+
+
+
+
+
+
+ + Markus + +
+ +
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+ + Markus + +
+ +
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+ + Markus + +
+ +
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+ + Markus + +
+ +
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+ + Markus + +
+ +
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+ + Markus + +
+ +
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+ + Markus + +
+ +
+
+
+
Hello world! (from server)", + } + } + /> +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`; + +exports[`renders comment stream 1`] = ` +
+
+
+
+
+
+ Signed in as + + Markus + + . +
+
+ + Not you?  + + +
+
+
+
+
+
+ +
+
+
+ + + +
+ +
+
+
+
+
+
+ + Powered by + + ⁨The Coral Project⁩ + + +
+ +
+
+ +
+
+
+
+
+
+
+
+ + Markus + +
+ +
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+ + Markus + +
+ +
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+ + Markus + +
+ +
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+ + Markus + +
+ +
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+ + Markus + +
+ +
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+ + Markus + +
+ +
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`; diff --git a/src/core/client/stream/test/comments/postLocalReply.spec.tsx b/src/core/client/stream/test/comments/postLocalReply.spec.tsx new file mode 100644 index 000000000..f0e5c8949 --- /dev/null +++ b/src/core/client/stream/test/comments/postLocalReply.spec.tsx @@ -0,0 +1,110 @@ +import { ReactTestRenderer } from "react-test-renderer"; +import timekeeper from "timekeeper"; + +import { timeout } from "talk-common/utils"; +import { createSinonStub } from "talk-framework/testHelpers"; + +import { assetWithDeepestReplies, users } from "../fixtures"; +import create from "./create"; + +let testRenderer: ReactTestRenderer; +beforeEach(() => { + const resolvers = { + Query: { + asset: createSinonStub( + s => s.throws(), + s => s.returns(assetWithDeepestReplies) + ), + me: createSinonStub(s => s.throws(), s => s.returns(users[0])), + }, + Mutation: { + createComment: createSinonStub( + s => s.throws(), + s => + s + .withArgs(undefined, { + input: { + assetID: assetWithDeepestReplies.id, + parentID: "comment-with-deepest-replies-5", + body: "Hello world!", + clientMutationId: "0", + }, + }) + .returns({ + edge: { + cursor: null, + node: { + id: "comment-x", + author: users[0], + body: "Hello world! (from server)", + createdAt: "2018-07-06T18:24:00.000Z", + replies: { + edges: [], + pageInfo: { endCursor: null, hasNextPage: false }, + }, + editing: { + edited: false, + editableUntil: "2018-07-06T18:24:30.000Z", + }, + }, + }, + clientMutationId: "0", + }) + ), + }, + }; + + ({ testRenderer } = create({ + // Set this to true, to see graphql responses. + logNetwork: false, + resolvers, + initLocalState: localRecord => { + localRecord.setValue(assetWithDeepestReplies.id, "assetID"); + }, + })); +}); + +it("renders comment stream", async () => { + // Wait for loading. + await timeout(); + expect(testRenderer.toJSON()).toMatchSnapshot(); +}); + +it("post a reply", async () => { + // Wait for loading. + await timeout(); + + // Open reply form. + testRenderer.root + .findByProps({ + id: + "comments-commentContainer-replyButton-comment-with-deepest-replies-5", + }) + .props.onClick(); + + await timeout(); + expect(testRenderer.toJSON()).toMatchSnapshot("open reply form"); + + // Write reply . + testRenderer.root + .findByProps({ + inputId: "comments-replyCommentForm-rte-comment-with-deepest-replies-5", + }) + .props.onChange({ html: "Hello world!" }); + + timekeeper.freeze(new Date("2018-07-06T18:24:00.000Z")); + testRenderer.root + .findByProps({ + id: "comments-replyCommentForm-form-comment-with-deepest-replies-5", + }) + .props.onSubmit(); + // Test optimistic response. + expect(testRenderer.toJSON()).toMatchSnapshot("optimistic response"); + timekeeper.reset(); + + // Wait for loading. + await timeout(); + + // Test after server response. + expect(testRenderer.toJSON()).toMatchSnapshot("server response"); +}); diff --git a/src/core/client/stream/test/fixtures.ts b/src/core/client/stream/test/fixtures.ts index 47485cfa7..a0e6e6a31 100644 --- a/src/core/client/stream/test/fixtures.ts +++ b/src/core/client/stream/test/fixtures.ts @@ -171,3 +171,110 @@ export const assetWithDeepReplies = { }, }, }; + +export const commentWithDeepestReplies = { + ...commentWithReplies, + id: "comment-with-deepest-replies", + body: "body 0", + replies: { + ...commentWithReplies.replies, + edges: [ + { + cursor: commentWithReplies.createdAt, + node: { + ...commentWithReplies, + id: "comment-with-deepest-replies-1", + body: "body 1", + replies: { + ...commentWithReplies.replies, + edges: [ + { + cursor: commentWithReplies.createdAt, + node: { + ...commentWithReplies, + id: "comment-with-deepest-replies-2", + body: "body 2", + replies: { + ...commentWithReplies.replies, + edges: [ + { + cursor: commentWithReplies.createdAt, + node: { + ...commentWithReplies, + id: "comment-with-deepest-replies-3", + body: "body 3", + replies: { + ...commentWithReplies.replies, + edges: [ + { + cursor: commentWithReplies.createdAt, + node: { + ...commentWithReplies, + id: "comment-with-deepest-replies-4", + body: "body 4", + replies: { + ...commentWithReplies.replies, + edges: [ + { + cursor: commentWithReplies.createdAt, + node: { + ...commentWithReplies, + id: "comment-with-deepest-replies-5", + body: "body 5", + replies: { + ...commentWithReplies.replies, + edges: [ + { + cursor: + commentWithReplies.createdAt, + node: { + ...commentWithReplies, + id: + "comment-with-deepest-replies-6", + body: "body 6", + replies: { + ...commentWithReplies.replies, + edges: [], + }, + }, + }, + ], + }, + }, + }, + ], + }, + }, + }, + ], + }, + }, + }, + ], + }, + }, + }, + ], + }, + }, + }, + ], + }, +}; + +export const assetWithDeepestReplies = { + id: "asset-with-deepest-replies", + url: "http://localhost/assets/asset-with-replies", + isClosed: false, + comments: { + edges: [ + { + node: commentWithDeepestReplies, + cursor: commentWithDeepestReplies.createdAt, + }, + ], + pageInfo: { + hasNextPage: false, + }, + }, +};