├── public
├── _redirects
├── favicons
│ ├── favicon.ico
│ ├── favicon-16x16.png
│ ├── favicon-32x32.png
│ ├── apple-touch-icon.png
│ ├── mstile-150x150.png
│ ├── android-chrome-192x192.png
│ ├── android-chrome-512x512.png
│ ├── browserconfig.xml
│ └── site.webmanifest
└── index.html
├── src
├── whatsapp-bg.jpg
├── whatsapp-original-bg.png
├── Components
│ ├── Loading.css
│ ├── Loading.js
│ ├── SidebarChat.css
│ ├── Login.css
│ ├── Login.js
│ ├── SidebarChat.js
│ ├── Sidebar.css
│ ├── Chat.css
│ ├── Sidebar.js
│ └── Chat.js
├── reducer.js
├── StateProvider.js
├── index.css
├── index.js
├── firebase.js
├── App.css
├── App.js
└── serviceWorker.js
├── .gitignore
├── package.json
└── README.md
/public/_redirects:
--------------------------------------------------------------------------------
1 | /* /index.html 200
--------------------------------------------------------------------------------
/src/whatsapp-bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Pinqua/whatsapp-web-clone/HEAD/src/whatsapp-bg.jpg
--------------------------------------------------------------------------------
/public/favicons/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Pinqua/whatsapp-web-clone/HEAD/public/favicons/favicon.ico
--------------------------------------------------------------------------------
/src/whatsapp-original-bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Pinqua/whatsapp-web-clone/HEAD/src/whatsapp-original-bg.png
--------------------------------------------------------------------------------
/public/favicons/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Pinqua/whatsapp-web-clone/HEAD/public/favicons/favicon-16x16.png
--------------------------------------------------------------------------------
/public/favicons/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Pinqua/whatsapp-web-clone/HEAD/public/favicons/favicon-32x32.png
--------------------------------------------------------------------------------
/public/favicons/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Pinqua/whatsapp-web-clone/HEAD/public/favicons/apple-touch-icon.png
--------------------------------------------------------------------------------
/public/favicons/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Pinqua/whatsapp-web-clone/HEAD/public/favicons/mstile-150x150.png
--------------------------------------------------------------------------------
/public/favicons/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Pinqua/whatsapp-web-clone/HEAD/public/favicons/android-chrome-192x192.png
--------------------------------------------------------------------------------
/public/favicons/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Pinqua/whatsapp-web-clone/HEAD/public/favicons/android-chrome-512x512.png
--------------------------------------------------------------------------------
/src/Components/Loading.css:
--------------------------------------------------------------------------------
1 | .loader {
2 | width: 100vw;
3 | height: 100vh;
4 | display: flex;
5 | align-items: center;
6 | justify-content: center;
7 | background-color: black;
8 | }
9 |
--------------------------------------------------------------------------------
/public/favicons/browserconfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | #da532c
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/Components/Loading.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Loader from "react-loader-spinner";
3 | import "./Loading.css";
4 |
5 | function Loading() {
6 | return (
7 |
8 |
9 |
10 | );
11 | }
12 |
13 | export default Loading;
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/src/reducer.js:
--------------------------------------------------------------------------------
1 | export const initialState = {
2 | user: null,
3 | };
4 |
5 | export const actionTypes = {
6 | SET_USER: "SET_USER",
7 | };
8 | const reducer = (state, action) => {
9 | switch (action.type) {
10 | case actionTypes.SET_USER:
11 | return {
12 | ...state,
13 | user: action.user,
14 | };
15 | default:
16 | return state;
17 | }
18 | };
19 |
20 | export default reducer;
21 |
--------------------------------------------------------------------------------
/src/Components/SidebarChat.css:
--------------------------------------------------------------------------------
1 | .sidebarChat {
2 | display: flex;
3 | padding: 20px;
4 | cursor: pointer;
5 | border-bottom: 1px solid #f6f6f6;
6 | }
7 |
8 | .sidebarChat:hover {
9 | background-color: #ebebeb;
10 | }
11 | .sidebarChat__info > h2 {
12 | font-size: 16px;
13 | margin-bottom: 8px;
14 | }
15 | .sidebarChat__info {
16 | margin-left: 15px;
17 | }
18 |
19 | a {
20 | text-decoration: none;
21 | color: black;
22 | }
23 |
--------------------------------------------------------------------------------
/src/StateProvider.js:
--------------------------------------------------------------------------------
1 | import React, { createContext, useContext, useReducer } from "react";
2 |
3 | export const StateContext = createContext();
4 |
5 | export const StateProvider = ({ reducer, initialState, children }) => {
6 | return (
7 |
8 | {children}
9 |
10 | );
11 | };
12 | export const useStateValue = () => useContext(StateContext);
13 |
--------------------------------------------------------------------------------
/public/favicons/site.webmanifest:
--------------------------------------------------------------------------------
1 | {
2 | "name": "",
3 | "short_name": "",
4 | "icons": [
5 | {
6 | "src": "/android-chrome-192x192.png",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | },
10 | {
11 | "src": "/android-chrome-512x512.png",
12 | "sizes": "512x512",
13 | "type": "image/png"
14 | }
15 | ],
16 | "theme_color": "#ffffff",
17 | "background_color": "#ffffff",
18 | "display": "standalone"
19 | }
20 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0;
3 | padding: 0;
4 | scroll-behavior: smooth;
5 | -webkit-tap-highlight-color: transparent;
6 | }
7 |
8 | body {
9 | margin: 0;
10 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
11 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
12 | sans-serif;
13 | -webkit-font-smoothing: antialiased;
14 | -moz-osx-font-smoothing: grayscale;
15 | text-rendering: optimizeLegibility;
16 | }
17 |
18 | code {
19 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
20 | monospace;
21 | }
22 |
--------------------------------------------------------------------------------
/src/Components/Login.css:
--------------------------------------------------------------------------------
1 | .login {
2 | background-color: #f8f8f8;
3 | height: 100vh;
4 | width: 100vw;
5 | display: grid;
6 | place-items: center;
7 | }
8 |
9 | .login__container {
10 | padding: 40px;
11 | margin: 10px;
12 | text-align: center;
13 | background-color: white;
14 | border-radius: 10px;
15 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 3px rgba(0, 0, 0, 0.12);
16 | }
17 | .login__container > img {
18 | -o-object-fit: contain;
19 | object-fit: contain;
20 | height: 100px;
21 | margin-bottom: 40px;
22 | }
23 | .login__container > button {
24 | margin-top: 50px;
25 | text-transform: inherit !important;
26 | background-color: #0a8d48 !important;
27 | color: white;
28 | }
29 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import "./index.css";
4 | import App from "./App";
5 | import * as serviceWorker from "./serviceWorker";
6 | import { StateProvider } from "./StateProvider";
7 | import reducer, { initialState } from "./reducer";
8 |
9 | ReactDOM.render(
10 |
11 |
12 |
13 |
14 | ,
15 | document.getElementById("root")
16 | );
17 |
18 | // If you want your app to work offline and load faster, you can change
19 | // unregister() to register() below. Note this comes with some pitfalls.
20 | // Learn more about service workers: https://bit.ly/CRA-PWA
21 | serviceWorker.unregister();
22 |
--------------------------------------------------------------------------------
/src/firebase.js:
--------------------------------------------------------------------------------
1 | import firebase from "firebase/app";
2 | import "firebase/auth";
3 | import "firebase/firestore";
4 |
5 | // For Firebase JS SDK v7.20.0 and later, measurementId is optional
6 | const firebaseConfig = {
7 | apiKey: "AIzaSyBoZcS0Pwr0QAddLW8j5wyRrdbJWnECyik",
8 | authDomain: "whatsapp-web-clone-a8d93.firebaseapp.com",
9 | databaseURL: "https://whatsapp-web-clone-a8d93.firebaseio.com",
10 | projectId: "whatsapp-web-clone-a8d93",
11 | storageBucket: "whatsapp-web-clone-a8d93.appspot.com",
12 | messagingSenderId: "830002031050",
13 | appId: "1:830002031050:web:3772d26aa86981b099c733",
14 | measurementId: "G-687Y7YYCPP",
15 | };
16 |
17 | const firebaseApp = firebase.initializeApp(firebaseConfig);
18 | const db = firebaseApp.firestore();
19 | const auth = firebase.auth();
20 | const provider = new firebase.auth.GoogleAuthProvider();
21 |
22 | export { auth, provider };
23 | export default db;
24 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "whatsapp-web-clone",
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 | "firebase": "^7.24.0",
12 | "react": "^16.13.1",
13 | "react-dom": "^16.13.1",
14 | "react-loader-spinner": "^3.1.14",
15 | "react-router-dom": "^5.2.0",
16 | "react-scripts": "3.4.3"
17 | },
18 | "scripts": {
19 | "start": "react-scripts start",
20 | "build": "react-scripts build",
21 | "test": "react-scripts test",
22 | "eject": "react-scripts eject"
23 | },
24 | "eslintConfig": {
25 | "extends": "react-app"
26 | },
27 | "browserslist": {
28 | "production": [
29 | ">0.2%",
30 | "not dead",
31 | "not op_mini all"
32 | ],
33 | "development": [
34 | "last 1 chrome version",
35 | "last 1 firefox version",
36 | "last 1 safari version"
37 | ]
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/Components/Login.js:
--------------------------------------------------------------------------------
1 | import { Button } from "@material-ui/core";
2 | import React from "react";
3 | import { auth, provider } from "../firebase";
4 | import { actionTypes } from "../reducer";
5 | import { useStateValue } from "../StateProvider";
6 | import "./Login.css";
7 |
8 | function Login() {
9 | const [{ user }, dispatch] = useStateValue();
10 |
11 | const signIn = () => {
12 | auth
13 | .signInWithPopup(provider)
14 | .then((result) => {
15 | dispatch({
16 | type: actionTypes.SET_USER,
17 | user: result.user,
18 | });
19 | //localStorage.setItem("user", JSON.stringify(result.user));
20 | })
21 | .catch((error) => {
22 | alert(error.message);
23 | });
24 | };
25 | return (
26 |
27 |
28 |
32 |
33 |
Sign in to WhatsApp
34 |
35 |
Sign In with Google
36 |
37 |
38 | );
39 | }
40 |
41 | export default Login;
42 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | .app {
2 | background-color: #dadbd3;
3 | /*display: grid;
4 | place-items: center;*/
5 | display: flex;
6 | align-items: center;
7 | justify-content: center;
8 | height: 100vh;
9 | width: 100vw;
10 | }
11 | .app__body {
12 | display: flex;
13 | background-color: #ededed;
14 | height: 90vh;
15 | width: 90vw;
16 | box-shadow: -1px 4px 20px -6px rgba(0, 0, 0, 0.7);
17 | }
18 |
19 | .project__info {
20 | flex: 0.65;
21 | display: flex;
22 | flex-direction: column;
23 | align-items: center;
24 | justify-content: center;
25 | color: white;
26 | background-image: linear-gradient(rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.6)),
27 | url("whatsapp-bg.jpg");
28 | background-position: center;
29 | background-repeat: no-repeat;
30 | background-color: #054740;
31 | }
32 | .project__info h1 {
33 | font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
34 | font-size: 2.2rem;
35 | font-weight: 400;
36 | }
37 | .project__info img {
38 | -o-object-fit: contain;
39 | object-fit: contain;
40 | max-width: 300px;
41 | }
42 |
43 | @media screen and (max-width: 800px) {
44 | .app__body {
45 | height: 100vh;
46 | width: 100vw;
47 | }
48 | }
49 |
50 | @media screen and (max-width: 630px) {
51 | .app__body {
52 | overflow: auto;
53 | }
54 | .app__body {
55 | overflow: auto;
56 | }
57 | .project__info {
58 | display: none;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/Components/SidebarChat.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import { Avatar } from "@material-ui/core";
3 | import "./SidebarChat.css";
4 | import db from "../firebase";
5 | import { Link } from "react-router-dom/cjs/react-router-dom.min";
6 |
7 | function SidebarChat({ id, name, addNewChat }) {
8 | const [seed, setSeed] = useState("");
9 | const [messages, setMessages] = useState("");
10 | useEffect(() => {
11 | setSeed(Math.floor(Math.random() * 50000));
12 | }, []);
13 |
14 | useEffect(() => {
15 | if (id) {
16 | db.collection("Rooms")
17 | .doc(id)
18 | .collection("messages")
19 | .orderBy("timestamp", "desc")
20 | .onSnapshot((resultsnap) => {
21 | setMessages(
22 | resultsnap.docs.map((doc) => {
23 | return doc.data();
24 | })
25 | );
26 | });
27 | }
28 | }, [id]);
29 |
30 | const createChat = () => {
31 | const roomName = prompt("Please enter name for Room");
32 | if (roomName) {
33 | db.collection("Rooms").add({
34 | name: roomName,
35 | });
36 | }
37 | };
38 |
39 | return !addNewChat ? (
40 |
41 |
42 |
43 |
44 |
{name}
45 |
46 | {messages[0]?.message.substring(0, 15)}
47 | {messages[0]?.message.length > 15 && "..."}
48 |
49 |
50 |
51 |
52 | ) : (
53 |
54 |
+ Add new Room
55 |
56 | );
57 | }
58 |
59 | export default SidebarChat;
60 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 |
18 | # Whatsapp Web Clone
19 |
20 | Whatsapp web clone with Whatsapp web like UI and functionalities built using React.js and Firebase
21 |
22 |
23 |
24 |
25 | ## Demo
26 |
27 | [LIVE](https://wwc-ps.vercel.app)
28 |
29 |
30 | ## Screenshots
31 |
32 | 
33 |
34 | ## Acknowledgements
35 |
36 | - [sonny sangha](https://github.com/sonnysangha)
37 |
38 | ## Features
39 |
40 | - Progressive Web App (PWA)
41 | - Fully responsive and mobile friendly
42 | - State management using Context API
43 | - User authentication + sign in with Google
44 | - UI almost similar to Whatsapp web
45 | - Real Time communication
46 |
47 |
48 | ## Run Locally
49 |
50 | Clone the project
51 |
52 | ```bash
53 | git clone https://github.com/Pinqua/whatsapp-web-clone.git
54 | ```
55 |
56 | Go to the project directory
57 |
58 | ```bash
59 | cd whatsapp-web-clone
60 | ```
61 |
62 | Install dependencies
63 |
64 | ```bash
65 | npm install
66 | ```
67 |
68 | Start the server
69 |
70 | ```bash
71 | npm run start
72 | ```
73 |
74 |
75 | ## Contributing
76 |
77 | Contributions are always welcome!
78 |
79 |
80 | ## License
81 |
82 | [MIT](https://choosealicense.com/licenses/mit/)
83 |
84 |
85 |
86 |
87 |
88 |
89 | If you liked the repository, show your ❤️ by starring and forking it.
90 |
91 |
92 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
13 |
19 |
25 |
26 |
27 |
28 |
32 |
41 | Whatsapp Web Clone
42 |
43 |
44 | You need to enable JavaScript to run this app.
45 |
46 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import "./App.css";
3 | import Sidebar from "./Components/Sidebar";
4 | import Chat from "./Components/Chat";
5 | import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
6 | import Login from "./Components/Login";
7 | import { useStateValue } from "./StateProvider";
8 | import db from "./firebase";
9 | import { auth } from "./firebase";
10 | import { actionTypes } from "./reducer";
11 | import Loading from "./Components/Loading";
12 |
13 | function App() {
14 | const [{ user }, dispatch] = useStateValue();
15 | const [loading, setLoading] = useState(true);
16 |
17 | useEffect(() => {
18 | setLoading(true);
19 | const listener = auth.onAuthStateChanged((authUser) => {
20 | setLoading(false);
21 | if (authUser) {
22 | dispatch({
23 | type: actionTypes.SET_USER,
24 | user: authUser,
25 | });
26 | } else {
27 | dispatch({
28 | type: actionTypes.SET_USER,
29 | user: null,
30 | });
31 | }
32 | });
33 | return () => listener();
34 | }, [dispatch]);
35 |
36 | const removeRoom = (roomid) => {
37 | db.collection("Rooms")
38 | .doc(roomid)
39 | .delete()
40 | .then(() => {
41 | alert("Room Deleted");
42 | });
43 | };
44 |
45 | if (loading) {
46 | return ;
47 | }
48 | return (
49 |
50 | {!user ? (
51 |
52 | ) : (
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
64 |
65 |
WhatsApp Web Clone
66 |
- By Piyush Sati
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 | )}
78 |
79 | );
80 | }
81 |
82 | export default App;
83 |
--------------------------------------------------------------------------------
/src/Components/Sidebar.css:
--------------------------------------------------------------------------------
1 | .sidebar {
2 | display: flex;
3 | flex-direction: column;
4 | flex: 0.35;
5 | }
6 | .sidebar__search {
7 | display: flex;
8 | align-items: center;
9 | background-color: #f6f6f6;
10 | height: 39px;
11 | padding: 10px;
12 | }
13 | .sidebar__searchContainer {
14 | display: flex;
15 | align-items: center;
16 | background-color: white;
17 | width: 100%;
18 | height: 35px;
19 | border-radius: 20px;
20 | }
21 | .sidebar__searchContainer > .MuiSvgIcon-root {
22 | color: gray;
23 | padding: 10px;
24 | }
25 | .sidebar__searchContainer > input {
26 | border: none;
27 | margin-left: 10px;
28 | border: none;
29 | box-sizing: border-box;
30 | min-width: 0;
31 | /* font-weight: 400;
32 | font-size: 15px;*/
33 | outline: none;
34 | will-change: width;
35 | }
36 | .sidebar__header {
37 | display: flex;
38 | justify-content: space-between;
39 | padding: 20px;
40 | border-right: 1px solid lightgray;
41 | }
42 | .sidebar__headerRight {
43 | display: flex;
44 | align-items: center;
45 | justify-content: space-between;
46 | min-width: 10vw;
47 | }
48 | .sidebar__headerRight > .MuiSvgIcon-root {
49 | margin-right: 2vw;
50 | font-size: 24px !important;
51 | }
52 | .sidebar__chats {
53 | background-color: white;
54 | flex: 1;
55 | overflow: scroll;
56 | -ms-overflow-style: none;
57 | scrollbar-width: none;
58 | }
59 |
60 | .sidebar__chats::-webkit-scrollbar {
61 | display: none;
62 | }
63 |
64 | .dropdown {
65 | position: relative;
66 | }
67 |
68 | .dropdown__list {
69 | display: block;
70 | position: absolute;
71 | top: 55px;
72 | right: 20px;
73 | font-size: 14px;
74 | z-index: 1;
75 | }
76 |
77 | .dropdown__list .MuiSvgIcon-root {
78 | font-size: inherit;
79 | margin-right: 5px;
80 | }
81 | .hide {
82 | display: none;
83 | }
84 | .dropdown ul {
85 | border-radius: 5px;
86 | width: 120px;
87 | background-color: white;
88 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 3px rgba(0, 0, 0, 0.12);
89 | }
90 | .dropdown__list ul li {
91 | list-style: none;
92 | padding: 10px;
93 | cursor: pointer;
94 | }
95 |
96 | .dropdown__list ul li:hover {
97 | background-color: #eeeeee;
98 | }
99 |
100 | @media screen and (max-width: 630px) {
101 | .sidebar {
102 | flex: 1;
103 | }
104 | .side__bar {
105 | display: none;
106 | }
107 | .sidebar__header {
108 | padding-right: 10px;
109 | }
110 | .sidebar__header .MuiIconButton-root {
111 | padding-right: 7px !important;
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/src/Components/Chat.css:
--------------------------------------------------------------------------------
1 | .chat {
2 | flex: 0.65;
3 | display: flex;
4 | flex-direction: column;
5 | }
6 | .chat__header {
7 | padding: 20px;
8 | display: flex;
9 | align-items: center;
10 | border-bottom: 1px solid lightgray;
11 | }
12 | .chat__headerInfo {
13 | flex: 1;
14 | padding-left: 20px;
15 | }
16 | .chat__headerInfo > p {
17 | color: gray;
18 | font-size: 13px;
19 | }
20 | .chat__headerRight {
21 | display: flex;
22 | justify-content: space-between;
23 | min-width: 60px;
24 | }
25 | .chat__body {
26 | flex: 1;
27 | background-image: url("../whatsapp-original-bg.png");
28 | /*background-image: url("https://user-images.githubusercontent.com/15075759/28719144-86dc0f70-73b1-11e7-911d-60d70fcded21.png");*/
29 | background-repeat: repeat;
30 | background-position: center;
31 | padding: 30px;
32 | overflow: scroll;
33 | -ms-overflow-style: none;
34 | scrollbar-width: none;
35 | }
36 | .chat__body::-webkit-scrollbar {
37 | display: none;
38 | }
39 | .chat__message {
40 | position: relative;
41 | justify-content: space-between;
42 | min-width: 150px;
43 | padding: 7px;
44 | font-size: 14px;
45 | background-color: #ffffff;
46 | border-radius: 10px;
47 | width: -webkit-fit-content;
48 | width: -moz-fit-content;
49 | width: fit-content;
50 | margin-bottom: 50px;
51 | max-width: 270px;
52 | word-wrap: break-word !important;
53 | word-break: keep-all;
54 | }
55 |
56 | .chat__name {
57 | position: absolute;
58 | top: -16px;
59 | font-weight: 700;
60 | font-size: x-small;
61 | }
62 | .chat__receiver {
63 | margin-left: auto;
64 | background-color: #dcf8c6;
65 | }
66 | .chat__timestamp {
67 | position: absolute;
68 | bottom: -15px;
69 | right: 0px;
70 | /*margin-left: 10px;*/
71 | font-size: xx-small;
72 | word-wrap: normal !important;
73 | word-break: keep-all;
74 | }
75 | /*
76 | .msg__status{
77 | position: absolute;
78 | bottom: 0;
79 | right: 5px;
80 | }
81 | .chat__message .MuiSvgIcon-root{
82 | font-size:16px;
83 | margin-left: 3px;
84 | color: gray;
85 | }
86 |
87 | */
88 | .chat__footer {
89 | display: flex;
90 | justify-content: space-between;
91 | align-items: center;
92 | height: 62px;
93 | border-top: 1px solid lightgray;
94 | }
95 | .chat__footer > form {
96 | flex: 1;
97 | display: flex;
98 | }
99 | .chat__footer > form > input {
100 | border-radius: 30px;
101 | margin: 5px 10px;
102 | padding: 9px 12px 11px;
103 | border: none;
104 | flex: 1 1 auto;
105 | box-sizing: border-box;
106 | width: inherit;
107 | min-width: 0;
108 | min-height: 20px;
109 | font-weight: 400;
110 | font-size: 15px;
111 | line-height: 20px;
112 | outline: none;
113 | will-change: width;
114 | }
115 | .chat__footer > form > button {
116 | display: none;
117 | }
118 | .chat__footer > .MuiSvgIcon-root {
119 | padding: 10px;
120 | color: gray;
121 | }
122 |
123 | .chat__headerRight .dropdown {
124 | position: relative;
125 | }
126 |
127 | .chat__headerRight .dropdown__list {
128 | display: block;
129 | position: absolute;
130 | top: 50px;
131 | right: 20px;
132 | z-index: 1;
133 | }
134 | .chat__headerRight .hide {
135 | display: none;
136 | }
137 | .chat__headerRight .dropdown ul {
138 | border-radius: 5px;
139 | width: 120px;
140 | background-color: white;
141 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 3px rgba(0, 0, 0, 0.12);
142 | }
143 | .chat__headerRight .dropdown__list ul li {
144 | list-style: none;
145 | padding: 10px;
146 | cursor: pointer;
147 | }
148 |
149 | .chat__headerRight .dropdown__list ul li:hover {
150 | background-color: #eeeeee;
151 | border-radius: 5px;
152 | }
153 | .Chat {
154 | display: none;
155 | }
156 |
157 | @media screen and (max-width: 630px) {
158 | .chat {
159 | flex: 1;
160 | height: 100vh;
161 | }
162 | .chat .MuiIconButton-root {
163 | padding: 0 !important;
164 | }
165 | .chat__body {
166 | padding: 30px 20px;
167 | }
168 | .chat__message {
169 | max-width: 200px;
170 | }
171 | .chat__header {
172 | padding-left: 10px;
173 | padding-right: 10px;
174 | }
175 | .chat__header .MuiAvatar-root {
176 | margin-left: 10px;
177 | }
178 | .chat__footer {
179 | padding: 0 10px;
180 | }
181 | .attach__file {
182 | display: none !important;
183 | }
184 | .chat__headerRight .dropdown__list {
185 | top: 40px;
186 | right: 10px;
187 | }
188 | }
189 |
--------------------------------------------------------------------------------
/src/Components/Sidebar.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import { Avatar, ClickAwayListener, IconButton } from "@material-ui/core";
3 | import DonutLargeIcon from "@material-ui/icons/DonutLarge";
4 | import ChatIcon from "@material-ui/icons/Chat";
5 | import MoreVertIcon from "@material-ui/icons/MoreVert";
6 | import "./Sidebar.css";
7 | import SidebarChat from "./SidebarChat";
8 | import { SearchOutlined } from "@material-ui/icons";
9 | import db, { auth } from "../firebase";
10 | import { useStateValue } from "../StateProvider";
11 | import { actionTypes } from "../reducer";
12 |
13 | function Sidebar({ hide }) {
14 | const [rooms, setRooms] = useState([]);
15 | const [{ user }, dispatch] = useStateValue();
16 | const [search, setSearch] = useState("");
17 | const [showdropdown, setDropdown] = useState(false);
18 |
19 | useEffect(() => {
20 | const unsubscribe = db.collection("Rooms").onSnapshot((snapshot) =>
21 | setRooms(
22 | snapshot.docs.map((doc) => ({
23 | id: doc.id,
24 | data: doc.data(),
25 | }))
26 | )
27 | );
28 |
29 | return () => {
30 | unsubscribe();
31 | };
32 | }, []);
33 |
34 | const signOut = () => {
35 | auth
36 | .signOut()
37 | .then(() => {
38 | dispatch({
39 | type: actionTypes.SET_USER,
40 | user: null,
41 | });
42 | setDropdown(!showdropdown);
43 | //localStorage.clear();
44 | })
45 | .catch((err) => {
46 | alert(err.message);
47 | });
48 | };
49 | const createChat = () => {
50 | setDropdown(!showdropdown);
51 | const roomName = prompt("Please enter name for Room");
52 | if (roomName) {
53 | db.collection("Rooms").add({
54 | name: roomName,
55 | });
56 | }
57 | };
58 |
59 | return (
60 |
61 |
62 |
63 |
64 |
66 | alert(
67 | "Not added this functionality.\nClick on three dots to logout and add new room."
68 | )
69 | }
70 | >
71 |
72 |
73 |
74 |
75 |
76 |
setDropdown(false)}>
77 |
78 |
{
80 | setDropdown(!showdropdown);
81 | }}
82 | >
83 |
84 |
85 |
90 |
91 | Add Room
92 | Log Out
93 |
95 | alert(
96 | "Not added this functionality.\nTry Logout and add Room options"
97 | )
98 | }
99 | >
100 | Help ?
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 | {
116 | setSearch(e.target.value);
117 | }}
118 | />
119 |
120 |
121 |
122 |
123 | {rooms.map((room) => {
124 | if (
125 | room.data.name.toLowerCase().includes(search.toLowerCase()) ||
126 | search === ""
127 | ) {
128 | return (
129 |
130 | );
131 | }
132 | return <>>;
133 | })}
134 |
135 |
136 | );
137 | }
138 |
139 | export default Sidebar;
140 |
--------------------------------------------------------------------------------
/src/serviceWorker.js:
--------------------------------------------------------------------------------
1 | // This optional code is used to register a service worker.
2 | // register() is not called by default.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on subsequent visits to a page, after all the
7 | // existing tabs open on the page have been closed, since previously cached
8 | // resources are updated in the background.
9 |
10 | // To learn more about the benefits of this model and instructions on how to
11 | // opt-in, read https://bit.ly/CRA-PWA
12 |
13 | const isLocalhost = Boolean(
14 | window.location.hostname === "localhost" ||
15 | // [::1] is the IPv6 localhost address.
16 | window.location.hostname === "[::1]" ||
17 | // 127.0.0.0/8 are considered localhost for IPv4.
18 | window.location.hostname.match(
19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
20 | )
21 | );
22 |
23 | export function register(config) {
24 | if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) {
25 | // The URL constructor is available in all browsers that support SW.
26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
27 | if (publicUrl.origin !== window.location.origin) {
28 | // Our service worker won't work if PUBLIC_URL is on a different origin
29 | // from what our page is served on. This might happen if a CDN is used to
30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
31 | return;
32 | }
33 |
34 | window.addEventListener("load", () => {
35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
36 |
37 | if (isLocalhost) {
38 | // This is running on localhost. Let's check if a service worker still exists or not.
39 | checkValidServiceWorker(swUrl, config);
40 |
41 | // Add some additional logging to localhost, pointing developers to the
42 | // service worker/PWA documentation.
43 | navigator.serviceWorker.ready.then(() => {
44 | console.log(
45 | "This web app is being served cache-first by a service " +
46 | "worker. To learn more, visit https://bit.ly/CRA-PWA"
47 | );
48 | });
49 | } else {
50 | // Is not localhost. Just register service worker
51 | registerValidSW(swUrl, config);
52 | }
53 | });
54 | }
55 | }
56 |
57 | function registerValidSW(swUrl, config) {
58 | navigator.serviceWorker
59 | .register(swUrl)
60 | .then((registration) => {
61 | registration.onupdatefound = () => {
62 | const installingWorker = registration.installing;
63 | if (installingWorker == null) {
64 | return;
65 | }
66 | installingWorker.onstatechange = () => {
67 | if (installingWorker.state === "installed") {
68 | if (navigator.serviceWorker.controller) {
69 | // At this point, the updated precached content has been fetched,
70 | // but the previous service worker will still serve the older
71 | // content until all client tabs are closed.
72 | console.log(
73 | "New content is available and will be used when all " +
74 | "tabs for this page are closed. See https://bit.ly/CRA-PWA."
75 | );
76 |
77 | // Execute callback
78 | if (config && config.onUpdate) {
79 | config.onUpdate(registration);
80 | }
81 | } else {
82 | // At this point, everything has been precached.
83 | // It's the perfect time to display a
84 | // "Content is cached for offline use." message.
85 | console.log("Content is cached for offline use.");
86 |
87 | // Execute callback
88 | if (config && config.onSuccess) {
89 | config.onSuccess(registration);
90 | }
91 | }
92 | }
93 | };
94 | };
95 | })
96 | .catch((error) => {
97 | console.error("Error during service worker registration:", error);
98 | });
99 | }
100 |
101 | function checkValidServiceWorker(swUrl, config) {
102 | // Check if the service worker can be found. If it can't reload the page.
103 | fetch(swUrl, {
104 | headers: { "Service-Worker": "script" },
105 | })
106 | .then((response) => {
107 | // Ensure service worker exists, and that we really are getting a JS file.
108 | const contentType = response.headers.get("content-type");
109 | if (
110 | response.status === 404 ||
111 | (contentType != null && contentType.indexOf("javascript") === -1)
112 | ) {
113 | // No service worker found. Probably a different app. Reload the page.
114 | navigator.serviceWorker.ready.then((registration) => {
115 | registration.unregister().then(() => {
116 | window.location.reload();
117 | });
118 | });
119 | } else {
120 | // Service worker found. Proceed as normal.
121 | registerValidSW(swUrl, config);
122 | }
123 | })
124 | .catch(() => {
125 | console.log(
126 | "No internet connection found. App is running in offline mode."
127 | );
128 | });
129 | }
130 |
131 | export function unregister() {
132 | if ("serviceWorker" in navigator) {
133 | navigator.serviceWorker.ready
134 | .then((registration) => {
135 | registration.unregister();
136 | })
137 | .catch((error) => {
138 | console.error(error.message);
139 | });
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/src/Components/Chat.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import { Avatar, ClickAwayListener, IconButton } from "@material-ui/core";
3 | import "./Chat.css";
4 | import {
5 | AttachFile,
6 | InsertEmoticon,
7 | Mic,
8 | MoreVert,
9 | SearchOutlined,
10 | ArrowBack,
11 | } from "@material-ui/icons";
12 | import { Link, useParams } from "react-router-dom/cjs/react-router-dom.min";
13 | import db from "../firebase";
14 | import { useStateValue } from "../StateProvider";
15 | import firebase from "firebase";
16 |
17 | function Chat({ hide, removeRoom }) {
18 | const [{ user }, dispatch] = useStateValue();
19 | const [seed, setSeed] = useState("");
20 | const [input, setInput] = useState("");
21 | const { roomId } = useParams();
22 | const [roomName, setRoomName] = useState("");
23 | const [messages, setMessages] = useState([]);
24 | const [showdropdown, setDropdown] = useState(false);
25 |
26 | useEffect(() => {
27 | setDropdown(false);
28 | setSeed(Math.floor(Math.random() * 50000));
29 | if (roomId) {
30 | db.collection("Rooms")
31 | .doc(roomId)
32 | .onSnapshot((snapshot) => {
33 | if (snapshot.data()) {
34 | setRoomName(snapshot.data().name);
35 | }
36 | });
37 | db.collection("Rooms")
38 | .doc(roomId)
39 | .collection("messages")
40 | .orderBy("timestamp", "asc")
41 | .onSnapshot((resultsnap) => {
42 | setMessages(
43 | resultsnap.docs.map((doc) => {
44 | return doc.data();
45 | })
46 | );
47 | });
48 | }
49 | }, [roomId]);
50 |
51 | useEffect(() => {
52 | setSeed(Math.floor(Math.random() * 50000));
53 | }, []);
54 |
55 | const sendMessage = (e) => {
56 | e.preventDefault();
57 | if (input) {
58 | db.collection("Rooms").doc(roomId).collection("messages").add({
59 | message: input,
60 | name: user.displayName,
61 | email: user.email,
62 | timestamp: firebase.firestore.FieldValue.serverTimestamp(),
63 | });
64 | setInput("");
65 | } else {
66 | alert("Type something first");
67 | }
68 | };
69 | return (
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
{roomName}
80 |
81 | {messages.length !== 0
82 | ? `Last seen at ` +
83 | new Date(
84 | messages[messages.length - 1]?.timestamp?.toDate()
85 | ).toUTCString()
86 | : ""}
87 |
88 |
89 |
90 |
92 | alert(
93 | "Not added this functionality.\nClick on three dots to delete room."
94 | )
95 | }
96 | >
97 |
98 |
99 | {/*
100 | s
101 | */}
102 |
setDropdown(false)}>
103 |
104 |
{
106 | setDropdown(!showdropdown);
107 | }}
108 | >
109 |
110 |
111 |
116 |
117 |
118 | {
120 | removeRoom(roomId);
121 | }}
122 | >
123 | Delete Room
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 | {messages.map((message) => (
134 |
135 |
140 | {message.name}
141 | {message.message}
142 |
143 | {new Date(message.timestamp?.toDate()).toUTCString()}
144 |
145 |
146 |
147 | ))}
148 |
149 |
150 |
152 | alert(
153 | "Not added this functionality.\nClick on three dots on top right to delete room."
154 | )
155 | }
156 | >
157 |
158 |
159 |
162 | alert(
163 | "Not added this functionality.\nClick on three dots on top right to delete room."
164 | )
165 | }
166 | >
167 |
168 |
169 |
181 |
183 | alert(
184 | "Not added this functionality.\nClick on three dots on top right to delete room."
185 | )
186 | }
187 | >
188 |
189 |
190 |
191 |
192 | );
193 | }
194 |
195 | export default Chat;
196 |
--------------------------------------------------------------------------------