1- import React , { useEffect , useState } from 'react' ;
1+ import React , { useEffect , useState , useRef } from 'react' ;
22import { useParams , Link } from 'react-router-dom' ;
33import { Loader2 , User as UserIcon , Clock , Send , Quote } from 'lucide-react' ;
44import { Topic , Post , getTopic , listPosts , createPost } from '../lib/forum' ;
@@ -19,16 +19,35 @@ export function TopicPage() {
1919 const [ isReplying , setIsReplying ] = useState ( false ) ;
2020 const [ authorSearch , setAuthorSearch ] = useState ( '' ) ;
2121 const [ textSearch , setTextSearch ] = useState ( '' ) ;
22+ const [ debouncedAuthorSearch , setDebouncedAuthorSearch ] = useState ( '' ) ;
23+ const [ debouncedTextSearch , setDebouncedTextSearch ] = useState ( '' ) ;
24+ const authorTimeoutRef = useRef < NodeJS . Timeout | null > ( null ) ;
25+ const textTimeoutRef = useRef < NodeJS . Timeout | null > ( null ) ;
2226 const { t } = useTranslation ( ) ;
2327 const { user } = useAuth ( ) ;
28+
29+ useEffect ( ( ) => {
30+ if ( authorTimeoutRef . current ) clearTimeout ( authorTimeoutRef . current ) ;
31+ authorTimeoutRef . current = setTimeout ( ( ) => {
32+ setDebouncedAuthorSearch ( authorSearch ) ;
33+ } , 300 ) ;
34+ } , [ authorSearch ] ) ;
35+
36+ useEffect ( ( ) => {
37+ if ( textTimeoutRef . current ) clearTimeout ( textTimeoutRef . current ) ;
38+ textTimeoutRef . current = setTimeout ( ( ) => {
39+ setDebouncedTextSearch ( textSearch ) ;
40+ } , 300 ) ;
41+ } , [ textSearch ] ) ;
42+
2443 const loadData = async ( ) => {
2544 if ( ! id ) return ;
2645 setIsLoading ( true ) ;
2746 setError ( '' ) ;
2847 try {
2948 const [ topicData , postsData ] = await Promise . all ( [
3049 getTopic ( id ) ,
31- listPosts ( id , { author : authorSearch || undefined , text : textSearch || undefined } )
50+ listPosts ( id , { author : debouncedAuthorSearch || undefined , text : debouncedTextSearch || undefined } )
3251 ] ) ;
3352 setTopic ( topicData ) ;
3453 setPosts ( postsData ) ;
@@ -41,14 +60,19 @@ export function TopicPage() {
4160 } ;
4261 useEffect ( ( ) => {
4362 loadData ( ) ;
44- } , [ id , authorSearch , textSearch ] ) ;
63+ } , [ id , debouncedAuthorSearch , debouncedTextSearch ] ) ;
4564 const allMessages = topic ? [
4665 { id : 'original' , author : topic . author , body : topic . body , createdAt : topic . createdAt } ,
4766 ...posts
4867 ] : [ ] ;
4968 const filteredMessages = allMessages . filter ( message =>
50- ( authorSearch === '' || normalizeText ( message . author ) . includes ( normalizeText ( authorSearch ) ) ) &&
51- ( textSearch === '' || normalizeText ( message . body ) . includes ( normalizeText ( textSearch ) ) )
69+ ( debouncedAuthorSearch === '' || normalizeText ( message . author ) . includes ( normalizeText ( debouncedAuthorSearch ) ) ) &&
70+ ( debouncedTextSearch === '' || ( ( ) => {
71+ const normalizedBody = normalizeText ( message . body ) ;
72+ const normalizedQuery = normalizeText ( debouncedTextSearch ) ;
73+ const words = normalizedBody . split ( / \s + / ) ;
74+ return words . some ( word => word . startsWith ( normalizedQuery ) ) ;
75+ } ) ( ) )
5276 ) ;
5377 const handleReply = async ( e : React . FormEvent ) => {
5478 e . preventDefault ( ) ;
0 commit comments