1- import {
2- type User , type Alert , type MessageEnteredEvent , type Message ,
3- } from 'devextreme/ui/chat' ;
4- import DataSource from 'devextreme/data/data_source' ;
5- import CustomStore from 'devextreme/data/custom_store' ;
1+ import { type ChatTypes } from 'devextreme-react/chat' ;
2+ import { DataSource , CustomStore } from 'devextreme-react/common/data' ;
63import { OpenAI } from 'openai' ;
74import { BehaviorSubject , Observable } from 'rxjs' ;
8- import TextArea from 'devextreme/ui/text_area ' ;
5+ import { ALERT_TIMEOUT , assistant , OpenAIConfig } from './data.ts ' ;
96
107class AppService {
118 chatService : OpenAI ;
129
13- OpenAIConfig = {
14- dangerouslyAllowBrowser : true ,
15- apiKey : 'OPEN_AI_KEY' ,
16- deployment : 'gpt-4o-mini' ,
17- } ;
18-
19- ALERT_TIMEOUT = 10000 ;
20-
21- user : User = {
22- id : 'user' ,
23- } ;
24-
25- assistant : User = {
26- id : 'assistant' ,
27- name : 'Virtual Assistant' ,
28- } ;
29-
30- store : Message [ ] = [ ] ;
10+ store : ChatTypes . Message [ ] = [ ] ;
3111
3212 messages : { role : 'user' | 'assistant' | 'system' ; content : string } [ ] = [ ] ;
3313
34- alerts : Alert [ ] = [ ] ;
14+ alerts : ChatTypes . Alert [ ] = [ ] ;
3515
3616 customStore ?: CustomStore ;
3717
3818 dataSource ?: DataSource ;
3919
40- private readonly typingUsersSubject : BehaviorSubject < User [ ] > = new BehaviorSubject < User [ ] > ( [ ] ) ;
20+ private readonly typingUsersSubject : BehaviorSubject < ChatTypes . User [ ] > = new BehaviorSubject < ChatTypes . User [ ] > ( [ ] ) ;
4121
42- private readonly alertsSubject : BehaviorSubject < Alert [ ] > = new BehaviorSubject < Alert [ ] > ( [ ] ) ;
22+ private readonly alertsSubject : BehaviorSubject < ChatTypes . Alert [ ] > = new BehaviorSubject < ChatTypes . Alert [ ] > ( [ ] ) ;
4323
4424 constructor ( ) {
45- this . chatService = new OpenAI ( this . OpenAIConfig ) ;
25+ this . chatService = new OpenAI ( OpenAIConfig ) ;
4626 this . initDataSource ( ) ;
4727 this . typingUsersSubject . next ( [ ] ) ;
4828 this . alertsSubject . next ( [ ] ) ;
4929 }
5030
51- get typingUsers$ ( ) : Observable < User [ ] > {
31+ get typingUsers$ ( ) : Observable < ChatTypes . User [ ] > {
5232 return this . typingUsersSubject . asObservable ( ) ;
5333 }
5434
55- get alerts$ ( ) : Observable < Alert [ ] > {
35+ get alerts$ ( ) : Observable < ChatTypes . Alert [ ] > {
5636 return this . alertsSubject . asObservable ( ) ;
5737 }
5838
@@ -74,7 +54,7 @@ class AppService {
7454 resolve ( [ ...this . store ] ) ;
7555 } , 0 ) ;
7656 } ) ,
77- insert : ( message : Message ) => new Promise ( ( resolve ) : void => {
57+ insert : ( message : ChatTypes . Message ) => new Promise ( ( resolve ) : void => {
7858 setTimeout ( ( ) => {
7959 this . store . push ( message ) ;
8060 resolve ( message ) ;
@@ -94,17 +74,18 @@ class AppService {
9474 role : msg . role ,
9575 content : msg . content ,
9676 } ) ) ,
97- model : this . OpenAIConfig . deployment ,
77+ model : OpenAIConfig . deployment ,
9878 } ;
9979
10080 const response = await this . chatService . chat . completions . create ( params ) ;
10181 const data = { choices : response . choices } ;
10282 return data . choices [ 0 ] . message ?. content ;
10383 }
10484
105- async processMessageSending ( ) : Promise < void > {
106- this . toggleDisabledState ( true ) ;
107- this . typingUsersSubject . next ( [ this . assistant ] ) ;
85+ async processMessageSending ( setDisabled : Function , event : Event | undefined ) : Promise < void > {
86+ setDisabled ( true ) ;
87+ ( event ?. target as HTMLElement ) . blur ( ) ;
88+ this . typingUsersSubject . next ( [ assistant ] ) ;
10889
10990 try {
11091 const aiResponse = await this . getAIResponse ( this . messages ) ;
@@ -114,21 +95,12 @@ class AppService {
11495 this . renderAssistantMessage ( aiResponse ?? '' ) ;
11596 } , 200 ) ;
11697 } catch {
98+ ( event ?. target as HTMLElement ) . focus ( ) ;
11799 this . typingUsersSubject . next ( [ ] ) ;
118100 this . alertLimitReached ( ) ;
119101 } finally {
120- this . toggleDisabledState ( false ) ;
121- }
122- }
123-
124- toggleDisabledState ( disabled : boolean ) : void {
125- let element = document . querySelector ( '.dx-chat-messagebox-textarea' ) ;
126- let textAreaInstance = element ? TextArea . getInstance ( element ) as TextArea : null ;
127-
128- textAreaInstance ?. option ( { disabled } ) ;
129-
130- if ( ! disabled ) {
131- textAreaInstance ?. focus ( ) ;
102+ ( event ?. target as HTMLElement ) . focus ( ) ;
103+ setDisabled ( false ) ;
132104 }
133105 }
134106
@@ -152,7 +124,7 @@ class AppService {
152124 const message = {
153125 id : Date . now ( ) ,
154126 timestamp : new Date ( ) ,
155- author : this . assistant ,
127+ author : assistant ,
156128 text,
157129 } ;
158130
@@ -168,10 +140,10 @@ class AppService {
168140
169141 setTimeout ( ( ) : void => {
170142 this . setAlerts ( [ ] ) ;
171- } , this . ALERT_TIMEOUT ) ;
143+ } , ALERT_TIMEOUT ) ;
172144 }
173145
174- setAlerts ( alerts : Alert [ ] ) : void {
146+ setAlerts ( alerts : ChatTypes . Alert [ ] ) : void {
175147 this . alerts = alerts ;
176148 this . alertsSubject . next ( alerts ) ;
177149 }
@@ -194,13 +166,14 @@ class AppService {
194166 }
195167 }
196168
197- onMessageEntered ( { message } : MessageEnteredEvent ) : void {
169+ onMessageEntered ( event : ChatTypes . MessageEnteredEvent , setDisabled : Function ) : void {
170+ let { message } = event ;
198171 this . dataSource
199172 ?. store ( )
200173 . push ( [ { type : 'insert' , data : { id : Date . now ( ) , ...message } } ] ) ;
201174
202175 this . messages . push ( { role : 'user' , content : message ?. text ?? '' } ) ;
203- void this . processMessageSending ( ) ;
176+ void this . processMessageSending ( setDisabled , event . event ) ;
204177 }
205178}
206179
0 commit comments