react中使用构建缓存
Live chat is a customer support method with a proven record. It’s fast and efficient since one agent can help many customers at once. Best of all, the quicker you can answer customer’s questions during the buying process, the more likely that person is to buy.
实时聊天是一种具有可靠记录的客户支持方法。 快速有效,因为一个代理商可以一次帮助许多客户。 最重要的是,您在购买过程中回答客户问题的速度越快,该人购买的可能性就越大。
So, how do you integrate a live chat into your React application?
那么,如何将实时聊天集成到React应用程序中?
In this tutorial, I’m going to show you how to integrate a live chat feature into your React app without the worry of maintaining your own chat server and architecture.
在本教程中,我将向您展示如何将实时聊天功能集成到您的React应用程序中,而无需担心维护自己的聊天服务器和架构。
Here’s a preview of what we’ll be building:
这是我们将要构建的预览:
To power our chat application, we’ll be using CometChat Pro.
为了支持我们的聊天应用程序,我们将使用CometChat Pro。
CometChat Pro is a powerful communication API that enables you to add chat features to your application. With easy integrations and clear documentation, you’ll be able to add a live chat feature into your application with just a few lines of code, as you will soon see. If you want to follow along, you can create a free account here.
CometChat Pro是一个功能强大的通信API,使您可以向应用程序添加聊天功能。 借助轻松的集成和清晰的文档,您将能够只用几行代码就可以在应用程序中添加实时聊天功能,您将很快看到。 如果您想继续,可以在这里创建一个免费帐户。
In addition to CometChat, we will use the following technologies:
除了CometChat,我们还将使用以下技术:
I encourage you to follow along but if you’d rather to skip ahead to the code, you can find the complete code for this application on GitHub.
我鼓励您继续学习,但是如果您想跳过代码,可以在GitHub上找到此应用程序的完整代码。
To power your chat application, you’ll be using CometChat. Before you can integrate CometChat, however, you must first create a CometChat app.
要使用您的聊天应用程序,您将使用CometChat。 但是,在集成CometChat之前,必须先创建一个CometChat应用程序。
To create a CometChat app, go to the CometChat dashboard (if you don’t have a free CometChat account already now is a good time to sign up) and hit the + icon.
要创建一个CometChat应用程序,转到CometChat仪表盘(如果你没有一个免费帐户CometChat现在已经是一个很好的时间注册 )和命中+图标。
I called my application “react-chat-widget” but you can call yours whatever you like.
我将我的应用程序称为“ react-chat-widget”,但是您可以随心所欲地调用自己的应用程序。
We’ll have two types of users connect to our chat: Customers who open the chat widget and one support agent who will access the chat and respond to inquiries from the dashboard. Users are a fundamental concept in CometChat, which you can read more about here.
我们将有两种类型的用户连接到我们的聊天:打开聊天小部件的客户和一个将访问聊天并响应来自仪表板的查询的支持代理。 用户是CometChat的基本概念,您可以在此处了解更多信息。
Because we will likely have many customers, for each customer who connects to our chat, we will need to dynamically create a CometChat user. However, because there will only be one agent, we can create an “Agent” user in advance from the dashboard.
因为我们可能会有很多客户,所以对于每个连接到我们聊天的客户,我们将需要动态创建一个CometChat用户。 但是,由于只有一个代理,因此我们可以提前从仪表板创建一个“代理”用户。
To do so, click Explore then head to the Users tab. Here, you can click Create User:
为此,请单击“浏览”,然后转到“用户”选项卡。 在这里,您可以单击创建用户:
For the user ID, I wrote “ecommerce-agent” and for the name, I wrote “Demo Agent”. I recommend you use the same values if you’re following along. In any case, take note of the user ID because you’ll need to reference it later.
对于用户ID,我写了“ ecommerce-agent”,对于名字,我写了“ Demo Agent”。 如果您要遵循,建议您使用相同的值。 无论如何,请记下该用户ID,因为以后需要引用它。
Before we move on from the dashboard and on to the code, we should create a CometChat full access key.
在从仪表板转到代码之前,我们应该创建一个CometChat完全访问密钥。
On the same page, click the API Keys tab then Create API Key:
在同一页面上,单击“ API密钥”选项卡,然后单击“创建API密钥”:
I called my key “react-chat-api” but it doesn’t really matter what you write here.
我将密钥称为“ react-chat-api”,但是您在此处写的内容并不重要。
Note your API key and app ID because, like the agent user ID, you’ll need them both later.
请注意您的API密钥和应用程序ID,因为像座席用户ID一样,以后您都需要它们。
In the previous step, we created a full access key, which we can use to create CometChat users dynamically. While we could do this on the client, that would mean sharing our private full access key in public, which is a no go.
在上一步中,我们创建了一个完整的访问密钥,可用于动态创建CometChat用户。 尽管我们可以在客户端上执行此操作,但这意味着要在公共场合共享我们的私有完全访问密钥,这是绝对不能的。
To avoid this problem, we’ll create a simple Express server that:
为避免此问题,我们将创建一个简单的Express服务器,该服务器:
Alright, let’s start.
好吧,让我们开始吧。
First, create a new empty directory for your Express app and run `npm init -y`
:
首先,为您的Express应用程序创建一个新的空目录,然后运行`npm init -y`
:
mkdir react-express-chat-widget
cd react-express-chat-widget
npm init -y
Next, install Express and axios:
接下来,安装Express和axios:
npm install express axios
Then, in a file called sever.js
paste:
然后,在名为sever.js
的文件中粘贴:
const express = require('express');
const axios = require('axios');
const app = express();
// enter CometChat Pro configurations here
const appID = '{appID}';
const apiKey = '{apiKey}';
const agentUID = '{agentUID}';
const url = 'https://api.cometchat.com/v1';
const headers = {
'Content-Type': 'application/json',
appid: appID,
apikey: apiKey,
};
In the above file, we:
在以上文件中,我们:
Define the CometChat API url
for convenient access
定义CometChat API url
以方便访问
Create a headers
object with our appID
and apiKey
. We'll send this header with every request to CometChat
使用我们的appID
和apiKey
创建headers
对象。 我们将在每次请求时将此标头发送给CometChat
In the same file, let’s now define a route to handle creating new CometChat users.
现在,在同一文件中,定义处理创建新CometChat用户的途径。
In order to create a new user, we need to send a POST request with the UID and name for the user.
为了创建一个新用户,我们需要发送一个带有UID和用户名的POST请求。
In this tutorial, we will hard-code the same name for all customers — we’ll call every customer “customer” — but the UID has to be unique. For the UID, we can use new Date().getTime()
to generate a random ID.
在本教程中,我们将为所有客户硬编码相同的名称-我们将每个客户称为“客户”-但UID必须唯一。 对于UID,我们可以使用new Date().getTime()
生成随机ID。
Add the following code to server.js
:
将以下代码添加到server.js
:
app.get('/api/create', (req, res) => {
// data for new user
const data = {
// you can use your own logic to generate random UID and name
// only uid has to be unique
uid: new Date().getTime(),
name: 'customer',
};
axios
.post(`${url}/users`, JSON.stringify(data), {
headers,
})
.then(response => {
// user is created, fetch auth token
requestAuthToken(response.data.data.uid)
.then(token => {
console.log('Success:' + JSON.stringify(token));
// token is returned to client
res.json(token);
})
.catch(error => console.error('Error:', error));
})
.catch(error => console.error('Error:', error));
});
// this function will fetch token
const requestAuthToken = uid => {
return new Promise((resolve, reject) => {
axios
.post(`${url}/users/${uid}/auth_tokens`, null, {
headers,
})
.then(response => {
console.log('New Auth Token:', response.data);
resolve(response.data.data);
})
.catch(error => reject(error));
});
};
When this route is called, Express will:
调用此路由时,Express将:
Send a POST request to https://api.cometchat.com/v1/users/ with the correct headers
and information about the new user
使用正确的headers
和有关新用户的信息将POST请求发送到https://api.cometchat.com/v1/users/
We also created a function called requestAuthToken
to help with fetching the authentication token.
我们还创建了一个名为requestAuthToken
的函数来帮助获取身份验证令牌。
Next, in the same file, let’s create an authentication route we can call to create tokens for returning users:
接下来,在同一个文件中,创建一个身份验证路由,我们可以调用该身份验证路由来为回头用户创建令牌:
//...
app.get('/api/auth', (req, res) => {
const uid = req.query.uid;
// if you have your own login method, call it here.
// then call CometChat for auth token
requestAuthToken(uid)
.then(token => {
console.log('Success:' + JSON.stringify(token));
res.json(token);
})
.catch(error => console.error('Error:', error));
});
//...
Finally, let’s create a function to return a list of users, excluding the agent.
最后,让我们创建一个函数以返回用户列表(不包括代理)。
We’ll call this endpoint from the dashboard later to show a list of users the agent can talk with (of course, the agent doesn’t want to talk to themselves, so we filter them from the list):
稍后我们将从仪表板中调用此终结点,以显示代理可以与之交谈的用户列表(当然,代理不想与自己交谈,因此我们从列表中过滤掉他们):
//...
app.get('/api/users', (req, res) => {
axios
.get(`${url}/users`, {
headers,
})
.then(response => {
const { data } = response.data;
const filterAgentData = data.filter(data => {
// filter agent out from the list of users
return data.uid !== agentUID;
});
res.json(filterAgentData);
})
.catch(error => console.error('Error:', error));
});
//...
At the very bottom of server.js
, run the server:
在server.js
最底部,运行服务器:
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
console.log(`Listening on port ${PORT}`);
});
If you’ve been following along, this is what server.js
should look like by now:
如果您一直在遵循,那么这就是server.js
样子:
const express = require('express');
const axios = require('axios');
const app = express();
const appID = '{appID}';
const apiKey = '{apiKey}';
const agentUID = '{agentUID}';
const url = 'https://api.cometchat.com/v1';
const headers = {
'Content-Type': 'application/json',
appid: appID,
apikey: apiKey,
};
app.get('/api/create', (req, res) => {
const data = {
uid: new Date().getTime(),
name: 'customer',
};
axios
.post(`${url}/users`, JSON.stringify(data), {
headers,
})
.then(response => {
requestAuthToken(response.data.data.uid)
.then(token => {
console.log('Success:' + JSON.stringify(token));
res.json(token);
})
.catch(error => console.error('Error:', error));
})
.catch(error => console.error('Error:', error));
});
app.get('/api/auth', (req, res) => {
const uid = req.query.uid;
requestAuthToken(uid)
.then(token => {
console.log('Success:' + JSON.stringify(token));
res.json(token);
})
.catch(error => console.error('Error:', error));
});
const requestAuthToken = uid => {
return new Promise((resolve, reject) => {
axios
.post(`${url}/users/${uid}/auth_tokens`, null, {
headers,
})
.then(response => {
console.log('New Auth Token:', response.data);
resolve(response.data.data);
})
.catch(error => reject(error));
});
};
app.get('/api/users', (req, res) => {
axios
.get(`${url}/users`, {
headers,
})
.then(response => {
const { data } = response.data;
const filterAgentData = data.filter(data => {
return data.uid !== agentUID;
});
res.json(filterAgentData);
})
.catch(error => console.error('Error:', error));
});
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
console.log(`Listening on port ${PORT}`);
});
In a terminal window, run node server.js
and look out for a message that says "Listening on port 5000". Now would be a good time to test the end-points with curl or Postman but we'll trust they work and move on to the client.
在终端窗口中,运行node server.js
并查找消息“正在侦听端口5000”。 现在是使用curl或Postman测试端点的好时机,但我们相信它们会起作用并继续向客户推广。
Inside your directory, run npx create-react-app
to scaffold a new React application:
在您的目录中,运行npx create-react-app
来搭建一个新的React应用程序:
npx create-react-app client
Your folder structure should look like this:
您的文件夹结构应如下所示:
|-- express-react-chat-widget
|-- package-lock.json
|-- package.json
|-- server.js
|-- client
|-- .gitignore
|-- package-lock.json
|-- package.json
|-- public
|-- src
With your React application in place, navigate to the client
directory install the following modules:
在您的React应用程序就位后,导航到client
目录,安装以下模块:
cd client
npm install @cometchat-pro/chat react-chat-widget react-router-dom bootstrap react-md-spinner
Create React app is really useful to bootstrap a React app, but it also generates a lot of files we don’t need (test files, and so on).
创建React应用程序对于引导React应用程序确实很有用,但它还会生成很多我们不需要的文件(测试文件,等等)。
Before we jump into the code, remove everything in the client/src
directory - we will start from scratch.
在进入代码之前,请删除client/src
目录中的所有内容-我们将从头开始。
To begin, create a config.js
file with your app ID and agent UID inside:
首先,创建一个config.js
文件,其中包含您的应用程序ID和代理UID:
// client/src/config.js
const config = {
appID: '{appID}',
agentUID: '{agentUID}',
}
export default config;
This is a bit of boilerplate we can use to reference our CometChat credentials from anywhere.
这是一个样板,我们可以从任何地方引用我们的CometChat凭据。
While we’re dealing with boilerplate, let’s also take this opportunity to create an index.css
file:
在处理样板时,让我们趁此机会创建一个index.css
文件:
body {
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
}
.message {
overflow: hidden;
}
.balon1 {
float: right;
background: #35cce6;
border-radius: 10px;
}
.balon2 {
float: left;
background: #f4f7f9;
border-radius: 10px;
}
We will reference this later from the dashboard.
我们稍后将在仪表板中进行引用。
Now, in a file called index.js
paste the following:
现在,在名为index.js
的文件中粘贴以下内容:
import React from 'react';
import ReactDOM from 'react-dom';
import 'bootstrap/dist/css/bootstrap.css';
import './index.css';
import App from './App';
import { CometChat } from '@cometchat-pro/chat';
import config from './config';
CometChat.init(config.appID)
ReactDOM.render(<App />, document.getElementById('root'));
Here, we import Bootstrap, CometChat, and the config file we just created before initialising CometChat and rendering our App
.
在这里,我们导入Bootstrap,CometChat和刚创建的配置文件,然后初始化CometChat并呈现我们的App
。
If you’re following along, you’ll have noticed we haven’t defined App
yet - let's do that now.
如果您一直在关注,您会发现我们尚未定义App
现在就开始做。
In a file called App.js
:
在名为App.js
的文件中:
import React from 'react';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
import Client from './Client';
import Agent from './Agent';
const App = () => {
return (
<Router>
<React.Fragment>
<ul>
<li>
<Link to='/'>Client Home</Link>
</li>
<li>
<Link to='/agent'>Agent Dashboard</Link>
</li>
</ul>
<hr />
<Route exact path='/' component={Client} />
<Route path='/agent' component={Agent} />
</React.Fragment>
</Router>
);
}
export default App;
Here, we define two routes:
在这里,我们定义了两条路线:
The /
or "Customer home"
route for the customer to chat with the agent
客户与代理聊天的"Customer home"
/
或"Customer home"
路线
And the /agent
or "Agent Dashboard"
route for quick and convenient access to the dashboard
/agent
或"Agent Dashboard"
路由可用于快速方便地访问仪表板
Let’s tackle the customer-facing component first. We’ll call this the client component.
让我们首先解决面向客户的组件。 我们将其称为客户端组件。
Our client component will have two main responsibilities:
我们的客户部分将承担两项主要职责:
Create a file called Client.js
and paste the following:
创建一个名为Client.js
的文件并粘贴以下内容:
// Client.js
import React, {Component} from 'react';
import { Widget, addResponseMessage, addUserMessage, dropMessages } from 'react-chat-widget';
import { CometChat } from '@cometchat-pro/chat';
import config from './config';
import 'react-chat-widget/lib/styles.css';
const agentUID = config.agentUID;
const CUSTOMER_MESSAGE_LISTENER_KEY = "client-listener";
const limit = 30;
class Client extends Component {
componentDidMount() {
addResponseMessage('Welcome to our store!');
addResponseMessage('Are you looking for anything in particular?');
}
render() {
return (
<div className='App'>
<Widget
handleNewUserMessage={this.handleNewUserMessage}
title='My E-commerce Live Chat'
subtitle='Ready to help you'
/>
</div>
);
}
createUser = async () => {
const response = await fetch(`/api/create`)
const result = await response.json()
return result;
}
handleNewUserMessage = newMessage => {
console.log(`New message incoming! ${newMessage}`);
var textMessage = new CometChat.TextMessage(
agentUID,
newMessage,
CometChat.MESSAGE_TYPE.TEXT,
CometChat.RECEIVER_TYPE.USER
);
let uid = localStorage.getItem("cc-uid");
if (uid === null) {
// no uid, create user
this.createUser().then(
result => {
console.log('auth token fetched', result);
localStorage.setItem("cc-uid",result.uid)
// do login
CometChat.login(result.authToken)
.then(user => {
console.log("Login successfully:", { user });
CometChat.sendMessage(textMessage).then(
message => {
console.log('Message sent successfully:', message);
},
error => {
console.log('Message sending failed with error:', error);
}
);
// create listener
CometChat.addMessageListener(
CUSTOMER_MESSAGE_LISTENER_KEY,
new CometChat.MessageListener({
onTextMessageReceived: message => {
console.log("Incoming Message Log", { message });
addResponseMessage(message.text);
}
})
);
})
},
error => {
console.log('Initialization failed with error:', error);
})
} else {
// we have uid, do send
CometChat.sendMessage(textMessage).then(
message => {
console.log('Message sent successfully:', message);
},
error => {
console.log('Message sending failed with error:', error);
}
);
}
};
componentWillUnmount() {
CometChat.removeMessageListener(CUSTOMER_MESSAGE_LISTENER_KEY);
CometChat.logout();
dropMessages();
}
}
export default Client;
Woah, that is a lot of new code. Let’s break it down.
哇,这是很多新代码。 让我们分解一下。
The render
function is simple enough, it mainly boils down to rendering the react-chat-widget.
render
功能非常简单,主要归结为渲染react-chat-widget 。
Most of the code is dedicated to handling new message sent by the customer in the function called handleNewUserMessage
.
大多数代码专用于处理客户在名为handleNewUserMessage
的函数中发送的handleNewUserMessage
。
In a nutshell, we first check to see if customer UID exists in localStorage. If it does, we will use this UID to log the user in and send messages. Otherwise, we call createUser()
and use the returned value to login. This createUser
function calls the endpoint we defined earlier in the tutorial.
简而言之,我们首先检查客户UID是否存在于localStorage中。 如果是这样,我们将使用此UID登录用户并发送消息。 否则,我们调用createUser()
并使用返回值登录。 这个createUser
函数调用我们在本教程前面定义的端点。
Finally, in a React lifecycle function called componentWillUnmount
, we remember to remove the message listener.
最后,在一个名为componentWillUnmount
的React生命周期函数中,我们记得要删除消息侦听器。
Before moving on, here’s a little tip: In the above code, rather than typing server url and port ("localhost:5000/users"
or something like that) in our front-end, we can instead add a proxy option to package.json
. This will allow us to write /users"
instead of //localhost:5000/users"
:
在继续之前,这里有个小提示:在上面的代码中,我们可以在package.json
添加代理选项,而不是在前端键入服务器的URL和端口("localhost:5000/users"
或类似的名称) package.json
。 这将使我们能够编写/users"
而不是//localhost:5000/users"
:
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
],
"proxy": "http://localhost:5000"
Here is what the application should look like:
这是应用程序的外观:
As you can see, you can send and receive messages, but if we refresh our page, the chat messages will all disappear and that’s no good.
如您所见,您可以发送和接收消息,但是如果我们刷新页面,则聊天消息将全部消失,这不好。
To solve this issue, we’ll setup the componentDidMount
method to look for customer UID in localStorage
, so that when customers refresh the page, they can continue chatting from where they left off.
为了解决此问题,我们将设置componentDidMount
方法在localStorage
查找客户UID,以便客户刷新页面时,他们可以从上次中断的地方继续聊天。
Once found, we’ll use this UID to initiate a chain of methods to login, fetch previous messages and create listener for incoming messages.
找到后,我们将使用此UID来启动一系列方法来登录,获取先前的消息并为传入的消息创建侦听器。
componentDidMount() {
addResponseMessage('Welcome to our store!');
addResponseMessage('Are you looking for anything in particular?');
let uid = localStorage.getItem("cc-uid");
// check for uid, if exist then get auth token
if ( uid !== null) {
this.fetchAuthToken(uid).then(
result => {
console.log('auth token fetched', result);
// SDK login
CometChat.login(result.authToken)
.then( user => {
console.log("Login successfully:", { user });
// listen to incoming message and fetch previous messages
this.createMessageListener();
this.fetchPreviousMessages();
})
},
error => {
console.log('Initialization failed with error:', error);
}
);
}
}
// The functions used above
fetchAuthToken = async uid => {
const response = await fetch(`/api/auth?uid=${uid}`)
const result = await response.json()
return result;
}
createMessageListener = () => {
CometChat.addMessageListener(
CUSTOMER_MESSAGE_LISTENER_KEY,
new CometChat.MessageListener({
onTextMessageReceived: message => {
console.log("Incoming Message Log", { message });
addResponseMessage(message.text);
}
})
);
}
fetchPreviousMessages = () => {
var messagesRequest = new CometChat.MessagesRequestBuilder()
.setUID(agentUID)
.setLimit(limit)
.build();
messagesRequest.fetchPrevious().then(
messages => {
console.log("Message list fetched:", messages);
// add messages to the widget chat bubbles
messages.forEach( message => {
if(message.receiver !== agentUID){
addResponseMessage(message.text);
} else {
addUserMessage(message.text)
}
});
},
error => {
console.log("Message fetching failed with error:", error);
}
);
}
Now if we refresh our page, the app will try to login to CometChat and fetch previous messages automatically by looking for our customer UID from the localStorage
. Good stuff!
现在,如果我们刷新页面,该应用程序将尝试登录到CometChat并通过从localStorage
查找我们的客户UID来自动获取以前的消息。 好东西!
There’s still a small problem, though. As it stands right now, there is still no way for an agent to reply to customer’s messages.
但是,仍然存在一个小问题。 就目前而言,代理商仍然无法回复客户的消息。
We will solve this problem by building the agent dashboard, where our agent can view and reply chat messages from customers.
我们将通过构建代理仪表板来解决此问题,在该仪表板上我们的代理可以查看和回复客户的聊天消息。
We’re finished with the Client.js
file, so you can grab a coffee before moving on to code the Agent.js
file ☕
我们已经完成了Client.js
文件,因此您可以在继续编码Agent.js
文件之前先Agent.js
咖啡☕
The main function of the agent dashboard is to grab all customers from CometChat Pro and display any incoming message from new customer into the customer list chat for agents to click on and reply. The core functionality is very similar to the client:
座席仪表板的主要功能是从CometChat Pro吸引所有客户,并将新客户收到的任何消息显示到客户列表聊天室中,以供座席点击和答复。 核心功能与客户端非常相似:
With CometChat, you could easily create multiple agents but to keep things simple and avoid user management, we have only one agent, which we created earlier.
使用CometChat,您可以轻松地创建多个代理,但是为了使事情变得简单并避免用户管理,我们只有一个代理,该代理是我们之前创建的。
Create a component called Agent.js
and set the initial state:
创建一个名为Agent.js
的组件并设置初始状态:
import React, {Component} from 'react';
import {CometChat} from '@cometchat-pro/chat';
import MDSpinner from "react-md-spinner";
import config from './config';
const agentUID = config.agentUID;
const AGENT_MESSAGE_LISTENER_KEY = 'agent-listener'
const limit = 30;
class Agent extends Component {
state = {
customers: [],
selectedCustomer: '',
chat: [],
chatIsLoading: false,
customerIsLoading:true
}
}
In the same file, create a componentDidMount
method:
在同一文件中,创建一个componentDidMount
方法:
componentDidMount(){
this.fetchAuthToken(agentUID).then(
authToken => {
console.log('auth token fetched', authToken);
CometChat.login(authToken)
.then( user => {
console.log("Login successfully:", { user });
// after login, fetch all users
// put them into customer state
this.fetchUsers().then(result => {
this.setState({
customers: result,
customerIsLoading: false
})
});
CometChat.addMessageListener(
AGENT_MESSAGE_LISTENER_KEY,
new CometChat.MessageListener({
onTextMessageReceived: message => {
let {customers, selectedCustomer, chat} = this.state;
console.log("Incoming Message Log", { message });
// check incoming message
// if from the same customer agent is currently chatting
// push a new chat item into chat state
if(selectedCustomer === message.sender.uid){
chat.push(message);
this.setState({
chat
})
} else {
// if new customer, push a new customer into customer state
let aRegisteredCustomer = customers.filter( customer => {
return customer.uid === message.sender.uid });
if(!aRegisteredCustomer.length){
customers.push(message.sender)
this.setState({
customers
})
}
}
}
})
);
})
},
error => {
console.log('Initialization failed with error:', error);
}
);
}
fetchUsers = async () => {
const response = await fetch(`/api/users`)
const result = await response.json()
return result;
}
There’s a lot happening in the above code, here’s a rundown to help you understand:
上面的代码中发生了很多事情,这里有一个摘要可以帮助您理解:
You’ll probably recognise the Express API we create to get a list of registered users. We use this to populate the list of users on the left-hand-side of the dashboard. We will position the list to the left-hand-side using a combination of Bootstrap classes and the index.css
file we defined earlier.
您可能会认识到我们创建的Express API,以获取已注册用户的列表。 我们使用它来填充仪表板左侧的用户列表。 我们将结合使用Bootstrap类和我们先前定义的index.css
文件将列表放置在左侧。
Next, let’s create the render function. It will render a conversation interface, styled with Bootstrap. To make the code easier to follow, we will separate CustomerList
and ChatBox
into their own components, which you can define in the same file:
接下来,让我们创建渲染函数。 它将呈现一个对话界面,其样式为Bootstrap。 为了使代码更易于理解,我们将CustomerList
和ChatBox
分为它们自己的组件,您可以在同一文件中定义它们:
render() {
return(
<div className='container-fluid'>
<div className='row'>
<div className='col-md-2'></div>
<div className="col-md-8 h-100pr border rounded">
<div className='row'>
<div className='col-lg-4 col-xs-12 bg-light' style={{ height: 658 }}>
<div className='row p-3'><h2>Customer List</h2></div>
<div className='row ml-0 mr-0 h-75 bg-white border rounded'
style={{ height: '100%', overflow:'auto' }}>
{/* The CustomerList component */}
<CustomerList {...this.state} selectCustomer={this.selectCustomer}/>
</div>
</div>
<div className='col-lg-8 col-xs-12 bg-light' style={{ height: 658 }}>
<div className='row p-3 bg-white'>
<h2>Who you gonna chat with?</h2>
</div>
<div className='row pt-5 bg-white'
style={{ height: 530, overflow:'auto' }}>
{/* The ChatBox component */}
<ChatBox {...this.state} />
</div>
<div className="row bg-light" style={{ bottom: 0, width: '100%' }}>
<form className="row m-0 p-0 w-100" onSubmit={this.handleSubmit}>
<div className="col-9 m-0 p-1">
<input id="text"
className="mw-100 border rounded form-control"
type="text"
name="text"
ref="message"
placeholder="Type a message..."/>
</div>
<div className="col-3 m-0 p-1">
<button className="btn btn-outline-secondary rounded border w-100"
title="Send"
style={{ paddingRight: 16 }}>Send</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
)
}
Chatbox
component:
Chatbox
组件:
class ChatBox extends Component {
render(){
const {chat, chatIsLoading} = this.props;
if (chatIsLoading) {
return (
<div className='col-xl-12 my-auto text-center'>
<MDSpinner size='72'/>
</div>
)
}
else {
// simple mapping of array from props
return (
<div className='col-xl-12'>
{
chat
.map(chat =>
<div key={chat.id} className="message">
<div className={
`${chat.receiver !== agentUID ? 'balon1': 'balon2'} p-3 m-1`
}>
{chat.text}
</div>
</div>)
}
</div>
)
}
}
}
CustomerList
component:
CustomerList
组件:
class CustomerList extends Component {
render(){
const {customers, customerIsLoading, selectedCustomer} = this.props;
if (customerIsLoading) {
return (
<div className='col-xl-12 my-auto text-center'>
<MDSpinner size='72'/>
</div>
)
}
else {
// simple mapping of array from props
return (
<ul className="list-group list-group-flush w-100">
{
customers
.map(customer =>
<li
key={customer.uid}
className={
`list-group-item ${customer.uid === selectedCustomer ? 'active':''}`
}
onClick={ () => this.props.selectCustomer(customer.uid) }>
{customer.name}
</li>)
}
</ul>
)
}
}
}
That makes up the foundation for our UI, but we still can’t send messages.
这构成了我们的UI的基础,但是我们仍然无法发送消息。
To send messages, we have to create a handler for when the agent submits a new message. How to send messages should be familiar to you now because we’ll make the the same sendMessage
call that we made in the Client component as well.
要发送消息,我们必须为代理提交新消息的时间创建一个处理程序。 现在您应该已经熟悉如何发送消息了,因为我们将进行与在Client组件中相同的sendMessage
调用。
handleSubmit = event => {
event.preventDefault();
let message = this.refs.message.value;
var textMessage = new CometChat.TextMessage(
this.state.selectedCustomer,
message,
CometChat.MESSAGE_TYPE.TEXT,
CometChat.RECEIVER_TYPE.USER
);
CometChat.sendMessage(textMessage).then(
message => {
let {chat} = this.state;
console.log('Message sent successfully:', message);
chat.push(message);
this.setState({
chat
})
},
error => {
console.log('Message sending failed with error:', error);
}
);
this.refs.message.value='';
}
We’ll also want to enable the agent to see historical messages like we did for the customer:
我们还希望使代理能够像我们为客户那样查看历史消息:
selectCustomer = uid => {
this.setState({
selectedCustomer: uid
}, ()=> {this.fetchPreviousMessage(uid)})
}
fetchPreviousMessage = uid => {
this.setState({
hat: [],
chatIsLoading: true
}, () => {
var messagesRequest = new CometChat.MessagesRequestBuilder()
.setUID(uid)
.setLimit(limit)
.build();
messagesRequest.fetchPrevious().then(
messages => {
console.log("Message list fetched:", messages);
this.setState({
chat: messages,
chatIsLoading: false
})
},
error => {
console.log("Message fetching failed with error:", error);
}
);
});
}
Remember to remove the message listener when the component unmounts:
当组件卸载时,请记住删除消息侦听器:
componentWillUnmount(){
CometChat.removeMessageListener(AGENT_MESSAGE_LISTENER_KEY);
CometChat.logout();
}
If you have any trouble, you can reference the complete Agent file here on GitHub.
如果有任何问题,可以在GitHub上参考完整的Agent文件。
Check out the final product:
查看最终产品:
If you’re wondering where those superhero users came from, they are automatically created by CometChat Pro when you create a new app. Don’t forget to delete them before using the app in production.
如果您想知道这些超级英雄用户来自何处,当您创建新应用时,它们将由CometChat Pro自动创建。 在生产中使用该应用程序之前,请不要忘记删除它们。
Now both the support agent and your customers are ready to chat with each other. You can open Client Home and Agent Dashboard in separate windows and try it out.
现在,支持代理和您的客户都准备好彼此聊天。 您可以在单独的窗口中打开Client Home和Agent Dashboard并尝试一下。
Congratulations!
恭喜你!
We’ve made our very own live chat widget for React application, and it took no time at all! Indeed, CometChat Pro enables you to send and receive message by only writing a few lines of code. You don’t need to deal with creating your own chat server and architecture. It also has more capabilities than just making a chat widget.
我们已经为React应用程序制作了自己的实时聊天窗口小部件,并且完全没有时间! 实际上,CometChat Pro使您仅需编写几行代码即可发送和接收消息。 您无需处理创建自己的聊天服务器和体系结构。 它不仅具有制作聊天小部件的功能,还具有更多的功能。
If you wanted to expand this application, try enabling customers to send media messages with CometChat.
如果要扩展此应用程序,请尝试使客户能够使用CometChat发送媒体消息 。
P.S: If you’re struggling to learn React, You might find React Distilled a great aid. Check it out here!
PS:如果您正在努力学习React,那么您可能会发现React Distilled很有帮助。 在这里查看 !
react中使用构建缓存