├── README.md
├── client
├── README.md
├── package-lock.json
├── package.json
├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
└── src
│ ├── Apis
│ └── chatApi.js
│ ├── App.css
│ ├── App.js
│ ├── components
│ ├── ChatWindow
│ │ ├── ChatWindow.jsx
│ │ ├── ChatWindow.min.css
│ │ └── ChatWindow.scss
│ ├── LoggedInUserInfo
│ │ ├── LoggedInUserInfo.jsx
│ │ ├── LoggedInUserInfo.min.css
│ │ └── LoggedInUserInfo.scss
│ ├── LoginForm
│ │ ├── LoginForm.jsx
│ │ ├── LoginForm.min.css
│ │ └── LoginForm.scss
│ ├── NewMsgForm
│ │ ├── NewMsgForm.jsx
│ │ ├── NewMsgForm.min.css
│ │ └── NewMsgForm.scss
│ ├── OtherUserInfo
│ │ ├── OtherUserInfo.jsx
│ │ ├── OtherUserInfo.min.css
│ │ └── OtherUserInfo.scss
│ ├── SelectionToolBar
│ │ ├── SelectionToolBar.jsx
│ │ ├── SelectionToolBar.min.css
│ │ └── SelectionToolBar.scss
│ ├── User
│ │ ├── User.jsx
│ │ ├── User.min.css
│ │ └── User.scss
│ ├── UserChats
│ │ ├── UserChats.jsx
│ │ ├── UserChats.min.css
│ │ └── UserChats.scss
│ └── UsersList
│ │ ├── UsersList.jsx
│ │ ├── UsersList.min.css
│ │ └── UsersList.scss
│ ├── contexts
│ ├── ActiveConversationProvider.jsx
│ ├── ActiveMessageOptionsProvider.jsx
│ ├── ChatDBProvider.jsx
│ ├── CurrentUserProvider.jsx
│ ├── NewMsgsMapProvider.jsx
│ ├── SelectedMessagesProvider.jsx
│ ├── SelectionToolBarStatusProvider.jsx
│ ├── SidebarRefProvider.jsx
│ ├── SidebarStatusProvider.jsx
│ ├── SocketProvider.jsx
│ ├── UserChatsProvider.jsx
│ ├── UsersProvider.jsx
│ └── ViewProvider.jsx
│ ├── hooks
│ ├── useLocalStorage.js
│ └── useSessionStorage.js
│ ├── index.css
│ └── index.js
├── package-lock.json
├── package.json
└── server
├── Models
├── chats.js
└── users.js
├── Routes
├── chat_router.js
└── users_router.js
├── mongo_connection.js
├── package-lock.json
├── package.json
├── server.js
└── socket_IO_handlers
└── chat_app_handler.js
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # MERN Stack - Chat Application
3 |
4 | Webapp is live [here](http://yml-chat-app.herokuapp.com/)
5 |
6 | #### Technologies Stack
7 | - MERN
8 | - ReacJS
9 | - NodeJS
10 | - ExpressJS
11 | - MongoDB
12 | - Socket.io
13 | - Material UI
14 |
15 | #### To-Do List
16 | ##### Fronted
17 | - [x] Login Page.
18 | - [x] Chat window template.
19 | - [x] List of users(Conversations).
20 | - [x] Set active conversation.
21 | - [x] Render particular user's chats.
22 | - [x] Create new message form.
23 | - [x] Connect backend.
24 | - [x] Setup socket IO Client.
25 | - [x] Add Emotes.
26 | - [x] Change the color scheme.
27 | - [x] Fetch users fom db.
28 | - [x] Change the msg background and font colors.
29 | - [x] Set up socket IO 'msg-sent', 'msg-pending', and 'msg-not-sent' events.
30 | - [x] Separate icons for msg statuses.
31 | - [x] Responsiveness.
32 | - [x] Back Button in mobile view.
33 | - [x] Add date.
34 | - [x] Select a message.
35 | - [x] Dropdown on a message.
36 | - [x] Align checkboxes, online status dot!
37 | - [x] Delete a message.
38 | - [x] Delete a whole convo (functionality of 3 dots).
39 | - [x] Message notifications.
40 | - [ ] Forward a message.
41 | - [ ] Identify the type of user, i.e, Learner/Educator/Institute.
42 | - [ ] Create a group conversation.
43 |
44 | ##### Backend
45 | - [x] Create a Mongo DB cluster.
46 | - [x] Create placeholder users DB.
47 | - [x] Create placeholder chats DB.
48 | - [x] Setup Socket IO server.
49 | - [x] Online user communication.
50 | - [x] Implement Wait Queues for offline users.
51 | - [x] Connect to mongoDB cluster.
52 | - [x] Insert new Message into Database.
53 | - [x] Connect to users database.
54 | - [x] Add status field to the chats collection.
55 | - [x] Set up socket IO 'msg-sent', 'msg-pending', and 'msg-not-sent' events.
56 | - [x] Besides flushing messages to a newly logged in user, inform those messages' senders that these messages are now sent.
57 | - [x] Setup Wait queues for socket io events as well.
58 | - [x] Update messages' statuses on the database after flushing them.
59 | - [x] Delete a message.
60 | - [x] Delete a whole convo end-point.
61 | - [ ] Message notifications.
62 | - [ ] Forward a message.
63 | - [ ] Create a group functionality.
64 | - [ ] Add users to group.
65 |
66 |
67 | ##### Other (Or Common)
68 | - [x] Differentiate sent, pending and unset message.
69 | - [x] Differentiate between active and inactive(offline) users.
70 | - [ ] Differentiate read and unread messages.
71 |
72 |
73 |
74 | ##### Feel free to contibute and open any issues.
75 |
--------------------------------------------------------------------------------
/client/README.md:
--------------------------------------------------------------------------------
1 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
2 |
3 | ## Available Scripts
4 |
5 | In the project directory, you can run:
6 |
7 | ### `npm start`
8 |
9 | Runs the app in the development mode.
10 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
11 |
12 | The page will reload if you make edits.
13 | You will also see any lint errors in the console.
14 |
15 | ### `npm test`
16 |
17 | Launches the test runner in the interactive watch mode.
18 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
19 |
20 | ### `npm run build`
21 |
22 | Builds the app for production to the `build` folder.
23 | It correctly bundles React in production mode and optimizes the build for the best performance.
24 |
25 | The build is minified and the filenames include the hashes.
26 | Your app is ready to be deployed!
27 |
28 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
29 |
30 | ### `npm run eject`
31 |
32 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
33 |
34 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
35 |
36 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
37 |
38 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
39 |
40 | ## Learn More
41 |
42 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
43 |
44 | To learn React, check out the [React documentation](https://reactjs.org/).
45 |
46 | ### Code Splitting
47 |
48 | This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting
49 |
50 | ### Analyzing the Bundle Size
51 |
52 | This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
53 |
54 | ### Making a Progressive Web App
55 |
56 | This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app
57 |
58 | ### Advanced Configuration
59 |
60 | This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration
61 |
62 | ### Deployment
63 |
64 | This section has moved here: https://facebook.github.io/create-react-app/docs/deployment
65 |
66 | ### `npm run build` fails to minify
67 |
68 | This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify
69 |
--------------------------------------------------------------------------------
/client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "chat-app-client",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@material-ui/core": "^4.11.0",
7 | "@material-ui/icons": "^4.9.1",
8 | "@testing-library/jest-dom": "^4.2.4",
9 | "@testing-library/react": "^9.5.0",
10 | "@testing-library/user-event": "^7.2.1",
11 | "axios": "^0.20.0",
12 | "emoji-picker-react": "^3.2.4",
13 | "react": "^16.13.1",
14 | "react-dom": "^16.13.1",
15 | "react-router-dom": "^5.2.0",
16 | "react-scripts": "^3.4.4",
17 | "socket.io-client": "^2.3.1",
18 | "uuid": "^8.3.1"
19 | },
20 | "scripts": {
21 | "start": "react-scripts start",
22 | "build": "react-scripts build",
23 | "test": "react-scripts test",
24 | "eject": "react-scripts eject"
25 | },
26 | "eslintConfig": {
27 | "extends": "react-app"
28 | },
29 | "browserslist": {
30 | "production": [
31 | ">0.2%",
32 | "not dead",
33 | "not op_mini all"
34 | ],
35 | "development": [
36 | "last 1 chrome version",
37 | "last 1 firefox version",
38 | "last 1 safari version"
39 | ]
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/client/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Subhash3/chat-app/c30ab81562aa83c384892340ab9f3519c7be432a/client/public/favicon.ico
--------------------------------------------------------------------------------
/client/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/client/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Subhash3/chat-app/c30ab81562aa83c384892340ab9f3519c7be432a/client/public/logo192.png
--------------------------------------------------------------------------------
/client/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Subhash3/chat-app/c30ab81562aa83c384892340ab9f3519c7be432a/client/public/logo512.png
--------------------------------------------------------------------------------
/client/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 |
--------------------------------------------------------------------------------
/client/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/client/src/Apis/chatApi.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 |
3 | // const URL = "http://localhost:3000/chat-app"
4 | const URL = "https://yml-chat-app.herokuapp.com/chat-app"
5 |
6 | export const chatAPI = axios.create({
7 | baseURL: URL
8 | })
--------------------------------------------------------------------------------
/client/src/App.css:
--------------------------------------------------------------------------------
1 | :root{
2 | --bg-color: #4745B1;
3 | /* --bg-color: rgb(6, 180, 99); */
4 | --msg-text: white;
5 | /* --other-msg-bg: rgb(17, 13, 78); */
6 | --other-msg-bg: #4745B1;
7 | /* --my-msg-bg: rgb(194, 100, 116); */
8 | --my-msg-bg: white;
9 | /* --secondary-bg: #1b3650; */
10 | --secondary-bg: #EEEEFF;
11 | --dark-secondary-bg: #07131d;
12 | }
13 |
14 | .App {
15 | text-align: center;
16 | }
17 |
18 | /* EEEEFF */
19 | /* 4745B1 */
20 |
--------------------------------------------------------------------------------
/client/src/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import './App.css';
3 | import ChatWindow from './components/ChatWindow/ChatWindow.jsx'
4 | import LoginForm from './components/LoginForm/LoginForm'
5 | import UsersProvider from './contexts/UsersProvider'
6 | import CurrentUserProvider from './contexts/CurrentUserProvider'
7 | import ActiveConversationProvider from './contexts/ActiveConversationProvider'
8 | import ChatDBProvider from './contexts/ChatDBProvider'
9 | import SocketProvider from './contexts/SocketProvider'
10 | import ViewProvider from './contexts/ViewProvider'
11 | import ActiveMessageOptionsProvider from './contexts/ActiveMessageOptionsProvider'
12 | import NewMsgsMapProvider from './contexts/NewMsgsMapProvider'
13 | // import SidebarRefProvider from './contexts/SidebarRefProvider'
14 | // import UserChatsProvider from './contexts/UserChatsProvider'
15 | import SelectedMessagesProvider from './contexts/SelectedMessagesProvider'
16 | import SelectionToolBarStatusProvider from './contexts/SelectionToolBarStatusProvider'
17 | import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
18 |
19 | function App() {
20 | return (
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | {/* */}
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | {/* */}
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | );
52 | }
53 |
54 | export default App;
55 |
--------------------------------------------------------------------------------
/client/src/components/ChatWindow/ChatWindow.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react';
2 | import UsersList from '../UsersList/UsersList'
3 | import LoggedInUserInfo from '../LoggedInUserInfo/LoggedInUserInfo'
4 | import UserChats from '../UserChats/UserChats'
5 | import NewMsgForm from '../NewMsgForm/NewMsgForm'
6 | import OtherUserInfo from '../OtherUserInfo/OtherUserInfo';
7 | import { useSocket } from '../../contexts/SocketProvider'
8 | import { useActiveConversation } from '../../contexts/ActiveConversationProvider'
9 | import { useCurrentUser } from '../../contexts/CurrentUserProvider'
10 | import { useActiveMessageOptionsID } from '../../contexts/ActiveMessageOptionsProvider.jsx'
11 | // import { useSidebarRef } from '../../contexts/SidebarRefProvider'
12 | import { Redirect } from 'react-router-dom'
13 | import './ChatWindow.min.css'
14 |
15 | const ChatWindow = () => {
16 | const [currentUser] = useCurrentUser()
17 | const socket = useSocket()
18 | const [activeConversationID] = useActiveConversation()
19 | const [_msgIDWithActiveOptions, setMsgIDWithActiveOptions] = useActiveMessageOptionsID()
20 |
21 | // console.log("rendering CHAT_WINDOW")
22 |
23 | useEffect(() => {
24 | const chatWindowElement = document.querySelector('.chat-window')
25 | if (chatWindowElement) {
26 | chatWindowElement.addEventListener('click', () => {
27 | console.log("CHAT_WINDOW clicked")
28 | setMsgIDWithActiveOptions(null)
29 | })
30 | }
31 | }, [])
32 |
33 | useEffect(() => {
34 | if (socket)
35 | socket.emit("logged-in", currentUser.id)
36 | }, [socket, currentUser]);
37 |
38 | return !currentUser ? : (
39 |
40 |
45 |
46 | {activeConversationID && }
47 |
48 |
49 |
50 |
51 |
52 | );
53 | }
54 |
55 | export default ChatWindow;
56 |
--------------------------------------------------------------------------------
/client/src/components/ChatWindow/ChatWindow.min.css:
--------------------------------------------------------------------------------
1 | .chat-window{position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%);width:100%;height:100vh;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;margin:0}.chat-window .window-sidebar{background-color:var(--secondary-bg);display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;width:25%;height:100%;min-width:270px;-webkit-transition:-webkit-transform 200ms ease-in-out;transition:-webkit-transform 200ms ease-in-out;transition:transform 200ms ease-in-out;transition:transform 200ms ease-in-out, -webkit-transform 200ms ease-in-out}.chat-window .window-sidebar .loggedin-user-info{width:100%;height:68px;background-color:var(--secondary-bg)}.chat-window .chat-box{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:center;-ms-flex-align:center;align-items:center;width:75%;--other-users-info-height: 60px}.chat-window .chat-box .other-user-info{height:var(--other-users-info-height);background-color:var(--secondary-bg)}.chat-window .chat-box .chats{width:100%;height:calc( 90% - var(--other-users-info-height));-webkit-box-shadow:inset 1px 1px 5px var(--other-msg-bg);box-shadow:inset 1px 1px 5px var(--other-msg-bg)}@media screen and (max-width: 750px){.chat-window{height:100%}.chat-window .window-sidebar{position:absolute;z-index:10;-webkit-box-shadow:1px 1px 5px var(--other-msg-bg);box-shadow:1px 1px 5px var(--other-msg-bg);-webkit-transform:translateX(-110%);transform:translateX(-110%);width:100%}.chat-window .window-sidebar.active{-webkit-transform:translateX(0%);transform:translateX(0%)}.chat-window .chat-box{width:100%}.chat-window .chat-box .chats .toggle-sidebar{display:-ms-grid;display:grid}}@media screen and (max-width: 416px){.msg{max-width:200px}}
2 |
--------------------------------------------------------------------------------
/client/src/components/ChatWindow/ChatWindow.scss:
--------------------------------------------------------------------------------
1 | .chat-window{
2 | position: absolute;
3 | top: 50%;
4 | left: 50%;
5 | transform: translate(-50%, -50%);
6 | width: 100%;
7 | height: 100vh;
8 | display: flex;
9 | flex-direction: row;
10 | margin: 0;
11 | // box-shadow: 0px 0px 3px black;
12 | // border: 1px solid black;
13 |
14 | .window-sidebar{
15 | // border: 1px solid black;
16 | background-color: var(--secondary-bg);
17 | display: flex;
18 | flex-direction: column;
19 | width: 25%;
20 | height: 100%;
21 | min-width: 270px;
22 | transition: transform 200ms ease-in-out;
23 | // overflow-y: auto;
24 |
25 | .loggedin-user-info{
26 | width: 100%;
27 | height: 68px;
28 | background-color: var(--secondary-bg);
29 | }
30 | }
31 |
32 | .chat-box{
33 | flex-direction: column;
34 | // background-color: #e3edee;
35 | align-items: center;
36 | width: 75%;
37 | --other-users-info-height: 60px;
38 |
39 | .other-user-info{
40 | height: var(--other-users-info-height);
41 | background-color: var(--secondary-bg);
42 | // border: 1px solid black;
43 | }
44 |
45 | .chats{
46 | width: 100%;
47 | height: calc( 90% - var(--other-users-info-height) );
48 | box-shadow: inset 1px 1px 5px var(--other-msg-bg);
49 | // border: 1px solid black;
50 | }
51 | }
52 | }
53 |
54 | @media screen and (max-width: 750px){
55 | .chat-window{
56 | // position: relative;
57 | height: 100%;
58 |
59 | .window-sidebar{
60 | position: absolute;
61 | z-index: 10;
62 | box-shadow: 1px 1px 5px var(--other-msg-bg);
63 | transform: translateX(-110%);
64 | width: 100%;
65 |
66 | &.active{
67 | transform: translateX(0%);
68 | }
69 | }
70 |
71 | .chat-box{
72 | width: 100%;
73 |
74 | // .other-user-info{
75 | // // justify-content: space-between;
76 | // // .image{
77 | // // margin-left: 20%;
78 | // // }
79 | // }
80 |
81 | .chats{
82 | .toggle-sidebar{
83 | display: grid;
84 | }
85 | }
86 | }
87 | }
88 | }
89 |
90 | @media screen and (max-width: 416px){
91 | .msg{
92 | max-width: 200px;
93 | }
94 | }
--------------------------------------------------------------------------------
/client/src/components/LoggedInUserInfo/LoggedInUserInfo.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import User from '../User/User'
3 | import { useCurrentUser } from '../../contexts/CurrentUserProvider'
4 | import './LoggedInUserInfo.min.css'
5 |
6 | const LoggedInUserInfo = () => {
7 | const [currentUser] = useCurrentUser()
8 | return (
9 |
10 |
11 |
12 | );
13 | }
14 |
15 | export default LoggedInUserInfo;
16 |
--------------------------------------------------------------------------------
/client/src/components/LoggedInUserInfo/LoggedInUserInfo.min.css:
--------------------------------------------------------------------------------
1 | .loggedin-user-info{display:-ms-grid;display:grid;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-shadow:0px 1px 10px gray;box-shadow:0px 1px 10px gray;background-color:white}.loggedin-user-info .user{border:none;background-color:transparent}.loggedin-user-info .user .image{--size: 35px;border:3px solid var(--bg-color)}.loggedin-user-info .user .name{font-family:'Courier New', Courier, monospace;font-size:18px}.loggedin-user-info .user:hover{-webkit-box-shadow:none;box-shadow:none}
2 |
--------------------------------------------------------------------------------
/client/src/components/LoggedInUserInfo/LoggedInUserInfo.scss:
--------------------------------------------------------------------------------
1 | .loggedin-user-info{
2 | display: grid;
3 | align-items: center;
4 | box-shadow: 0px 1px 10px gray;
5 | background-color: white;
6 |
7 | .user{
8 | border: none;
9 | background-color: transparent;
10 |
11 | .image{
12 | --size: 35px;
13 | border: 3px solid var(--bg-color);
14 | }
15 |
16 | .name{
17 | font-family: 'Courier New', Courier, monospace;
18 | font-size: 18px;
19 | }
20 |
21 | &:hover{
22 | box-shadow: none;
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/client/src/components/LoginForm/LoginForm.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect, useRef } from 'react';
2 | import { useCurrentUser } from '../../contexts/CurrentUserProvider.jsx'
3 | import './LoginForm.min.css'
4 |
5 | // const API = "localhost:3000/"
6 |
7 | const LoginForm = (props) => {
8 | const [formData, setFormData] = useState({
9 | username: "",
10 | userID: "",
11 | })
12 | const [error, setError] = useState("")
13 | const usernameInputRef = useRef()
14 | const [, setCurrentUser] = useCurrentUser()
15 |
16 | // console.log("In login component: ", { currentUser })
17 |
18 | useEffect(() => {
19 | usernameInputRef.current.focus()
20 | }, [])
21 |
22 | const handleUsernameChange = (e) => {
23 | let newFormData = { ...formData }
24 | newFormData.username = e.target.value
25 | setFormData(newFormData)
26 | }
27 |
28 | const handleUserIDChange = (e) => {
29 | let newFormData = { ...formData }
30 | newFormData.userID = e.target.value
31 | setFormData(newFormData)
32 | }
33 |
34 | const resetForm = () => {
35 | let newFormData = { ...formData }
36 | newFormData.username = ""
37 | newFormData.password = ""
38 | setFormData(newFormData)
39 | }
40 |
41 | const handleSubmit = (e) => {
42 | e.preventDefault()
43 | if (!formData.username) {
44 | setError("username cannot be empty")
45 | } else if (!formData.userID) {
46 | setError("UserID cannot be empty")
47 | } else {
48 | let currentUserInfo = {
49 | name: formData.username,
50 | id: formData.userID
51 | }
52 |
53 | setCurrentUser(currentUserInfo)
54 | resetForm()
55 | setError("")
56 | props.history.push('/')
57 | }
58 | }
59 |
60 | return (
61 |
85 | );
86 | }
87 |
88 | const ErrorMsg = ({ error }) => {
89 | const styles = {
90 | color: "red",
91 | fontStyle: "italic",
92 | fontSize: '12px',
93 | margin: '5px auto',
94 | textTransform: 'capitalize'
95 | }
96 | return (
97 | {error}
98 | )
99 | }
100 |
101 | export default LoginForm;
102 |
--------------------------------------------------------------------------------
/client/src/components/LoginForm/LoginForm.min.css:
--------------------------------------------------------------------------------
1 | .myform{position:absolute;left:50%;top:50%;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%);display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;width:300px;border:1px solid black;padding:20px;text-transform:uppercase;font-family:monospace}.myform .form-title{font-size:19px;display:inline-block;margin:0 auto}.myform .form-group{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:10px}.myform .form-group input{width:100%;padding:10px;outline:none}.myform .form-group input[type="submit"]{cursor:pointer;border:none;padding:10px;border-radius:5px;background:#3535b9;color:white;text-transform:uppercase}
2 |
--------------------------------------------------------------------------------
/client/src/components/LoginForm/LoginForm.scss:
--------------------------------------------------------------------------------
1 | .myform{
2 | position: absolute;
3 | left: 50%;
4 | top: 50%;
5 | transform: translate(-50%, -50%);
6 | display: flex;
7 | flex-direction: column;
8 | width: 300px;
9 | border: 1px solid black;
10 | padding: 20px;
11 | text-transform: uppercase;
12 | font-family: monospace;
13 |
14 | .form-title{
15 | font-size: 19px;
16 | display: inline-block;
17 | margin: 0 auto;
18 | }
19 |
20 | .form-group{
21 | display: flex;
22 | flex-direction: row;
23 | justify-content: space-between;
24 | align-items: center;
25 | padding: 10px;
26 | // border: 1px solid red;
27 |
28 | input{
29 | width: 100%;
30 | padding: 10px;
31 | outline: none;
32 |
33 | &[type="submit"]{
34 | cursor: pointer;
35 | border: none;
36 | padding: 10px;
37 | border-radius: 5px;
38 | background: rgb(53, 53, 185);
39 | color: white;
40 | text-transform: uppercase;
41 | }
42 | }
43 | }
44 |
45 | }
--------------------------------------------------------------------------------
/client/src/components/NewMsgForm/NewMsgForm.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect, useRef } from 'react';
2 | import { useCurrentUser } from '../../contexts/CurrentUserProvider'
3 | import { useActiveConversation } from '../../contexts/ActiveConversationProvider'
4 | import { useChatDB } from '../../contexts/ChatDBProvider'
5 | import { useSocket } from '../../contexts/SocketProvider'
6 | import { chatAPI } from '../../Apis/chatApi'
7 | import { useNewMessagesMap } from '../../contexts/NewMsgsMapProvider'
8 | import Picker from 'emoji-picker-react';
9 | import SendIcon from '@material-ui/icons/Send';
10 | import { v4 as uuid } from 'uuid'
11 | import './NewMsgForm.min.css'
12 |
13 | export const MSG_PENDING = "pending"
14 | export const MSG_SENT = "msg-sent"
15 | export const MSG_NOT_SENT = "msg-not-sent"
16 |
17 | const NewMsgForm = () => {
18 | const [newMsg, setNewMsg] = useState("")
19 | const [openEmojiPicker, setOpenEmojiPicker] = useState(false)
20 | const [chatDB, setChatDB] = useChatDB()
21 | const [newMsgsMap, setNewMsgMap] = useNewMessagesMap()
22 | const [currentUser] = useCurrentUser()
23 | const [activeConversationID] = useActiveConversation()
24 | const socket = useSocket()
25 | const inputRef = useRef()
26 |
27 | // console.log("rendering NEW_MSG_FORM")
28 |
29 | useEffect(() => {
30 | if (inputRef.current) {
31 | inputRef.current.focus()
32 | }
33 |
34 | return () => {
35 |
36 | }
37 | }, [activeConversationID, inputRef])
38 |
39 | const onEmojiClick = (event, emojiObject) => {
40 | setNewMsg(newMsg + emojiObject.emoji);
41 | if (inputRef.current) {
42 | inputRef.current.focus()
43 | }
44 | };
45 |
46 | const changeMessageState = (msgID, msgStatus) => {
47 | console.log("Changing the state of the message with ID: ", msgID)
48 | setChatDB((prevChatDB) => {
49 | return prevChatDB.map(chatObj => {
50 | if (chatObj.id === msgID) {
51 | return { ...chatObj, status: msgStatus }
52 | }
53 | return chatObj
54 | })
55 | })
56 | }
57 |
58 | const getFormattedDate = (date) => {
59 | let year = date.getFullYear();
60 |
61 | let month = (1 + date.getMonth()).toString();
62 | month = month.length > 1 ? month : '0' + month;
63 |
64 | let day = date.getDate().toString();
65 | day = day.length > 1 ? day : '0' + day;
66 |
67 | return day + '/' + month + '/' + year;
68 | }
69 |
70 | const updateNewMsgCount = (senderID) => {
71 | console.log("Updating new msg count")
72 | // console.log({ senderID, activeConversationID })
73 | // if (senderID == activeConversationID) {
74 | // return;
75 | // }
76 |
77 | let newCount
78 | console.log({ newMsgsMap })
79 | if (senderID in newMsgsMap) {
80 | newCount = newMsgsMap[senderID] + 1
81 | }
82 | else {
83 | newCount = 1
84 | }
85 |
86 | console.log({ newMsgsMap, senderID, newCount })
87 | setNewMsgMap({ ...newMsgsMap, [senderID]: newCount })
88 | }
89 |
90 | const sendMessage = (msgBody) => {
91 | setOpenEmojiPicker(false)
92 | let msgObject = {
93 | id: uuid(),
94 | senderID: currentUser.id,
95 | receiverID: activeConversationID,
96 | msgBody,
97 | status: MSG_PENDING,
98 | time: new Date().toLocaleString().split(' ')[1],
99 | date: getFormattedDate(new Date()),
100 | }
101 |
102 | console.log("Sending message: ", msgObject)
103 |
104 | socket.emit('send-message', JSON.stringify(msgObject))
105 | setChatDB([...chatDB, msgObject])
106 |
107 | socket.on('message-sent', (id) => {
108 | console.log("[message-sent]: Message Has been sent")
109 | // console.log(chatDB)
110 | changeMessageState(id, MSG_SENT)
111 | })
112 |
113 | socket.on('message-not-sent', (reason, id) => {
114 | console.log("[message-not-sent]: Message has been not sent. : " + reason)
115 | changeMessageState(id, MSG_NOT_SENT)
116 | })
117 |
118 | socket.on('pending', (reason, id) => {
119 | console.log("[pending]: Message pending... : " + reason)
120 | changeMessageState(id, MSG_PENDING)
121 | })
122 |
123 | socket.on('received-message', msbObjectString => {
124 | let msgObject = JSON.parse(msbObjectString)
125 | console.log("[received-message]: Received message:", msgObject)
126 |
127 | updateNewMsgCount(msgObject.senderID)
128 | // msgObject.id = uuid()
129 | getChatDB()
130 | })
131 |
132 | socket.on('flush-messages', allMessagesStringified => {
133 | console.log("[flush-messages]: Got flushed messages")
134 | let allMessages = JSON.parse(allMessagesStringified)
135 | addFlushedMessages(allMessages)
136 | })
137 |
138 | socket.on('deleted-msgs-ack', () => {
139 | console.log("[deleted-msgs-ack]:")
140 | getChatDB()
141 | })
142 | }
143 |
144 | const addFlushedMessages = (allMessages) => {
145 | if (!allMessages)
146 | return
147 |
148 | let allMesssageObjects = []
149 |
150 | allMessages.forEach(message => {
151 | allMesssageObjects.push(JSON.parse(message))
152 | })
153 |
154 | setChatDB([...chatDB, ...allMesssageObjects])
155 | }
156 |
157 | const getChatDB = async () => {
158 | console.log("***Fetching chat database***")
159 | let userID = currentUser.id
160 | let response = await chatAPI.get(`/chats/${userID}`)
161 | console.log("***Got chat DB***")
162 | console.log(response.data)
163 | setChatDB(response.data)
164 | }
165 |
166 | const handleSubmit = (e) => {
167 | e.preventDefault()
168 | if (!newMsg) return
169 | sendMessage(newMsg)
170 | setNewMsg("")
171 | }
172 |
173 | const handleChange = (e) => {
174 | setNewMsg(e.target.value)
175 | }
176 |
177 | return !activeConversationID ? null : (
178 |
202 | );
203 | }
204 |
205 | export default NewMsgForm;
206 |
--------------------------------------------------------------------------------
/client/src/components/NewMsgForm/NewMsgForm.min.css:
--------------------------------------------------------------------------------
1 | .new-msg-form{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-ms-flex-pack:distribute;justify-content:space-around;background-color:#fafafa;height:60px;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:10px}.new-msg-form input{width:70%;padding:10px;outline:none}.new-msg-form input:focus{outline:none}.new-msg-form input:active{outline:none}.new-msg-form button{border:none;width:50px;padding:10px;background-color:transparent;color:var(--bg-color);border-radius:3px;cursor:pointer;-webkit-transition:background 200ms ease-in-out;transition:background 200ms ease-in-out}.new-msg-form button svg{width:100%;height:100%}.new-msg-form .emotes{width:50px;height:30px;font-size:25px;color:var(--bg-color);text-align:center;cursor:pointer}.new-msg-form .emoji-picker-react{position:absolute;bottom:100%;right:30px}
2 |
--------------------------------------------------------------------------------
/client/src/components/NewMsgForm/NewMsgForm.scss:
--------------------------------------------------------------------------------
1 | .new-msg-form{
2 | position: relative;
3 | display: flex;
4 | flex-direction: row;
5 | justify-content: space-around;
6 | background-color: #fafafa;
7 | height: 60px;
8 | align-items: center;
9 | padding: 10px;
10 |
11 | input{
12 | width: 70%;
13 | padding: 10px;
14 | outline: none;
15 |
16 | &:focus{
17 | outline: none;
18 | }
19 | &:active{
20 | outline: none;
21 | }
22 | }
23 | button{
24 | border: none;
25 | width: 50px;
26 | padding: 10px;
27 | background-color: transparent;
28 | color: var(--bg-color);
29 | border-radius: 3px;
30 | cursor: pointer;
31 | // border: 1px solid black;
32 | transition:background 200ms ease-in-out;
33 |
34 | // &:hover{
35 | // background-color: rgb(13, 131, 76);
36 | // }
37 | // &:active{
38 | // background-color: var(--bg-color);
39 | // }
40 |
41 | svg{
42 | width: 100%;
43 | height: 100%;
44 | // transform: rotate(-10deg);
45 | }
46 | }
47 |
48 | .emotes{
49 | // border: 1px solid black;
50 | width: 50px;
51 | height: 30px;
52 | font-size: 25px;
53 | color: var(--bg-color);
54 | text-align: center;
55 | cursor: pointer;
56 | }
57 |
58 | .emoji-picker-react{
59 | position: absolute;
60 | bottom: 100%;
61 | right: 30px;
62 | }
63 | }
--------------------------------------------------------------------------------
/client/src/components/OtherUserInfo/OtherUserInfo.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { useUsers } from '../../contexts/UsersProvider'
3 | import { useActiveConversation } from '../../contexts/ActiveConversationProvider'
4 | import { useCurrentUser } from '../../contexts/CurrentUserProvider'
5 | import MoreVertIcon from '@material-ui/icons/MoreVert';
6 | import { toggleSidebar } from '../UserChats/UserChats'
7 | import { chatAPI } from '../../Apis/chatApi'
8 | import ArrowBackIcon from '@material-ui/icons/ArrowBack';
9 | import { useSocket } from '../../contexts/SocketProvider'
10 | import { useView } from '../../contexts/ViewProvider'
11 | import { MOBILE_VIEW } from '../../contexts/ViewProvider'
12 | import './OtherUserInfo.min.css'
13 |
14 | const OtherUserInfo = () => {
15 | const [users] = useUsers()
16 | const [activeConversationID] = useActiveConversation()
17 | const [isSettingsMenuActive, setSettingsActive] = useState(false)
18 | const [view, _setView] = useView()
19 |
20 | const getOtherUserName = () => {
21 | for (let user of users) {
22 | // console.log("HIHIH: ", user.name)
23 | if (user.id === activeConversationID) {
24 | return user.name
25 | }
26 | }
27 |
28 | return activeConversationID
29 | }
30 |
31 | const toggleSettingsMenu = () => {
32 | setSettingsActive(!isSettingsMenuActive)
33 | }
34 |
35 | return (
36 |
37 | {(view === MOBILE_VIEW) && (
)
43 | }
44 |
45 |
46 | {getOtherUserName()}
47 | {/*
{}
*/}
48 |
49 |
50 |
51 |
52 |
53 |
54 | );
55 | }
56 |
57 | const SettingsMenu = ({ isActive }) => {
58 | const [activeConversationID] = useActiveConversation()
59 | const [currentUser] = useCurrentUser()
60 | const socket = useSocket()
61 |
62 | const deleteConversation = async () => {
63 | let id1 = currentUser.id
64 | let id2 = activeConversationID
65 |
66 |
67 | console.log({ activeConversationID, myID: currentUser.id })
68 | console.log({ id1, id2 })
69 | let response = await chatAPI.post('/delete-convo', {
70 | IDs: { id1, id2 }
71 | })
72 | if (response.data.status === 1) {
73 | console.log("Deleted conversation!")
74 | console.log(`Emitting "deleted-msgs-ack to ${id2} and ${id1}`)
75 | socket.emit("deleted-msgs-ack", id2)
76 | socket.emit("deleted-msgs-ack", id1)
77 | } else {
78 | alert("Failed to delete conversation!")
79 | }
80 | }
81 |
82 | return (
83 |
84 |
Block User
85 |
Delete Conversation
88 |
Some Shit
89 |
90 | )
91 | }
92 |
93 | export default OtherUserInfo;
94 |
--------------------------------------------------------------------------------
/client/src/components/OtherUserInfo/OtherUserInfo.min.css:
--------------------------------------------------------------------------------
1 | .other-user-info{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:5px 10px;-webkit-box-shadow:1px 1px 3px var(--other-msg-bg);box-shadow:1px 1px 3px var(--other-msg-bg);z-index:1}.other-user-info .back-button{cursor:pointer}.other-user-info .image{width:40px;height:40px;border:1px solid var(--other-msg-bg);border-radius:50%;margin:0 10px}.other-user-info .name{font-family:monospace;font-size:16px}.other-user-info .settings{margin:0 10px 0 auto;cursor:pointer;font-family:monospace}.other-user-info .settings .settings-menu{position:absolute;right:20px;background:white;-webkit-box-shadow:1px 1px 3px var(--other-msg-bg);box-shadow:1px 1px 3px var(--other-msg-bg);display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;border-radius:7px;-webkit-transform:scale(0);transform:scale(0);-webkit-transform-origin:top right;transform-origin:top right;-webkit-transition:-webkit-transform 200ms ease-in-out;transition:-webkit-transform 200ms ease-in-out;transition:transform 200ms ease-in-out;transition:transform 200ms ease-in-out, -webkit-transform 200ms ease-in-out}.other-user-info .settings .settings-menu.active{-webkit-transform:scale(1);transform:scale(1)}.other-user-info .settings .settings-menu div{padding:13px 5px;border-bottom:1px solid var(--other-msg-bg)}.other-user-info .settings .settings-menu div:last-child{border:none}
2 |
--------------------------------------------------------------------------------
/client/src/components/OtherUserInfo/OtherUserInfo.scss:
--------------------------------------------------------------------------------
1 | .other-user-info{
2 | position: relative;
3 | display: flex;
4 | flex-direction: row;
5 | align-items: center;
6 | padding: 5px 10px;
7 | box-shadow: 1px 1px 3px var(--other-msg-bg);
8 | z-index: 1;
9 |
10 | .back-button{
11 | // border: 1px solid black;
12 | cursor: pointer;
13 | }
14 |
15 | .image{
16 | width: 40px;
17 | height: 40px;
18 | border: 1px solid var(--other-msg-bg);
19 | border-radius: 50%;
20 | margin: 0 10px;
21 | }
22 |
23 | .name{
24 | font-family: monospace;
25 | font-size: 16px;
26 | }
27 |
28 | .settings{
29 | margin: 0 10px 0 auto;
30 | cursor: pointer;
31 | font-family: monospace;
32 |
33 | .settings-menu{
34 | position: absolute;
35 | right: 20px;
36 | background: white;
37 | box-shadow: 1px 1px 3px var(--other-msg-bg);
38 | display: flex;
39 | flex-direction: column;
40 | border-radius: 7px;
41 | transform: scale(0);
42 | transform-origin: top right;
43 | transition: transform 200ms ease-in-out;
44 |
45 | &.active{
46 | transform: scale(1);
47 | }
48 |
49 | div{
50 | padding: 13px 5px;
51 | border-bottom: 1px solid var(--other-msg-bg);
52 |
53 | &:last-child{
54 | border: none;
55 | }
56 | }
57 | }
58 | }
59 | }
--------------------------------------------------------------------------------
/client/src/components/SelectionToolBar/SelectionToolBar.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import DeleteIcon from '@material-ui/icons/Delete';
3 | import ReplyIcon from '@material-ui/icons/Reply';
4 | import { useCurrentUser } from '../../contexts/CurrentUserProvider'
5 | import { useChatDB } from '../../contexts/ChatDBProvider'
6 | import { useSelectionToolBarStatus } from '../../contexts/SelectionToolBarStatusProvider'
7 | import { useSelectedMessages } from '../../contexts/SelectedMessagesProvider'
8 | import { chatAPI } from '../../Apis/chatApi'
9 | import { useSocket } from '../../contexts/SocketProvider'
10 | import './SelectionToolBar.min.css'
11 |
12 | const SelectionToolBar = () => {
13 | const [_isToolbarActive, setToolbarActive] = useSelectionToolBarStatus()
14 | const [selectedMessages, setSelectedMessages] = useSelectedMessages()
15 | const [chatDB, setChatDB] = useChatDB()
16 | const [currentUser] = useCurrentUser()
17 | const socket = useSocket()
18 |
19 | const clearSelection = () => {
20 | setSelectedMessages(new Set())
21 | }
22 |
23 | const closeToolBar = () => {
24 | setToolbarActive(false)
25 | clearSelection()
26 | }
27 |
28 | const getChatDB = async () => {
29 | console.log("***Fetching chat database***")
30 | let userID = currentUser.id
31 | let response = await chatAPI.get(`/chats/${userID}`)
32 | console.log("***Got chat DB***")
33 | console.log(response.data)
34 | setChatDB(response.data)
35 | }
36 |
37 | const deleteMessage = async () => {
38 | console.log("Deleting messages with IDs:", selectedMessages)
39 | // let response = await chatAPI.get(`/delete/${JSON.stringify([...selectedMessages])}`)
40 | // console.log("****************************************")
41 | // console.log(`SelectedMessages[0]: ${[...selectedMessages][0]}`)
42 | let msgObject = chatDB.filter(msg => msg.id === [...selectedMessages][0])
43 | console.log(msgObject)
44 | let response = await chatAPI.post('/delete', {
45 | // stringfiedIDs: JSON.stringify([...selectedMessages]),
46 | messageIDs: [...selectedMessages],
47 | senderID: msgObject.senderID,
48 | receiverID: msgObject.receiverID
49 |
50 | })
51 | console.log(response.data)
52 | closeToolBar()
53 | if (response.data.status === 1) {
54 | let msgObject = chatDB.filter(msg => msg.id === [...selectedMessages][0])[0]
55 | let receiverID = msgObject.receiverID
56 | let senderID = msgObject.senderID
57 |
58 | // console.log("****************************************")
59 | // console.log(msgObject)
60 | // Object.keys(msgObject).forEach(key => console.log(key, msgObject[key]))
61 | // console.log({ receiverID })
62 | console.log("Deleted messages!")
63 | console.log(`Emitting "deleted-msgs-ack to ${receiverID} and ${senderID}`)
64 | socket.emit("deleted-msgs-ack", receiverID)
65 | socket.emit("deleted-msgs-ack", senderID)
66 |
67 | // getChatDB()
68 | } else {
69 | alert("Failed to delete messages!")
70 | }
71 | }
72 |
73 | return (
74 |
75 |
78 |
79 |
80 |
81 |
82 |
83 |
Cancel
86 |
87 | );
88 | }
89 |
90 | export default SelectionToolBar;
91 |
--------------------------------------------------------------------------------
/client/src/components/SelectionToolBar/SelectionToolBar.min.css:
--------------------------------------------------------------------------------
1 | .selection-toolbar{position:fixed;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-align:center;-ms-flex-align:center;align-items:center;width:75%;padding:20px 30px;margin:10px 0;background-color:var(--secondary-bg);-webkit-box-shadow:1px 1px 5px var(--other-msg-bg);box-shadow:1px 1px 5px var(--other-msg-bg);font-family:monospace;-webkit-transform:translateY(-72%) translateX(-10px);transform:translateY(-72%) translateX(-10px);z-index:99}.selection-toolbar div{cursor:pointer;margin:0 15px}.selection-toolbar .cancel{background-color:var(--other-msg-bg);padding:7px;border-radius:2px;color:white;margin-left:auto}.selection-toolbar .delete{color:#cc3b3b}.selection-toolbar .forward{-webkit-transform:rotateY(180deg);transform:rotateY(180deg)}
2 |
--------------------------------------------------------------------------------
/client/src/components/SelectionToolBar/SelectionToolBar.scss:
--------------------------------------------------------------------------------
1 | .selection-toolbar
2 | {
3 | position: fixed;
4 | display: flex;
5 | flex-direction: row;
6 | // justify-content: space-between;
7 | align-items: center;
8 | width: 75%;
9 | padding: 20px 30px;
10 | margin: 10px 0;
11 | // top: 0;
12 | background-color: var(--secondary-bg);
13 | box-shadow: 1px 1px 5px var(--other-msg-bg);
14 | font-family: monospace;
15 | transform: translateY(-72%) translateX(-10px);
16 | z-index: 99;
17 |
18 | div{
19 | cursor: pointer;
20 | margin: 0 15px;
21 | }
22 |
23 | .cancel{
24 | background-color: var(--other-msg-bg);
25 | padding: 7px;
26 | border-radius: 2px;
27 | color: white;
28 | margin-left: auto;
29 | }
30 |
31 | .delete{
32 | color: rgb(204, 59, 59);
33 | }
34 |
35 | .forward{
36 | transform: rotateY(180deg);
37 | }
38 | }
--------------------------------------------------------------------------------
/client/src/components/User/User.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import './User.min.css'
3 |
4 | const User = ({ user, handleClick, active, online, newMsgCount }) => {
5 | // console.log(user, active)
6 |
7 | let className = `user ${active ? "active" : ""} ${online ? "online" : ""}`
8 | let username = user.name ? user.name : user.id
9 | return (
10 |
11 |
12 |
{username}
13 | {(newMsgCount && newMsgCount !== 0) &&
{newMsgCount}
}
14 |
15 | )
16 | }
17 |
18 | export default User;
19 |
--------------------------------------------------------------------------------
/client/src/components/User/User.min.css:
--------------------------------------------------------------------------------
1 | .user{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:10px;cursor:pointer;-webkit-transition:background 100ms ease-in-out, -webkit-box-shadow 200ms ease-in-out;transition:background 100ms ease-in-out, -webkit-box-shadow 200ms ease-in-out;transition:background 100ms ease-in-out, box-shadow 200ms ease-in-out;transition:background 100ms ease-in-out, box-shadow 200ms ease-in-out, -webkit-box-shadow 200ms ease-in-out;color:#4745B1;background-color:white;margin:2px 0}.user:first-child{margin:0}.user:hover{-webkit-box-shadow:1px 1px 2px var(--other-msg-bg);box-shadow:1px 1px 2px var(--other-msg-bg)}.user .image{position:relative;--size: 30px;width:var(--size);height:var(--size);border:1px solid var(--other-msg-bg);border-radius:50%;background-color:white}.user .new-msg-count{display:-ms-grid;display:grid;margin-left:auto;margin-right:25px;background-color:var(--other-msg-bg);color:white;border-radius:50%;padding:2px 6px;font-weight:bold;font-family:monospace}.user .name{position:relative;font-size:15px;font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;margin-left:10px}.user.active{background-color:var(--other-msg-bg);color:white}.user.active.online::after{background-color:white}.user.active .image{border:none}.user.online::after{position:absolute;content:'';width:8px;height:8px;border-radius:50%;background-color:var(--bg-color);right:10px}.loggedin-user-info .user .new-msg-count{display:none}
2 |
--------------------------------------------------------------------------------
/client/src/components/User/User.scss:
--------------------------------------------------------------------------------
1 | .user{
2 | display: flex;
3 | flex-direction: row;
4 | align-items: center;
5 | padding: 10px;
6 | cursor: pointer;
7 | transition: background 100ms ease-in-out,
8 | box-shadow 200ms ease-in-out;
9 | color: #4745B1;
10 | background-color: white;
11 | margin: 2px 0;
12 |
13 | &:first-child{
14 | margin: 0;
15 | }
16 |
17 | &:hover{
18 | box-shadow: 1px 1px 2px var(--other-msg-bg);
19 | }
20 |
21 | .image{
22 | position: relative;
23 | --size: 30px;
24 | width: var(--size);
25 | height: var(--size);
26 | border: 1px solid var(--other-msg-bg);
27 | border-radius: 50%;
28 | background-color: white;
29 | }
30 |
31 | .new-msg-count{
32 | display: grid;
33 | margin-left: auto;
34 | margin-right: 25px;
35 | background-color: var(--other-msg-bg);
36 | color: white;
37 | border-radius: 50%;
38 | padding: 2px 6px;
39 | font-weight: bold;
40 | font-family: monospace;
41 | }
42 |
43 | .name {
44 | position: relative;
45 | font-size: 15px;
46 | font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
47 | margin-left: 10px;
48 | // text-transform: capitalize;
49 | }
50 |
51 | &.active{
52 | background-color: var(--other-msg-bg);
53 | color: white;
54 |
55 | &.online{
56 | &::after{
57 | background-color: white;
58 | }
59 | }
60 |
61 | .image{
62 | border: none;
63 | }
64 | }
65 |
66 | &.online{
67 | &::after{
68 | position: absolute;
69 | content: '';
70 | width: 8px;
71 | height: 8px;
72 | border-radius: 50%;
73 | background-color: var(--bg-color);
74 | right: 10px;
75 | }
76 | }
77 | }
78 |
79 | .loggedin-user-info {
80 | .user{
81 | .new-msg-count{
82 | display: none;
83 | }
84 | }
85 | }
--------------------------------------------------------------------------------
/client/src/components/UserChats/UserChats.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import { useRef } from 'react';
3 | import { useCurrentUser } from '../../contexts/CurrentUserProvider'
4 | import { useActiveConversation } from '../../contexts/ActiveConversationProvider'
5 | import { useChatDB } from '../../contexts/ChatDBProvider'
6 | import { useSocket } from '../../contexts/SocketProvider'
7 | import { chatAPI } from '../../Apis/chatApi'
8 | import CheckCircleIcon from '@material-ui/icons/CheckCircle'
9 | import CachedIcon from '@material-ui/icons/Cached'
10 | import CancelIcon from '@material-ui/icons/Cancel'
11 | import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
12 | import { useActiveMessageOptionsID } from '../../contexts/ActiveMessageOptionsProvider.jsx'
13 | import SelectionToolBar from '../SelectionToolBar/SelectionToolBar'
14 | import { useSelectedMessages } from '../../contexts/SelectedMessagesProvider'
15 | import ContactsIcon from '@material-ui/icons/Contacts';// import ArrowRightAltIcon from '@material-ui/icons/ArrowRightAlt';
16 | import { MSG_PENDING, MSG_SENT, MSG_NOT_SENT } from '../NewMsgForm/NewMsgForm'
17 | import { useSelectionToolBarStatus } from '../../contexts/SelectionToolBarStatusProvider'
18 |
19 | import './UserChats.min.css'
20 |
21 | // Probably not an efficient way to do this!
22 | export const toggleSidebar = (e) => {
23 | let sidebar = document.querySelector('.window-sidebar')
24 | // let toggleButton = document.querySelector('.toggle-sidebar')
25 |
26 | sidebar.classList.toggle('active')
27 | // toggleButton.classList.toggle('move-left')
28 | }
29 |
30 | const UserChats = () => {
31 | const [chatDB, setChatDB] = useChatDB()
32 | const [userChats, setUserChats] = useState([])
33 | const [currentUser] = useCurrentUser()
34 | const [activeConversationID] = useActiveConversation()
35 | const [msgIDWithActiveOptions, setMsgIDWithActiveOptions] = useActiveMessageOptionsID()
36 | const [isToolbarActive, _setToolbarActive] = useSelectionToolBarStatus()
37 |
38 | const socket = useSocket()
39 | const chatsRef = useRef()
40 | let prevDate = undefined
41 | // const toggleSidebarRef = useRef()
42 |
43 | console.log("rendering USER_CHATS")
44 | // console.log({ userChats })
45 | // console.log({ chatDB })
46 |
47 | const getChatDB = async () => {
48 | console.log("***Fetching chat database***")
49 | let userID = currentUser.id
50 | let response = await chatAPI.get(`/chats/${userID}`)
51 | // console.log("***Got char DB***")
52 | // console.log(response.data)
53 | setChatDB(response.data)
54 | }
55 |
56 | const scrollToLatestMsg = () => {
57 | let chatsWindowProto = chatsRef.current.__proto__
58 | let scrollTopMax
59 | if (!("scrollTopMax" in chatsWindowProto)) {
60 | // console.log("Hiiii:", chatsRef.current.scrollHeight, chatsRef.current.clientHeight)
61 | scrollTopMax = chatsRef.current.scrollHeight - chatsRef.current.clientHeight
62 | } else {
63 | scrollTopMax = chatsRef.current.scrollTopMax
64 | }
65 | chatsRef.current.scroll(0, scrollTopMax)
66 | }
67 |
68 | useEffect(() => {
69 | getChatDB()
70 | // toggleSidebarRef.current.addEventListener('click', toggleSidebar)
71 | }, [])
72 |
73 | useEffect(() => {
74 | scrollToLatestMsg()
75 | }, [userChats])
76 |
77 | const extractConversations = () => {
78 | console.log("Extracting convos")
79 | // console.log("Chat DB Length: ", chatDB.length)
80 | let conversations = chatDB.filter(msgObject => {
81 | // console.log(msgObject.senderID, msgObject.receiverID, currentUser.id, activeConversationID, msgObject.msgBody)
82 | // if ((msgObject.senderID === currentUser.id && msgObject.receiverID === activeConversationID)
83 | // || (msgObject.receiverID === currentUser.id && msgObject.senderID === activeConversationID)) {
84 | // console.log("Meets")
85 | // }
86 | return (msgObject.senderID === currentUser.id && msgObject.receiverID === activeConversationID)
87 | || (msgObject.receiverID === currentUser.id && msgObject.senderID === activeConversationID)
88 | })
89 |
90 | setUserChats(conversations)
91 | }
92 |
93 | useEffect(() => {
94 | extractConversations()
95 | }, [activeConversationID, chatDB, currentUser.id, socket])
96 |
97 |
98 | console.log({ msgIDWithActiveOptions })
99 | const toggleMenu = (msgID) => {
100 | console.log("\tInside toggleMenu")
101 | console.log("Message Id: ", msgID)
102 | console.log("Actoive message options: ", msgIDWithActiveOptions)
103 | if (msgID === msgIDWithActiveOptions) {
104 | setMsgIDWithActiveOptions(null)
105 | } else {
106 | setMsgIDWithActiveOptions(msgID)
107 | }
108 |
109 | return
110 | }
111 |
112 | return (
113 |
114 | {isToolbarActive && }
115 | {activeConversationID ? (userChats.map(chatObject => {
116 | let returnElement = (prevDate === undefined || prevDate !== chatObject.date)
117 | ? (
118 | <>
119 |
120 | toggleMenu(chatObject.id)} />
121 | >
122 | )
123 | : ( toggleMenu(chatObject.id)} />)
124 | prevDate = chatObject.date
125 | return returnElement
126 | }))
127 | : }
128 |
129 | );
130 | }
131 |
132 | const Message = ({ msgObject, toggleMenu }) => {
133 | const [currentUser] = useCurrentUser()
134 | const [isToolbarActive, setToolbarActive] = useSelectionToolBarStatus()
135 | const [msgIDWithActiveOptions, _setMsgIDWithActiveOptions] = useActiveMessageOptionsID()
136 | const [selectedMessages, setSelectedMessages] = useSelectedMessages()
137 |
138 | let isSender = (msgObject.senderID === currentUser.id)
139 | let msgStatus = msgObject.status
140 |
141 | console.log("rendering Message component")
142 |
143 | // console.log("selectedMessages: ", selectedMessages)
144 |
145 | const selectMessage = (msgID) => {
146 | if (selectedMessages.has(msgID)) {
147 | selectedMessages.delete(msgID)
148 | setSelectedMessages(selectedMessages)
149 | }
150 | else {
151 | setSelectedMessages(new Set([...selectedMessages, msgID]))
152 | }
153 | }
154 |
155 | return (
156 |
157 | {isToolbarActive &&
158 |
159 | selectMessage(msgObject.id)} />
160 |
161 | }
162 |
163 | {/*
From: {msgObject.senderID}
*/}
164 |
{msgObject.msgBody}
165 |
{msgObject.time}
166 | {isSender &&
}
167 |
168 |
169 |
170 |
171 | )
172 | }
173 |
174 | const MessageOptions = ({ msgObject, msgIDWithActiveOptions, msgID }) => {
175 | const [isToolbarActive, setToolbarActive] = useSelectionToolBarStatus()
176 | const [selectedMessages, setSelectedMessages] = useSelectedMessages()
177 |
178 | console.log("Rendering message options")
179 | console.log({ msgIDWithActiveOptions, msgID })
180 |
181 | const forwardMessage = () => {
182 | console.log("Forwarding message with ID:", msgID)
183 | setToolbarActive(false)
184 | setSelectedMessages(new Set())
185 | }
186 |
187 | const copyMessage = () => {
188 | // console.log("Copying message with ID:", msgID)
189 | let clipboardPromise = navigator.clipboard.writeText(msgObject.msgBody)
190 | clipboardPromise
191 | .then(() => {
192 | console.log("Copied message with ID:", msgID)
193 | })
194 | .catch(() => {
195 | console.log("Error copying message with ID:", msgID)
196 | })
197 | }
198 |
199 | const activateToolBar = () => {
200 | console.log("Activating toolbar!")
201 | setToolbarActive(!isToolbarActive)
202 | }
203 |
204 |
205 | return (
206 |
207 |
Forward
208 |
Copy
209 |
More
210 |
211 | )
212 | }
213 |
214 | const MsgStatusIcon = ({ status }) => {
215 | if (status === MSG_SENT)
216 | return
217 | else if (status === MSG_NOT_SENT)
218 | return
219 | else if (status === MSG_PENDING)
220 | return
221 | }
222 |
223 | const SelectAChat = () => {
224 | return (
225 |
226 | Please select a conversation
227 |
228 | )
229 | }
230 |
231 | // const datesDifference = (date1, date2) => {
232 | // return Math.abs((date1 - date2) / (1000 * 60 * 60 * 24))
233 | // }
234 |
235 | const DateLabel = ({ date }) => {
236 | // let dateString = null
237 | return (
238 | {date}
239 | )
240 | }
241 |
242 | export default UserChats;
243 |
--------------------------------------------------------------------------------
/client/src/components/UserChats/UserChats.min.css:
--------------------------------------------------------------------------------
1 | .message-element{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content}.message-element.sender{-webkit-box-orient:horizontal;-webkit-box-direction:reverse;-ms-flex-direction:row-reverse;flex-direction:row-reverse;margin-left:auto;margin-right:4px}.message-element.sender .select-box{margin:0px 7px}.msg{position:relative;max-width:250px;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;height:-webkit-fit-content;height:-moz-fit-content;height:fit-content;background-color:var(--other-msg-bg);color:var(--msg-text);padding:5px 15px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;font-family:monospace;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;margin:0 0 4px 4px;border-bottom-left-radius:15px;border-top-right-radius:15px}.msg .msg-body{margin-bottom:3px;word-wrap:anywhere}.msg .time{margin-left:auto;margin-right:10px;font-size:10px}.msg.sender{background-color:var(--secondary-bg);margin-left:auto;margin-right:4px;border-top-right-radius:0;border-bottom-right-radius:15px;border-top-left-radius:15px;border-bottom-left-radius:0;color:var(--other-msg-bg);position:relative}.msg.sender .msg-options{-webkit-transform-origin:top right;transform-origin:top right}.msg.sender .msg-options{left:unset;right:100%}.msg.sender .msg-options-icon .MuiSvgIcon-root{fill:#4fd64f !important}.msg.sender .MuiSvgIcon-root{position:absolute;width:15px;height:15px;right:0;bottom:0}.msg.sender.msg-sent .MuiSvgIcon-root{fill:#4fd64f}.msg.sender.pending .MuiSvgIcon-root{fill:#949494}.msg.sender.msg-not-sent .MuiSvgIcon-root{fill:#ff3636}.msg .msg-options-icon{position:absolute;top:0;left:0;bottom:0;right:0;border-radius:inherit;background:rgba(0,0,0,0);-webkit-transition:background 200ms ease-in-out;transition:background 200ms ease-in-out}.msg .msg-options-icon .MuiSvgIcon-root{position:absolute;right:2px;top:0;opacity:0;-webkit-transition:opacity 200ms ease-in-out;transition:opacity 200ms ease-in-out;cursor:pointer}.msg:hover .msg-options-icon{background:rgba(0,0,0,0.4)}.msg:hover .msg-options-icon .MuiSvgIcon-root{opacity:1}.msg .msg-options{position:absolute;background:var(--secondary-bg);-webkit-box-shadow:1px 1px 5px var(--other-msg-bg);box-shadow:1px 1px 5px var(--other-msg-bg);padding:10px 0;color:black;left:100%;z-index:999;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;border-radius:7px;-webkit-transition:-webkit-transform 200ms ease-in-out;transition:-webkit-transform 200ms ease-in-out;transition:transform 200ms ease-in-out;transition:transform 200ms ease-in-out, -webkit-transform 200ms ease-in-out;-webkit-transform:scale(0);transform:scale(0);-webkit-transform-origin:top left;transform-origin:top left}.msg .msg-options.active{-webkit-transform:scale(1);transform:scale(1)}.msg .msg-options .option{border-bottom:1px solid var(--other-msg-bg);padding:10px 30px;cursor:pointer}.msg .msg-options .option:hover{opacity:0.7}.msg .msg-options .option:last-child{border:none}.chats{padding:40px 10px 10px 10px;position:relative;scroll-behavior:smooth;overflow-y:auto;overflow-x:hidden}.chats .toggle-sidebar{position:fixed;display:none;z-index:99;top:0;place-items:center;width:30px;height:30px;opacity:0.8;background-color:var(--other-msg-bg);-webkit-transform:translate(-10px, 60px);transform:translate(-10px, 60px);cursor:pointer;-webkit-transition:-webkit-transform 200ms ease-in-out;transition:-webkit-transform 200ms ease-in-out;transition:transform 200ms ease-in-out;transition:transform 200ms ease-in-out, -webkit-transform 200ms ease-in-out}.chats .toggle-sidebar .MuiSvgIcon-root{fill:white;width:30px;height:30px}.chats .toggle-sidebar.move-left{-webkit-transform:translate(260px, 60px);transform:translate(260px, 60px)}.select-chat{font-size:20px;font-family:monospace;position:absolute;top:50%;left:50%;width:300px;height:300px;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%);background-color:var(--bg-color);border-radius:50%;padding:50px;text-align:center;display:-ms-grid;display:grid;place-items:center;color:white}.date-label{font-size:12px;font-family:monospace;background-color:var(--secondary-bg);padding:5px 0;margin:5px 0 10px 0}
2 |
--------------------------------------------------------------------------------
/client/src/components/UserChats/UserChats.scss:
--------------------------------------------------------------------------------
1 | .message-element{
2 | position: relative;
3 | display: flex;
4 | flex-direction: row;
5 | // border: 1px solid red;
6 | width: fit-content;
7 |
8 | &.sender{
9 | flex-direction: row-reverse;
10 | margin-left: auto;
11 | margin-right: 4px;
12 |
13 | .select-box{
14 | margin: 0px 7px;
15 | }
16 | }
17 | }
18 |
19 | .msg{
20 | position: relative;
21 | max-width: 250px;
22 | width: fit-content;
23 | height: fit-content;
24 | background-color: var(--other-msg-bg);
25 | color: var(--msg-text);
26 | padding: 5px 15px;
27 | display: flex;
28 | flex-direction: column;
29 | font-family: monospace;
30 | align-items: flex-start;
31 | margin: 0 0 4px 4px;
32 | border-bottom-left-radius: 15px;
33 | border-top-right-radius: 15px;
34 |
35 | .msg-body{
36 | margin-bottom: 3px;
37 | word-wrap: anywhere;
38 | }
39 |
40 | .time{
41 | margin-left: auto;
42 | margin-right: 10px;
43 | font-size: 10px;
44 | }
45 |
46 | &.sender{
47 | background-color: var(--secondary-bg);
48 | margin-left: auto;
49 | margin-right: 4px;
50 | border-top-right-radius: 0;
51 | border-bottom-right-radius: 15px;
52 | border-top-left-radius: 15px;
53 | border-bottom-left-radius: 0;
54 | color: var(--other-msg-bg);
55 | position: relative;
56 |
57 | .msg-options{
58 | transform-origin: top right;
59 | }
60 |
61 | .msg-options{
62 | left: unset;
63 | right: 100%;
64 | }
65 |
66 | .msg-options-icon{
67 | .MuiSvgIcon-root{
68 | fill: rgb(79, 214, 79) !important;
69 | }
70 | }
71 |
72 | .MuiSvgIcon-root{
73 | position: absolute;
74 | width: 15px;
75 | height: 15px;
76 | right: 0;
77 | bottom: 0;
78 | }
79 |
80 | &.msg-sent{
81 | .MuiSvgIcon-root{
82 | fill: rgb(79, 214, 79);
83 | }
84 | }
85 |
86 | &.pending{
87 | .MuiSvgIcon-root{
88 | fill: rgb(148, 148, 148);
89 | }
90 | }
91 |
92 | &.msg-not-sent{
93 | .MuiSvgIcon-root{
94 | fill: rgb(255, 54, 54);
95 | }
96 | }
97 | }
98 |
99 | .msg-options-icon{
100 | position: absolute;
101 | top: 0;
102 | left: 0;
103 | bottom: 0;
104 | right: 0;
105 | border-radius: inherit;
106 | background: rgba(black, 0);
107 | transition: background 200ms ease-in-out;
108 |
109 | .MuiSvgIcon-root{
110 | position: absolute;
111 | right: 2px;
112 | top: 0;
113 | opacity: 0;
114 | transition: opacity 200ms ease-in-out;
115 | cursor: pointer;
116 | }
117 | }
118 |
119 | &:hover{
120 | .msg-options-icon{
121 | background: rgba(black, 0.4);
122 |
123 | .MuiSvgIcon-root{
124 | opacity: 1;
125 | }
126 | }
127 | }
128 |
129 | .msg-options{
130 | position: absolute;
131 | background: var(--secondary-bg);
132 | box-shadow: 1px 1px 5px var(--other-msg-bg);
133 | padding: 10px 0;
134 | color: black;
135 | left: 100%;
136 | z-index: 999;
137 | display: flex;
138 | flex-direction: column;
139 | border-radius: 7px;
140 | transition: transform 200ms ease-in-out;
141 | transform: scale(0);
142 | transform-origin: top left;
143 |
144 | &.active{
145 | transform: scale(1);
146 | }
147 |
148 | .option{
149 | border-bottom: 1px solid var(--other-msg-bg);
150 | padding: 10px 30px;
151 | cursor: pointer;
152 |
153 | &:hover{
154 | opacity: 0.7;
155 | }
156 |
157 | &:last-child{
158 | border: none;
159 | }
160 | }
161 | }
162 | }
163 |
164 | .chats{
165 | padding: 40px 10px 10px 10px;
166 | position: relative;
167 | // border: 3px solid yellow;
168 | scroll-behavior: smooth;
169 | overflow-y: auto;
170 | overflow-x: hidden;
171 |
172 | .toggle-sidebar{
173 | position: fixed;
174 | display: none;
175 | // display: grid;
176 | z-index: 99;
177 | top: 0;
178 | place-items: center;
179 | width: 30px;
180 | height: 30px;
181 | opacity: 0.8;
182 | background-color: var(--other-msg-bg);
183 | transform: translate(-10px, 60px);
184 | cursor: pointer;
185 | transition: transform 200ms ease-in-out;
186 |
187 | .MuiSvgIcon-root{
188 | fill: white;
189 | width: 30px;
190 | height: 30px;
191 | }
192 |
193 | &.move-left{
194 | transform: translate(260px, 60px);
195 | }
196 | }
197 | }
198 |
199 | .select-chat{
200 | font-size: 20px;
201 | font-family: monospace;
202 | position: absolute;
203 | top: 50%;
204 | left: 50%;
205 | width: 300px;
206 | height: 300px;
207 | transform: translate(-50%, -50%);
208 | background-color: var(--bg-color);
209 | border-radius: 50%;
210 | padding: 50px;
211 | text-align: center;
212 | display: grid;
213 | place-items: center;
214 | color: white;
215 | }
216 |
217 | .date-label{
218 | font-size: 12px;
219 | font-family: monospace;
220 | background-color: var(--secondary-bg);
221 | padding: 5px 0;
222 | margin: 5px 0 10px 0;
223 | }
--------------------------------------------------------------------------------
/client/src/components/UsersList/UsersList.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react';
2 | import { useUsers } from '../../contexts/UsersProvider'
3 | import User from '../User/User.jsx'
4 | import { useActiveConversation } from '../../contexts/ActiveConversationProvider'
5 | import { useCurrentUser } from '../../contexts/CurrentUserProvider'
6 | import { useSocket } from '../../contexts/SocketProvider'
7 | import { chatAPI } from '../../Apis/chatApi'
8 | import { toggleSidebar } from '../UserChats/UserChats'
9 | import { useNewMessagesMap } from '../../contexts/NewMsgsMapProvider'
10 | import './UsersList.min.css'
11 |
12 | const UsersList = () => {
13 | const [users, setUsers] = useUsers()
14 | const [currentUser] = useCurrentUser()
15 | const [activeConversationID, setActiveConversationID] = useActiveConversation()
16 | const [onlineStatus, setOnlineStatus] = useState({})
17 | const [newMsgsMap, setNewMsgMap] = useNewMessagesMap()
18 | const socket = useSocket()
19 |
20 | // console.log({ users })
21 | // console.log("rendering USERS_LIST")
22 |
23 | const removeThisUserFromUsersList = (usersList, thisUser) => {
24 | let newUsersList = usersList.filter(user => user.id !== thisUser.id)
25 | setUsers(newUsersList)
26 | }
27 |
28 | const getUsers = async () => {
29 | try {
30 | let response = await chatAPI.get('/users')
31 | let usersList = response.data
32 | removeThisUserFromUsersList(usersList, currentUser)
33 | } catch (err) {
34 | console.log("Errot while getting users..", err)
35 | }
36 | }
37 |
38 | useEffect(() => {
39 | getUsers()
40 | }, [currentUser])
41 |
42 | useEffect(() => {
43 | // Setup SocketIO events
44 | if (socket) {
45 | socket.on('online-statuses', (onlineStatusesString) => {
46 | let onlineStatuses = JSON.parse(onlineStatusesString)
47 | setOnlineStatus(onlineStatuses)
48 | console.log(`[online-statuses]: ${onlineStatuses}`)
49 | })
50 | }
51 |
52 | }, [socket])
53 |
54 | const handleClick = (e) => {
55 | console.log("Convo has been clicked", e.target)
56 | let userID = e.target.dataset.userId
57 | userID = userID
58 | console.log({ userID })
59 | setActiveConversationID(userID)
60 | console.log({ activeConversationID })
61 | setNewMsgMap({ ...newMsgsMap, [userID]: 0 })
62 | toggleSidebar() // Actually, we should execute this statement in the mobile view.
63 | }
64 |
65 | return (
66 |
67 | {
68 | (users.length === 0) ? (
69 |
70 | ) : (
71 | users.map(user => {
72 | return
80 | })
81 | )
82 | }
83 |
84 | );
85 | }
86 |
87 | const LoadingUsers = () => {
88 | return (
89 |
90 | )
91 | }
92 |
93 | export default UsersList;
94 |
--------------------------------------------------------------------------------
/client/src/components/UsersList/UsersList.min.css:
--------------------------------------------------------------------------------
1 | .users-list{overflow-y:auto;position:relative;height:100%}.users-list .loading-users{position:absolute;width:30px;height:30px;top:50%;left:45%;-webkit-transform:translateY(-50%);transform:translateY(-50%);border-bottom:none;border-top:none;border-radius:50%;-webkit-animation:loading 800ms cubic-bezier(0.69, -0.66, 0, 1.64) infinite alternate;animation:loading 800ms cubic-bezier(0.69, -0.66, 0, 1.64) infinite alternate}@-webkit-keyframes loading{from{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(720deg);transform:rotate(720deg)}}@keyframes loading{from{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(720deg);transform:rotate(720deg)}}
2 |
--------------------------------------------------------------------------------
/client/src/components/UsersList/UsersList.scss:
--------------------------------------------------------------------------------
1 | .users-list{
2 | overflow-y: auto;
3 | position: relative;
4 | // border: 4px solid yellow;
5 | height: 100%;
6 |
7 | .loading-users{
8 | position: absolute;
9 | width: 30px;
10 | height: 30px;
11 | top: 50%;
12 | left: 45%;
13 | transform: translateY(-50%);
14 | // background-color:red;
15 | // border: 2px solid white;
16 | border-bottom: none;
17 | border-top: none;
18 | border-radius: 50%;
19 | animation: loading 800ms cubic-bezier(.69,-0.66,0,1.64) infinite alternate;
20 | }
21 | }
22 |
23 | @keyframes loading {
24 | from {
25 | transform: rotate(0deg);
26 | }
27 | to{
28 | transform: rotate(720deg);
29 | }
30 | }
--------------------------------------------------------------------------------
/client/src/contexts/ActiveConversationProvider.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useContext, createContext } from 'react';
2 |
3 | const ActiveConversationContext = createContext()
4 |
5 | export const useActiveConversation = () => {
6 | return useContext(ActiveConversationContext)
7 | }
8 |
9 | const ActiveConversationProvider = ({ children }) => {
10 | const [activeConversationID, setActiveConversationID] = useState(null)
11 |
12 | return (
13 |
14 | {children}
15 |
16 | );
17 | }
18 |
19 | export default ActiveConversationProvider;
20 |
--------------------------------------------------------------------------------
/client/src/contexts/ActiveMessageOptionsProvider.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useContext, createContext } from 'react';
2 |
3 | const ActiveMessageOptionsContext = createContext()
4 |
5 | export const useActiveMessageOptionsID = () => {
6 | return useContext(ActiveMessageOptionsContext)
7 | }
8 |
9 | const ActiveMessageOptionsProvider = ({ children }) => {
10 | const [msgIDWithActiveOptions, setMsgIDWithActiveOptions] = useState(null)
11 |
12 | return (
13 |
14 | {children}
15 |
16 | );
17 | }
18 |
19 | export default ActiveMessageOptionsProvider;
20 |
--------------------------------------------------------------------------------
/client/src/contexts/ChatDBProvider.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useContext, createContext } from 'react';
2 | // import { useSessionStorage } from '../hooks/useSessionStorage'
3 |
4 | const ChatDBContext = createContext()
5 |
6 | export const useChatDB = () => {
7 | return useContext(ChatDBContext)
8 | }
9 |
10 | const ChatDBProvider = ({ children }) => {
11 | const [chatDB, setChatDB] = useState([])
12 |
13 | return (
14 |
15 | {children}
16 |
17 | );
18 | }
19 |
20 | export default ChatDBProvider;
21 |
22 |
23 | // const messagesDB = [
24 | // {
25 | // id: 1,
26 | // senderID: '2',
27 | // receiverID: '1',
28 | // msgBody: "ABCD",
29 | // time: '12:00PM',
30 | // },
31 | // {
32 | // id: 2,
33 | // senderID: '2',
34 | // receiverID: '3',
35 | // msgBody: "EFGH",
36 | // time: '12:00PM',
37 | // },
38 | // {
39 | // id: 3,
40 | // senderID: '1',
41 | // receiverID: '2',
42 | // msgBody: "IJKL",
43 | // time: '12:00PM',
44 | // },
45 | // {
46 | // id: 4,
47 | // senderID: '4',
48 | // receiverID: '1',
49 | // msgBody: "MNOP",
50 | // time: '12:00PM',
51 | // },
52 | // {
53 | // id: 1,
54 | // senderID: '2',
55 | // receiverID: '4',
56 | // msgBody: "QRST",
57 | // time: '12:00PM',
58 | // },
59 | // {
60 | // id: 5,
61 | // senderID: '2',
62 | // receiverID: '3',
63 | // msgBody: "UVW",
64 | // time: '12:00PM',
65 | // },
66 | // {
67 | // id: 6,
68 | // senderID: '1',
69 | // receiverID: '4',
70 | // msgBody: "XYZ",
71 | // time: '12:00PM',
72 | // },
73 | // {
74 | // id: 7,
75 | // senderID: '5',
76 | // receiverID: '1',
77 | // msgBody: "Hell",
78 | // time: '12:00PM',
79 | // },
80 | // ]
--------------------------------------------------------------------------------
/client/src/contexts/CurrentUserProvider.jsx:
--------------------------------------------------------------------------------
1 | import React, { createContext, useContext } from 'react';
2 | import { useSessionStorage } from '../hooks/useSessionStorage'
3 |
4 | const currentUserContext = createContext()
5 |
6 | export const useCurrentUser = () => {
7 | return useContext(currentUserContext)
8 | }
9 |
10 | const CurrentUserProvider = ({ children }) => {
11 | const [currentUser, setCurrentUser] = useSessionStorage('currentUser', null)
12 |
13 | return (
14 |
15 | {children}
16 |
17 | );
18 | }
19 |
20 | export default CurrentUserProvider;
21 |
--------------------------------------------------------------------------------
/client/src/contexts/NewMsgsMapProvider.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useContext, createContext } from 'react';
2 |
3 | const NewMsgsMapContext = createContext()
4 |
5 | export const useNewMessagesMap = () => {
6 | return useContext(NewMsgsMapContext)
7 | }
8 |
9 | const NewMsgsMapProvider = ({ children }) => {
10 | const [newMsgsMap, setNewMsgMap] = useState({})
11 |
12 | return (
13 |
14 | {children}
15 |
16 | );
17 | }
18 |
19 | export default NewMsgsMapProvider;
20 |
--------------------------------------------------------------------------------
/client/src/contexts/SelectedMessagesProvider.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useContext, createContext } from 'react'
2 |
3 | const SelectedMessagesContext = createContext()
4 |
5 | export const useSelectedMessages = () => {
6 | return useContext(SelectedMessagesContext)
7 | }
8 |
9 | const SelectedMessagesProvider = ({ children }) => {
10 | const [selectedMessages, setSelectedMessages] = useState(new Set())
11 |
12 | return (
13 |
14 | {children}
15 |
16 | );
17 | }
18 |
19 | export default SelectedMessagesProvider;
--------------------------------------------------------------------------------
/client/src/contexts/SelectionToolBarStatusProvider.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useContext, createContext } from 'react';
2 |
3 | const SelectionToolBarStatusContext = createContext()
4 |
5 | export const useSelectionToolBarStatus = () => {
6 | return useContext(SelectionToolBarStatusContext)
7 | }
8 |
9 | const SelectionToolBarStatusProvider = ({ children }) => {
10 | const [isToolbarActive, setToolbarActive] = useState(false)
11 |
12 | return (
13 |
14 | {children}
15 |
16 | );
17 | }
18 |
19 | export default SelectionToolBarStatusProvider;
20 |
--------------------------------------------------------------------------------
/client/src/contexts/SidebarRefProvider.jsx:
--------------------------------------------------------------------------------
1 | // import React, { useContext, createContext, useRef } from 'react';
2 |
3 | // const SidebarRefContext = createContext()
4 |
5 | // export const useSidebarRef = () => {
6 | // return useContext(SidebarRefContext)
7 | // }
8 |
9 | // const SidebarRefProvider = ({ children }) => {
10 | // // const sidebarRef = useRef()
11 | // const nice = 1
12 |
13 | // return (
14 | //
15 | // {children}
16 | //
17 | // );
18 | // }
19 |
20 | // export default SidebarRefProvider;
21 |
--------------------------------------------------------------------------------
/client/src/contexts/SidebarStatusProvider.jsx:
--------------------------------------------------------------------------------
1 | import React, { createContext, useContext, useState } from 'react';
2 |
3 | const SidebarStatusContext = createContext()
4 |
5 | export const useSidebarStatus = () => {
6 | return useContext(SidebarStatusContext)
7 | }
8 |
9 | const SidebarStatusProvider = ({ children }) => {
10 | const [sidebarActive, setSidebarActive] = useState(false)
11 |
12 | return (
13 |
14 | {children}
15 |
16 | );
17 | }
18 |
19 | export default SidebarStatusProvider;
20 |
--------------------------------------------------------------------------------
/client/src/contexts/SocketProvider.jsx:
--------------------------------------------------------------------------------
1 | import React, { useContext, createContext, useState, useEffect } from 'react';
2 | import socketIOClient from 'socket.io-client'
3 |
4 | const SocketContext = createContext()
5 |
6 | export const useSocket = () => {
7 | return useContext(SocketContext)
8 | }
9 |
10 | // const SOCKET_IO_SERVER = "http://localhost:3000"
11 | const SOCKET_IO_SERVER = "https://yml-chat-app.herokuapp.com/"
12 |
13 | const SocketProvider = ({ children }) => {
14 | const [socket, setSocket] = useState(undefined)
15 |
16 | useEffect(() => {
17 | setSocket(socketIOClient(SOCKET_IO_SERVER, {
18 | path: '/chat-app-socket.io'
19 | }))
20 | }, [])
21 |
22 |
23 | return (
24 |
25 | {children}
26 |
27 | );
28 | }
29 |
30 | export default SocketProvider;
31 |
--------------------------------------------------------------------------------
/client/src/contexts/UserChatsProvider.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useContext, createContext } from 'react';
2 |
3 | const UserChatsContext = createContext()
4 |
5 | export const useUserChats = () => {
6 | return useContext(UserChatsContext)
7 | }
8 |
9 | const UserChatsProvider = ({ children }) => {
10 | const [userChats, setUserChats] = useState([])
11 |
12 | return (
13 |
14 | {children}
15 |
16 | );
17 | }
18 |
19 | export default UserChatsProvider;
20 |
--------------------------------------------------------------------------------
/client/src/contexts/UsersProvider.jsx:
--------------------------------------------------------------------------------
1 | import React, { createContext, useContext, useState } from 'react';
2 | // import { useLocalStorage } from '../hooks/useLocalStorage'
3 |
4 | const usersContext = createContext()
5 |
6 | export const useUsers = () => {
7 | return useContext(usersContext)
8 | }
9 |
10 | const UsersProvider = ({ children }) => {
11 | const [users, setUsers] = useState([])
12 |
13 | return (
14 |
15 | {children}
16 |
17 | );
18 | }
19 |
20 | export default UsersProvider;
21 |
22 |
23 | // const usersDB = [
24 | // { id: '1', name: 'Shine' },
25 | // { id: '2', name: 'Buncy' },
26 | // { id: '3', name: 'Hanu' },
27 | // { id: '4', name: 'Duddu' },
28 | // { id: '5', name: 'Oju' },
29 | // { id: '6', name: 'Bajji' },
30 | // { id: '7', name: 'Frienduu' },
31 | // ]
--------------------------------------------------------------------------------
/client/src/contexts/ViewProvider.jsx:
--------------------------------------------------------------------------------
1 | import React, { createContext, useContext, useState } from 'react';
2 |
3 | const viewContext = createContext()
4 |
5 | export const useView = () => {
6 | return useContext(viewContext)
7 | }
8 |
9 | export const WEB_VIEW = "web-view"
10 | export const MOBILE_VIEW = "mobile-view"
11 |
12 | const ViewProvider = ({ children }) => {
13 | let currentView
14 | if (window.innerWidth < 750) {
15 | currentView = MOBILE_VIEW
16 | } else {
17 | currentView = WEB_VIEW
18 | }
19 |
20 | const [view, setView] = useState(currentView)
21 |
22 | window.addEventListener('resize', () => {
23 | if (window.innerWidth < 750) {
24 | setView(MOBILE_VIEW)
25 | } else {
26 | setView(WEB_VIEW)
27 | }
28 | })
29 |
30 | return (
31 |
32 | {children}
33 |
34 | );
35 | }
36 |
37 | export default ViewProvider;
--------------------------------------------------------------------------------
/client/src/hooks/useLocalStorage.js:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react'
2 |
3 | const PREFIX = 'YML_chat-app-'
4 |
5 | export function useLocalStorage(key, initialValue) {
6 | const prefixedKey = PREFIX + key
7 | const [value, setValue] = useState(() => {
8 | const jsonValue = localStorage.getItem(prefixedKey)
9 | if (jsonValue != null) return JSON.parse(jsonValue)
10 | if (typeof initialValue === 'function') {
11 | return initialValue()
12 | } else {
13 | return initialValue
14 | }
15 | })
16 |
17 | useEffect(() => {
18 | localStorage.setItem(prefixedKey, JSON.stringify(value))
19 | }, [prefixedKey, value])
20 |
21 | return [value, setValue]
22 | }
--------------------------------------------------------------------------------
/client/src/hooks/useSessionStorage.js:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react'
2 |
3 | const PREFIX = 'YML_chat-app-'
4 |
5 | export function useSessionStorage(key, initialValue) {
6 | const prefixedKey = PREFIX + key
7 | const [value, setValue] = useState(() => {
8 | const jsonValue = sessionStorage.getItem(prefixedKey)
9 | if (jsonValue != null) return JSON.parse(jsonValue)
10 | if (typeof initialValue === 'function') {
11 | return initialValue()
12 | } else {
13 | return initialValue
14 | }
15 | })
16 |
17 | useEffect(() => {
18 | sessionStorage.setItem(prefixedKey, JSON.stringify(value))
19 | }, [prefixedKey, value])
20 |
21 | return [value, setValue]
22 | }
--------------------------------------------------------------------------------
/client/src/index.css:
--------------------------------------------------------------------------------
1 | *, *::before, *::after{
2 | margin: 0;
3 | padding: 0;
4 | box-sizing: border-box;
5 | }
6 |
7 | body {
8 | margin: 0;
9 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
10 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
11 | sans-serif;
12 | -webkit-font-smoothing: antialiased;
13 | -moz-osx-font-smoothing: grayscale;
14 | overflow-x: hidden;
15 | /* background-color: #40c57c; */
16 | }
17 |
18 | code {
19 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
20 | monospace;
21 | }
22 |
--------------------------------------------------------------------------------
/client/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 |
6 | ReactDOM.render(
7 |
8 |
9 | ,
10 | document.getElementById('root')
11 | );
12 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "chat-app",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "ansi-regex": {
8 | "version": "4.1.0",
9 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
10 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
11 | "dev": true
12 | },
13 | "ansi-styles": {
14 | "version": "3.2.1",
15 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
16 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
17 | "dev": true,
18 | "requires": {
19 | "color-convert": "^1.9.0"
20 | }
21 | },
22 | "camelcase": {
23 | "version": "5.3.1",
24 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
25 | "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
26 | "dev": true
27 | },
28 | "chalk": {
29 | "version": "2.4.2",
30 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
31 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
32 | "dev": true,
33 | "requires": {
34 | "ansi-styles": "^3.2.1",
35 | "escape-string-regexp": "^1.0.5",
36 | "supports-color": "^5.3.0"
37 | },
38 | "dependencies": {
39 | "supports-color": {
40 | "version": "5.5.0",
41 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
42 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
43 | "dev": true,
44 | "requires": {
45 | "has-flag": "^3.0.0"
46 | }
47 | }
48 | }
49 | },
50 | "cliui": {
51 | "version": "5.0.0",
52 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
53 | "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
54 | "dev": true,
55 | "requires": {
56 | "string-width": "^3.1.0",
57 | "strip-ansi": "^5.2.0",
58 | "wrap-ansi": "^5.1.0"
59 | }
60 | },
61 | "color-convert": {
62 | "version": "1.9.3",
63 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
64 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
65 | "dev": true,
66 | "requires": {
67 | "color-name": "1.1.3"
68 | }
69 | },
70 | "color-name": {
71 | "version": "1.1.3",
72 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
73 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
74 | "dev": true
75 | },
76 | "concurrently": {
77 | "version": "5.3.0",
78 | "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-5.3.0.tgz",
79 | "integrity": "sha512-8MhqOB6PWlBfA2vJ8a0bSFKATOdWlHiQlk11IfmQBPaHVP8oP2gsh2MObE6UR3hqDHqvaIvLTyceNW6obVuFHQ==",
80 | "dev": true,
81 | "requires": {
82 | "chalk": "^2.4.2",
83 | "date-fns": "^2.0.1",
84 | "lodash": "^4.17.15",
85 | "read-pkg": "^4.0.1",
86 | "rxjs": "^6.5.2",
87 | "spawn-command": "^0.0.2-1",
88 | "supports-color": "^6.1.0",
89 | "tree-kill": "^1.2.2",
90 | "yargs": "^13.3.0"
91 | }
92 | },
93 | "date-fns": {
94 | "version": "2.16.1",
95 | "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.16.1.tgz",
96 | "integrity": "sha512-sAJVKx/FqrLYHAQeN7VpJrPhagZc9R4ImZIWYRFZaaohR3KzmuK88touwsSwSVT8Qcbd4zoDsnGfX4GFB4imyQ==",
97 | "dev": true
98 | },
99 | "decamelize": {
100 | "version": "1.2.0",
101 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
102 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
103 | "dev": true
104 | },
105 | "emoji-regex": {
106 | "version": "7.0.3",
107 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
108 | "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
109 | "dev": true
110 | },
111 | "error-ex": {
112 | "version": "1.3.2",
113 | "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
114 | "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
115 | "dev": true,
116 | "requires": {
117 | "is-arrayish": "^0.2.1"
118 | }
119 | },
120 | "escape-string-regexp": {
121 | "version": "1.0.5",
122 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
123 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
124 | "dev": true
125 | },
126 | "find-up": {
127 | "version": "3.0.0",
128 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
129 | "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
130 | "dev": true,
131 | "requires": {
132 | "locate-path": "^3.0.0"
133 | }
134 | },
135 | "function-bind": {
136 | "version": "1.1.1",
137 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
138 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
139 | "dev": true
140 | },
141 | "get-caller-file": {
142 | "version": "2.0.5",
143 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
144 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
145 | "dev": true
146 | },
147 | "has": {
148 | "version": "1.0.3",
149 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
150 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
151 | "dev": true,
152 | "requires": {
153 | "function-bind": "^1.1.1"
154 | }
155 | },
156 | "has-flag": {
157 | "version": "3.0.0",
158 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
159 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
160 | "dev": true
161 | },
162 | "hosted-git-info": {
163 | "version": "2.8.8",
164 | "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
165 | "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==",
166 | "dev": true
167 | },
168 | "is-arrayish": {
169 | "version": "0.2.1",
170 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
171 | "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
172 | "dev": true
173 | },
174 | "is-core-module": {
175 | "version": "2.0.0",
176 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.0.0.tgz",
177 | "integrity": "sha512-jq1AH6C8MuteOoBPwkxHafmByhL9j5q4OaPGdbuD+ZtQJVzH+i6E3BJDQcBA09k57i2Hh2yQbEG8yObZ0jdlWw==",
178 | "dev": true,
179 | "requires": {
180 | "has": "^1.0.3"
181 | }
182 | },
183 | "is-fullwidth-code-point": {
184 | "version": "2.0.0",
185 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
186 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
187 | "dev": true
188 | },
189 | "json-parse-better-errors": {
190 | "version": "1.0.2",
191 | "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
192 | "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
193 | "dev": true
194 | },
195 | "locate-path": {
196 | "version": "3.0.0",
197 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
198 | "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
199 | "dev": true,
200 | "requires": {
201 | "p-locate": "^3.0.0",
202 | "path-exists": "^3.0.0"
203 | }
204 | },
205 | "lodash": {
206 | "version": "4.17.20",
207 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
208 | "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
209 | "dev": true
210 | },
211 | "normalize-package-data": {
212 | "version": "2.5.0",
213 | "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
214 | "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
215 | "dev": true,
216 | "requires": {
217 | "hosted-git-info": "^2.1.4",
218 | "resolve": "^1.10.0",
219 | "semver": "2 || 3 || 4 || 5",
220 | "validate-npm-package-license": "^3.0.1"
221 | }
222 | },
223 | "p-limit": {
224 | "version": "2.3.0",
225 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
226 | "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
227 | "dev": true,
228 | "requires": {
229 | "p-try": "^2.0.0"
230 | }
231 | },
232 | "p-locate": {
233 | "version": "3.0.0",
234 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
235 | "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
236 | "dev": true,
237 | "requires": {
238 | "p-limit": "^2.0.0"
239 | }
240 | },
241 | "p-try": {
242 | "version": "2.2.0",
243 | "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
244 | "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
245 | "dev": true
246 | },
247 | "parse-json": {
248 | "version": "4.0.0",
249 | "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
250 | "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
251 | "dev": true,
252 | "requires": {
253 | "error-ex": "^1.3.1",
254 | "json-parse-better-errors": "^1.0.1"
255 | }
256 | },
257 | "path-exists": {
258 | "version": "3.0.0",
259 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
260 | "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
261 | "dev": true
262 | },
263 | "path-parse": {
264 | "version": "1.0.6",
265 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
266 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
267 | "dev": true
268 | },
269 | "pify": {
270 | "version": "3.0.0",
271 | "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
272 | "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
273 | "dev": true
274 | },
275 | "read-pkg": {
276 | "version": "4.0.1",
277 | "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-4.0.1.tgz",
278 | "integrity": "sha1-ljYlN48+HE1IyFhytabsfV0JMjc=",
279 | "dev": true,
280 | "requires": {
281 | "normalize-package-data": "^2.3.2",
282 | "parse-json": "^4.0.0",
283 | "pify": "^3.0.0"
284 | }
285 | },
286 | "require-directory": {
287 | "version": "2.1.1",
288 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
289 | "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
290 | "dev": true
291 | },
292 | "require-main-filename": {
293 | "version": "2.0.0",
294 | "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
295 | "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
296 | "dev": true
297 | },
298 | "resolve": {
299 | "version": "1.18.1",
300 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.18.1.tgz",
301 | "integrity": "sha512-lDfCPaMKfOJXjy0dPayzPdF1phampNWr3qFCjAu+rw/qbQmr5jWH5xN2hwh9QKfw9E5v4hwV7A+jrCmL8yjjqA==",
302 | "dev": true,
303 | "requires": {
304 | "is-core-module": "^2.0.0",
305 | "path-parse": "^1.0.6"
306 | }
307 | },
308 | "rxjs": {
309 | "version": "6.6.3",
310 | "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz",
311 | "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==",
312 | "dev": true,
313 | "requires": {
314 | "tslib": "^1.9.0"
315 | }
316 | },
317 | "semver": {
318 | "version": "5.7.1",
319 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
320 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
321 | "dev": true
322 | },
323 | "set-blocking": {
324 | "version": "2.0.0",
325 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
326 | "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
327 | "dev": true
328 | },
329 | "spawn-command": {
330 | "version": "0.0.2-1",
331 | "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz",
332 | "integrity": "sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A=",
333 | "dev": true
334 | },
335 | "spdx-correct": {
336 | "version": "3.1.1",
337 | "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
338 | "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
339 | "dev": true,
340 | "requires": {
341 | "spdx-expression-parse": "^3.0.0",
342 | "spdx-license-ids": "^3.0.0"
343 | }
344 | },
345 | "spdx-exceptions": {
346 | "version": "2.3.0",
347 | "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
348 | "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
349 | "dev": true
350 | },
351 | "spdx-expression-parse": {
352 | "version": "3.0.1",
353 | "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
354 | "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
355 | "dev": true,
356 | "requires": {
357 | "spdx-exceptions": "^2.1.0",
358 | "spdx-license-ids": "^3.0.0"
359 | }
360 | },
361 | "spdx-license-ids": {
362 | "version": "3.0.6",
363 | "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz",
364 | "integrity": "sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw==",
365 | "dev": true
366 | },
367 | "string-width": {
368 | "version": "3.1.0",
369 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
370 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
371 | "dev": true,
372 | "requires": {
373 | "emoji-regex": "^7.0.1",
374 | "is-fullwidth-code-point": "^2.0.0",
375 | "strip-ansi": "^5.1.0"
376 | }
377 | },
378 | "strip-ansi": {
379 | "version": "5.2.0",
380 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
381 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
382 | "dev": true,
383 | "requires": {
384 | "ansi-regex": "^4.1.0"
385 | }
386 | },
387 | "supports-color": {
388 | "version": "6.1.0",
389 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
390 | "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
391 | "dev": true,
392 | "requires": {
393 | "has-flag": "^3.0.0"
394 | }
395 | },
396 | "tree-kill": {
397 | "version": "1.2.2",
398 | "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
399 | "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
400 | "dev": true
401 | },
402 | "tslib": {
403 | "version": "1.14.1",
404 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
405 | "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
406 | "dev": true
407 | },
408 | "validate-npm-package-license": {
409 | "version": "3.0.4",
410 | "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
411 | "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
412 | "dev": true,
413 | "requires": {
414 | "spdx-correct": "^3.0.0",
415 | "spdx-expression-parse": "^3.0.0"
416 | }
417 | },
418 | "which-module": {
419 | "version": "2.0.0",
420 | "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
421 | "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
422 | "dev": true
423 | },
424 | "wrap-ansi": {
425 | "version": "5.1.0",
426 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
427 | "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
428 | "dev": true,
429 | "requires": {
430 | "ansi-styles": "^3.2.0",
431 | "string-width": "^3.0.0",
432 | "strip-ansi": "^5.0.0"
433 | }
434 | },
435 | "y18n": {
436 | "version": "4.0.0",
437 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
438 | "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
439 | "dev": true
440 | },
441 | "yargs": {
442 | "version": "13.3.2",
443 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
444 | "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
445 | "dev": true,
446 | "requires": {
447 | "cliui": "^5.0.0",
448 | "find-up": "^3.0.0",
449 | "get-caller-file": "^2.0.1",
450 | "require-directory": "^2.1.1",
451 | "require-main-filename": "^2.0.0",
452 | "set-blocking": "^2.0.0",
453 | "string-width": "^3.0.0",
454 | "which-module": "^2.0.0",
455 | "y18n": "^4.0.0",
456 | "yargs-parser": "^13.1.2"
457 | }
458 | },
459 | "yargs-parser": {
460 | "version": "13.1.2",
461 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
462 | "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
463 | "dev": true,
464 | "requires": {
465 | "camelcase": "^5.0.0",
466 | "decamelize": "^1.2.0"
467 | }
468 | }
469 | }
470 | }
471 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "chat-app",
3 | "version": "1.0.0",
4 | "description": "Chat application for 'theyoungminds.org'",
5 | "main": "index.js",
6 | "scripts": {
7 | "client": "cd client && npm start",
8 | "server": "cd server && npm run devStart",
9 | "test": "echo \"Error: no test specified\" && exit 1",
10 | "build-client": "cd client && npm install && npm run build",
11 | "build-server": "cd server && npm install",
12 | "start": "cd server && npm start",
13 | "chat-app": "concurrently --names \"CLIENT,SERVER\" -c \"blue.bold,green.bold\" \"npm run client\" \"npm run server\"",
14 | "heroku-postbuild": "npm run build-client; npm run build-server"
15 | },
16 | "repository": {
17 | "type": "git",
18 | "url": "git+https://github.com/Subhash3/chat-app.git"
19 | },
20 | "author": "Subhash Sarangi",
21 | "license": "ISC",
22 | "bugs": {
23 | "url": "https://github.com/Subhash3/chat-app/issues"
24 | },
25 | "homepage": "https://github.com/Subhash3/chat-app#readme",
26 | "devDependencies": {
27 | "concurrently": "^5.3.0"
28 | }
29 | }
--------------------------------------------------------------------------------
/server/Models/chats.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose')
2 | const Schema = mongoose.Schema
3 |
4 | const chatsSchema = new Schema({
5 | // _id: mongoose.Types.ObjectId,
6 | id: String,
7 | senderID: String,
8 | receiverID: String,
9 | msgBody: String,
10 | time: String,
11 | date: String,
12 | status: String,
13 | })
14 |
15 | const Chat = mongoose.model('chats', chatsSchema)
16 |
17 | module.exports = {
18 | ChatsModel: Chat
19 | }
--------------------------------------------------------------------------------
/server/Models/users.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose')
2 | const Schema = mongoose.Schema
3 |
4 | const leanersSchema = new Schema({
5 | _id: mongoose.Types.ObjectId,
6 | "profileOrdering": Object,
7 | "Music": Object,
8 | "Sports": Object,
9 | "Arts": Object,
10 | "Others": Object,
11 | "interests": Object,
12 | "email": String,
13 | "password": String,
14 | "educations": Object,
15 | "skills": Object,
16 | "accomplishments": Object,
17 | "workExperiences": Object,
18 | "internshipExperiences": Object,
19 | "volunteerExperiences": Object,
20 | "__v": Number,
21 | "country": String,
22 | "date": Object,
23 | "faculty": Object,
24 | "gender": String,
25 | "instituteName": Object,
26 | "name": String,
27 | "phone": String,
28 | "pincode": Number,
29 | "state": String,
30 | "bio": String,
31 | "summury": String,
32 | "imageUrl": String
33 |
34 | })
35 |
36 | const Learner = mongoose.model('learners', leanersSchema)
37 |
38 | module.exports = {
39 | LearnersModel: Learner
40 | }
--------------------------------------------------------------------------------
/server/Routes/chat_router.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const { LearnersModel } = require('../Models/users')
3 | const { ChatsModel } = require('../Models/chats')
4 |
5 | const router = express.Router()
6 | const welcomeMessage = "Welcome to Shine's chat app!"
7 | const mongoError = "Couldn't fetch database!"
8 |
9 | const getListOfUsers = async () => {
10 | // let usersList = await LearnersModel.find({}, {
11 | // email: true,
12 | // name: true
13 | // })
14 | let usersList = await LearnersModel.aggregate([
15 | {
16 | "$project": {
17 | "email": true,
18 | "name": true,
19 | "id": "$email"
20 | }
21 | }
22 | ])
23 | return usersList
24 | }
25 |
26 | const getChats = async (userID) => {
27 | let chats = await ChatsModel.find({
28 | $or: [
29 | { "senderID": userID },
30 | { "receiverID": userID }
31 | ]
32 | }, {
33 | _id: false
34 | })
35 | return chats
36 | }
37 |
38 | const deleteMessages = (messageIDs) => {
39 | let mongoPromise = ChatsModel.deleteMany({
40 | 'id': {
41 | '$in': [...messageIDs]
42 | }
43 | })
44 | // db.chats.deleteMany({id: {$in: ["95863fc1-10ca-4755-98b1-68497736fda8", "c30c3467-370a-4108-a3e6-e00466e22ad0"]}} )
45 |
46 | return mongoPromise
47 | }
48 |
49 |
50 | const deleteWholeConvo = (id1, id2) => {
51 | let mongoPromise = ChatsModel.deleteMany({
52 | '$or': [
53 | {
54 | '$and': [
55 | { "senderID": id1 },
56 | { "receiverID": id2 }
57 | ]
58 | },
59 | {
60 | '$and': [
61 | { "senderID": id2 },
62 | { "receiverID": id1 }
63 | ]
64 | }
65 | ]
66 | })
67 | return mongoPromise
68 | }
69 |
70 | router.get('/', (req, res) => {
71 | res.send({ "msg": welcomeMessage })
72 | })
73 |
74 | router.get('/users', (req, res) => {
75 | getListOfUsers()
76 | .then(users => {
77 | res.send(users)
78 | })
79 | .catch(err => {
80 | res.send(mongoError + err)
81 | })
82 | })
83 |
84 | router.get('/chats', (req, res) => {
85 | res.status(403).send("Access denied!")
86 | })
87 |
88 | router.get('/chats/:userID', (req, res) => {
89 | let userID = req.params.userID
90 | console.log(`Hit! (/chat-app/chats/${userID})`)
91 | getChats(userID)
92 | .then(chats => {
93 | res.send(chats)
94 | })
95 | .catch(err => {
96 | res.send(mongoError + err)
97 | })
98 | })
99 |
100 | router.post('/delete/', (req, res) => {
101 | let messageIDs = req.body.messageIDs
102 | console.log(messageIDs)
103 | // let senderID = req.body.senderID
104 | let receiverID = req.body.receiverID
105 |
106 | messageIDs.forEach(msgID => {
107 | console.log("Deleting message with ID: " + msgID)
108 | })
109 |
110 | let mongoPromise = deleteMessages(messageIDs)
111 | mongoPromise
112 | .then((data) => {
113 | console.log(data)
114 | res.send({
115 | message: `Deleted messages`,
116 | status: 1
117 | })
118 | })
119 | .catch(err => {
120 | console.log(err)
121 | res.send({
122 | message: `Failed to delete messages`,
123 | status: -1
124 | })
125 | })
126 |
127 | })
128 |
129 | router.post('/delete-convo', (req, res) => {
130 | let { id1, id2 } = req.body.IDs
131 | let mongoPromise = deleteWholeConvo(id1, id2)
132 | mongoPromise
133 | .then((data) => {
134 | console.log(data)
135 | res.send({
136 | message: `Deleted Conversation`,
137 | status: 1
138 | })
139 | })
140 | .catch(err => {
141 | console.log(err)
142 | res.send({
143 | message: `Failed to delete conversation`,
144 | status: -1
145 | })
146 | })
147 | })
148 |
149 | module.exports = {
150 | chatRouter: router
151 | }
--------------------------------------------------------------------------------
/server/Routes/users_router.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Subhash3/chat-app/c30ab81562aa83c384892340ab9f3519c7be432a/server/Routes/users_router.js
--------------------------------------------------------------------------------
/server/mongo_connection.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose')
2 |
3 | mongoose.Promise = global.Promise
4 |
5 | const connectToMongoDBAtlas = (URI, someMsg) => {
6 | console.log("[SERVER]: Connecting to MongoDB...: ", someMsg)
7 |
8 | let mongoConnection = mongoose.connect(URI, {
9 | useNewUrlParser: true,
10 | useUnifiedTopology: true,
11 | })
12 |
13 | mongoose.connection.once('open', () => {
14 | console.log("[SERVER]: Connected to MongoDB.")
15 | })
16 |
17 | mongoose.connection.on('error', (err) => {
18 | console.log("[SERVER]: MongoDB error!", err)
19 | })
20 |
21 | return mongoConnection
22 | }
23 |
24 | module.exports = {
25 | connectToMongoDBAtlas
26 | }
--------------------------------------------------------------------------------
/server/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "chat-app-server",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "accepts": {
8 | "version": "1.3.7",
9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
10 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
11 | "requires": {
12 | "mime-types": "~2.1.24",
13 | "negotiator": "0.6.2"
14 | }
15 | },
16 | "after": {
17 | "version": "0.8.2",
18 | "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
19 | "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8="
20 | },
21 | "array-flatten": {
22 | "version": "1.1.1",
23 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
24 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
25 | },
26 | "arraybuffer.slice": {
27 | "version": "0.0.7",
28 | "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz",
29 | "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog=="
30 | },
31 | "async-limiter": {
32 | "version": "1.0.1",
33 | "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
34 | "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
35 | },
36 | "backo2": {
37 | "version": "1.0.2",
38 | "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
39 | "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
40 | },
41 | "base64-arraybuffer": {
42 | "version": "0.1.4",
43 | "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz",
44 | "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI="
45 | },
46 | "base64id": {
47 | "version": "2.0.0",
48 | "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
49 | "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="
50 | },
51 | "better-assert": {
52 | "version": "1.0.2",
53 | "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz",
54 | "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=",
55 | "requires": {
56 | "callsite": "1.0.0"
57 | }
58 | },
59 | "bl": {
60 | "version": "2.2.1",
61 | "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz",
62 | "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==",
63 | "requires": {
64 | "readable-stream": "^2.3.5",
65 | "safe-buffer": "^5.1.1"
66 | }
67 | },
68 | "blob": {
69 | "version": "0.0.5",
70 | "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
71 | "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig=="
72 | },
73 | "bluebird": {
74 | "version": "3.5.1",
75 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz",
76 | "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA=="
77 | },
78 | "body-parser": {
79 | "version": "1.19.0",
80 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
81 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
82 | "requires": {
83 | "bytes": "3.1.0",
84 | "content-type": "~1.0.4",
85 | "debug": "2.6.9",
86 | "depd": "~1.1.2",
87 | "http-errors": "1.7.2",
88 | "iconv-lite": "0.4.24",
89 | "on-finished": "~2.3.0",
90 | "qs": "6.7.0",
91 | "raw-body": "2.4.0",
92 | "type-is": "~1.6.17"
93 | }
94 | },
95 | "bson": {
96 | "version": "1.1.5",
97 | "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.5.tgz",
98 | "integrity": "sha512-kDuEzldR21lHciPQAIulLs1LZlCXdLziXI6Mb/TDkwXhb//UORJNPXgcRs2CuO4H0DcMkpfT3/ySsP3unoZjBg=="
99 | },
100 | "bytes": {
101 | "version": "3.1.0",
102 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
103 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
104 | },
105 | "callsite": {
106 | "version": "1.0.0",
107 | "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
108 | "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA="
109 | },
110 | "component-bind": {
111 | "version": "1.0.0",
112 | "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
113 | "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E="
114 | },
115 | "component-emitter": {
116 | "version": "1.2.1",
117 | "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
118 | "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
119 | },
120 | "component-inherit": {
121 | "version": "0.0.3",
122 | "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
123 | "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM="
124 | },
125 | "content-disposition": {
126 | "version": "0.5.3",
127 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
128 | "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
129 | "requires": {
130 | "safe-buffer": "5.1.2"
131 | }
132 | },
133 | "content-type": {
134 | "version": "1.0.4",
135 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
136 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
137 | },
138 | "cookie": {
139 | "version": "0.4.0",
140 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
141 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
142 | },
143 | "cookie-signature": {
144 | "version": "1.0.6",
145 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
146 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
147 | },
148 | "core-util-is": {
149 | "version": "1.0.2",
150 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
151 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
152 | },
153 | "cors": {
154 | "version": "2.8.5",
155 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
156 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
157 | "requires": {
158 | "object-assign": "^4",
159 | "vary": "^1"
160 | }
161 | },
162 | "debug": {
163 | "version": "2.6.9",
164 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
165 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
166 | "requires": {
167 | "ms": "2.0.0"
168 | }
169 | },
170 | "denque": {
171 | "version": "1.4.1",
172 | "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz",
173 | "integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ=="
174 | },
175 | "depd": {
176 | "version": "1.1.2",
177 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
178 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
179 | },
180 | "destroy": {
181 | "version": "1.0.4",
182 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
183 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
184 | },
185 | "dotenv": {
186 | "version": "8.2.0",
187 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz",
188 | "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw=="
189 | },
190 | "ee-first": {
191 | "version": "1.1.1",
192 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
193 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
194 | },
195 | "encodeurl": {
196 | "version": "1.0.2",
197 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
198 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
199 | },
200 | "engine.io": {
201 | "version": "3.4.2",
202 | "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.4.2.tgz",
203 | "integrity": "sha512-b4Q85dFkGw+TqgytGPrGgACRUhsdKc9S9ErRAXpPGy/CXKs4tYoHDkvIRdsseAF7NjfVwjRFIn6KTnbw7LwJZg==",
204 | "requires": {
205 | "accepts": "~1.3.4",
206 | "base64id": "2.0.0",
207 | "cookie": "0.3.1",
208 | "debug": "~4.1.0",
209 | "engine.io-parser": "~2.2.0",
210 | "ws": "^7.1.2"
211 | },
212 | "dependencies": {
213 | "cookie": {
214 | "version": "0.3.1",
215 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
216 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
217 | },
218 | "debug": {
219 | "version": "4.1.1",
220 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
221 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
222 | "requires": {
223 | "ms": "^2.1.1"
224 | }
225 | },
226 | "ms": {
227 | "version": "2.1.2",
228 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
229 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
230 | }
231 | }
232 | },
233 | "engine.io-client": {
234 | "version": "3.4.4",
235 | "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.4.tgz",
236 | "integrity": "sha512-iU4CRr38Fecj8HoZEnFtm2EiKGbYZcPn3cHxqNGl/tmdWRf60KhK+9vE0JeSjgnlS/0oynEfLgKbT9ALpim0sQ==",
237 | "requires": {
238 | "component-emitter": "~1.3.0",
239 | "component-inherit": "0.0.3",
240 | "debug": "~3.1.0",
241 | "engine.io-parser": "~2.2.0",
242 | "has-cors": "1.1.0",
243 | "indexof": "0.0.1",
244 | "parseqs": "0.0.6",
245 | "parseuri": "0.0.6",
246 | "ws": "~6.1.0",
247 | "xmlhttprequest-ssl": "~1.5.4",
248 | "yeast": "0.1.2"
249 | },
250 | "dependencies": {
251 | "component-emitter": {
252 | "version": "1.3.0",
253 | "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
254 | "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
255 | },
256 | "debug": {
257 | "version": "3.1.0",
258 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
259 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
260 | "requires": {
261 | "ms": "2.0.0"
262 | }
263 | },
264 | "parseqs": {
265 | "version": "0.0.6",
266 | "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz",
267 | "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w=="
268 | },
269 | "parseuri": {
270 | "version": "0.0.6",
271 | "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz",
272 | "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow=="
273 | },
274 | "ws": {
275 | "version": "6.1.4",
276 | "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz",
277 | "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==",
278 | "requires": {
279 | "async-limiter": "~1.0.0"
280 | }
281 | }
282 | }
283 | },
284 | "engine.io-parser": {
285 | "version": "2.2.1",
286 | "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.1.tgz",
287 | "integrity": "sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg==",
288 | "requires": {
289 | "after": "0.8.2",
290 | "arraybuffer.slice": "~0.0.7",
291 | "base64-arraybuffer": "0.1.4",
292 | "blob": "0.0.5",
293 | "has-binary2": "~1.0.2"
294 | }
295 | },
296 | "escape-html": {
297 | "version": "1.0.3",
298 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
299 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
300 | },
301 | "etag": {
302 | "version": "1.8.1",
303 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
304 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
305 | },
306 | "express": {
307 | "version": "4.17.1",
308 | "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
309 | "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
310 | "requires": {
311 | "accepts": "~1.3.7",
312 | "array-flatten": "1.1.1",
313 | "body-parser": "1.19.0",
314 | "content-disposition": "0.5.3",
315 | "content-type": "~1.0.4",
316 | "cookie": "0.4.0",
317 | "cookie-signature": "1.0.6",
318 | "debug": "2.6.9",
319 | "depd": "~1.1.2",
320 | "encodeurl": "~1.0.2",
321 | "escape-html": "~1.0.3",
322 | "etag": "~1.8.1",
323 | "finalhandler": "~1.1.2",
324 | "fresh": "0.5.2",
325 | "merge-descriptors": "1.0.1",
326 | "methods": "~1.1.2",
327 | "on-finished": "~2.3.0",
328 | "parseurl": "~1.3.3",
329 | "path-to-regexp": "0.1.7",
330 | "proxy-addr": "~2.0.5",
331 | "qs": "6.7.0",
332 | "range-parser": "~1.2.1",
333 | "safe-buffer": "5.1.2",
334 | "send": "0.17.1",
335 | "serve-static": "1.14.1",
336 | "setprototypeof": "1.1.1",
337 | "statuses": "~1.5.0",
338 | "type-is": "~1.6.18",
339 | "utils-merge": "1.0.1",
340 | "vary": "~1.1.2"
341 | }
342 | },
343 | "finalhandler": {
344 | "version": "1.1.2",
345 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
346 | "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
347 | "requires": {
348 | "debug": "2.6.9",
349 | "encodeurl": "~1.0.2",
350 | "escape-html": "~1.0.3",
351 | "on-finished": "~2.3.0",
352 | "parseurl": "~1.3.3",
353 | "statuses": "~1.5.0",
354 | "unpipe": "~1.0.0"
355 | }
356 | },
357 | "forwarded": {
358 | "version": "0.1.2",
359 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
360 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
361 | },
362 | "fresh": {
363 | "version": "0.5.2",
364 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
365 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
366 | },
367 | "has-binary2": {
368 | "version": "1.0.3",
369 | "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz",
370 | "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==",
371 | "requires": {
372 | "isarray": "2.0.1"
373 | },
374 | "dependencies": {
375 | "isarray": {
376 | "version": "2.0.1",
377 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
378 | "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
379 | }
380 | }
381 | },
382 | "has-cors": {
383 | "version": "1.1.0",
384 | "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
385 | "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
386 | },
387 | "http-errors": {
388 | "version": "1.7.2",
389 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
390 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
391 | "requires": {
392 | "depd": "~1.1.2",
393 | "inherits": "2.0.3",
394 | "setprototypeof": "1.1.1",
395 | "statuses": ">= 1.5.0 < 2",
396 | "toidentifier": "1.0.0"
397 | }
398 | },
399 | "iconv-lite": {
400 | "version": "0.4.24",
401 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
402 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
403 | "requires": {
404 | "safer-buffer": ">= 2.1.2 < 3"
405 | }
406 | },
407 | "indexof": {
408 | "version": "0.0.1",
409 | "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
410 | "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
411 | },
412 | "inherits": {
413 | "version": "2.0.3",
414 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
415 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
416 | },
417 | "ipaddr.js": {
418 | "version": "1.9.1",
419 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
420 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
421 | },
422 | "isarray": {
423 | "version": "1.0.0",
424 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
425 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
426 | },
427 | "kareem": {
428 | "version": "2.3.1",
429 | "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.1.tgz",
430 | "integrity": "sha512-l3hLhffs9zqoDe8zjmb/mAN4B8VT3L56EUvKNqLFVs9YlFA+zx7ke1DO8STAdDyYNkeSo1nKmjuvQeI12So8Xw=="
431 | },
432 | "media-typer": {
433 | "version": "0.3.0",
434 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
435 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
436 | },
437 | "memory-pager": {
438 | "version": "1.5.0",
439 | "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
440 | "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==",
441 | "optional": true
442 | },
443 | "merge-descriptors": {
444 | "version": "1.0.1",
445 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
446 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
447 | },
448 | "methods": {
449 | "version": "1.1.2",
450 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
451 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
452 | },
453 | "mime": {
454 | "version": "1.6.0",
455 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
456 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
457 | },
458 | "mime-db": {
459 | "version": "1.44.0",
460 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
461 | "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg=="
462 | },
463 | "mime-types": {
464 | "version": "2.1.27",
465 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
466 | "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
467 | "requires": {
468 | "mime-db": "1.44.0"
469 | }
470 | },
471 | "mongodb": {
472 | "version": "3.6.2",
473 | "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.2.tgz",
474 | "integrity": "sha512-sSZOb04w3HcnrrXC82NEh/YGCmBuRgR+C1hZgmmv4L6dBz4BkRse6Y8/q/neXer9i95fKUBbFi4KgeceXmbsOA==",
475 | "requires": {
476 | "bl": "^2.2.1",
477 | "bson": "^1.1.4",
478 | "denque": "^1.4.1",
479 | "require_optional": "^1.0.1",
480 | "safe-buffer": "^5.1.2",
481 | "saslprep": "^1.0.0"
482 | }
483 | },
484 | "mongoose": {
485 | "version": "5.10.9",
486 | "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.10.9.tgz",
487 | "integrity": "sha512-7dkr1d6Uyk87hELzoc6B7Zo7kkPTx8rKummk51Y0je2V2Ttsw0KFPwTp1G8JIbBta7Wpw8j15PJi0d33Ode2nw==",
488 | "requires": {
489 | "bson": "^1.1.4",
490 | "kareem": "2.3.1",
491 | "mongodb": "3.6.2",
492 | "mongoose-legacy-pluralize": "1.0.2",
493 | "mpath": "0.7.0",
494 | "mquery": "3.2.2",
495 | "ms": "2.1.2",
496 | "regexp-clone": "1.0.0",
497 | "safe-buffer": "5.2.1",
498 | "sift": "7.0.1",
499 | "sliced": "1.0.1"
500 | },
501 | "dependencies": {
502 | "ms": {
503 | "version": "2.1.2",
504 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
505 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
506 | },
507 | "safe-buffer": {
508 | "version": "5.2.1",
509 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
510 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
511 | }
512 | }
513 | },
514 | "mongoose-legacy-pluralize": {
515 | "version": "1.0.2",
516 | "resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz",
517 | "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ=="
518 | },
519 | "mpath": {
520 | "version": "0.7.0",
521 | "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.7.0.tgz",
522 | "integrity": "sha512-Aiq04hILxhz1L+f7sjGyn7IxYzWm1zLNNXcfhDtx04kZ2Gk7uvFdgZ8ts1cWa/6d0TQmag2yR8zSGZUmp0tFNg=="
523 | },
524 | "mquery": {
525 | "version": "3.2.2",
526 | "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.2.2.tgz",
527 | "integrity": "sha512-XB52992COp0KP230I3qloVUbkLUxJIu328HBP2t2EsxSFtf4W1HPSOBWOXf1bqxK4Xbb66lfMJ+Bpfd9/yZE1Q==",
528 | "requires": {
529 | "bluebird": "3.5.1",
530 | "debug": "3.1.0",
531 | "regexp-clone": "^1.0.0",
532 | "safe-buffer": "5.1.2",
533 | "sliced": "1.0.1"
534 | },
535 | "dependencies": {
536 | "debug": {
537 | "version": "3.1.0",
538 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
539 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
540 | "requires": {
541 | "ms": "2.0.0"
542 | }
543 | }
544 | }
545 | },
546 | "ms": {
547 | "version": "2.0.0",
548 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
549 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
550 | },
551 | "negotiator": {
552 | "version": "0.6.2",
553 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
554 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
555 | },
556 | "object-assign": {
557 | "version": "4.1.1",
558 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
559 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
560 | },
561 | "object-component": {
562 | "version": "0.0.3",
563 | "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz",
564 | "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE="
565 | },
566 | "on-finished": {
567 | "version": "2.3.0",
568 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
569 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
570 | "requires": {
571 | "ee-first": "1.1.1"
572 | }
573 | },
574 | "parseqs": {
575 | "version": "0.0.5",
576 | "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz",
577 | "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=",
578 | "requires": {
579 | "better-assert": "~1.0.0"
580 | }
581 | },
582 | "parseuri": {
583 | "version": "0.0.5",
584 | "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz",
585 | "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=",
586 | "requires": {
587 | "better-assert": "~1.0.0"
588 | }
589 | },
590 | "parseurl": {
591 | "version": "1.3.3",
592 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
593 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
594 | },
595 | "path-to-regexp": {
596 | "version": "0.1.7",
597 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
598 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
599 | },
600 | "process-nextick-args": {
601 | "version": "2.0.1",
602 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
603 | "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
604 | },
605 | "proxy-addr": {
606 | "version": "2.0.6",
607 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
608 | "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
609 | "requires": {
610 | "forwarded": "~0.1.2",
611 | "ipaddr.js": "1.9.1"
612 | }
613 | },
614 | "qs": {
615 | "version": "6.7.0",
616 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
617 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
618 | },
619 | "range-parser": {
620 | "version": "1.2.1",
621 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
622 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
623 | },
624 | "raw-body": {
625 | "version": "2.4.0",
626 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
627 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
628 | "requires": {
629 | "bytes": "3.1.0",
630 | "http-errors": "1.7.2",
631 | "iconv-lite": "0.4.24",
632 | "unpipe": "1.0.0"
633 | }
634 | },
635 | "readable-stream": {
636 | "version": "2.3.7",
637 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
638 | "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
639 | "requires": {
640 | "core-util-is": "~1.0.0",
641 | "inherits": "~2.0.3",
642 | "isarray": "~1.0.0",
643 | "process-nextick-args": "~2.0.0",
644 | "safe-buffer": "~5.1.1",
645 | "string_decoder": "~1.1.1",
646 | "util-deprecate": "~1.0.1"
647 | }
648 | },
649 | "regexp-clone": {
650 | "version": "1.0.0",
651 | "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-1.0.0.tgz",
652 | "integrity": "sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw=="
653 | },
654 | "require_optional": {
655 | "version": "1.0.1",
656 | "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz",
657 | "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==",
658 | "requires": {
659 | "resolve-from": "^2.0.0",
660 | "semver": "^5.1.0"
661 | }
662 | },
663 | "resolve-from": {
664 | "version": "2.0.0",
665 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz",
666 | "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c="
667 | },
668 | "safe-buffer": {
669 | "version": "5.1.2",
670 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
671 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
672 | },
673 | "safer-buffer": {
674 | "version": "2.1.2",
675 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
676 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
677 | },
678 | "saslprep": {
679 | "version": "1.0.3",
680 | "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz",
681 | "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==",
682 | "optional": true,
683 | "requires": {
684 | "sparse-bitfield": "^3.0.3"
685 | }
686 | },
687 | "semver": {
688 | "version": "5.7.1",
689 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
690 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
691 | },
692 | "send": {
693 | "version": "0.17.1",
694 | "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
695 | "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
696 | "requires": {
697 | "debug": "2.6.9",
698 | "depd": "~1.1.2",
699 | "destroy": "~1.0.4",
700 | "encodeurl": "~1.0.2",
701 | "escape-html": "~1.0.3",
702 | "etag": "~1.8.1",
703 | "fresh": "0.5.2",
704 | "http-errors": "~1.7.2",
705 | "mime": "1.6.0",
706 | "ms": "2.1.1",
707 | "on-finished": "~2.3.0",
708 | "range-parser": "~1.2.1",
709 | "statuses": "~1.5.0"
710 | },
711 | "dependencies": {
712 | "ms": {
713 | "version": "2.1.1",
714 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
715 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
716 | }
717 | }
718 | },
719 | "serve-static": {
720 | "version": "1.14.1",
721 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
722 | "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
723 | "requires": {
724 | "encodeurl": "~1.0.2",
725 | "escape-html": "~1.0.3",
726 | "parseurl": "~1.3.3",
727 | "send": "0.17.1"
728 | }
729 | },
730 | "setprototypeof": {
731 | "version": "1.1.1",
732 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
733 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
734 | },
735 | "sift": {
736 | "version": "7.0.1",
737 | "resolved": "https://registry.npmjs.org/sift/-/sift-7.0.1.tgz",
738 | "integrity": "sha512-oqD7PMJ+uO6jV9EQCl0LrRw1OwsiPsiFQR5AR30heR+4Dl7jBBbDLnNvWiak20tzZlSE1H7RB30SX/1j/YYT7g=="
739 | },
740 | "sliced": {
741 | "version": "1.0.1",
742 | "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz",
743 | "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E="
744 | },
745 | "socket.io": {
746 | "version": "2.3.0",
747 | "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.3.0.tgz",
748 | "integrity": "sha512-2A892lrj0GcgR/9Qk81EaY2gYhCBxurV0PfmmESO6p27QPrUK1J3zdns+5QPqvUYK2q657nSj0guoIil9+7eFg==",
749 | "requires": {
750 | "debug": "~4.1.0",
751 | "engine.io": "~3.4.0",
752 | "has-binary2": "~1.0.2",
753 | "socket.io-adapter": "~1.1.0",
754 | "socket.io-client": "2.3.0",
755 | "socket.io-parser": "~3.4.0"
756 | },
757 | "dependencies": {
758 | "debug": {
759 | "version": "4.1.1",
760 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
761 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
762 | "requires": {
763 | "ms": "^2.1.1"
764 | }
765 | },
766 | "ms": {
767 | "version": "2.1.2",
768 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
769 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
770 | }
771 | }
772 | },
773 | "socket.io-adapter": {
774 | "version": "1.1.2",
775 | "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz",
776 | "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g=="
777 | },
778 | "socket.io-client": {
779 | "version": "2.3.0",
780 | "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.3.0.tgz",
781 | "integrity": "sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA==",
782 | "requires": {
783 | "backo2": "1.0.2",
784 | "base64-arraybuffer": "0.1.5",
785 | "component-bind": "1.0.0",
786 | "component-emitter": "1.2.1",
787 | "debug": "~4.1.0",
788 | "engine.io-client": "~3.4.0",
789 | "has-binary2": "~1.0.2",
790 | "has-cors": "1.1.0",
791 | "indexof": "0.0.1",
792 | "object-component": "0.0.3",
793 | "parseqs": "0.0.5",
794 | "parseuri": "0.0.5",
795 | "socket.io-parser": "~3.3.0",
796 | "to-array": "0.1.4"
797 | },
798 | "dependencies": {
799 | "base64-arraybuffer": {
800 | "version": "0.1.5",
801 | "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
802 | "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg="
803 | },
804 | "debug": {
805 | "version": "4.1.1",
806 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
807 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
808 | "requires": {
809 | "ms": "^2.1.1"
810 | }
811 | },
812 | "isarray": {
813 | "version": "2.0.1",
814 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
815 | "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
816 | },
817 | "ms": {
818 | "version": "2.1.2",
819 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
820 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
821 | },
822 | "socket.io-parser": {
823 | "version": "3.3.1",
824 | "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.1.tgz",
825 | "integrity": "sha512-1QLvVAe8dTz+mKmZ07Swxt+LAo4Y1ff50rlyoEx00TQmDFVQYPfcqGvIDJLGaBdhdNCecXtyKpD+EgKGcmmbuQ==",
826 | "requires": {
827 | "component-emitter": "~1.3.0",
828 | "debug": "~3.1.0",
829 | "isarray": "2.0.1"
830 | },
831 | "dependencies": {
832 | "component-emitter": {
833 | "version": "1.3.0",
834 | "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
835 | "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
836 | },
837 | "debug": {
838 | "version": "3.1.0",
839 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
840 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
841 | "requires": {
842 | "ms": "2.0.0"
843 | }
844 | },
845 | "ms": {
846 | "version": "2.0.0",
847 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
848 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
849 | }
850 | }
851 | }
852 | }
853 | },
854 | "socket.io-parser": {
855 | "version": "3.4.1",
856 | "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.1.tgz",
857 | "integrity": "sha512-11hMgzL+WCLWf1uFtHSNvliI++tcRUWdoeYuwIl+Axvwy9z2gQM+7nJyN3STj1tLj5JyIUH8/gpDGxzAlDdi0A==",
858 | "requires": {
859 | "component-emitter": "1.2.1",
860 | "debug": "~4.1.0",
861 | "isarray": "2.0.1"
862 | },
863 | "dependencies": {
864 | "debug": {
865 | "version": "4.1.1",
866 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
867 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
868 | "requires": {
869 | "ms": "^2.1.1"
870 | }
871 | },
872 | "isarray": {
873 | "version": "2.0.1",
874 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
875 | "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
876 | },
877 | "ms": {
878 | "version": "2.1.2",
879 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
880 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
881 | }
882 | }
883 | },
884 | "sparse-bitfield": {
885 | "version": "3.0.3",
886 | "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
887 | "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=",
888 | "optional": true,
889 | "requires": {
890 | "memory-pager": "^1.0.2"
891 | }
892 | },
893 | "statuses": {
894 | "version": "1.5.0",
895 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
896 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
897 | },
898 | "string_decoder": {
899 | "version": "1.1.1",
900 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
901 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
902 | "requires": {
903 | "safe-buffer": "~5.1.0"
904 | }
905 | },
906 | "to-array": {
907 | "version": "0.1.4",
908 | "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
909 | "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA="
910 | },
911 | "toidentifier": {
912 | "version": "1.0.0",
913 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
914 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
915 | },
916 | "type-is": {
917 | "version": "1.6.18",
918 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
919 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
920 | "requires": {
921 | "media-typer": "0.3.0",
922 | "mime-types": "~2.1.24"
923 | }
924 | },
925 | "unpipe": {
926 | "version": "1.0.0",
927 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
928 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
929 | },
930 | "util-deprecate": {
931 | "version": "1.0.2",
932 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
933 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
934 | },
935 | "utils-merge": {
936 | "version": "1.0.1",
937 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
938 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
939 | },
940 | "vary": {
941 | "version": "1.1.2",
942 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
943 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
944 | },
945 | "ws": {
946 | "version": "7.3.1",
947 | "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz",
948 | "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA=="
949 | },
950 | "xmlhttprequest-ssl": {
951 | "version": "1.5.5",
952 | "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz",
953 | "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4="
954 | },
955 | "yeast": {
956 | "version": "0.1.2",
957 | "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
958 | "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
959 | }
960 | }
961 | }
962 |
--------------------------------------------------------------------------------
/server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "chat-app-server",
3 | "version": "1.0.0",
4 | "description": "Chat app server",
5 | "main": "server.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "node server.js",
9 | "devStart": "nodemon server"
10 | },
11 | "author": "shine",
12 | "license": "ISC",
13 | "dependencies": {
14 | "body-parser": "^1.19.0",
15 | "cors": "^2.8.5",
16 | "dotenv": "^8.2.0",
17 | "express": "^4.17.1",
18 | "mongoose": "^5.10.9",
19 | "socket.io": "^2.3.0"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/server/server.js:
--------------------------------------------------------------------------------
1 | const express = require("express")
2 | const bodyParser = require("body-parser")
3 | const cors = require("cors")
4 | const http = require("http")
5 | const socketIO = require("socket.io")
6 | const dotenv = require("dotenv")
7 | const { chatRouter } = require('./Routes/chat_router')
8 | const { chatAppHandler } = require("./socket_IO_handlers/chat_app_handler")
9 | const { connectToMongoDBAtlas } = require('./mongo_connection')
10 | const path = require('path')
11 | // const { UsersModel } = require('./Models/users')
12 | // const { v4: uuid } = require("uuid")
13 |
14 | // Configure dotenv (.env)
15 | dotenv.config()
16 |
17 | // global variables
18 | const PORT = process.env.PORT || 3000
19 | const app = express()
20 | const server = http.Server(app)
21 |
22 | const chatIO = socketIO(server, {
23 | origins: '*:*',
24 | path: '/chat-app-socket.io'
25 | }, ['polling', 'websocket']) // I don't understand this part :(
26 |
27 | /* Express Middleware */
28 | app.use(bodyParser.json()) // Middleware to parse search params
29 | app.use(cors()) // Middleware to resolve cors!
30 |
31 | // app.get('/', (req, res) => {
32 | // res.send("Hello!")
33 | // })
34 |
35 | /*Custom Routes*/
36 | app.use('/chat-app', chatRouter) // Router setup for chat-app
37 |
38 | /* Setup socketIO handlers */
39 | chatAppHandler(chatIO)
40 |
41 | /*MongoDB*/
42 | // let chatsDBURI = process.env.CHATS_MONGO_URI
43 | // let usersDBURI = process.env.USERS_MONGO_URI
44 | let YML_MONGO_URI = process.env.YML_MONGO_URI
45 | connectToMongoDBAtlas(YML_MONGO_URI, "chatsDB")
46 | // connectToMongoDBAtlas(chatsDBURI, "usersDB")
47 |
48 | if (process.env.NODE_ENV === 'production') {
49 | console.log("PRODUCTION ENVIROMENT DETECTED!")
50 | app.use(express.static('../client/build'))
51 |
52 | app.get('*', (req, res) => {
53 | res.sendFile(path.join(__dirname, '../', 'client', 'build', 'index.html'))
54 | })
55 | }
56 |
57 | server.listen(PORT, function () {
58 | var host = server.address().address
59 | var port = server.address().port
60 | console.log("[SERVER]: Listening at http://<%s>:%s", host, port)
61 | })
62 |
63 | /** SOCKET IO CHEATSHEET **/
64 | // // send to current request socket client
65 | // socket.emit('message', "this is a test");// Hasn't changed
66 |
67 | // // sending to all clients, include sender
68 | // io.sockets.emit('message', "this is a test"); // Old way, still compatible
69 | // io.emit('message', 'this is a test');// New way, works only in 1.x
70 |
71 | // // sending to all clients except sender
72 | // socket.broadcast.emit('message', "this is a test");// Hasn't changed
73 |
74 | // // sending to all clients in 'game' room(channel) except sender
75 | // socket.broadcast.to('game').emit('message', 'nice game');// Hasn't changed
76 |
77 | // // sending to all clients in 'game' room(channel), include sender
78 | // io.sockets.in('game').emit('message', 'cool game');// Old way, DOES NOT WORK ANYMORE
79 | // io.in('game').emit('message', 'cool game');// New way
80 | // io.to('game').emit('message', 'cool game');// New way, "in" or "to" are the exact same: "And then simply use to or in (they are the same) when broadcasting or emitting:" from http://socket.io/docs/rooms-and-namespaces/
81 |
82 | // // sending to individual socketid, socketid is like a room
83 | // io.sockets.socket(socketid).emit('message', 'for your eyes only');// Old way, DOES NOT WORK ANYMORE
84 | // socket.broadcast.to(socketid).emit('message', 'for your eyes only');// New way
--------------------------------------------------------------------------------
/server/socket_IO_handlers/chat_app_handler.js:
--------------------------------------------------------------------------------
1 | const { ChatsModel } = require('../Models/chats')
2 |
3 | const chatAppHandler = (io) => {
4 | const MSG_PENDING = "pending"
5 | const MSG_SENT = "msg-sent"
6 | const MSG_NOT_SENT = "msg-not-sent"
7 |
8 | const WAIT_QUEUE_MSG_KEY = "messages"
9 | const WAIT_QUEUE_SOCKET_EVENTS_KEY = "socket-events"
10 |
11 | let userIDToSocketMap = {}
12 | let userIDToWaitQueueMap = {}
13 | let userIDToOnlineStatusMap = {}
14 |
15 | const getUserIDFromSocketID = (socketID) => {
16 | for (userID in userIDToSocketMap) {
17 | let userSocket = userIDToSocketMap[userID]
18 | if (userSocket.id === socketID)
19 | return userID
20 | }
21 |
22 | return null
23 | }
24 |
25 | const isUserOnline = (userID) => {
26 | console.log("isUserOnline")
27 | return (userID in userIDToSocketMap)
28 | }
29 |
30 | const initWaitQueue = (userID) => {
31 | userIDToWaitQueueMap[userID] = {
32 | [WAIT_QUEUE_MSG_KEY]: [],
33 | [WAIT_QUEUE_SOCKET_EVENTS_KEY]: []
34 | }
35 |
36 | return
37 | }
38 |
39 | const addMessageToWaitQueue = (msgObjectString, userID) => {
40 | console.log("Adding message to wait queue...")
41 | if (!(userID in userIDToWaitQueueMap)) {
42 | console.log("\tUserID:", userID, "is not in wait queue")
43 | initWaitQueue(userID)
44 | }
45 | userIDToWaitQueueMap[userID][WAIT_QUEUE_MSG_KEY].push(msgObjectString)
46 |
47 | return
48 | }
49 |
50 | const addSocketEventToWaitQueue = (userID, socketEventParams) => {
51 | console.log("Adding socket event to wait queue...")
52 | if (!(userID in userIDToWaitQueueMap)) {
53 | // userIDToWaitQueueMap[userID] = [msgObjectString]
54 | initWaitQueue(userID)
55 | }
56 | userIDToWaitQueueMap[userID][WAIT_QUEUE_SOCKET_EVENTS_KEY].push(socketEventParams)
57 |
58 | return
59 | }
60 |
61 |
62 | const updateMessageStatusOnDatabase = (messagesIDs) => {
63 | // db.chats.update({id: {$in: ["1168fe5a-72e4-4506-8467-45060e3ecc46", "57524518-c329-4baa-9163-f94538c8b094"]}}, {$set: {status: "msg-sent"}}, {multi: 1})
64 | console.log("Message Ids to update on database: ", messagesIDs)
65 | let mongoUpdatePromise = ChatsModel.updateMany(
66 | {
67 | 'id': { '$in': [...messagesIDs] }
68 | },
69 | {
70 | '$set': {
71 | "status": "msg-sent"
72 | }
73 | }
74 | )
75 |
76 | mongoUpdatePromise
77 | .then((data) => {
78 | // console.log("Mongo update worked.", data)
79 | console.log("Messages' statuses have been update on database")
80 | })
81 | .catch(err => {
82 | console.log("Mongo update failed", err)
83 | })
84 |
85 | return
86 | }
87 |
88 | const flushMessagesToUser = (socket, userID) => {
89 | console.log("[SOCKET.IO]: Flushing messages to user", userID)
90 | let allMessages = userIDToWaitQueueMap[userID][WAIT_QUEUE_MSG_KEY]
91 | let allMessagesStringified = JSON.stringify(allMessages)
92 | socket.emit('flush-messages', allMessagesStringified)
93 |
94 | // console.log("All Msgs: ", allMessages)
95 | if (!allMessages) return
96 |
97 | let messagesIDToChangeStatus = []
98 | for (let msgString of allMessages) {
99 | let msg = JSON.parse(msgString)
100 | let senderID = msg.senderID
101 |
102 | messagesIDToChangeStatus.push(msg.id)
103 |
104 | let socketEventParams = ['message-sent', msg.id]
105 | if (isUserOnline(senderID)) {
106 | let senderSocket = userIDToSocketMap[msg.senderID]
107 | console.log(`\tEmitting [${socketEventParams[0]}] to ${senderID}`)
108 | senderSocket.emit(...socketEventParams)
109 | } else {
110 | addSocketEventToWaitQueue(senderID, socketEventParams)
111 | }
112 | }
113 |
114 | updateMessageStatusOnDatabase(messagesIDToChangeStatus)
115 |
116 | return
117 | }
118 |
119 | const flushSocketEventsToUser = (socket, userID) => {
120 | let allSocketEvents = userIDToWaitQueueMap[userID][WAIT_QUEUE_SOCKET_EVENTS_KEY]
121 | for (socketEvent of allSocketEvents) {
122 | socket.emit(...socketEvent)
123 | }
124 |
125 | return
126 | }
127 |
128 | const removeUserTraces = (socketID) => {
129 | let userID = getUserIDFromSocketID(socketID)
130 | if (!userID)
131 | return
132 |
133 | userIDToOnlineStatusMap[userID] = false
134 | delete userIDToSocketMap[userID]
135 | return
136 | }
137 |
138 | const insertIntoDB = (msgObject) => {
139 | // let chatObject = new ChatsModel(msgObject)
140 | // let dbPromise = chatObject.save()
141 |
142 | let dbPromise = ChatsModel.create(msgObject)
143 | return dbPromise
144 | }
145 |
146 | // listen on the connection event for incoming sockets
147 | io.on('connection', function (socket) {
148 | console.log('[SOCKET.IO]: A new client connected', socket.id);
149 |
150 | socket.on('logged-in', userID => {
151 | console.log(".on(logged-in)")
152 | console.log('\t[logged-in]: userID: ', userID)
153 |
154 | // Save his online status and socket
155 | userIDToSocketMap[userID] = socket
156 | userIDToOnlineStatusMap[userID] = true
157 |
158 | // Flush messages and socket_io events
159 | if (userID in userIDToWaitQueueMap) {
160 | flushMessagesToUser(socket, userID)
161 | flushSocketEventsToUser(socket, userID)
162 | }
163 |
164 | console.log('\t[logged-in]: Wait Queue: ', userIDToWaitQueueMap[userID])
165 | // userIDToWaitQueueMap[userID] = {}
166 | initWaitQueue(userID)
167 |
168 | // Inform other users that he is online
169 | console.log("\tEmitting [online-statuses] to all users")
170 | io.emit('online-statuses', JSON.stringify(userIDToOnlineStatusMap))
171 | })
172 |
173 | socket.on('send-message', msgObjectString => {
174 | console.log(".on(send-message)")
175 | let msgObject = JSON.parse(msgObjectString)
176 | console.log("\t[send-message]: Received a new message.", msgObjectString)
177 | let { receiverID, id, senderID } = { ...msgObject }
178 |
179 | if (isUserOnline(receiverID)) {
180 | // console.log("\t[SOCKET.IO]: userIDToSockerMap: ", JSON.stringify(userIDToSocketMap))
181 | console.log("\t[SOCKET.IO]: Reciever is online!", receiverID)
182 | msgObject.status = MSG_SENT
183 |
184 | insertIntoDB(msgObject)
185 | .then((data) => {
186 | // console.log("\t[SOCKET.IO]: Messages has been inserted", data)
187 | console.log("\t[SOCKET.IO]: Messages has been inserted")
188 |
189 | let receiverSocket = userIDToSocketMap[receiverID]
190 | console.log("\tEmitting [received-message] to", receiverID)
191 | receiverSocket.emit('received-message', msgObjectString)
192 | socket.emit('message-sent', id)
193 | })
194 | .catch((error) => {
195 | console.log("\t[SOCKET.IO]: Error while inserting", error)
196 | console.log("\tEmitting [message-not-sent] to ", receiverID)
197 | socket.emit('message-not-sent', JSON.stringify(error), id)
198 | return
199 | })
200 | } else {
201 | let reason = "Reciever is offline!"
202 | msgObject.status = MSG_PENDING // It is the default. But I'm just being explicit.
203 | addMessageToWaitQueue(msgObjectString, receiverID)
204 |
205 | insertIntoDB(msgObject)
206 | .then((data) => {
207 | console.log("\t[SOCKET.IO]: Messages has been inserted", data)
208 | console.log("\tEmitting [pending] to", senderID)
209 | socket.emit('pending', reason, id)
210 | })
211 | .catch((error) => {
212 | console.log("\t[SOCKET.IO]: Error while inserting", error)
213 | console.log("\tEmitting [message-not-sent] to ", receiverID)
214 | socket.emit('message-not-sent', JSON.stringify(error), id)
215 | return
216 | })
217 | }
218 | })
219 |
220 | socket.on('deleted-msgs-ack', (receiverID) => {
221 | console.log(`.on(deleted-msg-ack), ${receiverID}`)
222 | if (isUserOnline(receiverID)) {
223 | console.log(`user: ${receiverID} is online`)
224 | let socket = userIDToSocketMap[receiverID]
225 | console.log(socket.id)
226 |
227 | console.log(`Emitting "deleted-msg-ack" to ${receiverID}`)
228 | socket.emit("deleted-msgs-ack")
229 | } else {
230 | addSocketEventToWaitQueue(receiverID, ["deleted-msgs-ack", receiverID])
231 | }
232 | })
233 |
234 | socket.on('delete-whole-convo', (id1, id2) => {
235 | deleteWholeConvo(id1, id2)
236 | })
237 |
238 | socket.on('disconnect', (reason) => {
239 | console.log("Disconnected", socket.id, reason)
240 | removeUserTraces(socket.id)
241 | io.emit('online-statuses', JSON.stringify(userIDToOnlineStatusMap))
242 | })
243 | });
244 | }
245 |
246 | module.exports = {
247 | chatAppHandler
248 | }
--------------------------------------------------------------------------------