@@ -35,8 +35,22 @@ import {
3535 MessageProps ,
3636} from '@patternfly/chatbot' ;
3737import ChatbotConversationHistoryNav from '@patternfly/chatbot/dist/dynamic/ChatbotConversationHistoryNav' ;
38- import { DropdownItem , DropEvent , Title } from '@patternfly/react-core' ;
39- import { PlusIcon , SearchIcon } from '@patternfly/react-icons' ;
38+ import {
39+ DropdownItem ,
40+ DropEvent ,
41+ MenuToggle ,
42+ MenuToggleElement ,
43+ Select ,
44+ SelectList ,
45+ SelectOption ,
46+ Title ,
47+ Tooltip ,
48+ } from '@patternfly/react-core' ;
49+ import {
50+ PlusIcon ,
51+ SearchIcon ,
52+ SortAmountDownIcon ,
53+ } from '@patternfly/react-icons' ;
4054import { useQueryClient } from '@tanstack/react-query' ;
4155
4256import { supportedFileTypes , TEMP_CONVERSATION_ID } from '../const' ;
@@ -47,6 +61,7 @@ import {
4761 useIsMobile ,
4862 useLastOpenedConversation ,
4963 useLightspeedDeletePermission ,
64+ usePinnedChatsSettings ,
5065} from '../hooks' ;
5166import { useLightspeedUpdatePermission } from '../hooks/useLightspeedUpdatePermission' ;
5267import { useTranslation } from '../hooks/useTranslation' ;
@@ -56,6 +71,7 @@ import { getAttachments } from '../utils/attachment-utils';
5671import {
5772 getCategorizeMessages ,
5873 getFootnoteProps ,
74+ SortOption ,
5975} from '../utils/lightspeed-chatbox-utils' ;
6076import Attachment from './Attachment' ;
6177import { useFileAttachmentContext } from './AttachmentContext' ;
@@ -100,6 +116,10 @@ const useStyles = makeStyles(theme => ({
100116 maxWidth : '100%' ,
101117 } ,
102118 } ,
119+ sortDropdown : {
120+ padding : 0 ,
121+ margin : 0 ,
122+ } ,
103123} ) ) ;
104124
105125type LightspeedChatProps = {
@@ -134,14 +154,23 @@ export const LightspeedChat = ({
134154 const [ newChatCreated , setNewChatCreated ] = useState < boolean > ( false ) ;
135155 const [ isSendButtonDisabled , setIsSendButtonDisabled ] =
136156 useState < boolean > ( false ) ;
137- const [ isPinningChatsEnabled , setIsPinningChatsEnabled ] = useState ( true ) ; // read from user settings in future
138- const [ pinnedChats , setPinnedChats ] = useState < string [ ] > ( [ ] ) ; // read from user settings in future
139157 const [ targetConversationId , setTargetConversationId ] = useState < string > ( '' ) ;
140158 const [ isDeleteModalOpen , setIsDeleteModalOpen ] = useState < boolean > ( false ) ;
141159 const [ isRenameModalOpen , setIsRenameModalOpen ] = useState < boolean > ( false ) ;
160+ const [ isSortSelectOpen , setIsSortSelectOpen ] = useState < boolean > ( false ) ;
142161 const { isReady, lastOpenedId, setLastOpenedId, clearLastOpenedId } =
143162 useLastOpenedConversation ( user ) ;
144163
164+ const {
165+ isPinningChatsEnabled,
166+ pinnedChats,
167+ selectedSort,
168+ handlePinningChatsToggle,
169+ pinChat,
170+ unpinChat,
171+ handleSortChange,
172+ } = usePinnedChatsSettings ( user ) ;
173+
145174 const {
146175 uploadError,
147176 showAlert,
@@ -159,12 +188,6 @@ export const LightspeedChat = ({
159188 }
160189 } , [ lastOpenedId , isReady ] ) ;
161190
162- useEffect ( ( ) => {
163- if ( ! isPinningChatsEnabled ) {
164- setPinnedChats ( [ ] ) ;
165- }
166- } , [ isPinningChatsEnabled ] ) ;
167-
168191 const queryClient = useQueryClient ( ) ;
169192
170193 const {
@@ -282,14 +305,6 @@ export const LightspeedChat = ({
282305 setIsDeleteModalOpen ( false ) ;
283306 } , [ clearLastOpenedId , lastOpenedId , onNewChat , targetConversationId ] ) ;
284307
285- const pinChat = ( convId : string ) => {
286- setPinnedChats ( prev => [ ...prev , convId ] ) ; // write to user settings in future
287- } ;
288-
289- const unpinChat = ( convId : string ) => {
290- setPinnedChats ( prev => prev . filter ( id => id !== convId ) ) ; // write to user settings in future
291- } ;
292-
293308 const additionalMessageProps = useCallback (
294309 ( conversationSummary : ConversationSummary ) => {
295310 const isChatFavorite = pinnedChats ?. find (
@@ -337,7 +352,15 @@ export const LightspeedChat = ({
337352 ) ,
338353 } ;
339354 } ,
340- [ pinnedChats , hasDeleteAccess , isPinningChatsEnabled , hasUpdateAccess , t ] ,
355+ [
356+ pinnedChats ,
357+ hasDeleteAccess ,
358+ isPinningChatsEnabled ,
359+ hasUpdateAccess ,
360+ t ,
361+ pinChat ,
362+ unpinChat ,
363+ ] ,
341364 ) ;
342365
343366 const categorizedMessages = useMemo (
@@ -347,8 +370,9 @@ export const LightspeedChat = ({
347370 pinnedChats ,
348371 additionalMessageProps ,
349372 t ,
373+ selectedSort ,
350374 ) ,
351- [ additionalMessageProps , conversations , pinnedChats , t ] ,
375+ [ additionalMessageProps , conversations , pinnedChats , t , selectedSort ] ,
352376 ) ;
353377
354378 const filterConversations = useCallback (
@@ -469,6 +493,80 @@ export const LightspeedChat = ({
469493 setIsDrawerOpen ( isOpen => ! isOpen ) ;
470494 } , [ ] ) ;
471495
496+ const onSortToggle = useCallback ( ( ) => {
497+ setIsSortSelectOpen ( prev => ! prev ) ;
498+ } , [ ] ) ;
499+
500+ const onSortSelect = useCallback (
501+ ( _event ?: React . MouseEvent < Element > , value ?: string | number ) => {
502+ handleSortChange ( value as SortOption ) ;
503+ setIsSortSelectOpen ( false ) ;
504+ } ,
505+ [ handleSortChange ] ,
506+ ) ;
507+
508+ const getSortLabel = useCallback (
509+ ( option : SortOption ) : string => {
510+ const labels : Record < SortOption , string > = {
511+ newest : t ( 'sort.newest' ) ,
512+ oldest : t ( 'sort.oldest' ) ,
513+ alphabeticalAsc : t ( 'sort.alphabeticalAsc' ) ,
514+ alphabeticalDesc : t ( 'sort.alphabeticalDesc' ) ,
515+ } ;
516+ return labels [ option ] ;
517+ } ,
518+ [ t ] ,
519+ ) ;
520+
521+ const sortDropdown = useMemo (
522+ ( ) => (
523+ < Select
524+ id = "sort-select"
525+ isOpen = { isSortSelectOpen }
526+ selected = { selectedSort }
527+ onSelect = { onSortSelect }
528+ onOpenChange = { ( isOpen : boolean ) => setIsSortSelectOpen ( isOpen ) }
529+ popperProps = { { position : 'end' } }
530+ toggle = { ( toggleRef : React . Ref < MenuToggleElement > ) => (
531+ < Tooltip
532+ content = { `${ t ( 'sort.label' ) } - ${ getSortLabel ( selectedSort ) } ` }
533+ >
534+ < MenuToggle
535+ ref = { toggleRef }
536+ aria-label = { t ( 'sort.label' ) }
537+ variant = "plain"
538+ onClick = { onSortToggle }
539+ isExpanded = { isSortSelectOpen }
540+ >
541+ < SortAmountDownIcon />
542+ </ MenuToggle >
543+ </ Tooltip >
544+ ) }
545+ shouldFocusToggleOnSelect
546+ >
547+ < SelectList className = { classes . sortDropdown } >
548+ < SelectOption value = "newest" > { t ( 'sort.newest' ) } </ SelectOption >
549+ < SelectOption value = "oldest" > { t ( 'sort.oldest' ) } </ SelectOption >
550+ < SelectOption value = "alphabeticalAsc" >
551+ { t ( 'sort.alphabeticalAsc' ) }
552+ </ SelectOption >
553+ < SelectOption value = "alphabeticalDesc" >
554+ { t ( 'sort.alphabeticalDesc' ) }
555+ </ SelectOption >
556+ </ SelectList >
557+ </ Select >
558+ ) ,
559+ [
560+ isSortSelectOpen ,
561+ selectedSort ,
562+ onSortSelect ,
563+ onSortToggle ,
564+ getSortLabel ,
565+ t ,
566+ classes . sortDropdown ,
567+ ] ,
568+ ) ;
569+
472570 const handleAttach = ( data : File [ ] , event : DropEvent ) => {
473571 event . preventDefault ( ) ;
474572 handleFileUpload ( data ) ;
@@ -527,7 +625,7 @@ export const LightspeedChat = ({
527625 handleSelectedModel = { item => handleSelectedModel ( item ) }
528626 models = { models }
529627 isPinningChatsEnabled = { isPinningChatsEnabled }
530- onPinnedChatsToggle = { setIsPinningChatsEnabled }
628+ onPinnedChatsToggle = { handlePinningChatsToggle }
531629 />
532630 </ ChatbotHeader >
533631 < Divider />
@@ -560,6 +658,7 @@ export const LightspeedChat = ({
560658 setFilterValue ( '' ) ;
561659 } ,
562660 } }
661+ searchActionEnd = { sortDropdown }
563662 noResultsState = {
564663 filterValue &&
565664 Object . keys ( filterConversations ( filterValue ) ) . length === 0
0 commit comments