├── .eslintcache
├── .eslintrc.js
├── README.md
├── package-lock.json
├── package.json
├── public
├── favicon.ico
├── index.html
├── logo192.png
├── logo512.png
├── manifest.json
└── robots.txt
└── src
├── App.css
├── App.js
├── components
├── ChatFeed.jsx
├── LoginForm.jsx
├── MessageForm.jsx
├── MyMessage.jsx
└── TheirMessage.jsx
└── index.js
/.eslintcache:
--------------------------------------------------------------------------------
1 | [{"C:\\Users\\Adrian\\Desktop\\chat_app_project\\src\\index.js":"1","C:\\Users\\Adrian\\Desktop\\chat_app_project\\src\\App.js":"2","C:\\Users\\Adrian\\Desktop\\chat_app_project\\src\\components\\ChatFeed.jsx":"3","C:\\Users\\Adrian\\Desktop\\chat_app_project\\src\\components\\MessageForm.jsx":"4","C:\\Users\\Adrian\\Desktop\\chat_app_project\\src\\components\\MyMessage.jsx":"5","C:\\Users\\Adrian\\Desktop\\chat_app_project\\src\\components\\TheirMessage.jsx":"6","C:\\Users\\Adrian\\Desktop\\chat_app_project\\src\\components\\LoginForm.jsx":"7"},{"size":147,"mtime":1610875361799,"results":"8","hashOfConfig":"9"},{"size":746,"mtime":1611421704910,"results":"10","hashOfConfig":"9"},{"size":2049,"mtime":1611404917028,"results":"11","hashOfConfig":"9"},{"size":1506,"mtime":1611421605786,"results":"12","hashOfConfig":"9"},{"size":509,"mtime":1611404919192,"results":"13","hashOfConfig":"9"},{"size":988,"mtime":1611416727413,"results":"14","hashOfConfig":"9"},{"size":1540,"mtime":1611421710887,"results":"15","hashOfConfig":"9"},{"filePath":"16","messages":"17","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"18"},"7np4es",{"filePath":"19","messages":"20","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"18"},{"filePath":"21","messages":"22","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"18"},{"filePath":"23","messages":"24","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"18"},{"filePath":"25","messages":"26","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"18"},{"filePath":"27","messages":"28","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"29","messages":"30","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"18"},"C:\\Users\\Adrian\\Desktop\\chat_app_project\\src\\index.js",[],["31","32","33","34","35","36"],"C:\\Users\\Adrian\\Desktop\\chat_app_project\\src\\App.js",[],"C:\\Users\\Adrian\\Desktop\\chat_app_project\\src\\components\\ChatFeed.jsx",[],"C:\\Users\\Adrian\\Desktop\\chat_app_project\\src\\components\\MessageForm.jsx",[],"C:\\Users\\Adrian\\Desktop\\chat_app_project\\src\\components\\MyMessage.jsx",[],"C:\\Users\\Adrian\\Desktop\\chat_app_project\\src\\components\\TheirMessage.jsx",[],"C:\\Users\\Adrian\\Desktop\\chat_app_project\\src\\components\\LoginForm.jsx",[],{"ruleId":"37","replacedBy":"38"},{"ruleId":"39","replacedBy":"40"},{"ruleId":"41","replacedBy":"42"},{"ruleId":"43","replacedBy":"44"},{"ruleId":"45","replacedBy":"46"},{"ruleId":"47","replacedBy":"48"},"lines-around-directive",["49"],"no-spaced-func",["50"],"global-require",[],"no-buffer-constructor",[],"no-new-require",[],"no-path-concat",[],"padding-line-between-statements","func-call-spacing"]
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | browser: true,
4 | es6: true,
5 | },
6 | extends: [
7 | 'plugin:react/recommended',
8 | 'airbnb',
9 | ],
10 | globals: {
11 | Atomics: 'readonly',
12 | SharedArrayBuffer: 'readonly',
13 | },
14 | parserOptions: {
15 | ecmaFeatures: {
16 | jsx: true,
17 | },
18 | ecmaVersion: 2018,
19 | sourceType: 'module',
20 | },
21 | parser: 'babel-eslint',
22 | plugins: [
23 | 'react',
24 | ],
25 | rules: {
26 | 'react/react-in-jsx-scope': 'off',
27 | 'import/extensions': 0,
28 | 'react/prop-types': 0,
29 | 'linebreak-style': 0,
30 | 'react/state-in-constructor': 0,
31 | 'jsx-a11y/label-has-associated-control': 'off',
32 | 'import/prefer-default-export': 0,
33 | 'max-len': [
34 | 2,
35 | 250,
36 | ],
37 | 'no-multiple-empty-lines': [
38 | 'error',
39 | {
40 | max: 1,
41 | maxEOF: 1,
42 | },
43 | ],
44 | 'no-underscore-dangle': [
45 | 'error',
46 | {
47 | allow: [
48 | '_d',
49 | '_dh',
50 | '_h',
51 | '_id',
52 | '_m',
53 | '_n',
54 | '_t',
55 | '_text',
56 | ],
57 | },
58 | ],
59 | 'react/jsx-props-no-spreading': 'off',
60 | 'object-curly-newline': 0,
61 | 'react/jsx-filename-extension': 0,
62 | 'react/jsx-one-expression-per-line': 0,
63 | 'jsx-a11y/click-events-have-key-events': 0,
64 | 'jsx-a11y/alt-text': 0,
65 | 'jsx-a11y/no-autofocus': 0,
66 | 'jsx-a11y/no-static-element-interactions': 0,
67 | 'react/no-array-index-key': 0,
68 | 'jsx-a11y/anchor-is-valid': [
69 | 'error',
70 | {
71 | components: [
72 | 'Link',
73 | ],
74 | specialLink: [
75 | 'to',
76 | 'hrefLeft',
77 | 'hrefRight',
78 | ],
79 | aspects: [
80 | 'noHref',
81 | 'invalidHref',
82 | 'preferButton',
83 | ],
84 | },
85 | ],
86 | },
87 | };
88 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Realtime Chat Application
2 |
3 | 
4 |
5 | 
6 |
7 |
8 |
9 | ## Introduction
10 | We will going to create a full Realtime Chat Application.
11 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "chat_app_project",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@ant-design/icons": "^4.3.0",
7 | "@testing-library/jest-dom": "^5.11.9",
8 | "@testing-library/react": "^11.2.3",
9 | "@testing-library/user-event": "^12.6.0",
10 | "axios": "^0.21.1",
11 | "react": "^17.0.1",
12 | "react-chat-engine": "^1.5.7",
13 | "react-dom": "^17.0.1",
14 | "react-scripts": "4.0.1",
15 | "web-vitals": "^0.2.4"
16 | },
17 | "scripts": {
18 | "start": "react-scripts start",
19 | "build": "react-scripts build",
20 | "test": "react-scripts test",
21 | "postinstall": "patch-package",
22 | "eject": "react-scripts eject"
23 | },
24 | "eslintConfig": {
25 | "extends": [
26 | "react-app",
27 | "react-app/jest"
28 | ]
29 | },
30 | "browserslist": {
31 | "production": [
32 | ">0.2%",
33 | "not dead",
34 | "not op_mini all"
35 | ],
36 | "development": [
37 | "last 1 chrome version",
38 | "last 1 firefox version",
39 | "last 1 safari version"
40 | ]
41 | },
42 | "devDependencies": {
43 | "eslint": "^7.17.0",
44 | "eslint-config-airbnb": "^18.2.1",
45 | "eslint-plugin-import": "^2.22.1",
46 | "eslint-plugin-jsx-a11y": "^6.4.1",
47 | "eslint-plugin-react": "^7.22.0",
48 | "eslint-plugin-react-hooks": "^4.2.0",
49 | "patch-package": "^6.2.2",
50 | "postinstall-postinstall": "^2.1.0"
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Saiprasad16/ReactChatApplication/4b60e5a06ad439ed2ab1eeb783071edf29782856/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | Chat Application
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Saiprasad16/ReactChatApplication/4b60e5a06ad439ed2ab1eeb783071edf29782856/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Saiprasad16/ReactChatApplication/4b60e5a06ad439ed2ab1eeb783071edf29782856/public/logo512.png
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | /* CHAT STYLES */
2 | * {
3 | font-family: Avenir, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto,
4 | Helvetica Neue, Arial, Noto Sans, sans-serif, Apple Color Emoji,
5 | Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;
6 | letter-spacing: 0.5px;
7 | }
8 |
9 | .ce-chat-list {
10 | background-color: rgb(240, 240, 240) !important;
11 | }
12 |
13 | .ce-chats-container {
14 | background-color: rgb(240, 240, 240) !important;
15 | }
16 |
17 | .ce-active-chat-card {
18 | background-color: #cabcdc !important;
19 | border: 4px solid #cabcdc !important;
20 | border-radius: 0px !important;
21 | }
22 |
23 | .ce-chat-subtitle-text {
24 | color: #595959 !important;
25 | }
26 |
27 | .ce-chat-form-container {
28 | padding-bottom: 20px !important;
29 | }
30 |
31 | .ce-text-input {
32 | border-radius: 6px !important;
33 | border: 1px solid #3b2a50 !important;
34 | }
35 |
36 | .ce-primary-button {
37 | border-radius: 6px !important;
38 | background-color: #7554a0 !important;
39 | position: relative;
40 | bottom: 1px;
41 | }
42 |
43 | .ce-danger-button {
44 | background-color: white !important;
45 | border-radius: 22px !important;
46 | }
47 |
48 | .ce-settings {
49 | background-color: rgb(240, 240, 240) !important;
50 | }
51 |
52 | .ce-autocomplete-input {
53 | border-radius: 6px !important;
54 | border: 1px solid #3b2a50 !important;
55 | }
56 |
57 | .ce-autocomplete-options {
58 | border-radius: 6px !important;
59 | border: 1px solid #3b2a50 !important;
60 | background-color: white !important;
61 | }
62 |
63 | .ce-chat-settings-container {
64 | padding-top: 12px !important;
65 | }
66 |
67 | .ce-chat-avatars-row {
68 | display: none !important;
69 | }
70 |
71 | /* CUSTOM FEED */
72 |
73 | .chat-feed {
74 | height: 100%;
75 | width: 100%;
76 | overflow: scroll;
77 | background-color: rgb(240, 240, 240);
78 | }
79 |
80 | .chat-title-container {
81 | width: calc(100% - 36px);
82 | padding: 18px;
83 | text-align: center;
84 | }
85 |
86 | .chat-title {
87 | color: #7554a0;
88 | font-weight: 800;
89 | font-size: 24px;
90 | }
91 |
92 | .chat-subtitle {
93 | color: #7554a0;
94 | font-weight: 600;
95 | font-size: 12px;
96 | padding-top: 4px;
97 | }
98 |
99 | .message-row {
100 | float: left;
101 | width: 100%;
102 | display: flex;
103 | margin-left: 18px;
104 | }
105 |
106 | .message-block {
107 | width: 100%;
108 | display: inline-block;
109 | }
110 |
111 | .message-avatar {
112 | width: 44px;
113 | height: 44px;
114 | border-radius: 22px;
115 | color: white;
116 | text-align: center;
117 | background-repeat: no-repeat;
118 | background-position: center;
119 | background-size: 48px;
120 | }
121 |
122 | .message {
123 | padding: 12px;
124 | font-size: 16px;
125 | border-radius: 6px;
126 | max-width: 60%;
127 | }
128 |
129 | .message-image {
130 | margin-right: 18px;
131 | object-fit: cover;
132 | border-radius: 6px;
133 | height: 30vw;
134 | /* width: 30vw; */
135 | max-height: 200px;
136 | max-width: 200px;
137 | min-height: 100px;
138 | min-width: 100px;
139 | }
140 |
141 | .read-receipts {
142 | position: relative;
143 | bottom: 6px;
144 | }
145 |
146 | .read-receipt {
147 | width: 13px;
148 | height: 13px;
149 | border-radius: 13px;
150 | margin: 1.5px;
151 | background-repeat: no-repeat;
152 | background-position: center;
153 | background-size: 14px;
154 | }
155 |
156 | .message-form-container {
157 | position: absolute;
158 | bottom: 0px;
159 | width: calc(100% - 36px);
160 | padding: 18px;
161 | background-color: rgb(240, 240, 240);
162 | }
163 |
164 | .message-form {
165 | overflow: hidden;
166 | border-radius: 6px;
167 | border: 1px solid #3b2a50;
168 | background-color: white;
169 | }
170 |
171 | .message-input {
172 | height: 40px;
173 | width: calc(100% - 132px);
174 | background-color: white;
175 | border: 1px solid white;
176 | padding: 0px 18px;
177 | outline: none;
178 | font-size: 15px;
179 | }
180 |
181 | .image-button {
182 | cursor: pointer;
183 | padding: 0px 12px;
184 | height: 100%;
185 | }
186 |
187 | .send-button {
188 | height: 42px;
189 | background-color: white;
190 | border: 1px solid white;
191 | padding: 0px 18px;
192 | cursor: pointer;
193 | }
194 |
195 | .send-icon {
196 | top: 1px;
197 | position: relative;
198 | transform: rotate(-90deg);
199 | }
200 |
201 | .picture-icon {
202 | top: 1px;
203 | position: relative;
204 | font-size: 14px;
205 | }
206 |
207 | /* FORM STYLES */
208 | *,
209 | *::after,
210 | *::before {
211 | margin: 0;
212 | padding: 0;
213 | box-sizing: border-box;
214 | font-size: 62,5%;
215 | }
216 |
217 | .wrapper {
218 | height: 100vh;
219 | width: 100%;
220 | background: rgb(117,84,160);
221 | background: linear-gradient(90deg, rgba(117,84,160,1) 7%, rgba(117,84,160,1) 17%, rgba(106,95,168,1) 29%, rgba(99,103,174,1) 44%, rgba(87,116,184,1) 66%, rgba(70,135,198,1) 83%, rgba(44,163,219,1) 96%, rgba(22,188,237,1) 100%, rgba(0,212,255,1) 100%);
222 | display: flex;
223 | justify-content: center;
224 | align-items: center;
225 | }
226 |
227 | .input {
228 | color: #333;
229 | font-size: 1.2rem;
230 | margin: 0 auto;
231 | padding: 1.5rem 2rem;
232 | border-radius: 0.2rem;
233 | background-color: rgb(255, 255, 255);
234 | border: none;
235 | width: 90%;
236 | display: block;
237 | border-bottom: 0.3rem solid transparent;
238 | transition: all 0.3s;
239 | outline: none;
240 | margin-bottom: 25px;
241 | }
242 |
243 | .form {
244 | width: 400px;
245 | }
246 |
247 | .title {
248 | text-align: center;
249 | color: white;
250 | margin-bottom: 30px;
251 | width: 100%;
252 | font-size: 2.3em;;
253 | }
254 |
255 | .button {
256 | border-radius: 4px;
257 | border: none;
258 | background-color: white;
259 | color: black;
260 | text-align: center;
261 | text-transform: uppercase;
262 | font-size: 22px;
263 | padding: 20px;
264 | width: 200px;
265 | transition: all 0.4s;
266 | cursor: pointer;
267 | margin: 5px;
268 | width: 90%;
269 | }
270 | .button span {
271 | cursor: pointer;
272 | display: inline-block;
273 | position: relative;
274 | transition: 0.4s;
275 | }
276 | .button span:after {
277 | content: '\00bb';
278 | position: absolute;
279 | opacity: 0;
280 | top: 0;
281 | right: -20px;
282 | transition: 0.5s;
283 | }
284 | .button:hover span {
285 | padding-right: 25px;
286 | }
287 | .button:hover span:after {
288 | opacity: 1;
289 | right: 0;
290 | }
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import { ChatEngine } from 'react-chat-engine';
2 |
3 | import ChatFeed from './components/ChatFeed';
4 | import LoginForm from './components/LoginForm';
5 | import './App.css';
6 |
7 | const projectID = '1b7801d6-8a66-4be4-a442-89219d833dfc';
8 |
9 | const App = () => {
10 | if (!localStorage.getItem('username')) return ;
11 |
12 | return (
13 | }
19 | onNewMessage={() => new Audio('https://chat-engine-assets.s3.amazonaws.com/click.mp3').play()}
20 | />
21 | );
22 | };
23 |
24 | // infinite scroll, logout, more customizations...
25 |
26 | export default App;
27 |
--------------------------------------------------------------------------------
/src/components/ChatFeed.jsx:
--------------------------------------------------------------------------------
1 | import MyMessage from './MyMessage';
2 | import TheirMessage from './TheirMessage';
3 | import MessageForm from './MessageForm';
4 |
5 | const ChatFeed = (props) => {
6 | const { chats, activeChat, userName, messages } = props;
7 |
8 | const chat = chats && chats[activeChat];
9 |
10 | const renderReadReceipts = (message, isMyMessage) => chat.people.map((person, index) => person.last_read === message.id && (
11 |
19 | ));
20 |
21 | const renderMessages = () => {
22 | const keys = Object.keys(messages);
23 |
24 | return keys.map((key, index) => {
25 | const message = messages[key];
26 | const lastMessageKey = index === 0 ? null : keys[index - 1];
27 | const isMyMessage = userName === message.sender.username;
28 |
29 | return (
30 |
31 |
32 | {isMyMessage
33 | ?
34 | : }
35 |
36 |
37 | {renderReadReceipts(message, isMyMessage)}
38 |
39 |
40 | );
41 | });
42 | };
43 |
44 | if (!chat) return ;
45 |
46 | return (
47 |
48 |
49 |
{chat?.title}
50 |
51 | {chat.people.map((person) => ` ${person.person.username}`)}
52 |
53 |
54 | {renderMessages()}
55 |
56 |
57 |
58 |
59 |
60 | );
61 | };
62 |
63 | export default ChatFeed;
64 |
65 |
--------------------------------------------------------------------------------
/src/components/LoginForm.jsx:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 | import axios from 'axios';
3 |
4 | const projectID = '1b7801d6-8a66-4be4-a442-89219d833dfc';
5 |
6 | const Modal = () => {
7 | const [username, setUsername] = useState('');
8 | const [password, setPassword] = useState('');
9 | const [error, setError] = useState('');
10 |
11 | const handleSubmit = async (e) => {
12 | e.preventDefault();
13 |
14 | const authObject = { 'Project-ID': projectID, 'User-Name': username, 'User-Secret': password };
15 |
16 | try {
17 | await axios.get('https://api.chatengine.io/chats', { headers: authObject });
18 |
19 | localStorage.setItem('username', username);
20 | localStorage.setItem('password', password);
21 |
22 | window.location.reload();
23 | setError('');
24 | } catch (err) {
25 | setError('Oops, incorrect credentials.');
26 | }
27 | };
28 |
29 | return (
30 |
31 |
32 |
Chat Application
33 |
42 |
{error}
43 |
44 |
45 |
46 | );
47 | };
48 |
49 | export default Modal;
50 |
--------------------------------------------------------------------------------
/src/components/MessageForm.jsx:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 | import { SendOutlined, PictureOutlined } from '@ant-design/icons';
3 | import { sendMessage, isTyping } from 'react-chat-engine';
4 |
5 | const MessageForm = (props) => {
6 | const [value, setValue] = useState('');
7 | const { chatId, creds } = props;
8 |
9 | const handleChange = (event) => {
10 | setValue(event.target.value);
11 |
12 | isTyping(props, chatId);
13 | };
14 |
15 | const handleSubmit = (event) => {
16 | event.preventDefault();
17 |
18 | const text = value.trim();
19 |
20 | if (text.length > 0) {
21 | sendMessage(creds, chatId, { text });
22 | }
23 |
24 | setValue('');
25 | };
26 |
27 | const handleUpload = (event) => {
28 | sendMessage(creds, chatId, { files: event.target.files, text: '' });
29 | };
30 |
31 | return (
32 |
56 | );
57 | };
58 |
59 | export default MessageForm;
60 |
--------------------------------------------------------------------------------
/src/components/MyMessage.jsx:
--------------------------------------------------------------------------------
1 | const MyMessage = ({ message }) => {
2 | if (message.attachments && message.attachments.length > 0) {
3 | return (
4 |
10 | );
11 | }
12 |
13 | return (
14 |
15 | {message.text}
16 |
17 | );
18 | };
19 |
20 | export default MyMessage;
21 |
--------------------------------------------------------------------------------
/src/components/TheirMessage.jsx:
--------------------------------------------------------------------------------
1 | const TheirMessage = ({ lastMessage, message }) => {
2 | const isFirstMessageByUser = !lastMessage || lastMessage.sender.username !== message.sender.username;
3 |
4 | return (
5 |
6 | {isFirstMessageByUser && (
7 |
11 | )}
12 | {message.attachments && message.attachments.length > 0
13 | ? (
14 |

20 | )
21 | : (
22 |
23 | {message.text}
24 |
25 | )}
26 |
27 | );
28 | };
29 |
30 | export default TheirMessage;
31 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 |
4 | import App from './App';
5 |
6 | ReactDOM.render(, document.getElementById('root'));
7 |
--------------------------------------------------------------------------------