@@ -16,6 +16,8 @@ export function TopicPage() {
1616 const [ error , setError ] = useState ( '' ) ;
1717 const [ replyBody , setReplyBody ] = useState ( '' ) ;
1818 const [ isReplying , setIsReplying ] = useState ( false ) ;
19+ const [ authorSearch , setAuthorSearch ] = useState ( '' ) ;
20+ const [ textSearch , setTextSearch ] = useState ( '' ) ;
1921 const { t } = useTranslation ( ) ;
2022 const { user } = useAuth ( ) ;
2123 const loadData = async ( ) => {
@@ -39,6 +41,14 @@ export function TopicPage() {
3941 useEffect ( ( ) => {
4042 loadData ( ) ;
4143 } , [ id ] ) ;
44+ const allMessages = topic ? [
45+ { id : 'original' , author : topic . author , body : topic . body , createdAt : topic . createdAt } ,
46+ ...posts
47+ ] : [ ] ;
48+ const filteredMessages = allMessages . filter ( message =>
49+ ( authorSearch === '' || message . author . toLowerCase ( ) . includes ( authorSearch . toLowerCase ( ) ) ) &&
50+ ( textSearch === '' || message . body . toLowerCase ( ) . includes ( textSearch . toLowerCase ( ) ) )
51+ ) ;
4252 const handleReply = async ( e : React . FormEvent ) => {
4353 e . preventDefault ( ) ;
4454 if ( ! user || ! id || ! replyBody . trim ( ) ) return ;
@@ -65,6 +75,39 @@ export function TopicPage() {
6575 const quote = `> ${ author } ${ t ( 'said' ) } :\n> ${ body . replace ( / \n / g, '\n> ' ) } \n\n` ;
6676 setReplyBody ( prev => prev + quote ) ;
6777 } ;
78+ const renderMessage = ( message : any , isOriginal : boolean ) => (
79+ < div
80+ key = { message . id }
81+ className = "bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 overflow-hidden" >
82+
83+ < div className = "flex flex-col sm:flex-row" >
84+ < div className = "bg-gray-50 dark:bg-gray-800/50 p-4 sm:w-48 border-b sm:border-b-0 sm:border-r border-gray-200 dark:border-gray-700 flex flex-col items-center sm:items-start" >
85+ < div className = "w-16 h-16 rounded-full bg-blue-100 dark:bg-blue-900 flex items-center justify-center text-blue-600 dark:text-blue-400 mb-2" >
86+ < UserIcon className = "w-8 h-8" />
87+ </ div >
88+ < span className = "font-medium text-center sm:text-left w-full break-words" >
89+ { message . author }
90+ </ span >
91+ < span className = "text-xs text-gray-500 dark:text-gray-400 mt-1 flex items-center gap-1" >
92+ < Clock className = "w-3 h-3" />
93+ { new Date ( message . createdAt ) . toLocaleDateString ( ) }
94+ </ span >
95+ </ div >
96+ < div className = "p-6 flex-1" >
97+ < MarkdownContent content = { message . body } />
98+ { user && (
99+ < button
100+ onClick = { ( ) => handleQuote ( message . author , message . body ) }
101+ className = "mt-2 px-3 py-1 text-sm bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-300 rounded flex items-center gap-1"
102+ >
103+ < Quote className = "w-3 h-3" />
104+ { t ( 'quote' ) }
105+ </ button >
106+ ) }
107+ </ div >
108+ </ div >
109+ </ div >
110+ ) ;
68111 if ( isLoading ) {
69112 return (
70113 < div className = "flex justify-center items-center py-20" >
@@ -94,70 +137,27 @@ export function TopicPage() {
94137 < h1 className = "text-2xl font-bold" > { topic . title } </ h1 >
95138 </ div >
96139
97- { /* Original Topic Post */ }
98- < div className = "bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 overflow-hidden" >
99- < div className = "flex flex-col sm:flex-row" >
100- < div className = "bg-gray-50 dark:bg-gray-800/50 p-4 sm:w-48 border-b sm:border-b-0 sm:border-r border-gray-200 dark:border-gray-700 flex flex-col items-center sm:items-start" >
101- < div className = "w-16 h-16 rounded-full bg-blue-100 dark:bg-blue-900 flex items-center justify-center text-blue-600 dark:text-blue-400 mb-2" >
102- < UserIcon className = "w-8 h-8" />
103- </ div >
104- < span className = "font-medium text-center sm:text-left w-full break-words" >
105- { topic . author }
106- </ span >
107- < span className = "text-xs text-gray-500 dark:text-gray-400 mt-1 flex items-center gap-1" >
108- < Clock className = "w-3 h-3" />
109- { new Date ( topic . createdAt ) . toLocaleDateString ( ) }
110- </ span >
111- </ div >
112- < div className = "p-6 flex-1" >
113- < MarkdownContent content = { topic . body } />
114- { user && (
115- < button
116- onClick = { ( ) => handleQuote ( topic . author , topic . body ) }
117- className = "mt-2 px-3 py-1 text-sm bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-300 rounded flex items-center gap-1"
118- >
119- < Quote className = "w-3 h-3" />
120- { t ( 'quote' ) }
121- </ button >
122- ) }
123- </ div >
140+ < div className = "bg-white dark:bg-gray-800 p-4 rounded-lg border border-gray-200 dark:border-gray-700" >
141+ < div className = "flex gap-4" >
142+ < input
143+ type = "text"
144+ placeholder = { t ( 'searchByAuthor' ) }
145+ value = { authorSearch }
146+ onChange = { ( e ) => setAuthorSearch ( e . target . value ) }
147+ className = "flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100"
148+ />
149+ < input
150+ type = "text"
151+ placeholder = { t ( 'searchByText' ) }
152+ value = { textSearch }
153+ onChange = { ( e ) => setTextSearch ( e . target . value ) }
154+ className = "flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100"
155+ />
124156 </ div >
125157 </ div >
126158
127- { /* Replies */ }
128- { posts . map ( ( post ) =>
129- < div
130- key = { post . id }
131- className = "bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 overflow-hidden" >
132-
133- < div className = "flex flex-col sm:flex-row" >
134- < div className = "bg-gray-50 dark:bg-gray-800/50 p-4 sm:w-48 border-b sm:border-b-0 sm:border-r border-gray-200 dark:border-gray-700 flex flex-col items-center sm:items-start" >
135- < div className = "w-16 h-16 rounded-full bg-blue-100 dark:bg-blue-900 flex items-center justify-center text-blue-600 dark:text-blue-400 mb-2" >
136- < UserIcon className = "w-8 h-8" />
137- </ div >
138- < span className = "font-medium text-center sm:text-left w-full break-words" >
139- { post . author }
140- </ span >
141- < span className = "text-xs text-gray-500 dark:text-gray-400 mt-1 flex items-center gap-1" >
142- < Clock className = "w-3 h-3" />
143- { new Date ( post . createdAt ) . toLocaleDateString ( ) }
144- </ span >
145- </ div >
146- < div className = "p-6 flex-1" >
147- < MarkdownContent content = { post . body } />
148- { user && (
149- < button
150- onClick = { ( ) => handleQuote ( post . author , post . body ) }
151- className = "mt-2 px-3 py-1 text-sm bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-300 rounded flex items-center gap-1"
152- >
153- < Quote className = "w-3 h-3" />
154- { t ( 'quote' ) }
155- </ button >
156- ) }
157- </ div >
158- </ div >
159- </ div >
160- ) }
159+ { /* Messages */ }
160+ { filteredMessages . map ( ( message ) => renderMessage ( message , message . id === 'original' ) ) }
161161
162162 { /* Reply Form */ }
163163 { user ?
0 commit comments