Skip to content

Commit

Permalink
feat: new comment observer
Browse files Browse the repository at this point in the history
Signed-off-by: Innei <i@innei.in>
  • Loading branch information
Innei committed Apr 20, 2024
1 parent 6f75a2e commit d8595b8
Show file tree
Hide file tree
Showing 8 changed files with 126 additions and 8 deletions.
4 changes: 2 additions & 2 deletions src/components/modules/comment/CommentBox/hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ import { apiClient } from '~/lib/request'
import { getErrorMessageFromRequestError } from '~/lib/request.shared'
import { jotaiStore } from '~/lib/store'
import { toast } from '~/lib/toast'
import { buildCommentsQueryKey } from '~/queries/keys'

import { buildQueryKey } from '../Comments'
import { MAX_COMMENT_TEXT_LENGTH } from './constants'
import {
CommentBoxContext,
Expand Down Expand Up @@ -221,7 +221,7 @@ export const useSendComment = () => {
? '感谢你的回复!'
: '感谢你的评论!'

const commentListQueryKey = buildQueryKey(originalRefId)
const commentListQueryKey = buildCommentsQueryKey(originalRefId)

toast.success(toastCopy)
jotaiStore.set(textAtom, '')
Expand Down
4 changes: 2 additions & 2 deletions src/components/modules/comment/CommentPinButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import type { Draft } from 'immer'
import type { SVGProps } from 'react'

import { apiClient } from '~/lib/request'
import { buildCommentsQueryKey } from '~/queries/keys'

import { PinIconToggle } from '../shared/PinIconToggle'
import { useCommentBoxRefIdValue } from './CommentBox/hooks'
import { buildQueryKey } from './Comments'

export const CommentPinButton = ({ comment }: { comment: CommentModel }) => {
const queryClient = useQueryClient()
Expand All @@ -20,7 +20,7 @@ export const CommentPinButton = ({ comment }: { comment: CommentModel }) => {
pin={!!comment.pin}
onPinChange={async (nextPin) => {
queryClient.setQueryData<InfiniteData<PaginateResult<CommentModel>>>(
buildQueryKey(refId),
buildCommentsQueryKey(refId),
(old) => {
return produce(old, (draft) => {
if (!draft) return draft
Expand Down
38 changes: 35 additions & 3 deletions src/components/modules/comment/Comments.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,55 @@
'use client'

import { useInfiniteQuery } from '@tanstack/react-query'
import { memo, useMemo } from 'react'
import { memo, useEffect, useMemo } from 'react'
import type { FC } from 'react'
import type { CommentBaseProps } from './types'

import { BusinessEvents } from '@mx-space/webhook'

import { ErrorBoundary } from '~/components/common/ErrorBoundary'
import { NotSupport } from '~/components/common/NotSupport'
import { BottomToUpSoftScaleTransitionView } from '~/components/ui/transition'
import { apiClient } from '~/lib/request'
import { buildCommentsQueryKey } from '~/queries/keys'
import { WsEvent } from '~/socket/util'

import { LoadMoreIndicator } from '../shared/LoadMoreIndicator'
import { Comment } from './Comment'
import { CommentBoxProvider } from './CommentBox/providers'
import { CommentSkeleton } from './CommentSkeleton'

export const buildQueryKey = (refId: string) => ['comments', refId]
const useNewCommentObserver = (refId: string) => {
useEffect(() => {
const currentTitle = document.title

// 当标签页回复前台状态时,将标题重置
const onVisibilityChange = () => {
if (document.visibilityState === 'visible') {
document.title = currentTitle
}
}
document.addEventListener('visibilitychange', onVisibilityChange)

const cleaner = WsEvent.on(BusinessEvents.COMMENT_CREATE, (data: any) => {
if (data.ref === refId) {
// 如果标签页在后台

if (document.visibilityState === 'hidden') {
document.title = `新评论!${currentTitle}`
}
}
})
return () => {
cleaner()
document.removeEventListener('visibilitychange', onVisibilityChange)
}
}, [refId])
}
export const Comments: FC<CommentBaseProps> = ({ refId }) => {
const key = useMemo(() => buildQueryKey(refId), [refId])
useNewCommentObserver(refId)

const key = useMemo(() => buildCommentsQueryKey(refId), [refId])
const { data, isLoading, fetchNextPage, hasNextPage } = useInfiniteQuery({
queryKey: key,
queryFn: async ({ queryKey, pageParam }) => {
Expand Down
19 changes: 19 additions & 0 deletions src/lib/biz.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { getCurrentNoteData } from '~/providers/note/CurrentNoteDataProvider'
import { getGlobalCurrentPostData } from '~/providers/post/CurrentPostDataProvider'

import { isServerSide } from './env'

export const getCurrentPageId = () => {
if (isServerSide) return
const pathname = window.location.pathname

if (pathname.startsWith('/notes/')) {
const noteId = getCurrentNoteData()

return noteId?.data.id
}

if (pathname.startsWith('/posts/')) {
return getGlobalCurrentPostData().id
}
}
1 change: 1 addition & 0 deletions src/queries/keys/comment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const buildCommentsQueryKey = (refId: string) => ['comments', refId]
1 change: 1 addition & 0 deletions src/queries/keys/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './comment'
35 changes: 34 additions & 1 deletion src/socket/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import { queryClient } from '~/providers/root/react-query-provider'
import React from 'react'
import { produce } from 'immer'
import type {
CommentModel,
NoteModel,
PaginateResult,
PostModel,
RecentlyModel,
SayModel,
} from '@mx-space/api-client'
import type { BusinessEvents } from '@mx-space/webhook'
import type { InfiniteData } from '@tanstack/react-query'
import type { ActivityPresence } from '~/models/activity'
import type { AppRouterInstance } from 'next/dist/shared/lib/app-router-context.shared-runtime'
Expand Down Expand Up @@ -43,8 +45,11 @@ import {
setGlobalCurrentPostData,
} from '~/providers/post/CurrentPostDataProvider'
import { queries } from '~/queries/definition'
import { buildCommentsQueryKey } from '~/queries/keys'
import { EventTypes } from '~/types/events'

import { WsEvent } from './util'

const trackerRealtimeEvent = () => {
document.dispatchEvent(
new CustomEvent('impression', {
Expand Down Expand Up @@ -250,6 +255,34 @@ export const eventHandler = (
break
}

case EventTypes.COMMENT_CREATE: {
const payload = data as {
ref: string
id: string
}

const queryData = queryClient.getQueryData<
InfiniteData<PaginateResult<CommentModel>>
>(buildCommentsQueryKey(payload.ref))

if (!queryData) return
for (const page of queryData.pages) {
if (page.data.some((comment) => comment.id === payload.id)) {
return
}
}

requestAnimationFrame(() => {
requestAnimationFrame(() => {
queryClient.invalidateQueries({
queryKey: buildCommentsQueryKey(payload.ref),
})
})
})

break
}

case EventTypes.ACTIVITY_LEAVE_PRESENCE: {
const payload = data as {
identity: string
Expand Down Expand Up @@ -313,13 +346,13 @@ export const eventHandler = (
}

default: {
window.dispatchEvent(new CustomEvent(`event:${type}`, { detail: data }))
if (isDev) {
// eslint-disable-next-line no-console
console.log(type, data)
}
}
}
WsEvent.emit(type as BusinessEvents, data)
}

interface ProcessInfo {
Expand Down
32 changes: 32 additions & 0 deletions src/socket/util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import type { BusinessEvents } from '@mx-space/webhook'

export const buildSocketEventType = (type: string) =>
`ws_event:${type}` as const

export class WsEvent extends Event {
constructor(
type: BusinessEvents,
public data: unknown,
) {
super(buildSocketEventType(type))
}

static on(
type: BusinessEvents,

cb: (data: unknown) => void,
) {
const _cb = (e: any) => {
cb(e.data)
}
document.addEventListener(buildSocketEventType(type), _cb)

return () => {
document.removeEventListener(buildSocketEventType(type), _cb)
}
}

static emit(type: BusinessEvents, data: unknown) {
document.dispatchEvent(new WsEvent(type, data))
}
}

0 comments on commit d8595b8

Please sign in to comment.