mirror of
https://github.com/fergalmoran/onearmy-community-platform.git
synced 2025-12-22 09:37:54 +00:00
fix: improve comments performance (#3816)
* fix: improve comments performance * fix: discussion stores
This commit is contained in:
@@ -50,7 +50,9 @@ export const CreateComment = (props: Props) => {
|
||||
},
|
||||
}}
|
||||
>
|
||||
{isLoggedIn ? (
|
||||
{!isLoggedIn ? (
|
||||
<LoginPrompt />
|
||||
) : (
|
||||
<>
|
||||
<Textarea
|
||||
value={comment}
|
||||
@@ -83,22 +85,6 @@ export const CreateComment = (props: Props) => {
|
||||
{comment.length}/{maxLength}
|
||||
</Text>
|
||||
</>
|
||||
) : (
|
||||
<Box sx={{ padding: [3, 4] }}>
|
||||
<Text data-cy="comments-login-prompt">
|
||||
Hi there!{' '}
|
||||
<Link
|
||||
to="/sign-in"
|
||||
style={{
|
||||
textDecoration: 'underline',
|
||||
color: 'inherit',
|
||||
}}
|
||||
>
|
||||
Login
|
||||
</Link>{' '}
|
||||
to leave a comment
|
||||
</Text>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</Flex>
|
||||
@@ -121,3 +107,23 @@ export const CreateComment = (props: Props) => {
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
||||
const LoginPrompt = () => {
|
||||
return (
|
||||
<Box sx={{ padding: [3, 4] }}>
|
||||
<Text data-cy="comments-login-prompt">
|
||||
Hi there!{' '}
|
||||
<Link
|
||||
to="/sign-in"
|
||||
style={{
|
||||
textDecoration: 'underline',
|
||||
color: 'inherit',
|
||||
}}
|
||||
>
|
||||
Login
|
||||
</Link>{' '}
|
||||
to leave a comment
|
||||
</Text>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ export const Default: Story = {
|
||||
onMoreComments={() => null}
|
||||
onSubmit={() => null}
|
||||
onSubmitReply={() => Promise.resolve()}
|
||||
isSubmitting={false}
|
||||
isLoggedIn={false}
|
||||
/>
|
||||
)
|
||||
@@ -51,6 +52,7 @@ export const NoComments: Story = {
|
||||
onMoreComments={() => null}
|
||||
onSubmit={() => null}
|
||||
onSubmitReply={() => Promise.resolve()}
|
||||
isSubmitting={false}
|
||||
isLoggedIn={false}
|
||||
/>
|
||||
)
|
||||
@@ -73,6 +75,7 @@ export const LoggedIn: Story = {
|
||||
onMoreComments={() => null}
|
||||
onSubmit={() => null}
|
||||
onSubmitReply={() => Promise.resolve()}
|
||||
isSubmitting={false}
|
||||
isLoggedIn={true}
|
||||
/>
|
||||
)
|
||||
@@ -95,6 +98,7 @@ export const Expandable: Story = {
|
||||
onMoreComments={() => null}
|
||||
onSubmit={() => null}
|
||||
onSubmitReply={() => Promise.resolve()}
|
||||
isSubmitting={false}
|
||||
isLoggedIn={true}
|
||||
/>
|
||||
)
|
||||
@@ -121,6 +125,7 @@ export const WithReplies: Story = {
|
||||
onMoreComments={() => null}
|
||||
onSubmit={() => null}
|
||||
isLoggedIn={true}
|
||||
isSubmitting={false}
|
||||
onSubmitReply={async (commentId, comment) =>
|
||||
alert(`reply to commentId: ${commentId} with comment: ${comment}`)
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ export interface IProps {
|
||||
onMoreComments: () => void
|
||||
onSubmit: (comment: string) => void
|
||||
onSubmitReply: (_id: string, reply: string) => Promise<void>
|
||||
isSubmitting: boolean
|
||||
supportReplies?: boolean
|
||||
}
|
||||
|
||||
@@ -38,11 +39,12 @@ export const DiscussionContainer = (props: IProps) => {
|
||||
onMoreComments,
|
||||
onSubmit,
|
||||
isLoggedIn,
|
||||
isSubmitting,
|
||||
supportReplies = false,
|
||||
} = props
|
||||
|
||||
const [commentBeingRepliedTo, setCommentBeingRepliedTo] = useState<
|
||||
null | string
|
||||
string | null
|
||||
>(null)
|
||||
const structuredComments = useMemo(
|
||||
() => transformToTree(comments),
|
||||
@@ -51,7 +53,8 @@ export const DiscussionContainer = (props: IProps) => {
|
||||
|
||||
const handleSetCommentBeingRepliedTo = (commentId: string | null): void => {
|
||||
if (commentId === commentBeingRepliedTo) {
|
||||
return setCommentBeingRepliedTo(null)
|
||||
setCommentBeingRepliedTo(null)
|
||||
return
|
||||
}
|
||||
setCommentBeingRepliedTo(commentId)
|
||||
}
|
||||
@@ -85,6 +88,7 @@ export const DiscussionContainer = (props: IProps) => {
|
||||
}}
|
||||
>
|
||||
<CreateComment
|
||||
isLoading={isSubmitting}
|
||||
maxLength={maxLength}
|
||||
comment={comment}
|
||||
onChange={onChange}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { observer } from 'mobx-react'
|
||||
import { DiscussionContainer, Loader } from 'oa-components'
|
||||
import { useCommonStores } from 'src/common/hooks/useCommonStores'
|
||||
import { transformToUserComments } from 'src/common/transformToUserComments'
|
||||
@@ -18,9 +19,9 @@ const LOADING_LABEL = 'Loading the awesome discussion'
|
||||
interface IProps {
|
||||
sourceType: IDiscussion['sourceType']
|
||||
sourceId: string
|
||||
setTotalCommentsCount: (number) => void
|
||||
canHideComments?: boolean
|
||||
setTotalCommentsCount: (number: number) => void
|
||||
showComments?: boolean
|
||||
canHideComments?: boolean
|
||||
primaryContentId?: string | undefined
|
||||
}
|
||||
|
||||
@@ -32,7 +33,7 @@ const getHighlightedCommentId = () => {
|
||||
return filterOutResearchUpdate.replace('#comment:', '')
|
||||
}
|
||||
|
||||
export const DiscussionWrapper = (props: IProps) => {
|
||||
export const DiscussionWrapper = observer((props: IProps) => {
|
||||
const {
|
||||
canHideComments,
|
||||
primaryContentId,
|
||||
@@ -45,6 +46,7 @@ export const DiscussionWrapper = (props: IProps) => {
|
||||
const [comment, setComment] = useState('')
|
||||
const [discussion, setDiscussion] = useState<IDiscussion | null>(null)
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||
|
||||
const { discussionStore } = useCommonStores().stores
|
||||
const highlightedCommentId = getHighlightedCommentId()
|
||||
@@ -86,6 +88,7 @@ export const DiscussionWrapper = (props: IProps) => {
|
||||
const handleEdit = async (_id: string, comment: string) => {
|
||||
if (!discussion) return
|
||||
|
||||
setIsSubmitting(true)
|
||||
const updatedDiscussion = await discussionStore.editComment(
|
||||
discussion,
|
||||
_id,
|
||||
@@ -96,6 +99,8 @@ export const DiscussionWrapper = (props: IProps) => {
|
||||
if (updatedDiscussion) {
|
||||
transformComments(updatedDiscussion)
|
||||
}
|
||||
|
||||
setIsSubmitting(false)
|
||||
}
|
||||
|
||||
const handleEditRequest = async () => {
|
||||
@@ -105,6 +110,7 @@ export const DiscussionWrapper = (props: IProps) => {
|
||||
const handleDelete = async (_id: string) => {
|
||||
if (!discussion) return
|
||||
|
||||
setIsSubmitting(true)
|
||||
const updatedDiscussion = await discussionStore.deleteComment(
|
||||
discussion,
|
||||
_id,
|
||||
@@ -114,11 +120,15 @@ export const DiscussionWrapper = (props: IProps) => {
|
||||
if (updatedDiscussion) {
|
||||
transformComments(updatedDiscussion)
|
||||
}
|
||||
|
||||
setIsSubmitting(false)
|
||||
}
|
||||
|
||||
const onSubmit = async (comment: string) => {
|
||||
if (!comment || !discussion) return
|
||||
|
||||
setIsSubmitting(true)
|
||||
|
||||
const updatedDiscussion = await discussionStore.addComment(
|
||||
discussion,
|
||||
comment,
|
||||
@@ -131,11 +141,15 @@ export const DiscussionWrapper = (props: IProps) => {
|
||||
if (updatedDiscussion) {
|
||||
setComment('')
|
||||
}
|
||||
|
||||
setIsSubmitting(false)
|
||||
}
|
||||
|
||||
const handleSubmitReply = async (commentId: string, reply) => {
|
||||
const handleSubmitReply = async (commentId: string, reply: string) => {
|
||||
if (!discussion) return
|
||||
|
||||
setIsSubmitting(true)
|
||||
|
||||
const updatedDiscussion = await discussionStore.addComment(
|
||||
discussion,
|
||||
reply,
|
||||
@@ -146,6 +160,8 @@ export const DiscussionWrapper = (props: IProps) => {
|
||||
if (updatedDiscussion) {
|
||||
transformComments(updatedDiscussion)
|
||||
}
|
||||
|
||||
setIsSubmitting(false)
|
||||
}
|
||||
|
||||
const discussionProps = {
|
||||
@@ -161,6 +177,7 @@ export const DiscussionWrapper = (props: IProps) => {
|
||||
highlightedCommentId,
|
||||
onSubmit,
|
||||
onSubmitReply: handleSubmitReply,
|
||||
isSubmitting,
|
||||
isLoggedIn: discussionStore?.activeUser
|
||||
? !!discussionStore.activeUser
|
||||
: false,
|
||||
@@ -183,4 +200,4 @@ export const DiscussionWrapper = (props: IProps) => {
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useState } from 'react'
|
||||
import React, { useMemo, useState } from 'react'
|
||||
import { Button } from 'oa-components'
|
||||
import { Box } from 'theme-ui'
|
||||
|
||||
interface IProps {
|
||||
children
|
||||
commentCount: number
|
||||
children: React.ReactNode | React.ReactNode[]
|
||||
showComments?: boolean
|
||||
}
|
||||
|
||||
@@ -13,13 +13,9 @@ export const HideDiscussionContainer = ({
|
||||
commentCount,
|
||||
showComments,
|
||||
}: IProps) => {
|
||||
const [viewComments, setViewComments] = useState(showComments || false)
|
||||
const [viewComments, setViewComments] = useState(() => showComments || false)
|
||||
|
||||
const onButtonClick = () => {
|
||||
setViewComments(!viewComments)
|
||||
}
|
||||
|
||||
const setButtonText = () => {
|
||||
const buttonText = useMemo(() => {
|
||||
if (!viewComments) {
|
||||
switch (commentCount) {
|
||||
case 0:
|
||||
@@ -32,7 +28,7 @@ export const HideDiscussionContainer = ({
|
||||
}
|
||||
|
||||
return 'Collapse Comments'
|
||||
}
|
||||
}, [viewComments])
|
||||
|
||||
return (
|
||||
<Box
|
||||
@@ -53,14 +49,14 @@ export const HideDiscussionContainer = ({
|
||||
display: 'block',
|
||||
marginBottom: viewComments ? 2 : 0,
|
||||
}}
|
||||
onClick={onButtonClick}
|
||||
onClick={() => setViewComments((prev) => !prev)}
|
||||
backgroundColor={viewComments ? '#c2daf0' : '#e2edf7'}
|
||||
className={viewComments ? 'viewComments' : ''}
|
||||
data-cy={`HideDiscussionContainer: button ${
|
||||
!viewComments && 'open-comments'
|
||||
}`}
|
||||
>
|
||||
<>{setButtonText()}</>
|
||||
{buttonText}
|
||||
</Button>
|
||||
{viewComments && children}
|
||||
</Box>
|
||||
|
||||
@@ -74,7 +74,9 @@ export const updateDiscussionMetadata = async (
|
||||
const researchRef = db.collection('research').doc(primaryContentId)
|
||||
const research = toJS(await researchRef.get('server')) as IResearch.Item
|
||||
|
||||
if (!research || !research.updates) return
|
||||
if (!research || !research.updates || research.updates.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
const updates = research.updates.map((update) => {
|
||||
return update._id === sourceId ? { ...update, commentCount } : update
|
||||
|
||||
@@ -22,11 +22,11 @@ import type {
|
||||
import type { DocReference } from '../databaseV2/DocReference'
|
||||
import type { IRootStore } from '../RootStore'
|
||||
|
||||
const COLLECTION_NAME = 'discussions'
|
||||
const DISCUSSIONS_COLLECTION = 'discussions'
|
||||
|
||||
export class DiscussionStore extends ModuleStore {
|
||||
constructor(rootStore: IRootStore) {
|
||||
super(rootStore, COLLECTION_NAME)
|
||||
super(rootStore, DISCUSSIONS_COLLECTION)
|
||||
}
|
||||
|
||||
public async fetchOrCreateDiscussionBySource(
|
||||
@@ -37,7 +37,7 @@ export class DiscussionStore extends ModuleStore {
|
||||
const foundDiscussion =
|
||||
toJS(
|
||||
await this.db
|
||||
.collection<IDiscussion>(COLLECTION_NAME)
|
||||
.collection<IDiscussion>(DISCUSSIONS_COLLECTION)
|
||||
.getWhere('sourceId', '==', sourceId),
|
||||
)[0] || null
|
||||
|
||||
@@ -74,7 +74,7 @@ export class DiscussionStore extends ModuleStore {
|
||||
}
|
||||
|
||||
const dbRef = this.db
|
||||
.collection<IDiscussion>(COLLECTION_NAME)
|
||||
.collection<IDiscussion>(DISCUSSIONS_COLLECTION)
|
||||
.doc(newDiscussion._id)
|
||||
|
||||
return await this._updateDiscussion(dbRef, newDiscussion)
|
||||
@@ -91,7 +91,7 @@ export class DiscussionStore extends ModuleStore {
|
||||
|
||||
if (user && comment) {
|
||||
const dbRef = this.db
|
||||
.collection<IDiscussion>(COLLECTION_NAME)
|
||||
.collection<IDiscussion>(DISCUSSIONS_COLLECTION)
|
||||
.doc(discussion._id)
|
||||
|
||||
const currentDiscussion = toJS(await dbRef.get('server'))
|
||||
@@ -120,7 +120,8 @@ export class DiscussionStore extends ModuleStore {
|
||||
newComment,
|
||||
)
|
||||
|
||||
await this._addNotifications(newComment, currentDiscussion)
|
||||
// Do not await so it doesn't block adding a comment
|
||||
this._addNotifications(newComment, currentDiscussion)
|
||||
|
||||
return this._updateDiscussion(dbRef, currentDiscussion)
|
||||
}
|
||||
@@ -143,7 +144,7 @@ export class DiscussionStore extends ModuleStore {
|
||||
|
||||
if (user && comment) {
|
||||
const dbRef = this.db
|
||||
.collection<IDiscussion>(COLLECTION_NAME)
|
||||
.collection<IDiscussion>(DISCUSSIONS_COLLECTION)
|
||||
.doc(discussion._id)
|
||||
|
||||
const currentDiscussion = toJS(await dbRef.get('server'))
|
||||
@@ -185,7 +186,7 @@ export class DiscussionStore extends ModuleStore {
|
||||
|
||||
if (user) {
|
||||
const dbRef = this.db
|
||||
.collection<IDiscussion>(COLLECTION_NAME)
|
||||
.collection<IDiscussion>(DISCUSSIONS_COLLECTION)
|
||||
.doc(discussion._id)
|
||||
|
||||
const currentDiscussion = toJS(await dbRef.get('server'))
|
||||
@@ -271,14 +272,16 @@ export class DiscussionStore extends ModuleStore {
|
||||
)
|
||||
|
||||
if (update.collaborators) {
|
||||
await update.collaborators.map((collaborator) => {
|
||||
this.userNotificationsStore.triggerNotification(
|
||||
'new_comment_discussion',
|
||||
collaborator,
|
||||
url,
|
||||
research.title,
|
||||
)
|
||||
})
|
||||
await Promise.all(
|
||||
update.collaborators.map((collaborator) => {
|
||||
this.userNotificationsStore.triggerNotification(
|
||||
'new_comment_discussion',
|
||||
collaborator,
|
||||
url,
|
||||
research.title,
|
||||
)
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
return
|
||||
@@ -344,7 +347,10 @@ export class DiscussionStore extends ModuleStore {
|
||||
discussion: IDiscussion,
|
||||
): Promise<IDiscussionDB | null> {
|
||||
await dbRef.set({ ...cloneDeep(discussion) })
|
||||
await updateDiscussionMetadata(this.db, discussion)
|
||||
|
||||
// Do not await so it doesn't block adding a comment
|
||||
updateDiscussionMetadata(this.db, discussion)
|
||||
|
||||
const updatedDiscussion = toJS(await dbRef.get('server'))
|
||||
|
||||
return updatedDiscussion ? updatedDiscussion : null
|
||||
@@ -361,8 +367,8 @@ export class DiscussionStore extends ModuleStore {
|
||||
)
|
||||
return discussion
|
||||
|
||||
const dbRef = await this.db
|
||||
.collection<IDiscussion>(COLLECTION_NAME)
|
||||
const dbRef = this.db
|
||||
.collection<IDiscussion>(DISCUSSIONS_COLLECTION)
|
||||
.doc(discussion._id)
|
||||
|
||||
return await this._updateDiscussion(dbRef, {
|
||||
|
||||
Reference in New Issue
Block a user