@@ -29,19 +29,18 @@ const COPY_CONTENT = 'copy_content';
2929const REGENERATE = 'regenerate' ;
3030
3131type DefaultMessageRenderers = {
32- message : ChatTemplateRenderer < ChatMessageRenderContext > ;
3332 messageHeader : ChatTemplateRenderer < ChatMessageRenderContext > ;
3433 messageContent : ChatTemplateRenderer < ChatMessageRenderContext > ;
3534 messageAttachments : ChatTemplateRenderer < ChatMessageRenderContext > ;
3635 messageActions : ChatTemplateRenderer < ChatMessageRenderContext > ;
3736} ;
3837
38+ /* blazorSuppress */
3939/**
4040 * A chat message component for displaying individual messages in `<igc-chat>`.
4141 *
4242 * @element igc-chat-message
4343 *
44- * @fires igcMessageReact - Fired when a message is reacted to.
4544 *
4645 * This component renders a single chat message including:
4746 * - Message text (sanitized)
@@ -68,7 +67,6 @@ export default class IgcChatMessageComponent extends LitElement {
6867 }
6968
7069 private readonly _defaults = Object . freeze < DefaultMessageRenderers > ( {
71- message : ( ) => this . _renderMessage ( ) ,
7270 messageHeader : ( ) => this . _renderHeader ( ) ,
7371 messageContent : ( ) => this . _renderContent ( ) ,
7472 messageAttachments : ( ) => this . _renderAttachments ( ) ,
@@ -82,7 +80,7 @@ export default class IgcChatMessageComponent extends LitElement {
8280 * The chat message to render.
8381 */
8482 @property ( { attribute : false } )
85- public message ? : IgcChatMessage ;
83+ public message ! : IgcChatMessage ;
8684
8785 constructor ( ) {
8886 super ( ) ;
@@ -99,36 +97,38 @@ export default class IgcChatMessageComponent extends LitElement {
9997 : this . _defaults [ name ] ;
10098 }
10199
102- private async _handleCopy ( ) {
103- if ( ! this . message ) return ;
100+ private async _handleCopy ( ) : Promise < void > {
101+ const text = this . message . text ;
102+ const separator = text ? '\n\n' : '' ;
103+ const attachments = this . message . attachments ?? [ ] ;
104+ const { attachmentLabel, attachmentsListLabel, messageCopied } =
105+ this . _state . resourceStrings ! ;
104106
105- let clipboardText = this . message . text ;
107+ const attachmentsText = isEmpty ( attachments )
108+ ? ''
109+ : attachments
110+ . map ( ( { name, url } ) => `${ name ?? attachmentLabel } : ${ url ?? '' } ` )
111+ . join ( '\n' ) ;
106112
107- const resourceStrings = this . _state . resourceStrings ! ;
108- if ( this . message . attachments && ! isEmpty ( this . message . attachments ) ) {
109- const attachmentList = this . message . attachments
110- . map (
111- ( att ) =>
112- `${ att . name ?? resourceStrings . attachmentLabel } : ${ att . url ?? '' } `
113- )
114- . join ( '\n' ) ;
115- clipboardText += `${ clipboardText ? '\n\n' : '' } ${ resourceStrings . attachmentsListLabel } :\n${ attachmentList } ` ;
116- }
113+ const payload = attachmentsText
114+ ? `${ text } ${ separator } ${ attachmentsListLabel } :\n${ attachmentsText } `
115+ : text ;
117116
118- if ( navigator . clipboard ?. writeText ) {
119- try {
120- await navigator . clipboard . writeText ( clipboardText ) ;
121- this . _state . showActionToast ( resourceStrings . messageCopied ) ;
122- } catch ( err ) {
123- throw new Error ( `Failed to copy message via Clipboard API: ${ err } ` ) ;
124- }
117+ try {
118+ await navigator . clipboard . writeText ( payload ) ;
119+ this . _state . showActionToast ( messageCopied ) ;
120+ } catch ( err ) {
121+ throw new Error ( `Failed to copy message: ${ err } ` ) ;
125122 }
126123 }
127124
128125 private _handleMessageActionClick ( event : PointerEvent ) : void {
129126 const targetButton = event . target as HTMLElement ;
130127 const button = targetButton . closest ( IgcIconButtonComponent . tagName ) ;
131- if ( ! button || ! this . message ) return ;
128+
129+ if ( ! button ) {
130+ return ;
131+ }
132132
133133 let reaction = button . name ;
134134
@@ -153,32 +153,26 @@ export default class IgcChatMessageComponent extends LitElement {
153153 reaction = REGENERATE ;
154154 break ;
155155 default :
156- reaction = undefined ;
156+ reaction = '' ;
157157 }
158158
159159 this . message . reactions = reaction ? [ reaction ] : [ ] ;
160+ this . _state . emitMessageReaction ( { message : this . message , reaction } ) ;
160161 this . requestUpdate ( ) ;
161-
162- this . _state . emitEvent ( 'igcMessageReact' , {
163- detail : { message : this . message , reaction } ,
164- } ) ;
165162 }
166163
167164 private _renderHeader ( ) {
168165 return nothing ;
169166 }
170167
171168 private _renderContent ( ) {
172- return this . message ?. text
173- ? html `< pre part ="plain-text "> ${ this . message . text } </ pre > `
174- : nothing ;
169+ return html `${ this . message . text } ` ;
175170 }
176171
177172 private _renderActions ( ) {
178- const isSent = this . message ?. sender === this . _state . currentUserId ;
179- const hasText = this . message ?. text . trim ( ) ;
180- const hasAttachments =
181- this . message ?. attachments && ! isEmpty ( this . message ?. attachments ) ;
173+ const isSent = this . message . sender === this . _state . currentUserId ;
174+ const hasText = this . message . text . trim ( ) ;
175+ const hasAttachments = ! isEmpty ( this . message . attachments ?? [ ] ) ;
182176 const isTyping = this . _state . _isTyping ;
183177 const isLastMessage = this . message === this . _state . messages . at ( - 1 ) ;
184178 const resourceStrings = this . _state . resourceStrings ! ;
@@ -188,25 +182,23 @@ export default class IgcChatMessageComponent extends LitElement {
188182 }
189183
190184 return html `
191- < div @click =${ this . _handleMessageActionClick } part ="message-actions">
192- ${ this . _renderActionButton ( COPY_CONTENT , resourceStrings . reactionCopy ) }
193- ${ this . _renderActionButton (
194- this . message ?. reactions ?. includes ( LIKE_ACTIVE )
195- ? LIKE_ACTIVE
196- : LIKE_INACTIVE ,
197- resourceStrings . reactionLike
198- ) }
199- ${ this . _renderActionButton (
200- this . message ?. reactions ?. includes ( DISLIKE_ACTIVE )
201- ? DISLIKE_ACTIVE
202- : DISLIKE_INACTIVE ,
203- resourceStrings . reactionDislike
204- ) }
205- ${ this . _renderActionButton (
206- REGENERATE ,
207- resourceStrings . reactionRegenerate
208- ) }
209- </ div >
185+ ${ this . _renderActionButton ( COPY_CONTENT , resourceStrings . reactionCopy ) }
186+ ${ this . _renderActionButton (
187+ this . message . reactions ?. includes ( LIKE_ACTIVE )
188+ ? LIKE_ACTIVE
189+ : LIKE_INACTIVE ,
190+ resourceStrings . reactionLike
191+ ) }
192+ ${ this . _renderActionButton (
193+ this . message . reactions ?. includes ( DISLIKE_ACTIVE )
194+ ? DISLIKE_ACTIVE
195+ : DISLIKE_INACTIVE ,
196+ resourceStrings . reactionDislike
197+ ) }
198+ ${ this . _renderActionButton (
199+ REGENERATE ,
200+ resourceStrings . reactionRegenerate
201+ ) }
210202 ` ;
211203 }
212204
@@ -224,9 +216,8 @@ export default class IgcChatMessageComponent extends LitElement {
224216 ` ;
225217 }
226218
227- // Default rendering logic for attachments
228219 private _renderAttachments ( ) {
229- return isEmpty ( this . message ? .attachments ?? [ ] )
220+ return isEmpty ( this . message . attachments ?? [ ] )
230221 ? nothing
231222 : html `
232223 < igc-message-attachments
@@ -236,14 +227,31 @@ export default class IgcChatMessageComponent extends LitElement {
236227 }
237228
238229 private _renderMessage ( ) {
239- return this . message
240- ? html `${ this . _renderHeader ( ) } ${ this . _renderContent ( ) } ${ this . _renderAttachments ( ) } ${ this . _renderActions ( ) } `
241- : nothing ;
230+ const ctx : ChatMessageRenderContext = {
231+ message : this . message ,
232+ instance : this . _state . host ,
233+ } ;
234+
235+ return html `
236+ < div part ="message-header ">
237+ ${ until ( this . _getRenderer ( 'messageHeader' ) ( ctx ) ) }
238+ </ div >
239+ < div part ="plain-text ">
240+ ${ until ( this . _getRenderer ( 'messageContent' ) ( ctx ) ) }
241+ </ div >
242+ < div part ="message-attachments ">
243+ ${ until ( this . _getRenderer ( 'messageAttachments' ) ( ctx ) ) }
244+ </ div >
245+ < div part ="message-actions " @click =${ this . _handleMessageActionClick } >
246+ ${ until ( this . _getRenderer ( 'messageActions' ) ( ctx ) ) }
247+ </ div >
248+ ` ;
242249 }
243250
244251 protected override render ( ) {
252+ const messageRenderer = this . _state . options ?. renderers ?. message ;
245253 const ctx : ChatMessageRenderContext = {
246- message : this . message ! ,
254+ message : this . message ,
247255 instance : this . _state . host ,
248256 } ;
249257
@@ -258,14 +266,7 @@ export default class IgcChatMessageComponent extends LitElement {
258266 ? html `
259267 < div part =${ partMap ( parts ) } >
260268 ${ cache (
261- this . _state . options ?. renderers ?. message
262- ? html `${ until ( this . _getRenderer ( 'message' ) ( ctx ) ) } `
263- : html `
264- ${ until ( this . _getRenderer ( 'messageHeader' ) ( ctx ) ) }
265- ${ until ( this . _getRenderer ( 'messageContent' ) ( ctx ) ) }
266- ${ until ( this . _getRenderer ( 'messageAttachments' ) ( ctx ) ) }
267- ${ until ( this . _getRenderer ( 'messageActions' ) ( ctx ) ) }
268- `
269+ messageRenderer ? messageRenderer ( ctx ) : this . _renderMessage ( )
269270 ) }
270271 </ div >
271272 `
0 commit comments