11import { createRef , type Ref } from 'lit/directives/ref.js' ;
22import { enterKey } from '../common/controllers/key-bindings.js' ;
33import { IgcChatResourceStringEN } from '../common/i18n/chat.resources.js' ;
4+ import { nanoid } from '../common/util.js' ;
45import type IgcTextareaComponent from '../textarea/textarea.js' ;
56import type IgcChatComponent from './chat.js' ;
67import type { IgcChatComponentEventMap } from './chat.js' ;
@@ -19,6 +20,10 @@ import { type ChatAcceptedFileTypes, parseAcceptedFileTypes } from './utils.js';
1920export class ChatState {
2021 //#region Internal properties and state /** The host `<igc-chat>` component instance */
2122 private readonly _host : IgcChatComponent ;
23+
24+ private readonly _contextUpdateFn : ( ) => unknown ;
25+ private readonly _userInputContextUpdateFn : ( ) => unknown ;
26+
2227 /** Reference to the text area input component */
2328 private _textArea : IgcTextareaComponent | null = null ;
2429 /** The current list of messages */
@@ -85,11 +90,7 @@ export class ChatState {
8590 */
8691 public set options ( value : IgcChatOptions ) {
8792 this . _options = value ;
88- this . _host . requestUpdate ( ) ;
89- // Notify context consumers about the state change
90- if ( this . _host . updateContextValue ) {
91- this . _host . updateContextValue ( ) ;
92- }
93+ this . _contextUpdateFn . call ( this . _host ) ;
9394 }
9495
9596 /**
@@ -146,11 +147,7 @@ export class ChatState {
146147 */
147148 public set inputAttachments ( value : IgcMessageAttachment [ ] ) {
148149 this . _inputAttachments = value ;
149- this . _host . requestUpdate ( ) ; // Notify the host component to re-render
150- // Notify context consumers about the state change
151- if ( this . _host . updateContextValue ) {
152- this . _host . updateContextValue ( ) ;
153- }
150+ this . _userInputContextUpdateFn . call ( this . _host ) ;
154151 }
155152
156153 /**
@@ -165,31 +162,27 @@ export class ChatState {
165162 */
166163 public set inputValue ( value : string ) {
167164 this . _inputValue = value ;
168- this . _host . requestUpdate ( ) ;
169- // Notify context consumers about the state change
170- if ( this . _host . updateContextValue ) {
171- this . _host . updateContextValue ( ) ;
172- }
165+ this . _userInputContextUpdateFn . call ( this . _host ) ;
173166 }
174167
175168 //#endregion
176169
177- /**
178- * Creates an instance of ChatState.
179- * @param chat The host `<igc-chat>` component.
180- */
181- constructor ( chat : IgcChatComponent ) {
170+ constructor (
171+ chat : IgcChatComponent ,
172+ contextUpdateFn : ( ) => unknown ,
173+ userInputContextUpdateFn : ( ) => unknown
174+ ) {
182175 this . _host = chat ;
176+ this . _contextUpdateFn = contextUpdateFn ;
177+ this . _userInputContextUpdateFn = userInputContextUpdateFn ;
178+ }
179+
180+ public isCurrentUserMessage ( message ?: IgcMessage ) : boolean {
181+ return this . currentUserId === message ?. sender ;
183182 }
184183
185184 //#region Event handlers
186185
187- /**
188- * Emits a custom event from the host component.
189- * @param name Event name (key of IgcChatComponentEventMap)
190- * @param args Event detail or options
191- * @returns true if event was not canceled, false otherwise
192- */
193186 public emitEvent = ( name : keyof IgcChatComponentEventMap , args ?: any ) => {
194187 return this . _host . emitEvent ( name , args ) ;
195188 } ;
@@ -204,16 +197,10 @@ export class ChatState {
204197 * Clears input value and attachments on success.
205198 * @param message Partial message object with optional id, sender, timestamp
206199 */
207- public addMessage = ( message : {
208- id ?: string ;
209- text : string ;
210- sender ?: string ;
211- timestamp ?: Date ;
212- attachments ?: IgcMessageAttachment [ ] ;
213- } ) : void => {
200+ public addMessage ( message : Partial < IgcMessage > ) : void {
214201 const newMessage : IgcMessage = {
215- id : message . id ?? Date . now ( ) . toString ( ) ,
216- text : message . text ,
202+ id : message . id ?? nanoid ( ) ,
203+ text : message . text ?? '' ,
217204 sender : message . sender ?? this . currentUserId ,
218205 timestamp : message . timestamp ?? new Date ( ) ,
219206 attachments : message . attachments || [ ] ,
@@ -228,10 +215,12 @@ export class ChatState {
228215 if ( ! this . messages . some ( ( msg ) => msg . id === newMessage . id ) ) {
229216 this . messages = [ ...this . messages , newMessage ] ;
230217 }
218+ this . _host . requestUpdate ( 'messages' ) ;
219+
231220 this . inputValue = '' ;
232221 this . inputAttachments = [ ] ;
233222 }
234- } ;
223+ }
235224
236225 /**
237226 * Adds files as attachments to the input.
@@ -240,21 +229,26 @@ export class ChatState {
240229 */
241230 public attachFiles ( files : File [ ] ) {
242231 const newAttachments : IgcMessageAttachment [ ] = [ ] ;
243- let count = this . inputAttachments . length ;
244- files . forEach ( ( file ) => {
245- if ( this . inputAttachments . find ( ( a ) => a . name === file . name ) ) {
246- return ;
232+ const fileNames = new Set (
233+ this . inputAttachments . map ( ( attachment ) => attachment . file ?. name ?? '' )
234+ ) ;
235+
236+ for ( const file of files ) {
237+ if ( fileNames . has ( file . name ) ) {
238+ continue ;
247239 }
248240
249241 const isImage = file . type . startsWith ( 'image/' ) ;
242+ const url = URL . createObjectURL ( file ) ;
243+
250244 newAttachments . push ( {
251- id : Date . now ( ) . toString ( ) + count ++ ,
252- url : URL . createObjectURL ( file ) ,
245+ id : nanoid ( ) ,
246+ url,
253247 name : file . name ,
254- file : file ,
255- thumbnail : isImage ? URL . createObjectURL ( file ) : undefined ,
248+ file,
249+ thumbnail : isImage ? url : undefined ,
256250 } ) ;
257- } ) ;
251+ }
258252
259253 const allowed = this . emitEvent ( 'igcAttachmentChange' , {
260254 detail : [ ...this . inputAttachments , ...newAttachments ] ,
@@ -389,7 +383,3 @@ export class ChatState {
389383
390384 //#endregion
391385}
392-
393- export function createChatState ( host : IgcChatComponent ) : ChatState {
394- return new ChatState ( host ) ;
395- }
0 commit comments