11import { clsx } from 'clsx'
2+ import { uniqWith } from 'lodash-es'
23import { observer } from 'mobx-react-lite'
34import type { NextPage } from 'next'
4- import { Fragment , useCallback , useEffect , useRef , useState } from 'react'
5+ import {
6+ Fragment ,
7+ useCallback ,
8+ useEffect ,
9+ useMemo ,
10+ useRef ,
11+ useState ,
12+ } from 'react'
513import { useInView } from 'react-intersection-observer'
614import { message } from 'react-message-popup'
715import useSWR from 'swr'
@@ -30,19 +38,18 @@ import styles from './index.module.css'
3038
3139const FETCH_SIZE = 10
3240
33- const useDataEventHandler = ( data , setData ) => {
41+ const useDataEventHandler = ( ) => {
42+ const [ newData , setNewData ] = useState <
43+ ( RecentlyModel & { comments : number } ) [ ]
44+ > ( [ ] )
45+ const [ deleteIds , setDeleteIds ] = useState < string [ ] > ( [ ] )
3446 useEffect ( ( ) => {
3547 const create = ( payload : RecentlyModel ) => {
36- data . unshift ( payload )
37- setData ( [ ...data ] )
48+ setNewData ( ( data ) => [ { ...payload , comments : 0 } , ...data ] )
3849 }
3950
4051 const del = ( { id } ) => {
41- const index = data . findIndex ( ( d ) => d . id === id )
42- if ( index != - 1 ) {
43- data . splice ( index , 1 )
44- setData ( [ ...data ] )
45- }
52+ setDeleteIds ( ( ids ) => [ ...ids , id ] )
4653 }
4754
4855 eventBus . on ( EventTypes . RECENTLY_CREATE , create )
@@ -52,7 +59,9 @@ const useDataEventHandler = (data, setData) => {
5259 eventBus . off ( EventTypes . RECENTLY_CREATE , create )
5360 eventBus . off ( EventTypes . RECENTLY_DElETE , del )
5461 }
55- } , [ data ] )
62+ } , [ ] )
63+
64+ return { newData, deleteIds : new Set ( deleteIds ) }
5665}
5766
5867const RecentlyPage : NextPage = ( ) => {
@@ -74,31 +83,42 @@ const RecentlyPage: NextPage = () => {
7483 if ( data . length < FETCH_SIZE ) {
7584 setHasNext ( false )
7685 }
77- return data
86+ return [ before || 'root' , data ] as const
7887 } ,
7988 {
80- revalidateIfStale : false ,
81- revalidateOnFocus : false ,
82- refreshWhenOffline : false ,
83- refreshInterval : 0 ,
84- refreshWhenHidden : false ,
85- revalidateOnReconnect : false ,
89+ isPaused ( ) {
90+ return ! hasNext
91+ } ,
8692 } ,
8793 )
8894
89- const [ data , setData ] = useState < ( RecentlyModel & { comments ?: number } ) [ ] > (
90- [ ] ,
95+ const [ slicedData , setSliceData ] = useState <
96+ Record < string , ( RecentlyModel & { comments ?: number } ) [ ] >
97+ > ( { } )
98+ const data = useMemo (
99+ ( ) => [ ...Object . values ( slicedData ) ] . flat ( 1 ) ,
100+ [ slicedData ] ,
91101 )
92102
93103 useEffect ( ( ) => {
94104 if ( ! fetchedData ) {
95105 return
96106 }
107+ const [ key , data ] = fetchedData
97108
98- setData ( ( d ) => [ ...d , ...fetchedData ] )
109+ if ( ! data ) {
110+ return
111+ }
112+
113+ setSliceData ( ( d ) => {
114+ return {
115+ ...d ,
116+ [ key ] : data ,
117+ }
118+ } )
99119 } , [ fetchedData ] )
100120
101- useDataEventHandler ( data , setData )
121+ const { deleteIds , newData } = useDataEventHandler ( )
102122
103123 const { ref, inView } = useInView ( )
104124
@@ -152,53 +172,63 @@ const RecentlyPage: NextPage = () => {
152172 < Loading />
153173 ) : (
154174 < Fragment >
155- { data . length == 0 ? (
175+ { data . length == 0 && newData . length == 0 ? (
156176 < p className = "text-center" > 这里还没有内容哦</ p >
157177 ) : (
158178 < div className = { styles [ 'bubble-list' ] } >
159- { data . map ( ( d ) => (
160- < Fragment key = { d . id } >
161- < div
162- className = { clsx (
163- 'bubble' ,
164- isLogged ? 'from-me' : 'from-them' ,
165- ) }
166- >
167- < div className = "overflow-hidden" >
168- < Markdown
169- forceBlock
170- value = { d . content }
171- wrapperProps = { wrapperProps }
172- />
173- { d . ref && < RefPreview refModel = { d . ref } /> }
174- </ div >
175-
176- < div className = "text-sm float-right mt-1 flex items-center" >
177- < button
178- className = "inline-flex items-center"
179- onClick = { ( ) =>
180- d . allowComment ? handleClickComment ( d . id ) : void 0
181- }
179+ { uniqWith ( [ ...newData , ...data ] , ( a , b ) => a . id === b . id ) . map (
180+ ( d ) => {
181+ if ( deleteIds . has ( d . id ) ) {
182+ return null
183+ }
184+ return (
185+ < Fragment key = { d . id } >
186+ < div
187+ className = { clsx (
188+ 'bubble' ,
189+ isLogged ? 'from-me' : 'from-them' ,
190+ ) }
182191 >
183- < FontistoComments className = "ml-2 mr-1 opacity-80" />
184- < span className = "mr-2 opacity-80" >
185- { d . comments || 0 }
186- </ span >
187- </ button >
188- < RelativeTime date = { d . created } />
189- </ div >
190-
191- { isLogged && (
192- < div className = "del" onClick = { ( ) => handleDelete ( d . id ) } >
193- < JamTrash className = "mr-2" />
194- 删除
192+ < div className = "overflow-hidden" >
193+ < Markdown
194+ forceBlock
195+ value = { d . content }
196+ wrapperProps = { wrapperProps }
197+ />
198+ { d . ref && < RefPreview refModel = { d . ref } /> }
199+ </ div >
200+
201+ < div className = "text-sm float-right mt-1 flex items-center" >
202+ < button
203+ className = "inline-flex items-center"
204+ onClick = { ( ) =>
205+ d . allowComment ? handleClickComment ( d . id ) : void 0
206+ }
207+ >
208+ < FontistoComments className = "ml-2 mr-1 opacity-80" />
209+ < span className = "mr-2 opacity-80" >
210+ { d . comments || 0 }
211+ </ span >
212+ </ button >
213+ < RelativeTime date = { d . created } />
214+ </ div >
215+
216+ { isLogged && (
217+ < div
218+ className = "del"
219+ onClick = { ( ) => handleDelete ( d . id ) }
220+ >
221+ < JamTrash className = "mr-2" />
222+ 删除
223+ </ div >
224+ ) }
195225 </ div >
196- ) }
197- </ div >
198226
199- < div className = "clear-both" />
200- </ Fragment >
201- ) ) }
227+ < div className = "clear-both" />
228+ </ Fragment >
229+ )
230+ } ,
231+ ) }
202232 </ div >
203233 ) }
204234
0 commit comments