diff --git a/.all-contributorsrc b/.all-contributorsrc
index f13836e2b8..b420a56a0e 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -607,6 +607,15 @@
"contributions": [
"code"
]
+ },
+ {
+ "login": "dariusmihut",
+ "name": "dariusmihut",
+ "avatar_url": "https://avatars.githubusercontent.com/u/7417010?v=4",
+ "profile": "https://github.com/dariusmihut",
+ "contributions": [
+ "code"
+ ]
}
],
"projectName": "community-platform",
diff --git a/README.md b/README.md
index eb2a173f79..ce4d94ae88 100644
--- a/README.md
+++ b/README.md
@@ -140,6 +140,7 @@ Thanks go to these wonderful people ([emoji key](https://allcontributors.org/doc
viracoding 💻 |
Gashmoh 💻 |
+ dariusmihut 💻 |
diff --git a/package.json b/package.json
index 2025d79fb4..eb2ca2e449 100644
--- a/package.json
+++ b/package.json
@@ -38,7 +38,7 @@
"format:style": "prettier --write '**/*.{md,json,js,tsx,ts}'",
"serve": "npx serve -s build",
"test": "yarn workspace oa-cypress start",
- "test:components": "yarn workspace oa-components test",
+ "test:components": "yarn workspace oa-components test-ci",
"test:unit": "yarn build:themes && yarn build:components && env-cmd -e cra craco test --env=jsdom --runInBand --logHeapUsage --coverage --reporters=default --reporters=jest-junit",
"test:madge": "npx madge --circular --extensions ts,tsx ./ --exclude src/stores",
"storybook": "yarn workspace oa-components start",
diff --git a/packages/components/package.json b/packages/components/package.json
index fba42d713f..e849dd88a2 100644
--- a/packages/components/package.json
+++ b/packages/components/package.json
@@ -14,7 +14,8 @@
"dev": "tsc --watch",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx src --color",
"new-component": "ts-node scripts/newComponent.ts",
- "test": "vitest --coverage"
+ "test": "vitest",
+ "test-ci": "vitest --coverage"
},
"dependencies": {
"@emotion/react": "^11.10.6",
diff --git a/packages/components/src/CommentItem/CommentItem.tsx b/packages/components/src/CommentItem/CommentItem.tsx
index 609af76d76..96547b2f74 100644
--- a/packages/components/src/CommentItem/CommentItem.tsx
+++ b/packages/components/src/CommentItem/CommentItem.tsx
@@ -19,8 +19,9 @@ export interface CommentItemProps {
_id: string
_edited?: string
_created?: string
- handleEdit?: (commentId: string, newCommentText: string) => void
+ handleCommentReply?: (commentId: string | null) => void
handleDelete?: (commentId: string) => Promise
+ handleEdit?: (commentId: string, newCommentText: string) => void
handleEditRequest?: (commentId: string) => Promise
}
@@ -49,6 +50,7 @@ export const CommentItem = (props: CommentItemProps) => {
handleDelete,
handleEdit,
isEditable,
+ handleCommentReply,
} = props
const date = formatDate(_edited || _created)
@@ -103,37 +105,52 @@ export const CommentItem = (props: CommentItemProps) => {
{date}
- {isEditable && (
-
+
+ {isEditable && (
+ <>
+
+
+ >
+ )}
+ {typeof handleCommentReply === 'function' ? (
-
-
- )}
+ ) : null}
+
= () => (
)
export const WithNestedComments: StoryFn = () => {
- // TODO: This is a temporary solution to get nested comments to pass type check
- const comments: any = [
+ const comments = [
fakeComment({
replies: [fakeComment(), fakeComment()],
}),
@@ -49,6 +48,56 @@ export const WithNestedComments: StoryFn = () => {
)
}
+export const WithNestedCommentsAndReplies: StoryFn = () => {
+ const comments = [
+ fakeComment({
+ replies: [fakeComment(), fakeComment()],
+ }),
+ fakeComment(),
+ fakeComment(),
+ ]
+
+ return (
+ {}}
+ handleDelete={() => Promise.resolve()}
+ handleEditRequest={() => Promise.resolve()}
+ handleEdit={() => Promise.resolve()}
+ onMoreComments={() => Promise.resolve()}
+ />
+ )
+}
+
+export const WithNestedCommentsAndRepliesMaxDepthTwo: StoryFn<
+ typeof CommentList
+> = () => {
+ const comments = [
+ fakeComment({
+ replies: [
+ fakeComment({
+ replies: [fakeComment()],
+ }),
+ ],
+ }),
+ ]
+
+ return (
+ {}}
+ handleDelete={() => Promise.resolve()}
+ handleEditRequest={() => Promise.resolve()}
+ handleEdit={() => Promise.resolve()}
+ onMoreComments={() => Promise.resolve()}
+ />
+ )
+}
+
const highlightedCommentList = createFakeComments(20, { isEditable: false })
export const Highlighted: StoryFn = () => (
diff --git a/packages/components/src/CommentList/CommentList.test.tsx b/packages/components/src/CommentList/CommentList.test.tsx
index 4ceb165baf..41f21a3433 100644
--- a/packages/components/src/CommentList/CommentList.test.tsx
+++ b/packages/components/src/CommentList/CommentList.test.tsx
@@ -86,4 +86,33 @@ describe('CommentList', () => {
expect(screen.getAllByTestId('CommentList: item')).toHaveLength(5)
})
+
+ it('does not show reply once max depth is reached', () => {
+ const mockComments = [
+ fakeComment({
+ replies: [
+ fakeComment({
+ replies: [fakeComment()],
+ }),
+ ],
+ }),
+ ]
+
+ const screen = render(
+ <>>}
+ setCommentBeingRepliedTo={() => {}}
+ handleEdit={mockHandleEdit}
+ handleEditRequest={mockHandleEditRequest}
+ handleDelete={mockHandleDelete}
+ onMoreComments={mockOnMoreComments}
+ />,
+ )
+
+ expect(screen.getAllByText('reply')).toHaveLength(2)
+ })
})
diff --git a/packages/components/src/CommentList/CommentList.tsx b/packages/components/src/CommentList/CommentList.tsx
index 5010de2d86..68f8041416 100644
--- a/packages/components/src/CommentList/CommentList.tsx
+++ b/packages/components/src/CommentList/CommentList.tsx
@@ -10,12 +10,17 @@ export type CommentWithReplies = Comment & { replies?: Comment[] }
const MAX_COMMENTS = 5
export interface IProps {
+ supportReplies?: boolean
comments: CommentWithReplies[]
handleEdit: (_id: string, comment: string) => Promise
handleEditRequest: () => Promise
handleDelete: (_id: string) => Promise
highlightedCommentId?: string
onMoreComments: () => void
+ setCommentBeingRepliedTo?: (commentId: string | null) => void
+ replyForm?: (commentId: string) => JSX.Element
+ currentDepth?: number
+ maxDepth?: number
}
export const CommentList = (props: IProps) => {
@@ -26,7 +31,15 @@ export const CommentList = (props: IProps) => {
highlightedCommentId,
handleEdit,
onMoreComments,
+ replyForm,
+ setCommentBeingRepliedTo,
+ supportReplies = false,
+ maxDepth = 9999,
+ currentDepth = 0,
} = props
+
+ const hasRepliesEnabled = supportReplies && currentDepth < maxDepth
+
const [moreComments, setMoreComments] = useState(1)
const shownComments = moreComments * MAX_COMMENTS
@@ -44,6 +57,11 @@ export const CommentList = (props: IProps) => {
}, 0)
}
+ const handleCommentReply =
+ hasRepliesEnabled && setCommentBeingRepliedTo
+ ? setCommentBeingRepliedTo
+ : undefined
+
useEffect(() => {
if (!highlightedCommentId) return
@@ -81,20 +99,27 @@ export const CommentList = (props: IProps) => {
>
+ {replyForm && replyForm(comment._id)}
{comment.replies ? (
) : null}
diff --git a/packages/components/src/CreateComment/CreateComment.tsx b/packages/components/src/CreateComment/CreateComment.tsx
index 87bb48c349..29a5710280 100644
--- a/packages/components/src/CreateComment/CreateComment.tsx
+++ b/packages/components/src/CreateComment/CreateComment.tsx
@@ -11,12 +11,14 @@ export interface Props {
comment: string
placeholder?: string
userProfileType?: string
+ buttonLabel?: string
}
export const CreateComment = (props: Props) => {
const { comment, isLoggedIn, maxLength, onSubmit } = props
const userProfileType = props.userProfileType || 'member'
const placeholder = props.placeholder || 'Leave your questions or feedback...'
+ const buttonLabel = props.buttonLabel ?? 'Leave a comment'
const onChange = (newValue: string) => {
props.onChange && props?.onChange(newValue)
@@ -107,7 +109,7 @@ export const CreateComment = (props: Props) => {
onClick={() => onSubmit(comment)}
sx={{ marginTop: 3 }}
>
- Leave a comment
+ {buttonLabel}
diff --git a/packages/components/src/DiscussionContainer/DiscussionContainer.stories.tsx b/packages/components/src/DiscussionContainer/DiscussionContainer.stories.tsx
index 14eb153809..29fcf4efd8 100644
--- a/packages/components/src/DiscussionContainer/DiscussionContainer.stories.tsx
+++ b/packages/components/src/DiscussionContainer/DiscussionContainer.stories.tsx
@@ -3,7 +3,7 @@ import { useState } from 'react'
import { createFakeComments } from '../utils'
import { DiscussionContainer } from './DiscussionContainer'
-import type { Meta, StoryFn } from '@storybook/react'
+import type { Meta, StoryObj } from '@storybook/react'
export default {
title: 'Components/DiscussionContainer',
@@ -13,74 +13,115 @@ export default {
const fakeComments = createFakeComments(3)
const expandableFakeComments = createFakeComments(15)
-export const Default: StoryFn = () => {
- return (
- Promise.resolve()}
- handleEditRequest={() => Promise.resolve()}
- handleEdit={() => Promise.resolve()}
- maxLength={1000}
- comment={''}
- onChange={() => null}
- onMoreComments={() => null}
- onSubmit={() => null}
- isLoggedIn={false}
- />
- )
+type Story = StoryObj & {
+ render: () => JSX.Element
}
-export const NoComments: StoryFn = () => {
- return (
- Promise.resolve()}
- handleEditRequest={() => Promise.resolve()}
- handleEdit={() => Promise.resolve()}
- maxLength={1000}
- comment={''}
- onChange={() => null}
- onMoreComments={() => null}
- onSubmit={() => null}
- isLoggedIn={false}
- />
- )
+export const Default: Story = {
+ render: () => {
+ return (
+ Promise.resolve()}
+ handleEditRequest={() => Promise.resolve()}
+ handleEdit={() => Promise.resolve()}
+ maxLength={1000}
+ comment={''}
+ onChange={() => null}
+ onMoreComments={() => null}
+ onSubmit={() => null}
+ isLoggedIn={false}
+ />
+ )
+ },
}
-export const LoggedIn: StoryFn = () => {
- const [comment, setComment] = useState('')
+export const NoComments: Story = {
+ render: () => {
+ return (
+ Promise.resolve()}
+ handleEditRequest={() => Promise.resolve()}
+ handleEdit={() => Promise.resolve()}
+ maxLength={1000}
+ comment={''}
+ onChange={() => null}
+ onMoreComments={() => null}
+ onSubmit={() => null}
+ isLoggedIn={false}
+ />
+ )
+ },
+}
+
+export const LoggedIn: Story = {
+ render: () => {
+ const [comment, setComment] = useState('')
- return (
- Promise.resolve()}
- handleEditRequest={() => Promise.resolve()}
- handleEdit={() => Promise.resolve()}
- maxLength={1000}
- comment={comment}
- onChange={setComment}
- onMoreComments={() => null}
- onSubmit={() => null}
- isLoggedIn={true}
- />
- )
+ return (
+ Promise.resolve()}
+ handleEditRequest={() => Promise.resolve()}
+ handleEdit={() => Promise.resolve()}
+ maxLength={1000}
+ comment={comment}
+ onChange={setComment}
+ onMoreComments={() => null}
+ onSubmit={() => null}
+ isLoggedIn={true}
+ />
+ )
+ },
}
-export const Expandable: StoryFn = () => {
- const [comment, setComment] = useState('')
+export const Expandable: Story = {
+ render: () => {
+ const [comment, setComment] = useState('')
+
+ return (
+ Promise.resolve()}
+ handleEditRequest={() => Promise.resolve()}
+ handleEdit={() => Promise.resolve()}
+ maxLength={1000}
+ comment={comment}
+ onChange={setComment}
+ onMoreComments={() => null}
+ onSubmit={() => null}
+ isLoggedIn={true}
+ />
+ )
+ },
+}
+
+export const WithReplies: Story = {
+ render: () => {
+ const [comment, setComment] = useState('')
+
+ const fakeComments = createFakeComments(3)
+
+ fakeComments[0].replies = createFakeComments(2)
- return (
- Promise.resolve()}
- handleEditRequest={() => Promise.resolve()}
- handleEdit={() => Promise.resolve()}
- maxLength={1000}
- comment={comment}
- onChange={setComment}
- onMoreComments={() => null}
- onSubmit={() => null}
- isLoggedIn={true}
- />
- )
+ return (
+ Promise.resolve()}
+ handleEditRequest={() => Promise.resolve()}
+ handleEdit={() => Promise.resolve()}
+ maxLength={1000}
+ comment={comment}
+ onChange={setComment}
+ onMoreComments={() => null}
+ onSubmit={() => null}
+ isLoggedIn={true}
+ onSubmitReply={async (commentId, comment) =>
+ alert(`reply to commentId: ${commentId} with comment: ${comment}`)
+ }
+ />
+ )
+ },
}
diff --git a/packages/components/src/DiscussionContainer/DiscussionContainer.test.tsx b/packages/components/src/DiscussionContainer/DiscussionContainer.test.tsx
index 255b7cd355..098746c4ed 100644
--- a/packages/components/src/DiscussionContainer/DiscussionContainer.test.tsx
+++ b/packages/components/src/DiscussionContainer/DiscussionContainer.test.tsx
@@ -1,13 +1,104 @@
-import { render } from '../tests/utils'
-import { Default } from './DiscussionContainer.stories'
+import { act } from 'react-dom/test-utils'
+import { fireEvent } from '@testing-library/react'
-import type { IProps } from './DiscussionContainer'
+import { render } from '../tests/utils'
+import { Default, WithReplies } from './DiscussionContainer.stories'
describe('DiscussionContainer', () => {
it('validates the component behaviour', () => {
- const { getByText } = render()
+ const { getByText } = render()
expect(getByText('3 Comments')).toBeInTheDocument()
expect(getByText('Leave a comment')).toBeInTheDocument()
+
+ expect(() => getByText('reply')).toThrow()
+ })
+
+ it('allows replying to a comment', async () => {
+ const screen = render()
+
+ const replyButton = screen.getAllByText('reply')[0]
+ expect(replyButton).toBeInTheDocument()
+
+ // Show reply form
+ await act(async () => {
+ await fireEvent.click(replyButton)
+ expect(screen.getAllByText('Send your reply')).toHaveLength(1)
+ })
+
+ // Hide reply form
+ await act(async () => {
+ await fireEvent.click(replyButton)
+ expect(() => {
+ screen.getAllByText('Send your reply')
+ }).toThrow()
+ })
+
+ const SecondReplyButton = screen.getAllByText('reply')[2]
+ expect(SecondReplyButton).toBeInTheDocument()
+
+ // Show reply form
+ await act(async () => {
+ await fireEvent.click(SecondReplyButton)
+ expect(screen.getAllByText('Send your reply')).toHaveLength(1)
+ })
+
+ // Hide reply form
+ await act(async () => {
+ await fireEvent.click(SecondReplyButton)
+ expect(() => {
+ screen.getAllByText('Send your reply')
+ }).toThrow()
+ })
+ })
+
+ it('does not show the reply form more than once', async () => {
+ const screen = render()
+
+ const replyButton = screen.getAllByText('reply')[0]
+ expect(replyButton).toBeInTheDocument()
+
+ // Show first reply form
+ await act(async () => {
+ await fireEvent.click(replyButton)
+ expect(screen.getAllByText('Send your reply')).toHaveLength(1)
+ })
+
+ const SecondReplyButton = screen.getAllByText('reply')[2]
+ expect(SecondReplyButton).toBeInTheDocument()
+
+ // Show second reply form
+ await act(async () => {
+ await fireEvent.click(SecondReplyButton)
+ expect(screen.getAllByText('Send your reply')).toHaveLength(1)
+ })
})
+
+ it.todo('allows replying to a comment', async () => {
+ // const handleSubmitReply: any = vi.fn()
+ // const screen = render(
+ // ,
+ // )
+ // const replyButton = screen.getAllByText('reply')[0]
+ // expect(replyButton).toBeInTheDocument()
+ // // Show reply form
+ // await act(async () => {
+ // await fireEvent.click(replyButton)
+ // expect(screen.getAllByText('Send your reply')).toHaveLength(1)
+ // const textarea = screen.getAllByPlaceholderText(
+ // 'Leave your questions or feedback...',
+ // )[0]
+ // await fireEvent.change(textarea, { target: { value: 'New comment' } })
+ // await fireEvent.click(screen.getByText('Send your reply'))
+ // expect(handleSubmitReply).toHaveBeenCalled()
+ // })
+ })
+
+ it.todo(
+ 'adding a reply to a comment does not affect the primary create comment form',
+ async () => {},
+ )
})
diff --git a/packages/components/src/DiscussionContainer/DiscussionContainer.tsx b/packages/components/src/DiscussionContainer/DiscussionContainer.tsx
index 26df8bb61a..dc3c54f1d4 100644
--- a/packages/components/src/DiscussionContainer/DiscussionContainer.tsx
+++ b/packages/components/src/DiscussionContainer/DiscussionContainer.tsx
@@ -1,6 +1,8 @@
-import { Flex } from 'theme-ui'
+import { useMemo, useState } from 'react'
+import { Box, Flex } from 'theme-ui'
import { CommentList, CreateComment, DiscussionTitle } from '../'
+import { transformToTree } from './transformToStructuredComments'
import type { CommentItemProps as Comment } from '../CommentItem/CommentItem'
@@ -15,7 +17,9 @@ export interface IProps {
onChange: (comment: string) => void
onMoreComments: () => void
onSubmit: (comment: string) => void
+ onSubmitReply?: (_id: string, comment: string) => Promise
isLoggedIn: boolean
+ supportReplies?: boolean
}
export const DiscussionContainer = (props: IProps) => {
@@ -25,25 +29,78 @@ export const DiscussionContainer = (props: IProps) => {
handleDelete,
handleEdit,
handleEditRequest,
+ onSubmitReply,
highlightedCommentId,
maxLength,
onChange,
onMoreComments,
onSubmit,
isLoggedIn,
+ supportReplies = false,
} = props
+ const [commentBeingRepliedTo, setCommentBeingRepliedTo] = useState<
+ null | string
+ >(null)
+ const structuredComments = useMemo(
+ () => transformToTree(comments),
+ [comments],
+ )
+
+ const reployForm = (commentId: string) => {
+ if (commentId !== commentBeingRepliedTo) {
+ return <>>
+ }
+
+ return (
+
+ {
+ if (commentId && onSubmitReply) {
+ onSubmitReply(commentId, comment)
+ }
+ setCommentBeingRepliedTo(null)
+ }}
+ buttonLabel="Send your reply"
+ isLoggedIn={isLoggedIn}
+ />
+
+ )
+ }
+
+ const handleSetCommentBeingRepliedTo = (commentId: string | null): void => {
+ if (commentId === commentBeingRepliedTo) {
+ return setCommentBeingRepliedTo(null)
+ }
+ setCommentBeingRepliedTo(commentId)
+ }
+
return (
<>
{
+ const rootComments: CommentWithRepliesParent[] = []
+ const commentsById: any = {}
+
+ // Traverse the comments and map them to their parent IDs
+ for (const comment of comments) {
+ commentsById[comment._id] = comment
+
+ if (comment.parentCommentId) {
+ const parentComment = commentsById[comment.parentCommentId]
+ if (!parentComment.replies) {
+ parentComment.replies = []
+ }
+
+ parentComment.replies.push(comment)
+ }
+ }
+
+ // Extract the root comments (those with no parent IDs)
+ for (const comment of comments) {
+ if (!comment.parentCommentId) {
+ rootComments.push(comment)
+ }
+ }
+
+ return rootComments
+}
diff --git a/packages/components/src/ImageGallery/ImageGallery.stories.tsx b/packages/components/src/ImageGallery/ImageGallery.stories.tsx
index 26810871d0..4ae11dfe2a 100644
--- a/packages/components/src/ImageGallery/ImageGallery.stories.tsx
+++ b/packages/components/src/ImageGallery/ImageGallery.stories.tsx
@@ -66,3 +66,16 @@ export const ShowNextPrevButtons: StoryFn = (
/>
)
}
+
+export const DoNotShowNextPrevButtons: StoryFn = (
+ props: Omit,
+) => {
+ return (
+
+ )
+}
diff --git a/packages/components/src/ImageGallery/ImageGallery.test.tsx b/packages/components/src/ImageGallery/ImageGallery.test.tsx
index 22af2f6527..c7e3df1e9f 100644
--- a/packages/components/src/ImageGallery/ImageGallery.test.tsx
+++ b/packages/components/src/ImageGallery/ImageGallery.test.tsx
@@ -5,6 +5,7 @@ import { render } from '../tests/utils'
import { ImageGallery } from './ImageGallery'
import {
Default,
+ DoNotShowNextPrevButtons,
NoThumbnails,
ShowNextPrevButtons,
testImages,
@@ -175,4 +176,18 @@ describe('ImageGallery', () => {
expect(nextBtn).toBeInTheDocument()
expect(previousBtn).toBeInTheDocument()
})
+
+ it('does not support show next/previous buttons because only one image', async () => {
+ const { queryByRole } = render(
+ ,
+ )
+
+ const nextBtn = queryByRole('button', { name: 'Next image' })
+ const previousBtn = queryByRole('button', { name: 'Previous image' })
+
+ expect(nextBtn).not.toBeInTheDocument()
+ expect(previousBtn).not.toBeInTheDocument()
+ })
})
diff --git a/packages/components/src/ImageGallery/ImageGallery.tsx b/packages/components/src/ImageGallery/ImageGallery.tsx
index 5a0434f620..b878b7f538 100644
--- a/packages/components/src/ImageGallery/ImageGallery.tsx
+++ b/packages/components/src/ImageGallery/ImageGallery.tsx
@@ -127,7 +127,7 @@ export const ImageGallery = (props: ImageGalleryProps) => {
const activeImage = images[activeImageIndex]
const imageNumber = images.length
const showThumbnails = !props.hideThumbnails && images.length >= 1
- const showNextPrevButton = !!props.showNextPrevButton
+ const showNextPrevButton = !!props.showNextPrevButton && images.length > 1
return activeImage ? (
diff --git a/src/common/transformToUserComments.ts b/src/common/transformToUserComments.ts
new file mode 100644
index 0000000000..97061e8031
--- /dev/null
+++ b/src/common/transformToUserComments.ts
@@ -0,0 +1,10 @@
+import type { IDiscussionComment, IUserPPDB } from 'src/models'
+
+export const transformToUserComments = (
+ comments: IDiscussionComment[],
+ loggedInUser: IUserPPDB | null | undefined,
+) =>
+ comments.map((c) => ({
+ ...c,
+ isEditable: c._creatorId === loggedInUser?._id,
+ }))
diff --git a/src/pages/Question/QuestionComments.tsx b/src/pages/Question/QuestionComments.tsx
index a6f0ee30fb..593c7c7b41 100644
--- a/src/pages/Question/QuestionComments.tsx
+++ b/src/pages/Question/QuestionComments.tsx
@@ -1,5 +1,6 @@
import { useEffect, useState } from 'react'
import { DiscussionContainer } from 'oa-components'
+import { transformToUserComments } from 'src/common/transformToUserComments'
import { MAX_COMMENT_LENGTH } from 'src/constants'
import { logger } from 'src/logger'
import { useDiscussionStore } from 'src/stores/Discussions/discussions.store'
@@ -64,6 +65,21 @@ export const QuestionComments = ({
}
}
+ const handleSubmitReply = async (commentId: string, reply) => {
+ logger.info({ commentId, reply }, 'reply submitted')
+ if (discussionObject) {
+ const updatedObj = await store.addComment(
+ discussionObject,
+ reply,
+ commentId,
+ )
+ commentsUpdated &&
+ commentsUpdated(
+ transformToUserComments(updatedObj?.comments || [], activeUser),
+ )
+ }
+ }
+
return (
)
}
-
-const transformToUserComments = (
- comments: IDiscussionComment[],
- loggedInUser: IUserPPDB | null | undefined,
-) =>
- comments.map((c) => ({
- ...c,
- isEditable: c._creatorId === loggedInUser?._id,
- }))
diff --git a/src/pages/Question/QuestionPage.tsx b/src/pages/Question/QuestionPage.tsx
index 2f484b4ea7..d04a5e83d0 100644
--- a/src/pages/Question/QuestionPage.tsx
+++ b/src/pages/Question/QuestionPage.tsx
@@ -6,6 +6,7 @@ import {
ModerationStatus,
UsefulStatsButton,
} from 'oa-components'
+import { transformToUserComments } from 'src/common/transformToUserComments'
import { logger } from 'src/logger'
import { useDiscussionStore } from 'src/stores/Discussions/discussions.store'
import { useQuestionStore } from 'src/stores/Question/question.store'
@@ -15,7 +16,7 @@ import { Box, Button, Card, Flex, Heading, Text } from 'theme-ui'
import { ContentAuthorTimestamp } from '../common/ContentAuthorTimestamp/ContentAuthorTimestamp'
import { QuestionComments } from './QuestionComments'
-import type { IDiscussionComment, IQuestion, IUserPPDB } from 'src/models'
+import type { IDiscussionComment, IQuestion } from 'src/models'
export const QuestionPage = () => {
const { slug } = useParams()
@@ -190,12 +191,3 @@ export const QuestionPage = () => {
)
}
-
-const transformToUserComments = (
- comments: IDiscussionComment[],
- loggedInUser: IUserPPDB | null | undefined,
-) =>
- comments.map((c) => ({
- ...c,
- isEditable: c._creatorId === loggedInUser?._id,
- }))