Skip to content

Commit e9863f6

Browse files
Strider2342EugeniyKiyashko
andauthored
Chat: implement P2P demo (#28363)
Co-authored-by: EugeniyKiyashko <[email protected]>
1 parent 1a7142e commit e9863f6

22 files changed

+715
-0
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
.demo-container {
2+
display: flex;
3+
gap: 20px;
4+
}
5+
6+
::ng-deep .dx-chat {
7+
height: 810px;
8+
}
9+
10+
::ng-deep .dx-avatar {
11+
border: 1px solid var(--dx-color-border);
12+
}
13+
14+
::ng-deep .dx-chat-messagegroup.dx-chat-messagegroup-alignment-end {
15+
padding-left: 44px;
16+
}
17+
18+
::ng-deep .dx-chat-messagegroup .dx-chat-messagegroup-content {
19+
max-width: 82.5%;
20+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<div class="demo-container">
2+
<dx-chat
3+
[user]="currentUser"
4+
[items]="messages$ | async"
5+
[typingUsers]="userChatTypingUsers$ | async"
6+
(onMessageEntered)="onMessageEntered($event)"
7+
(onTypingStart)="userChatOnTypingStart()"
8+
(onTypingEnd)="userChatOnTypingEnd()"
9+
>
10+
</dx-chat>
11+
12+
<dx-chat
13+
[user]="supportAgent"
14+
[items]="messages$ | async"
15+
[typingUsers]="supportChatTypingUsers$ | async"
16+
(onMessageEntered)="onMessageEntered($event)"
17+
(onTypingStart)="supportChatOnTypingStart()"
18+
(onTypingEnd)="supportChatOnTypingEnd()"
19+
>
20+
</dx-chat>
21+
</div>
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { NgModule, Component, enableProdMode } from '@angular/core';
2+
import { BrowserModule } from '@angular/platform-browser';
3+
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
4+
5+
import { DxChatModule } from 'devextreme-angular';
6+
import { User, Message, MessageEnteredEvent } from 'devextreme/ui/chat';
7+
import { AppService } from './app.service';
8+
import { Observable } from 'rxjs';
9+
10+
if (!/localhost/.test(document.location.host)) {
11+
enableProdMode();
12+
}
13+
14+
let modulePrefix = '';
15+
// @ts-ignore
16+
if (window && window.config.packageConfigPaths) {
17+
modulePrefix = '/app';
18+
}
19+
20+
@Component({
21+
selector: 'demo-app',
22+
templateUrl: `.${modulePrefix}/app.component.html`,
23+
styleUrls: [`.${modulePrefix}/app.component.css`],
24+
})
25+
export class AppComponent {
26+
currentUser: User;
27+
supportAgent: User;
28+
messages$: Observable<Message[]>;
29+
userChatTypingUsers$: Observable<User[]>;
30+
supportChatTypingUsers$: Observable<User[]>;
31+
32+
constructor(private appService: AppService) {
33+
[this.currentUser, this.supportAgent] = this.appService.getUsers();
34+
this.messages$ = this.appService.messages$;
35+
this.userChatTypingUsers$ = this.appService.userChatTypingUsers$;
36+
this.supportChatTypingUsers$ = this.appService.supportChatTypingUsers$;
37+
}
38+
39+
onMessageEntered(event: MessageEnteredEvent) {
40+
this.appService.onMessageEntered(event);
41+
}
42+
43+
userChatOnTypingStart() {
44+
this.appService.userChatOnTypingStart();
45+
}
46+
47+
userChatOnTypingEnd() {
48+
this.appService.userChatOnTypingEnd();
49+
}
50+
51+
supportChatOnTypingStart() {
52+
this.appService.supportChatOnTypingStart();
53+
}
54+
55+
supportChatOnTypingEnd() {
56+
this.appService.supportChatOnTypingEnd();
57+
}
58+
}
59+
60+
@NgModule({
61+
imports: [
62+
BrowserModule,
63+
DxChatModule,
64+
],
65+
declarations: [AppComponent],
66+
bootstrap: [AppComponent],
67+
providers: [AppService],
68+
})
69+
export class AppModule { }
70+
71+
platformBrowserDynamic().bootstrapModule(AppModule);
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import { Injectable } from '@angular/core';
2+
import { Observable, BehaviorSubject } from 'rxjs';
3+
4+
import { User, Message, MessageEnteredEvent } from 'devextreme/ui/chat';
5+
6+
@Injectable({
7+
providedIn: 'root',
8+
})
9+
export class AppService {
10+
date: Date;
11+
12+
currentUser: User = {
13+
id: 'c94c0e76-fb49-4b9b-8f07-9f93ed93b4f3',
14+
name: 'John Doe',
15+
};
16+
17+
supportAgent: User = {
18+
id: 'd16d1a4c-5c67-4e20-b70e-2991c22747c3',
19+
name: 'Support Agent',
20+
avatarUrl: '../../../../images/petersmith.png',
21+
};
22+
23+
messages: Message[] = [];
24+
25+
userChatTypingUsersSubject: BehaviorSubject<User[]> = new BehaviorSubject([]);
26+
supportChatTypingUsersSubject: BehaviorSubject<User[]> = new BehaviorSubject([]);
27+
28+
messagesSubject: BehaviorSubject<Message[]> = new BehaviorSubject([]);
29+
30+
constructor() {
31+
this.date = new Date();
32+
this.date.setHours(0, 0, 0, 0);
33+
34+
this.messages = [
35+
{
36+
timestamp: this.getTimestamp(this.date, -9),
37+
author: this.supportAgent,
38+
text: 'Hello, John!\nHow can I assist you today?'
39+
},
40+
{
41+
timestamp: this.getTimestamp(this.date, -7),
42+
author: this.currentUser,
43+
text: 'Hi, I\'m having trouble accessing my account.'
44+
},
45+
{
46+
timestamp: this.getTimestamp(this.date, -7),
47+
author: this.currentUser,
48+
text: 'It says my password is incorrect.'
49+
},
50+
{
51+
timestamp: this.getTimestamp(this.date, -7),
52+
author: this.supportAgent,
53+
text: 'I can help with that. Can you please confirm your UserID for security purposes?'
54+
},
55+
{
56+
timestamp: this.getTimestamp(this.date, 1),
57+
author: this.currentUser,
58+
text: 'john.doe1357'
59+
},
60+
{
61+
timestamp: this.getTimestamp(this.date, 1),
62+
author: this.supportAgent,
63+
text: '✅ Instructions to restore access have been sent to the email address registered to your account.'
64+
},
65+
];
66+
67+
this.messagesSubject.next(this.messages);
68+
this.userChatTypingUsersSubject.next([]);
69+
this.supportChatTypingUsersSubject.next([]);
70+
}
71+
72+
get userChatTypingUsers$(): Observable<User[]> {
73+
return this.userChatTypingUsersSubject.asObservable();
74+
}
75+
76+
get supportChatTypingUsers$(): Observable<Message[]> {
77+
return this.supportChatTypingUsersSubject.asObservable();
78+
}
79+
80+
get messages$(): Observable<Message[]> {
81+
return this.messagesSubject.asObservable();
82+
}
83+
84+
getUsers(): User[] {
85+
return [this.currentUser, this.supportAgent];
86+
}
87+
88+
getTimestamp(date: Date, offsetMinutes: number = 0): number {
89+
return date.getTime() + offsetMinutes * 60000;
90+
}
91+
92+
onMessageEntered(event: MessageEnteredEvent) {
93+
this.messages = [...this.messages, event.message];
94+
this.messagesSubject.next(this.messages);
95+
}
96+
97+
userChatOnTypingStart() {
98+
this.supportChatTypingUsersSubject.next([this.supportAgent]);
99+
}
100+
101+
userChatOnTypingEnd() {
102+
this.supportChatTypingUsersSubject.next([]);
103+
}
104+
105+
supportChatOnTypingStart() {
106+
this.userChatTypingUsersSubject.next([this.currentUser]);
107+
}
108+
109+
supportChatOnTypingEnd() {
110+
this.userChatTypingUsersSubject.next([]);
111+
}
112+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<!DOCTYPE html>
2+
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
3+
<head>
4+
<title>DevExtreme Demo</title>
5+
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
6+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
7+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0" />
8+
<link rel="stylesheet" type="text/css" href="../../../../node_modules/devextreme-dist/css/dx.light.css" />
9+
10+
<script src="../../../../node_modules/core-js/client/shim.min.js"></script>
11+
<script src="../../../../node_modules/zone.js/bundles/zone.umd.js"></script>
12+
<script src="../../../../node_modules/reflect-metadata/Reflect.js"></script>
13+
<script src="../../../../node_modules/systemjs/dist/system.js"></script>
14+
15+
<script src="config.js"></script>
16+
<script>
17+
System.import("app").catch(console.error.bind(console));
18+
</script>
19+
</head>
20+
21+
<body class="dx-viewport">
22+
<div class="demo-container">
23+
<demo-app>Loading...</demo-app>
24+
</div>
25+
</body>
26+
</html>
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import React, { useState } from 'react';
2+
3+
import Chat, { ChatTypes } from 'devextreme-react/chat';
4+
import { MessageEnteredEvent, TypingEndEvent, TypingStartEvent } from 'devextreme/ui/chat';
5+
6+
import { currentUser, supportAgent, initialMessages } from './data.ts';
7+
8+
export default function App() {
9+
const [userChatTypingUsers, setUserChatTypingUsers] = useState<ChatTypes.User[]>([]);
10+
const [supportChatTypingUsers, setSupportChatTypingUsers] = useState<ChatTypes.User[]>([]);
11+
const [messages, setMessages] = useState<ChatTypes.Message[]>(initialMessages);
12+
13+
function onMessageEntered({ message }: MessageEnteredEvent) {
14+
setMessages(prevMessages => [...prevMessages, message]);
15+
}
16+
17+
function typingStart({ user }: TypingStartEvent) {
18+
if (user.id === currentUser.id) {
19+
setSupportChatTypingUsers([supportAgent]);
20+
} else {
21+
setUserChatTypingUsers([currentUser]);
22+
}
23+
}
24+
25+
function typingEnd({ user }: TypingEndEvent) {
26+
if (user.id === currentUser.id) {
27+
setSupportChatTypingUsers([]);
28+
} else {
29+
setUserChatTypingUsers([]);
30+
}
31+
}
32+
33+
return (
34+
<React.Fragment>
35+
<Chat
36+
user={currentUser}
37+
items={messages}
38+
onMessageEntered={onMessageEntered}
39+
onTypingStart={typingStart}
40+
onTypingEnd={typingEnd}
41+
typingUsers={userChatTypingUsers}
42+
/>
43+
<Chat
44+
user={supportAgent}
45+
items={messages}
46+
onMessageEntered={onMessageEntered}
47+
onTypingStart={typingStart}
48+
onTypingEnd={typingEnd}
49+
typingUsers={supportChatTypingUsers}
50+
/>
51+
</React.Fragment>
52+
);
53+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { ChatTypes } from 'devextreme-react/chat';
2+
3+
const date = new Date();
4+
date.setHours(0, 0, 0, 0);
5+
6+
function getTimestamp(date, offsetMinutes = 0) {
7+
return date.getTime() + offsetMinutes * 60000;
8+
}
9+
10+
export const currentUser: ChatTypes.User = {
11+
id: 'c94c0e76-fb49-4b9b-8f07-9f93ed93b4f3',
12+
name: 'John Doe',
13+
};
14+
15+
export const supportAgent: ChatTypes.User = {
16+
id: 'd16d1a4c-5c67-4e20-b7v0e-2991c22747c3',
17+
name: 'Support Agent',
18+
avatarUrl: '../../../../images/petersmith.png',
19+
};
20+
21+
export const initialMessages = [
22+
{
23+
timestamp: getTimestamp(date, -9),
24+
author: supportAgent,
25+
text: 'Hello, John!\nHow can I assist you today?'
26+
},
27+
{
28+
timestamp: getTimestamp(date, -7),
29+
author: currentUser,
30+
text: 'Hi, I\'m having trouble accessing my account.'
31+
},
32+
{
33+
timestamp: getTimestamp(date, -7),
34+
author: currentUser,
35+
text: 'It says my password is incorrect.'
36+
},
37+
{
38+
timestamp: getTimestamp(date, -7),
39+
author: supportAgent,
40+
text: 'I can help with that. Can you please confirm your UserID for security purposes?'
41+
},
42+
{
43+
timestamp: getTimestamp(date, 1),
44+
author: currentUser,
45+
text: 'john.doe1357'
46+
},
47+
{
48+
timestamp: getTimestamp(date, 1),
49+
author: supportAgent,
50+
text: '✅ Instructions to restore access have been sent to the email address registered to your account.'
51+
},
52+
];
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<title>DevExtreme Demo</title>
5+
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
6+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
7+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0" />
8+
<link rel="stylesheet" type="text/css" href="../../../../node_modules/devextreme-dist/css/dx.light.css" />
9+
<link rel="stylesheet" type="text/css" href="styles.css" />
10+
11+
<script src="../../../../node_modules/core-js/client/shim.min.js"></script>
12+
<script src="../../../../node_modules/systemjs/dist/system.js"></script>
13+
<script type="text/javascript" src="config.js"></script>
14+
<script type="text/javascript">
15+
System.import("./index.tsx");
16+
</script>
17+
</head>
18+
19+
<body class="dx-viewport">
20+
<div class="demo-container">
21+
<div id="app"></div>
22+
</div>
23+
</body>
24+
</html>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import React from 'react';
2+
import ReactDOM from 'react-dom';
3+
4+
import App from './App.tsx';
5+
6+
ReactDOM.render(
7+
<App />,
8+
document.getElementById('app'),
9+
);

0 commit comments

Comments
 (0)