├── .gitignore
├── README.md
├── client
├── .gitignore
├── package-lock.json
├── package.json
├── public
│ ├── favicon.ico
│ └── index.html
└── src
│ ├── Agent.js
│ ├── App.js
│ ├── Client.js
│ ├── config.js
│ ├── index.css
│ └── index.js
├── package-lock.json
├── package.json
├── screenshot
├── screenshot_1.png
└── screenshot_2.png
└── server.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Build a Customer Support Live Chat Widget with React
2 |
3 |
4 | Read the full tutorial here:
5 |
6 | [**>> Build a Customer Support Live Chat Widget with React**](https://www.cometchat.com/tutorials/build-a-customer-support-live-chat-widget-with-react/?utm_source=github&utm_medium=example-code-readme)
7 |
8 | This demo app shows how to build an Android group chat app with React:
9 |
10 | 
11 | 
12 |
13 |
14 | ## Running the demo
15 |
16 | To run the demo first setup CometChat:
17 |
18 | 1. Head to [CometChat Pro](https://cometchat.com/pro?utm_source=github&utm_medium=example-code-readme) and create an account
19 | 2. From the [dashboard](https://app.cometchat.com/#/apps?utm_source=github&utm_medium=example-code-readme), create a new app called "React chat widget"
20 | 3. One created, click **Explore**
21 | 4. Go to the **API Keys** tab and click **Create API Key**
22 | 5. Create an API key called "React chat widget key" with **Full Access**
23 | 6. Go to the **Users** tab and click **Create User**
24 | 7. Create a user with the name "Agent" and the UID "Agent"
25 | 6. Download the repository [here](https://github.com/cometchat-pro-samples/react-customer-support-live-widget/archive/master.zip) or by running `git clone https://github.com/cometchat-pro-samples/react-customer-support-live-widget.git`
26 |
27 | Setup the server:
28 |
29 | 1. In the root directory run `npm install`
30 | 2. Open [sever.js](https://github.com/cometchat-pro-samples/react-customer-support-live-widget/blog/master/sever.js) and update [`appID`](https://github.com/cometchat-pro-samples/react-customer-support-live-widget/blob/master/server.js#L5), [`apiKey`](https://github.com/cometchat-pro-samples/react-customer-support-live-widget/blob/master/server.js#L6) to use your own credentials
31 | 3. Set [`agentUID`](https://github.com/cometchat-pro-samples/react-customer-support-live-widget/blob/master/server.js#L7) to "Agent"
32 | 3. Run the server by running `node server.js`
33 |
34 | Setup the client:
35 |
36 | 1. Go to the `client` directory
37 | 2. Run `npm install` there too
38 | 3. Update [config.js](https://github.com/cometchat-pro-samples/react-customer-support-live-widget/blob/master/client/src/config.js) with your credentials too
39 | 4. In another terminal run `npm start` to start the client
40 |
41 | Questions about running the demo? [Open an issue](https://github.com/cometchat-pro-samples/react-customer-support-live-widget/issues). We're here to help ✌🏻
42 |
43 |
44 | ## Useful links
45 |
46 | - 🏠 [CometChat Homepage](https://cometchat.com/pro?utm_source=github&utm_medium=example-code-readme)
47 | - 🚀 [Create your free account](https://app.cometchat.com?utm_source=github&utm_medium=example-code-readme)
48 | - 📚 [Documentation](https://prodocs.cometchat.com/docs?utm_source=github&utm_medium=example-code-readme)
49 | - 👾 [GitHub](https://github.com/CometChat-Pro)
50 |
--------------------------------------------------------------------------------
/client/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "client",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@cometchat-pro/chat": "^1.0.3",
7 | "bootstrap": "^4.2.1",
8 | "react": "^16.7.0",
9 | "react-chat-widget": "^2.1.4-1",
10 | "react-dom": "^16.7.0",
11 | "react-md-spinner": "^0.3.0",
12 | "react-router-dom": "^4.3.1",
13 | "react-scripts": "2.1.3"
14 | },
15 | "scripts": {
16 | "start": "react-scripts start",
17 | "build": "react-scripts build",
18 | "test": "react-scripts test",
19 | "eject": "react-scripts eject"
20 | },
21 | "eslintConfig": {
22 | "extends": "react-app"
23 | },
24 | "browserslist": [
25 | ">0.2%",
26 | "not dead",
27 | "not ie <= 11",
28 | "not op_mini all"
29 | ],
30 | "proxy": "http://localhost:5000"
31 | }
32 |
--------------------------------------------------------------------------------
/client/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cometchat-pro-tutorials/react-customer-support-live-widget/14bbac981be2e3cb0f113758576d4b61cb9329d7/client/public/favicon.ico
--------------------------------------------------------------------------------
/client/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 | React App
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/client/src/Agent.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 |
3 | import {CometChat} from '@cometchat-pro/chat';
4 | import MDSpinner from "react-md-spinner";
5 | import config from './config';
6 |
7 | const agentUID = config.agentUID;
8 | const AGENT_MESSAGE_LISTENER_KEY = 'agent-listener'
9 | const limit = 30;
10 |
11 | class Agent extends Component {
12 |
13 | state = {
14 | customers: [],
15 | selectedCustomer: '',
16 | chat: [],
17 | chatIsLoading: false,
18 | customerIsLoading:true
19 | }
20 |
21 | componentDidMount(){
22 | this.fetchAuthToken(agentUID).then(
23 | authToken => {
24 | console.log('auth token fetched', authToken);
25 | CometChat.login(authToken)
26 | .then( user => {
27 | console.log("Login successfully:", { user });
28 | this.fetchUsers().then(result => {
29 | this.setState({
30 | customers: result,
31 | customerIsLoading: false
32 | })
33 | });
34 |
35 | CometChat.addMessageListener(
36 | AGENT_MESSAGE_LISTENER_KEY,
37 | new CometChat.MessageListener({
38 | onTextMessageReceived: message => {
39 | let {customers, selectedCustomer, chat} = this.state;
40 | console.log("Incoming Message Log", { message });
41 | if(selectedCustomer === message.sender.uid){
42 | chat.push(message);
43 | this.setState({
44 | chat
45 | })
46 | } else {
47 | let aRegisteredCustomer = customers.filter( customer => { return customer.uid === message.sender.uid });
48 | if(!aRegisteredCustomer.length){
49 | customers.push(message.sender)
50 | this.setState({
51 | customers
52 | })
53 | }
54 | }
55 | }
56 | })
57 | );
58 | })
59 | },
60 | error => {
61 | console.log('Initialization failed with error:', error);
62 | }
63 | );
64 | }
65 |
66 | fetchAuthToken = async uid => {
67 | const response = await fetch(`/api/auth?uid=${uid}`)
68 | const result = await response.json()
69 | return result.authToken;
70 | }
71 |
72 | fetchUsers = async () => {
73 | const response = await fetch(`/api/users`)
74 | const result = await response.json()
75 | return result;
76 | }
77 |
78 | handleSubmit = event => {
79 | event.preventDefault();
80 | let message = this.refs.message.value;
81 |
82 | var textMessage = new CometChat.TextMessage(
83 | this.state.selectedCustomer,
84 | message,
85 | CometChat.MESSAGE_TYPE.TEXT,
86 | CometChat.RECEIVER_TYPE.USER
87 | );
88 |
89 | CometChat.sendMessage(textMessage).then(
90 | message => {
91 | let {chat} = this.state;
92 | console.log('Message sent successfully:', message);
93 | chat.push(message);
94 | this.setState({
95 | chat
96 | })
97 | },
98 | error => {
99 | console.log('Message sending failed with error:', error);
100 | }
101 | );
102 | this.refs.message.value='';
103 | }
104 |
105 | componentWillUnmount(){
106 | CometChat.removeMessageListener(AGENT_MESSAGE_LISTENER_KEY);
107 | CometChat.logout();
108 | }
109 |
110 | selectCustomer = uid => {
111 | this.setState({
112 | selectedCustomer: uid
113 | }, ()=> {this.fetchPreviousMessage(uid)})
114 | }
115 |
116 | fetchPreviousMessage = uid => {
117 | this.setState({
118 | chat: [],
119 | chatIsLoading: true
120 | }, () => {
121 | var messagesRequest = new CometChat.MessagesRequestBuilder()
122 | .setUID(uid)
123 | .setLimit(limit)
124 | .build();
125 |
126 | messagesRequest.fetchPrevious().then(
127 | messages => {
128 | console.log("Message list fetched:", messages);
129 | this.setState({
130 | chat: messages,
131 | chatIsLoading: false
132 | })
133 | },
134 | error => {
135 | console.log("Message fetching failed with error:", error);
136 | }
137 | );
138 | });
139 | }
140 |
141 | render() {
142 | return(
143 |
144 |
145 |
146 |
147 |
148 |
149 |
Customer List
150 |
151 |
152 |
153 |
154 |
155 |
156 |
Who you gonna chat with?
157 |
158 |
159 |
160 |
161 |
179 |
180 |
181 |
182 |
183 |
184 | )
185 | }
186 | }
187 |
188 | class ChatBox extends Component {
189 | render(){
190 | const {chat, chatIsLoading} = this.props;
191 | if (chatIsLoading) {
192 | return (
193 |
194 |
195 |
196 | )
197 | }
198 | else {
199 | return (
200 |
201 | {
202 | chat
203 | .map(chat =>
204 |
205 |
206 | {chat.text}
207 |
208 |
)
209 | }
210 |
211 | )
212 | }
213 | }
214 |
215 | }
216 |
217 |
218 | class CustomerList extends Component {
219 | render(){
220 | const {customers, customerIsLoading, selectedCustomer} = this.props;
221 | if (customerIsLoading) {
222 | return (
223 |
224 |
225 |
226 | )
227 | }
228 | else {
229 | return (
230 |
231 | {
232 | customers
233 | .map(customer =>
234 | - this.props.selectCustomer(customer.uid)}>{customer.name}
)
238 | }
239 |
240 | )
241 | }
242 | }
243 | }
244 |
245 | export default Agent;
--------------------------------------------------------------------------------
/client/src/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
3 |
4 | import Client from './Client';
5 | import Agent from './Agent';
6 |
7 | const App = () => {
8 | return (
9 |
10 |
11 |
12 | -
13 | Client Home
14 |
15 | -
16 | Agent Dashboard
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | );
25 | }
26 |
27 | export default App;
28 |
--------------------------------------------------------------------------------
/client/src/Client.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import {Widget, addResponseMessage, addUserMessage, dropMessages} from 'react-chat-widget';
3 | import {CometChat} from '@cometchat-pro/chat';
4 |
5 | import config from './config';
6 | import 'react-chat-widget/lib/styles.css';
7 |
8 | const agentUID = config.agentUID;
9 | const CUSTOMER_MESSAGE_LISTENER_KEY = "client-listener";
10 | const limit = 30;
11 |
12 | class Client extends Component {
13 | componentDidMount() {
14 | addResponseMessage('Welcome to our store!');
15 | addResponseMessage('Are you looking for anything in particular?');
16 |
17 | let uid = localStorage.getItem("cc-uid");
18 | // check for uid, if exist then get auth token, login, create message listener and fetch previous messages
19 | if ( uid !== null) {
20 | this.fetchAuthToken(uid).then(
21 | result => {
22 | console.log('auth token fetched', result);
23 | CometChat.login(result.authToken)
24 | .then( user => {
25 | console.log("Login successfully:", { user });
26 | this.createMessageListener();
27 | this.fetchPreviousMessages();
28 |
29 | })
30 | },
31 | error => {
32 | console.log('Initialization failed with error:', error);
33 | }
34 | );
35 | }
36 | }
37 |
38 | fetchAuthToken = async uid => {
39 | const response = await fetch(`/api/auth?uid=${uid}`)
40 | const result = await response.json()
41 | return result;
42 | }
43 |
44 | createUser = async () => {
45 | const response = await fetch(`/api/create`)
46 | const result = await response.json()
47 | return result;
48 | }
49 |
50 | createMessageListener = () => {
51 | CometChat.addMessageListener(
52 | CUSTOMER_MESSAGE_LISTENER_KEY,
53 | new CometChat.MessageListener({
54 | onTextMessageReceived: message => {
55 | console.log("Incoming Message Log", { message });
56 | addResponseMessage(message.text);
57 | }
58 | })
59 | );
60 | }
61 |
62 | fetchPreviousMessages = () => {
63 | var messagesRequest = new CometChat.MessagesRequestBuilder()
64 | .setUID(agentUID)
65 | .setLimit(limit)
66 | .build();
67 |
68 | messagesRequest.fetchPrevious().then(
69 | messages => {
70 | console.log("Message list fetched:", messages);
71 | messages.forEach( message => {
72 | if(message.receiver !== agentUID){
73 | addResponseMessage(message.text);
74 | } else {
75 | addUserMessage(message.text)
76 | }
77 | });
78 | },
79 | error => {
80 | console.log("Message fetching failed with error:", error);
81 | }
82 | );
83 | }
84 |
85 | handleNewUserMessage = newMessage => {
86 | console.log(`New message incoming! ${newMessage}`);
87 | var textMessage = new CometChat.TextMessage(
88 | agentUID,
89 | newMessage,
90 | CometChat.MESSAGE_TYPE.TEXT,
91 | CometChat.RECEIVER_TYPE.USER
92 | );
93 | let uid = localStorage.getItem("cc-uid");
94 |
95 | if (uid === null) {
96 | this.createUser().then(
97 | result => {
98 | console.log('auth token fetched', result);
99 | localStorage.setItem("cc-uid",result.uid)
100 | CometChat.login(result.authToken)
101 | .then(user => {
102 | console.log("Login successfully:", { user });
103 | CometChat.sendMessage(textMessage).then(
104 | message => {
105 | console.log('Message sent successfully:', message);
106 | },
107 | error => {
108 | console.log('Message sending failed with error:', error);
109 | }
110 | );
111 | CometChat.addMessageListener(
112 | CUSTOMER_MESSAGE_LISTENER_KEY,
113 | new CometChat.MessageListener({
114 | onTextMessageReceived: message => {
115 | console.log("Incoming Message Log", { message });
116 | addResponseMessage(message.text);
117 | }
118 | })
119 | );
120 | })
121 | },
122 | error => {
123 | console.log('Initialization failed with error:', error);
124 | })
125 | } else {
126 | // we have uid, do send
127 | CometChat.sendMessage(textMessage).then(
128 | message => {
129 | console.log('Message sent successfully:', message);
130 | },
131 | error => {
132 | console.log('Message sending failed with error:', error);
133 | }
134 | );
135 | }
136 | };
137 |
138 | componentWillUnmount() {
139 | CometChat.removeMessageListener(CUSTOMER_MESSAGE_LISTENER_KEY);
140 | CometChat.logout();
141 | dropMessages();
142 | }
143 |
144 | render() {
145 | return (
146 |
147 |
152 |
153 | );
154 | }
155 | }
156 |
157 | export default Client;
158 |
--------------------------------------------------------------------------------
/client/src/config.js:
--------------------------------------------------------------------------------
1 | const config = {
2 | appID: '{appID}',
3 | agentUID: '{agentUID}',
4 | }
5 |
6 | export default config;
7 |
--------------------------------------------------------------------------------
/client/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
6 | sans-serif;
7 | -webkit-font-smoothing: antialiased;
8 | -moz-osx-font-smoothing: grayscale;
9 | }
10 |
11 | code {
12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
13 | monospace;
14 | }
15 |
16 |
17 | .message {
18 | overflow: hidden;
19 | }
20 |
21 | .balon1 {
22 | float: right;
23 | background: #35cce6;
24 | border-radius: 10px;
25 | }
26 |
27 | .balon2 {
28 | float: left;
29 | background: #f4f7f9;
30 | border-radius: 10px;
31 | }
32 |
--------------------------------------------------------------------------------
/client/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import 'bootstrap/dist/css/bootstrap.css';
4 | import './index.css';
5 | import App from './App';
6 | import { CometChat } from '@cometchat-pro/chat';
7 | import config from './config';
8 |
9 | CometChat.init(config.appID)
10 |
11 | ReactDOM.render(, document.getElementById('root'));
12 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-express-cometchat-widget",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "accepts": {
8 | "version": "1.3.5",
9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz",
10 | "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=",
11 | "requires": {
12 | "mime-types": "~2.1.18",
13 | "negotiator": "0.6.1"
14 | }
15 | },
16 | "array-flatten": {
17 | "version": "1.1.1",
18 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
19 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
20 | },
21 | "axios": {
22 | "version": "0.18.0",
23 | "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.0.tgz",
24 | "integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=",
25 | "requires": {
26 | "follow-redirects": "^1.3.0",
27 | "is-buffer": "^1.1.5"
28 | }
29 | },
30 | "body-parser": {
31 | "version": "1.18.3",
32 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz",
33 | "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=",
34 | "requires": {
35 | "bytes": "3.0.0",
36 | "content-type": "~1.0.4",
37 | "debug": "2.6.9",
38 | "depd": "~1.1.2",
39 | "http-errors": "~1.6.3",
40 | "iconv-lite": "0.4.23",
41 | "on-finished": "~2.3.0",
42 | "qs": "6.5.2",
43 | "raw-body": "2.3.3",
44 | "type-is": "~1.6.16"
45 | }
46 | },
47 | "bytes": {
48 | "version": "3.0.0",
49 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
50 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
51 | },
52 | "content-disposition": {
53 | "version": "0.5.2",
54 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
55 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ="
56 | },
57 | "content-type": {
58 | "version": "1.0.4",
59 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
60 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
61 | },
62 | "cookie": {
63 | "version": "0.3.1",
64 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
65 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
66 | },
67 | "cookie-signature": {
68 | "version": "1.0.6",
69 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
70 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
71 | },
72 | "debug": {
73 | "version": "2.6.9",
74 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
75 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
76 | "requires": {
77 | "ms": "2.0.0"
78 | }
79 | },
80 | "depd": {
81 | "version": "1.1.2",
82 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
83 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
84 | },
85 | "destroy": {
86 | "version": "1.0.4",
87 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
88 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
89 | },
90 | "ee-first": {
91 | "version": "1.1.1",
92 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
93 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
94 | },
95 | "encodeurl": {
96 | "version": "1.0.2",
97 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
98 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
99 | },
100 | "escape-html": {
101 | "version": "1.0.3",
102 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
103 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
104 | },
105 | "etag": {
106 | "version": "1.8.1",
107 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
108 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
109 | },
110 | "express": {
111 | "version": "4.16.4",
112 | "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz",
113 | "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==",
114 | "requires": {
115 | "accepts": "~1.3.5",
116 | "array-flatten": "1.1.1",
117 | "body-parser": "1.18.3",
118 | "content-disposition": "0.5.2",
119 | "content-type": "~1.0.4",
120 | "cookie": "0.3.1",
121 | "cookie-signature": "1.0.6",
122 | "debug": "2.6.9",
123 | "depd": "~1.1.2",
124 | "encodeurl": "~1.0.2",
125 | "escape-html": "~1.0.3",
126 | "etag": "~1.8.1",
127 | "finalhandler": "1.1.1",
128 | "fresh": "0.5.2",
129 | "merge-descriptors": "1.0.1",
130 | "methods": "~1.1.2",
131 | "on-finished": "~2.3.0",
132 | "parseurl": "~1.3.2",
133 | "path-to-regexp": "0.1.7",
134 | "proxy-addr": "~2.0.4",
135 | "qs": "6.5.2",
136 | "range-parser": "~1.2.0",
137 | "safe-buffer": "5.1.2",
138 | "send": "0.16.2",
139 | "serve-static": "1.13.2",
140 | "setprototypeof": "1.1.0",
141 | "statuses": "~1.4.0",
142 | "type-is": "~1.6.16",
143 | "utils-merge": "1.0.1",
144 | "vary": "~1.1.2"
145 | }
146 | },
147 | "finalhandler": {
148 | "version": "1.1.1",
149 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
150 | "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
151 | "requires": {
152 | "debug": "2.6.9",
153 | "encodeurl": "~1.0.2",
154 | "escape-html": "~1.0.3",
155 | "on-finished": "~2.3.0",
156 | "parseurl": "~1.3.2",
157 | "statuses": "~1.4.0",
158 | "unpipe": "~1.0.0"
159 | }
160 | },
161 | "follow-redirects": {
162 | "version": "1.6.1",
163 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.6.1.tgz",
164 | "integrity": "sha512-t2JCjbzxQpWvbhts3l6SH1DKzSrx8a+SsaVf4h6bG4kOXUuPYS/kg2Lr4gQSb7eemaHqJkOThF1BGyjlUkO1GQ==",
165 | "requires": {
166 | "debug": "=3.1.0"
167 | },
168 | "dependencies": {
169 | "debug": {
170 | "version": "3.1.0",
171 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
172 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
173 | "requires": {
174 | "ms": "2.0.0"
175 | }
176 | }
177 | }
178 | },
179 | "forwarded": {
180 | "version": "0.1.2",
181 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
182 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
183 | },
184 | "fresh": {
185 | "version": "0.5.2",
186 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
187 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
188 | },
189 | "http-errors": {
190 | "version": "1.6.3",
191 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
192 | "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
193 | "requires": {
194 | "depd": "~1.1.2",
195 | "inherits": "2.0.3",
196 | "setprototypeof": "1.1.0",
197 | "statuses": ">= 1.4.0 < 2"
198 | }
199 | },
200 | "iconv-lite": {
201 | "version": "0.4.23",
202 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
203 | "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
204 | "requires": {
205 | "safer-buffer": ">= 2.1.2 < 3"
206 | }
207 | },
208 | "inherits": {
209 | "version": "2.0.3",
210 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
211 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
212 | },
213 | "ipaddr.js": {
214 | "version": "1.8.0",
215 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz",
216 | "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4="
217 | },
218 | "is-buffer": {
219 | "version": "1.1.6",
220 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
221 | "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
222 | },
223 | "media-typer": {
224 | "version": "0.3.0",
225 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
226 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
227 | },
228 | "merge-descriptors": {
229 | "version": "1.0.1",
230 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
231 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
232 | },
233 | "methods": {
234 | "version": "1.1.2",
235 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
236 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
237 | },
238 | "mime": {
239 | "version": "1.4.1",
240 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
241 | "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ=="
242 | },
243 | "mime-db": {
244 | "version": "1.37.0",
245 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz",
246 | "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg=="
247 | },
248 | "mime-types": {
249 | "version": "2.1.21",
250 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz",
251 | "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==",
252 | "requires": {
253 | "mime-db": "~1.37.0"
254 | }
255 | },
256 | "ms": {
257 | "version": "2.0.0",
258 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
259 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
260 | },
261 | "negotiator": {
262 | "version": "0.6.1",
263 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
264 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
265 | },
266 | "on-finished": {
267 | "version": "2.3.0",
268 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
269 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
270 | "requires": {
271 | "ee-first": "1.1.1"
272 | }
273 | },
274 | "parseurl": {
275 | "version": "1.3.2",
276 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
277 | "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M="
278 | },
279 | "path-to-regexp": {
280 | "version": "0.1.7",
281 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
282 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
283 | },
284 | "proxy-addr": {
285 | "version": "2.0.4",
286 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz",
287 | "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==",
288 | "requires": {
289 | "forwarded": "~0.1.2",
290 | "ipaddr.js": "1.8.0"
291 | }
292 | },
293 | "qs": {
294 | "version": "6.5.2",
295 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
296 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
297 | },
298 | "range-parser": {
299 | "version": "1.2.0",
300 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
301 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4="
302 | },
303 | "raw-body": {
304 | "version": "2.3.3",
305 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz",
306 | "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==",
307 | "requires": {
308 | "bytes": "3.0.0",
309 | "http-errors": "1.6.3",
310 | "iconv-lite": "0.4.23",
311 | "unpipe": "1.0.0"
312 | }
313 | },
314 | "safe-buffer": {
315 | "version": "5.1.2",
316 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
317 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
318 | },
319 | "safer-buffer": {
320 | "version": "2.1.2",
321 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
322 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
323 | },
324 | "send": {
325 | "version": "0.16.2",
326 | "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
327 | "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==",
328 | "requires": {
329 | "debug": "2.6.9",
330 | "depd": "~1.1.2",
331 | "destroy": "~1.0.4",
332 | "encodeurl": "~1.0.2",
333 | "escape-html": "~1.0.3",
334 | "etag": "~1.8.1",
335 | "fresh": "0.5.2",
336 | "http-errors": "~1.6.2",
337 | "mime": "1.4.1",
338 | "ms": "2.0.0",
339 | "on-finished": "~2.3.0",
340 | "range-parser": "~1.2.0",
341 | "statuses": "~1.4.0"
342 | }
343 | },
344 | "serve-static": {
345 | "version": "1.13.2",
346 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz",
347 | "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==",
348 | "requires": {
349 | "encodeurl": "~1.0.2",
350 | "escape-html": "~1.0.3",
351 | "parseurl": "~1.3.2",
352 | "send": "0.16.2"
353 | }
354 | },
355 | "setprototypeof": {
356 | "version": "1.1.0",
357 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
358 | "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
359 | },
360 | "statuses": {
361 | "version": "1.4.0",
362 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
363 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
364 | },
365 | "type-is": {
366 | "version": "1.6.16",
367 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz",
368 | "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==",
369 | "requires": {
370 | "media-typer": "0.3.0",
371 | "mime-types": "~2.1.18"
372 | }
373 | },
374 | "unpipe": {
375 | "version": "1.0.0",
376 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
377 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
378 | },
379 | "utils-merge": {
380 | "version": "1.0.1",
381 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
382 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
383 | },
384 | "vary": {
385 | "version": "1.1.2",
386 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
387 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
388 | }
389 | }
390 | }
391 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-express-cometchat-widget",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "node server.js"
9 | },
10 | "keywords": [],
11 | "author": "",
12 | "license": "ISC",
13 | "dependencies": {
14 | "axios": "^0.18.0",
15 | "express": "^4.16.4"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/screenshot/screenshot_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cometchat-pro-tutorials/react-customer-support-live-widget/14bbac981be2e3cb0f113758576d4b61cb9329d7/screenshot/screenshot_1.png
--------------------------------------------------------------------------------
/screenshot/screenshot_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cometchat-pro-tutorials/react-customer-support-live-widget/14bbac981be2e3cb0f113758576d4b61cb9329d7/screenshot/screenshot_2.png
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const axios = require('axios');
3 | const app = express();
4 |
5 | const appID = '{appID}';
6 | const apiKey = '{apiKey}';
7 | const agentUID = '{agentUID}';
8 |
9 | const url = 'https://api.cometchat.com/v1';
10 |
11 | const headers = {
12 | 'Content-Type': 'application/json',
13 | appid: appID,
14 | apikey: apiKey,
15 | };
16 |
17 | app.get('/api/create', (req, res) => {
18 | const data = {
19 | uid: new Date().getTime(),
20 | name: 'customer',
21 | };
22 | axios
23 | .post(`${url}/users`, JSON.stringify(data), {
24 | headers,
25 | })
26 | .then(response => {
27 | requestAuthToken(response.data.data.uid)
28 | .then(token => {
29 | console.log('Success:' + JSON.stringify(token));
30 | res.json(token);
31 | })
32 | .catch(error => console.error('Error:', error));
33 | })
34 | .catch(error => console.error('Error:', error));
35 | });
36 |
37 | app.get('/api/auth', (req, res) => {
38 | const uid = req.query.uid;
39 | requestAuthToken(uid)
40 | .then(token => {
41 | console.log('Success:' + JSON.stringify(token));
42 | res.json(token);
43 | })
44 | .catch(error => console.error('Error:', error));
45 | });
46 |
47 | const requestAuthToken = uid => {
48 | return new Promise((resolve, reject) => {
49 | axios
50 | .post(`${url}/users/${uid}/auth_tokens`, null, {
51 | headers,
52 | })
53 | .then(response => {
54 | console.log('New Auth Token:', response.data);
55 | resolve(response.data.data);
56 | })
57 | .catch(error => reject(error));
58 | });
59 | };
60 |
61 | app.get('/api/users', (req, res) => {
62 | axios
63 | .get(`${url}/users`, {
64 | headers,
65 | })
66 | .then(response => {
67 | const { data } = response.data;
68 | const filterAgentData = data.filter(data => {
69 | return data.uid !== agentUID;
70 | });
71 | res.json(filterAgentData);
72 | })
73 | .catch(error => console.error('Error:', error));
74 | });
75 |
76 | const PORT = process.env.PORT || 5000;
77 | app.listen(PORT, () => {
78 | console.log(`Listening on port ${PORT}`);
79 | });
80 |
--------------------------------------------------------------------------------