1
1
import React , { useEffect } from 'react' ;
2
- import { ComputeItemKey , Virtuoso } from 'react-virtuoso' ;
2
+ import { ComputeItemKey , Virtuoso , VirtuosoProps } from 'react-virtuoso' ;
3
3
4
4
import type { ComponentType , PointerEvent } from 'react' ;
5
5
import type { InferStoreValueType , Thread , ThreadManager } from 'stream-chat' ;
6
6
7
7
import { ThreadListItem } from './ThreadListItem' ;
8
+ import { Icon } from '../icons' ;
8
9
import { useChatContext } from '../../../context' ;
9
10
import { useSimpleStateStore } from '../hooks/useSimpleStateStore' ;
10
11
@@ -23,40 +24,63 @@ import type { ThreadListItemProps } from './ThreadListItem';
23
24
* - probably good idea to move component context up to a Chat component
24
25
*/
25
26
26
- const selector = ( nextValue : InferStoreValueType < ThreadManager > ) => [ nextValue . threads ] as const ;
27
+ const selector = ( nextValue : InferStoreValueType < ThreadManager > ) =>
28
+ [ nextValue . unreadThreads . newIds , nextValue . threads ] as const ;
27
29
28
30
const computeItemKey : ComputeItemKey < Thread , unknown > = ( _ , item ) => item . id ;
29
31
30
32
type ThreadListProps = {
31
- onItemPointerDown ?: ( event : PointerEvent < HTMLButtonElement > , thread : Thread ) => void ;
32
33
ThreadListItem ?: ComponentType < ThreadListItemProps > ;
33
- // threads?: Thread[]
34
+ threadListItemProps ?: Omit < ThreadListItemProps , 'thread' | 'onPointerDown' > & {
35
+ onPointerDown ?: ( event : PointerEvent < HTMLButtonElement > , thread : Thread ) => void ;
36
+ } ;
37
+ virtuosoProps ?: VirtuosoProps < Thread , unknown > ;
34
38
} ;
35
39
36
40
export const ThreadList = ( {
37
41
ThreadListItem : PropsThreadListItem = ThreadListItem ,
38
- onItemPointerDown,
42
+ virtuosoProps,
43
+ threadListItemProps : { onPointerDown, ...restThreadListItemProps } = { } ,
39
44
} : ThreadListProps ) => {
40
45
const { client } = useChatContext ( ) ;
41
- const [ threads ] = useSimpleStateStore ( client . threads . state , selector ) ;
46
+ const [ unreadThreadIds , threads ] = useSimpleStateStore ( client . threads . state , selector ) ;
42
47
43
48
useEffect ( ( ) => {
44
49
client . threads . loadNextPage ( ) ;
45
50
} , [ client ] ) ;
46
51
47
52
return (
48
- < Virtuoso
49
- atBottomStateChange = { ( atBottom ) => atBottom && client . threads . loadNextPage ( ) }
50
- className = 'str-chat str-chat__thread-list'
51
- computeItemKey = { computeItemKey }
52
- data = { threads }
53
- itemContent = { ( _ , thread ) => (
54
- < PropsThreadListItem
55
- onPointerDown = { ( e ) => onItemPointerDown ?.( e , thread ) }
56
- thread = { thread }
57
- />
53
+ < div className = 'str-chat__thread-list-container' >
54
+ { /* TODO: create a replaceable banner component, wait for BE to support "in" keyword for query threads */ }
55
+ { /* TODO: use query threads with limit (unreadThreadsId.length) - should be top of the list, and prepend
56
+ - this does not work when we reply to an non-loaded thread and then reply to a loaded thread
57
+ - querying afterwards will return only the latest, which was already in the list but not the one we need
58
+ */ }
59
+ { unreadThreadIds . length > 0 && (
60
+ < div className = 'str-chat__unread-threads-banner' >
61
+ { unreadThreadIds . length } unread threads
62
+ < button
63
+ className = 'str-chat__unread-threads-banner__button'
64
+ onClick = { client . threads . loadUnreadThreads }
65
+ >
66
+ < Icon . Reload />
67
+ </ button >
68
+ </ div >
58
69
) }
59
- style = { { height : '100%' , width : '50%' } }
60
- />
70
+ < Virtuoso
71
+ atBottomStateChange = { ( atBottom ) => atBottom && client . threads . loadNextPage ( ) }
72
+ className = 'str-chat__thread-list'
73
+ computeItemKey = { computeItemKey }
74
+ data = { threads }
75
+ itemContent = { ( _ , thread ) => (
76
+ < PropsThreadListItem
77
+ onPointerDown = { ( e ) => onPointerDown ?.( e , thread ) }
78
+ thread = { thread }
79
+ { ...restThreadListItemProps }
80
+ />
81
+ ) }
82
+ { ...virtuosoProps }
83
+ />
84
+ </ div >
61
85
) ;
62
86
} ;
0 commit comments