1
1
import * as React from 'react' ;
2
2
import { shallow } from 'zustand/shallow' ;
3
3
4
- import { Box , IconButton , ListDivider , ListItemDecorator , MenuItem , Tooltip } from '@mui/joy' ;
4
+ import { Box , IconButton , ListDivider , ListItemDecorator , MenuItem , Tooltip , Input , InputProps } from '@mui/joy' ;
5
5
import AddIcon from '@mui/icons-material/Add' ;
6
6
import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline' ;
7
7
import FileDownloadIcon from '@mui/icons-material/FileDownload' ;
8
8
import FileUploadIcon from '@mui/icons-material/FileUpload' ;
9
9
import FolderOpenOutlinedIcon from '@mui/icons-material/FolderOpenOutlined' ;
10
10
import FolderOutlinedIcon from '@mui/icons-material/FolderOutlined' ;
11
+ import SearchIcon from '@mui/icons-material/Search' ;
11
12
12
13
import { DFolder , useFoldersToggle , useFolderStore } from '~/common/state/store-folders' ;
13
14
import { PageDrawerHeader } from '~/common/layout/optima/components/PageDrawerHeader' ;
@@ -19,6 +20,7 @@ import { useUXLabsStore } from '~/common/state/store-ux-labs';
19
20
20
21
import { ChatFolderList } from './folder/ChatFolderList' ;
21
22
import { ChatDrawerItemMemo , ChatNavigationItemData } from './ChatNavigationItem' ;
23
+ import Search from '@mui/icons-material/Search' ;
22
24
23
25
// type ListGrouping = 'off' | 'persona';
24
26
@@ -67,6 +69,41 @@ export const useChatNavigationItems = (activeConversationId: DConversationId | n
67
69
return { chatNavItems, folders } ;
68
70
} ;
69
71
72
+ type DebounceProps = {
73
+ handleDebounce : ( value : string ) => void ;
74
+ debounceTimeout : number ;
75
+ } ;
76
+
77
+ function DebounceInput ( props : InputProps & DebounceProps ) {
78
+ const { handleDebounce, debounceTimeout, ...rest } = props ;
79
+ const [ inputValue , setInputValue ] = React . useState ( '' ) ; // Local state for the input value
80
+ const timerRef = React . useRef < number > ( ) ;
81
+
82
+ const handleChange = ( event : React . ChangeEvent < HTMLInputElement > ) => {
83
+ const value = event . target . value ;
84
+ setInputValue ( value ) ; // Update local state immediately
85
+
86
+ if ( timerRef . current ) {
87
+ clearTimeout ( timerRef . current ) ;
88
+ }
89
+
90
+ timerRef . current = window . setTimeout ( ( ) => {
91
+ handleDebounce ( value ) ; // Only call handleDebounce after the timeout
92
+ } , debounceTimeout ) ;
93
+ } ;
94
+
95
+ // Clean up the timer when the component unmounts
96
+ React . useEffect ( ( ) => {
97
+ return ( ) => {
98
+ if ( timerRef . current ) {
99
+ clearTimeout ( timerRef . current ) ;
100
+ }
101
+ } ;
102
+ } , [ ] ) ;
103
+
104
+ return < Input { ...rest } value = { inputValue } onChange = { handleChange } /> ;
105
+ }
106
+
70
107
71
108
export const ChatDrawerContentMemo = React . memo ( ChatDrawerItems ) ;
72
109
@@ -84,6 +121,8 @@ function ChatDrawerItems(props: {
84
121
} ) {
85
122
86
123
// local state
124
+ const [ searchQuery , setSearchQuery ] = React . useState ( '' ) ;
125
+
87
126
// const [grouping] = React.useState<ListGrouping>('off');
88
127
const { onConversationDelete, onConversationNew, onConversationActivate } = props ;
89
128
@@ -117,6 +156,35 @@ function ChatDrawerItems(props: {
117
156
! singleChat && conversationId && onConversationDelete ( conversationId , true ) ;
118
157
} , [ onConversationDelete , singleChat ] ) ;
119
158
159
+ // Handle debounced search input changes
160
+ const handleDebounce = ( value : string ) => {
161
+ setSearchQuery ( value ) ;
162
+ } ;
163
+
164
+ // Filter chatNavItems based on the search query and rank them by search frequency
165
+ const filteredChatNavItems = React . useMemo ( ( ) => {
166
+ if ( ! searchQuery ) return chatNavItems ;
167
+ return chatNavItems
168
+ . map ( item => {
169
+ // Get the conversation by ID
170
+ const conversation = useChatStore . getState ( ) . conversations . find ( c => c . id === item . conversationId ) ;
171
+ // Calculate the frequency of the search term in the title and messages
172
+ const titleFrequency = ( item . title . toLowerCase ( ) . match ( new RegExp ( searchQuery . toLowerCase ( ) , 'g' ) ) || [ ] ) . length ;
173
+ const messageFrequency = conversation ?. messages . reduce ( ( count , message ) => {
174
+ return count + ( message . text . toLowerCase ( ) . match ( new RegExp ( searchQuery . toLowerCase ( ) , 'g' ) ) || [ ] ) . length ;
175
+ } , 0 ) || 0 ;
176
+ // Return the item with the searchFrequency property
177
+ return {
178
+ ...item ,
179
+ searchFrequency : titleFrequency + messageFrequency ,
180
+ } ;
181
+ } )
182
+ // Exclude items with a searchFrequency of 0
183
+ . filter ( item => item . searchFrequency > 0 )
184
+ // Sort the items by searchFrequency in descending order
185
+ . sort ( ( a , b ) => b . searchFrequency ! - a . searchFrequency ! ) ;
186
+ } , [ chatNavItems , searchQuery ] ) ;
187
+
120
188
121
189
// grouping
122
190
/*let sortedIds = conversationIDs;
@@ -167,6 +235,17 @@ function ChatDrawerItems(props: {
167
235
168
236
{ useFolders && < ListDivider sx = { { mb : 0 } } /> }
169
237
238
+ { /* Search Input Field */ }
239
+ < DebounceInput
240
+ startDecorator = { < SearchIcon /> }
241
+ placeholder = "Search chats..."
242
+ variant = "outlined"
243
+ value = { searchQuery }
244
+ handleDebounce = { handleDebounce }
245
+ debounceTimeout = { 300 }
246
+ sx = { { m : 2 } }
247
+ />
248
+
170
249
< MenuItem disabled = { props . disableNewButton } onClick = { handleButtonNew } sx = { PageDrawerTallItemSx } >
171
250
< ListItemDecorator > < AddIcon /> </ ListItemDecorator >
172
251
< Box sx = { {
@@ -201,7 +280,7 @@ function ChatDrawerItems(props: {
201
280
{ /* </ToggleButtonGroup>*/ }
202
281
{ /*</ListItem>*/ }
203
282
204
- { chatNavItems . map ( item =>
283
+ { filteredChatNavItems . map ( item =>
205
284
< ChatDrawerItemMemo
206
285
key = { 'nav-' + item . conversationId }
207
286
item = { item }
0 commit comments