@@ -7,9 +7,14 @@ import { Box, Typography } from '@mui/material';
7
7
import React , { useMemo } from 'react' ;
8
8
9
9
import { Avatar } from '../avatar' ;
10
+ import { IChatModel } from '../../model' ;
11
+ import { ISignal , Signal } from '@lumino/signaling' ;
10
12
import { IUser } from '../../types' ;
11
13
12
14
const WRITERS_CLASS = 'jp-chat-writers' ;
15
+ const WRITERS_ROW_CLASS = 'jp-chat-writers-row' ;
16
+
17
+ const DEFAULT_TEXT = 'is writing' ;
13
18
14
19
/**
15
20
* The writers component props.
@@ -18,7 +23,11 @@ type writersProps = {
18
23
/**
19
24
* The list of users currently writing.
20
25
*/
21
- writers : IUser [ ] ;
26
+ writers : IChatModel . IWriter [ ] ;
27
+ /**
28
+ * The component to render next to the writers.
29
+ */
30
+ writerComponent ?: WriterComponent ;
22
31
} ;
23
32
24
33
/**
@@ -36,14 +45,53 @@ const TypingIndicator = (): JSX.Element => (
36
45
* The writers component, displaying the current writers.
37
46
*/
38
47
export function WritingUsersList ( props : writersProps ) : JSX . Element | null {
48
+ const { writers, writerComponent } = props ;
49
+
50
+ // Don't render if no writers
51
+ if ( writers . length === 0 ) {
52
+ return null ;
53
+ }
54
+
55
+ // Default rendering for users without custom typing indicator and if there is no
56
+ // component to add to the writing notification.
57
+ const defaultWriters = writerComponent ?. component
58
+ ? [ ]
59
+ : writers . filter ( writer => ! writer . typingIndicator ) ;
60
+ const defaultWritersComponent = defaultWritingUsers ( {
61
+ writers : defaultWriters . map ( writer => writer . user )
62
+ } ) ;
63
+
64
+ // Custom rendering for users with custom typing indicator or if there is a component
65
+ // to add to the writing notification.
66
+ const customWriters = writerComponent ?. component
67
+ ? writers
68
+ : writers . filter ( writer => writer . typingIndicator ) ;
69
+ const customWritersComponent = customWritingUser ( {
70
+ writers : customWriters ,
71
+ writerComponent : writerComponent ?. component
72
+ } ) ;
73
+
74
+ return (
75
+ < Box className = { WRITERS_CLASS } >
76
+ { defaultWritersComponent !== null && defaultWritersComponent }
77
+ { customWritersComponent !== null && customWritersComponent }
78
+ </ Box >
79
+ ) ;
80
+ }
81
+
82
+ /**
83
+ * The default rendering of writing users, all in a row.
84
+ * This renderer is used if there is no custom component and no custom typing indicator.
85
+ */
86
+ function defaultWritingUsers ( props : { writers : IUser [ ] } ) : JSX . Element | null {
39
87
const { writers } = props ;
40
88
41
89
// Don't render if no writers
42
90
if ( writers . length === 0 ) {
43
91
return null ;
44
92
}
45
93
46
- const writersText = writers . length > 1 ? ' are writing' : ' is writing' ;
94
+ const writersText = writers . length > 1 ? 'are writing' : DEFAULT_TEXT ;
47
95
48
96
const writingUsers : JSX . Element [ ] = useMemo (
49
97
( ) =>
@@ -66,16 +114,100 @@ export function WritingUsersList(props: writersProps): JSX.Element | null {
66
114
) ;
67
115
68
116
return (
69
- < Box className = { `${ WRITERS_CLASS } ` } >
117
+ < Box className = { `${ WRITERS_ROW_CLASS } ` } >
70
118
< Box className = "jp-chat-writers-content" >
71
119
{ writingUsers }
72
120
< Box className = "jp-chat-writing-status" >
73
121
< Typography variant = "body2" className = "jp-chat-writing-text" >
74
- { writersText }
122
+ { ` ${ writersText } ` }
75
123
</ Typography >
76
124
< TypingIndicator />
77
125
</ Box >
78
126
</ Box >
79
127
</ Box >
80
128
) ;
81
129
}
130
+
131
+ /**
132
+ * The custom rendering of writing users, one per row.
133
+ * This renderer is used if there is a custom component or a custom typing indicator.
134
+ */
135
+ function customWritingUser ( props : {
136
+ writers : IChatModel . IWriter [ ] ;
137
+ writerComponent ?: React . FC < WriterComponentProps > ;
138
+ } ) : JSX . Element | null {
139
+ const { writers } = props ;
140
+
141
+ // Don't render if no writers
142
+ if ( writers . length === 0 ) {
143
+ return null ;
144
+ }
145
+
146
+ const writingUsers : JSX . Element [ ] = writers . map ( writer => {
147
+ const username =
148
+ writer . user . display_name ??
149
+ writer . user . name ??
150
+ ( writer . user . username || 'User undefined' ) ;
151
+
152
+ const writerText = writer . typingIndicator ?? DEFAULT_TEXT ;
153
+ return (
154
+ < Box key = { writer . user . username } className = "jp-chat-writer-item" >
155
+ < Avatar user = { writer . user } small />
156
+ < Typography variant = "body2" className = "jp-chat-writer-name" >
157
+ { username }
158
+ </ Typography >
159
+ < Box className = "jp-chat-writing-status" >
160
+ < Typography variant = "body2" className = "jp-chat-writing-text" >
161
+ { ` ${ writerText } ` }
162
+ </ Typography >
163
+ < TypingIndicator />
164
+ </ Box >
165
+ { props . writerComponent && < props . writerComponent writer = { writer } /> }
166
+ </ Box >
167
+ ) ;
168
+ } ) ;
169
+
170
+ return (
171
+ < >
172
+ { writingUsers . map ( writingUser => (
173
+ < Box className = { `${ WRITERS_ROW_CLASS } ` } > { writingUser } </ Box >
174
+ ) ) }
175
+ </ >
176
+ ) ;
177
+ }
178
+
179
+ export type WriterComponentProps = {
180
+ /**
181
+ * The writer associated to this component.
182
+ */
183
+ writer : IChatModel . IWriter ;
184
+ } ;
185
+
186
+ export class WriterComponent {
187
+ /**
188
+ * The react component.
189
+ */
190
+ get component ( ) : React . FC < WriterComponentProps > | undefined {
191
+ return this . _component ;
192
+ }
193
+ set component ( value : React . FC < WriterComponentProps > | undefined ) {
194
+ this . _component = value ;
195
+ this . _changed . emit ( this . _component ) ;
196
+ }
197
+
198
+ /**
199
+ * Emitting when the component changed.
200
+ */
201
+ get changed ( ) : ISignal <
202
+ WriterComponent ,
203
+ React . FC < WriterComponentProps > | undefined
204
+ > {
205
+ return this . _changed ;
206
+ }
207
+
208
+ private _component : React . FC < WriterComponentProps > | undefined ;
209
+ private _changed = new Signal <
210
+ WriterComponent ,
211
+ React . FC < WriterComponentProps > | undefined
212
+ > ( this ) ;
213
+ }
0 commit comments