├── .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 | ![Client](screenshot/screenshot_1.png) 11 | ![Agent](screenshot/screenshot_2.png) 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 |
162 |
163 | 164 |
165 | 171 |
172 |
173 | 176 |
177 |
178 |
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 | 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 | 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 | --------------------------------------------------------------------------------