├── public ├── _redirects └── index.html ├── src ├── styles.css ├── Loading.js ├── index.js ├── Navbar.js ├── PrivateRoute.js ├── FileUpload.js ├── auth.js ├── Messages.js ├── Profile.js ├── App.js ├── firebase.js ├── Home.css ├── Register.css ├── SelectedUser.css ├── Dashboard.css ├── Dashboard.js ├── Home.js ├── Register.js └── SelectedUser.js ├── README.md ├── .codesandbox └── workspace.json ├── .env └── package.json /public/_redirects: -------------------------------------------------------------------------------- 1 | /* /index.html 200 -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | .App { 2 | font-family: sans-serif; 3 | text-align: center; 4 | } 5 | -------------------------------------------------------------------------------- /src/Loading.js: -------------------------------------------------------------------------------- 1 | export default function Loading() { 2 | return

Loading....

; 3 | } 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # web-chat-pingo 2 | Simple, fast and user-friendly chat-app using ReactJS and Firebase. 3 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import { StrictMode } from "react"; 2 | import ReactDOM from "react-dom"; 3 | 4 | import App from "./App"; 5 | 6 | const rootElement = document.getElementById("root"); 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | rootElement 12 | ); 13 | -------------------------------------------------------------------------------- /.codesandbox/workspace.json: -------------------------------------------------------------------------------- 1 | { 2 | "responsive-preview": { 3 | "Mobile": [ 4 | 320, 5 | 675 6 | ], 7 | "Tablet": [ 8 | 1024, 9 | 765 10 | ], 11 | "Desktop": [ 12 | 1400, 13 | 800 14 | ], 15 | "Desktop HD": [ 16 | 1920, 17 | 1080 18 | ] 19 | } 20 | } -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | REACT_APP_API_KEY=AIzaSyAcT9ddqrbogrMgjodrKTyJhqptLqOalK4 2 | REACT_APP_AUTH_DOMAIN=web-chat-app-b43ff.firebaseapp.com 3 | REACT_APP_DATABSE_URL=http://web-chat-app-b43ff.firebaseapp.com 4 | REACT_APP_PROJECT_ID=web-chat-app-b43ff 5 | REACT_APP_STORAGE_BUCKET=web-chat-app-b43ff.appspot.com 6 | REACT_APP_MESSAGING_SENDER_ID=291594762208 7 | REACT_APP_APP_ID=1:291594762208:web:493151bc6e5293c0b29d85 -------------------------------------------------------------------------------- /src/Navbar.js: -------------------------------------------------------------------------------- 1 | import { Link } from "react-router-dom"; 2 | 3 | const Navbar = () => { 4 | return ( 5 | 14 | ); 15 | }; 16 | export default Navbar; 17 | -------------------------------------------------------------------------------- /src/PrivateRoute.js: -------------------------------------------------------------------------------- 1 | import { useContext } from "react"; 2 | import { AuthContext } from "../src/auth"; 3 | import { Redirect, Route } from "react-router-dom"; 4 | 5 | export default function PrivateRoute({ component: Component, ...rest }) { 6 | const { user } = useContext(AuthContext); 7 | 8 | return ( 9 | 13 | user ? : 14 | } 15 | /> 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /src/FileUpload.js: -------------------------------------------------------------------------------- 1 | export default function FileUpload() { 2 | return ( 3 | 11 | 17 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /src/auth.js: -------------------------------------------------------------------------------- 1 | import { createContext, useEffect, useState } from "react"; 2 | import { onAuthStateChanged } from "firebase/auth"; 3 | import { auth } from "../src/firebase"; 4 | export const AuthContext = createContext(); 5 | 6 | const AuthProvider = ({ children }) => { 7 | const [user, setUser] = useState(null); 8 | const [loading, setLoading] = useState(true); 9 | 10 | useEffect(() => { 11 | onAuthStateChanged(auth, (user) => { 12 | setUser(user); 13 | setLoading(false); 14 | }); 15 | }, []); 16 | if (loading) { 17 | return "Web Chat Pingo Loadiing... "; 18 | } 19 | return ( 20 | {children} 21 | ); 22 | }; 23 | 24 | export default AuthProvider; 25 | -------------------------------------------------------------------------------- /src/Messages.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from "react"; 2 | 3 | export default function Messages({ msg, user1id, user2 }) { 4 | const scrollRef = useRef(); 5 | 6 | useEffect(() => { 7 | console.log(msg); 8 | scrollRef.current?.scrollIntoView({ behavior: "smooth" }); 9 | }, [msg]); 10 | 11 | return ( 12 |
13 |

14 | {msg.from === user1id ? ( 15 | You: 16 | ) : ( 17 | 18 | {user2.name}:{" "} 19 | 20 | )} 21 | {msg.text ? msg.text : ""} 22 | 23 |
24 |

25 | {msg.media ? ( 26 | 27 | ) : null} 28 |
29 |
30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /src/Profile.js: -------------------------------------------------------------------------------- 1 | export default function Profile() { 2 | return ( 3 |
4 |
5 | {/*
6 | //Image Div 7 |
*/} 8 | 9 |
10 | 16 | 21 | 22 |

User Name

23 |

User Email

24 |
25 | Joined on: ... 26 |
27 |
28 |
29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import "./styles.css"; 2 | import { BrowserRouter, Switch, Route } from "react-router-dom"; 3 | import Home from "./Home"; 4 | import Register from "./Register"; 5 | import Dashboard from "./Dashboard"; 6 | import AuthProvider from "./auth"; 7 | import PrivateRoute from "./PrivateRoute"; 8 | import Profile from "./Profile"; 9 | import SelectedUser from "./SelectedUser"; 10 | 11 | export default function App() { 12 | return ( 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react", 3 | "version": "1.0.0", 4 | "description": "React example starter project", 5 | "keywords": [ 6 | "react", 7 | "starter" 8 | ], 9 | "main": "src/index.js", 10 | "dependencies": { 11 | "@emotion/react": "11.7.1", 12 | "@emotion/styled": "11.6.0", 13 | "@mui/icons-material": "5.2.5", 14 | "@mui/material": "5.2.8", 15 | "firebase": "9.6.1", 16 | "react": "17.0.2", 17 | "react-dom": "17.0.2", 18 | "react-page-visibility": "6.4.0", 19 | "react-router-dom": "5.1.2", 20 | "react-scripts": "4.0.0" 21 | }, 22 | "devDependencies": { 23 | "@babel/runtime": "7.13.8", 24 | "typescript": "4.1.3" 25 | }, 26 | "scripts": { 27 | "start": "react-scripts start", 28 | "build": "react-scripts build", 29 | "test": "react-scripts test --env=jsdom", 30 | "eject": "react-scripts eject" 31 | }, 32 | "browserslist": [ 33 | ">0.2%", 34 | "not dead", 35 | "not ie <= 11", 36 | "not op_mini all" 37 | ] 38 | } -------------------------------------------------------------------------------- /src/firebase.js: -------------------------------------------------------------------------------- 1 | // Import the functions you need from the SDKs you need 2 | import { initializeApp, getApp } from "firebase/app"; 3 | import { getAuth } from "firebase/auth"; 4 | import { getFirestore } from "firebase/firestore"; 5 | import { getStorage } from "firebase/storage"; 6 | 7 | // TODO: Add SDKs for Firebase products that you want to use 8 | // https://firebase.google.com/docs/web/setup#available-libraries 9 | 10 | // Your web app's Firebase configuration 11 | 12 | var app; 13 | var storage; 14 | 15 | try { 16 | app = getApp(); 17 | } catch (error) { 18 | const firebaseConfig = { 19 | apiKey: "AIzaSyBzKuzjKriUidssayDTBTnOzhYhI6EgmbY", 20 | authDomain: "web-chat-app-2-f13ae.firebaseapp.com", 21 | projectId: "web-chat-app-2-f13ae", 22 | storageBucket: "web-chat-app-2-f13ae.appspot.com", 23 | messagingSenderId: "229937947974", 24 | appId: "1:229937947974:web:0ed237852f9333d6977147" 25 | }; 26 | app = initializeApp(firebaseConfig); 27 | } 28 | storage = getStorage(app); 29 | 30 | // Initialize Firebase 31 | 32 | const auth = getAuth(app); 33 | const db = getFirestore(app); 34 | 35 | export { auth, db, storage }; 36 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 14 | 23 | Web Chat Pingo 24 | 25 | 26 | 27 | 30 |
31 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/Home.css: -------------------------------------------------------------------------------- 1 | html { 2 | font-family: sans-serif; 3 | } 4 | * { 5 | margin: 0; 6 | padding: 0; 7 | } 8 | 9 | .myHeader { 10 | background: #502a75; 11 | color: #fff; 12 | padding: 20px 20px 20px; 13 | height: 190px; 14 | margin-bottom: 1rem; 15 | } 16 | 17 | /* span svg{ 18 | display: block; 19 | position: absolute; 20 | height: 70px; 21 | left: 44%; 22 | margin: -0.5%; */ 23 | 24 | /* } */ 25 | 26 | main { 27 | max-width: 65%; 28 | min-height: 60%; 29 | border-radius: 6px; 30 | background: #fff; 31 | padding: 10px 40px 40px 40px; 32 | margin: 0 auto; 33 | margin-top: -130px; 34 | 35 | box-shadow: 0px 1px 5px rgba(0, 0, 0, 0.7); 36 | } 37 | 38 | main h3 { 39 | padding: 2px 0px 20px; 40 | font-size: 20px; 41 | } 42 | 43 | main h3 span { 44 | float: right; 45 | font-size: 16px; 46 | color: #502a75; 47 | padding-top: 30px; 48 | } 49 | 50 | .formClass { 51 | text-align: center; 52 | top: 40px; 53 | padding: 20px; 54 | } 55 | .form-group { 56 | padding: 7px; 57 | border-radius: 20px; 58 | } 59 | input { 60 | height: 20px; 61 | width: 250px; 62 | border-radius: 5px; 63 | padding: 10px; 64 | background-color: #eaebff; 65 | border-color: #eaebff; 66 | border: 0px; 67 | } 68 | .btn { 69 | padding: 10px; 70 | height: 40px; 71 | width: 80px; 72 | margin: 15px; 73 | background-color: #502a75; 74 | color: white; 75 | border-radius: 5px; 76 | cursor: pointer; 77 | font-size: 17px; 78 | } 79 | 80 | a { 81 | text-decoration: none; 82 | } 83 | 84 | @media (max-width: 890px) { 85 | main { 86 | max-width: 85%; 87 | padding: 5%; 88 | } 89 | 90 | .myHeader h1 { 91 | font-size: 25px; 92 | margin-top: 1rem; 93 | margin-bottom: 1rem; 94 | } 95 | 96 | main h3 { 97 | font-size: 15px; 98 | } 99 | 100 | main h3 span { 101 | font-size: 12px; 102 | } 103 | 104 | input { 105 | height: 20px; 106 | width: 230px; 107 | border-radius: 5px; 108 | padding: 10px; 109 | background-color: #eaebff; 110 | border-color: #eaebff; 111 | border: 0px; 112 | } 113 | .form-group { 114 | padding: 7px; 115 | border-radius: 20px; 116 | width: 100%; 117 | } 118 | 119 | /* span svg{ 120 | display: block; 121 | position: absolute; 122 | height: 70px; 123 | left: 44%; 124 | margin: -0.5%; 125 | */ 126 | /* } */ 127 | } 128 | -------------------------------------------------------------------------------- /src/Register.css: -------------------------------------------------------------------------------- 1 | html { 2 | font-family: sans-serif; 3 | } 4 | * { 5 | margin: 0; 6 | padding: 0; 7 | } 8 | 9 | .myHeader { 10 | background: #502a75; 11 | color: #fff; 12 | padding: 20px 20px 20px; 13 | height: 190px; 14 | margin-bottom: 1rem; 15 | } 16 | 17 | /* span svg{ 18 | display: block; 19 | position: absolute; 20 | height: 70px; 21 | left: 44%; 22 | margin: -0.5%; */ 23 | 24 | /* } */ 25 | 26 | main { 27 | max-width: 65%; 28 | min-height: 60%; 29 | border-radius: 6px; 30 | background: #fff; 31 | padding: 10px 40px 40px 40px; 32 | margin: 0 auto; 33 | margin-top: -130px; 34 | 35 | box-shadow: 0px 1px 5px rgba(0, 0, 0, 0.7); 36 | } 37 | 38 | main h3 { 39 | padding: 2px 0px 20px; 40 | font-size: 20px; 41 | } 42 | 43 | main h3 span { 44 | float: right; 45 | font-size: 16px; 46 | color: #502a75; 47 | padding-top: 30px; 48 | } 49 | 50 | .formClass { 51 | text-align: center; 52 | top: 40px; 53 | padding: 20px; 54 | } 55 | .form-group { 56 | padding: 7px; 57 | border-radius: 20px; 58 | } 59 | input { 60 | height: 20px; 61 | width: 250px; 62 | border-radius: 5px; 63 | padding: 10px; 64 | background-color: #eaebff; 65 | border-color: #eaebff; 66 | border: 0px; 67 | } 68 | .btn { 69 | padding: 10px; 70 | height: 40px; 71 | width: 80px; 72 | margin: 15px; 73 | background-color: #502a75; 74 | color: white; 75 | border-radius: 5px; 76 | cursor: pointer; 77 | font-size: 17px; 78 | } 79 | 80 | a { 81 | text-decoration: none; 82 | } 83 | 84 | @media (max-width: 890px) { 85 | main { 86 | max-width: 85%; 87 | padding: 5%; 88 | } 89 | 90 | .myHeader h1 { 91 | font-size: 25px; 92 | margin-top: 1rem; 93 | margin-bottom: 1rem; 94 | } 95 | 96 | main h3 { 97 | font-size: 15px; 98 | } 99 | 100 | main h3 span { 101 | font-size: 12px; 102 | } 103 | 104 | input { 105 | height: 20px; 106 | width: 83%; 107 | border-radius: 5px; 108 | padding: 10px; 109 | background-color: #eaebff; 110 | border-color: #eaebff; 111 | border: 0px; 112 | } 113 | .form-group { 114 | padding: 7px; 115 | border-radius: 20px; 116 | width: 100%; 117 | } 118 | 119 | /* span svg{ 120 | display: block; 121 | position: absolute; 122 | height: 70px; 123 | left: 44%; 124 | margin: -0.5%; 125 | */ 126 | /* } */ 127 | } 128 | -------------------------------------------------------------------------------- /src/SelectedUser.css: -------------------------------------------------------------------------------- 1 | html { 2 | font-family: sans-serif; 3 | } 4 | * { 5 | margin: 0; 6 | padding: 0; 7 | } 8 | 9 | .myHeader { 10 | background: #502a75; 11 | color: #fff; 12 | padding: 20px 20px 20px; 13 | height: 190px; 14 | margin-bottom: 1rem; 15 | } 16 | 17 | /* span svg{ 18 | display: block; 19 | position: absolute; 20 | height: 70px; 21 | left: 44%; 22 | margin: -0.5%; */ 23 | 24 | /* } */ 25 | 26 | main { 27 | max-width: 65%; 28 | min-height: 60%; 29 | border-radius: 6px; 30 | background: #fff; 31 | padding: 10px 40px 40px 40px; 32 | margin: 0 auto; 33 | margin-top: -130px; 34 | 35 | box-shadow: 0px 1px 5px rgba(0, 0, 0, 0.7); 36 | } 37 | 38 | main h3 { 39 | padding: 2px 0px 20px; 40 | font-size: 20px; 41 | } 42 | 43 | main h3 span { 44 | float: right; 45 | font-size: 16px; 46 | color: #502a75; 47 | padding-top: 30px; 48 | } 49 | 50 | .formClass { 51 | text-align: center; 52 | top: 40px; 53 | padding: 20px; 54 | } 55 | .form-group { 56 | padding: 7px; 57 | border-radius: 20px; 58 | } 59 | input { 60 | height: 20px; 61 | width: 250px; 62 | border-radius: 5px; 63 | padding: 10px; 64 | background-color: #eaebff; 65 | border-color: #eaebff; 66 | border: 0px; 67 | } 68 | .btn { 69 | padding: 10px; 70 | height: 40px; 71 | width: 80px; 72 | margin: 15px; 73 | background-color: #502a75; 74 | color: white; 75 | border-radius: 5px; 76 | cursor: pointer; 77 | font-size: 17px; 78 | } 79 | 80 | a { 81 | text-decoration: none; 82 | } 83 | 84 | @media (max-width: 890px) { 85 | main { 86 | max-width: 85%; 87 | padding: 5%; 88 | } 89 | 90 | .myHeader h1 { 91 | font-size: 25px; 92 | margin-top: 1rem; 93 | margin-bottom: 1rem; 94 | } 95 | 96 | main h3 { 97 | font-size: 15px; 98 | } 99 | 100 | main h3 span { 101 | font-size: 12px; 102 | } 103 | 104 | input { 105 | height: 20px; 106 | width: 230px; 107 | border-radius: 5px; 108 | padding: 10px; 109 | background-color: #eaebff; 110 | border-color: #eaebff; 111 | border: 0px; 112 | } 113 | .form-group { 114 | padding: 7px; 115 | border-radius: 20px; 116 | width: 100%; 117 | } 118 | 119 | /* span svg{ 120 | display: block; 121 | position: absolute; 122 | height: 70px; 123 | left: 44%; 124 | margin: -0.5%; 125 | */ 126 | /* } */ 127 | } 128 | 129 | button { 130 | cursor: pointer; 131 | } 132 | 133 | :root { 134 | --chatboxH: 45px; 135 | --headerH: 55px; 136 | --paddingF: 10px; 137 | } 138 | 139 | .textA { 140 | position: relative; 141 | width: 100%; 142 | height: 100%; 143 | } 144 | 145 | #message { 146 | box-sizing: border-box; 147 | resize: none; 148 | padding: 8px 10px; 149 | border-radius: 20px; 150 | border: none; 151 | background: rgb(230, 227, 255); 152 | outline: none; 153 | color: #502a75; 154 | width: 100%; 155 | font-size: 17px; 156 | position: absolute; 157 | width: 100%; 158 | bottom: 0; 159 | } 160 | 161 | .button-s1 { 162 | width: 40px; 163 | padding: 5px; 164 | border-radius: 6rem; 165 | border: none; 166 | background: transparent; 167 | outline: none; 168 | color: #502a75; 169 | height: 40px; 170 | display: flex; 171 | align-items: center; 172 | justify-content: center; 173 | } 174 | 175 | .button-s1 span { 176 | font-size: calc(40px / 1.5) !important; 177 | } 178 | 179 | .messageBox { 180 | box-sizing: border-box; 181 | background-color: white; 182 | bottom: 0; 183 | left: 0; 184 | width: 100%; 185 | height: calc(var(--chatboxH) + 5px); 186 | display: flex; 187 | align-items: center; 188 | padding: 7px 0px; 189 | } 190 | 191 | .mMess .messArea .textM { 192 | background-color: blue; 193 | color: white; 194 | margin-left: auto; 195 | transition: 0.6s cubic-bezier(0.07, 0.76, 0.13, 1.02); 196 | } 197 | 198 | .messArea .textM a { 199 | font-weight: 500; 200 | } 201 | 202 | .new::-webkit-scrollbar{ 203 | width: 6px; 204 | } 205 | 206 | .new::-webkit-scrollbar-track { 207 | background-color: white; 208 | border-radius: 100px; 209 | } 210 | 211 | .new::-webkit-scrollbar-thumb { 212 | background-color: #502A75; 213 | border-radius: 100px; 214 | } -------------------------------------------------------------------------------- /src/Dashboard.css: -------------------------------------------------------------------------------- 1 | html { 2 | font-family: sans-serif; 3 | } 4 | * { 5 | margin: 0; 6 | padding: 0; 7 | } 8 | 9 | .myHeader { 10 | background: #502a75; 11 | color: #fff; 12 | padding: 20px 20px 20px; 13 | height: 190px; 14 | margin-bottom: 1rem; 15 | } 16 | 17 | /* span svg{ 18 | display: block; 19 | position: absolute; 20 | height: 70px; 21 | left: 44%; 22 | margin: -0.5%; */ 23 | 24 | /* } */ 25 | 26 | main { 27 | max-width: 65%; 28 | min-height: 60%; 29 | border-radius: 6px; 30 | background: #fff; 31 | padding: 10px 40px 40px 40px; 32 | margin: 0 auto; 33 | margin-top: -130px; 34 | 35 | box-shadow: 0px 1px 5px rgba(0, 0, 0, 0.7); 36 | } 37 | 38 | main h3 { 39 | padding: 2px 0px 20px; 40 | font-size: 20px; 41 | } 42 | 43 | main h3 span { 44 | float: right; 45 | font-size: 16px; 46 | color: #502a75; 47 | padding-top: 30px; 48 | } 49 | 50 | .formClass { 51 | text-align: center; 52 | top: 40px; 53 | padding: 20px; 54 | } 55 | .form-group { 56 | padding: 7px; 57 | border-radius: 20px; 58 | } 59 | input { 60 | height: 20px; 61 | width: 250px; 62 | border-radius: 5px; 63 | padding: 10px; 64 | background-color: #eaebff; 65 | border-color: #eaebff; 66 | border: 0px; 67 | } 68 | .btn { 69 | padding: 10px; 70 | height: 40px; 71 | width: 80px; 72 | margin: 15px; 73 | background-color: #502a75; 74 | color: white; 75 | border-radius: 5px; 76 | cursor: pointer; 77 | font-size: 17px; 78 | } 79 | 80 | a { 81 | text-decoration: none; 82 | } 83 | 84 | .container { 85 | display: grid; 86 | grid-gap: 0.5rem; 87 | grid-template-columns: repeat(3, 1fr); 88 | } 89 | 90 | @media (max-width: 890px) { 91 | .container { 92 | grid-template-columns: repeat(2, 1fr); 93 | } 94 | 95 | main { 96 | max-width: 85%; 97 | padding: 5%; 98 | } 99 | 100 | .myHeader h1 { 101 | font-size: 25px; 102 | margin-top: 1rem; 103 | margin-bottom: 1rem; 104 | } 105 | 106 | main h3 { 107 | font-size: 15px; 108 | } 109 | 110 | main h3 span { 111 | font-size: 12px; 112 | } 113 | 114 | input { 115 | height: 20px; 116 | width: 230px; 117 | border-radius: 5px; 118 | padding: 10px; 119 | background-color: #eaebff; 120 | border-color: #eaebff; 121 | border: 0px; 122 | } 123 | .form-group { 124 | padding: 7px; 125 | border-radius: 20px; 126 | width: 100%; 127 | } 128 | 129 | /* span svg{ 130 | display: block; 131 | position: absolute; 132 | height: 70px; 133 | left: 44%; 134 | margin: -0.5%; 135 | */ 136 | /* } */ 137 | } 138 | 139 | button { 140 | cursor: pointer; 141 | } 142 | 143 | :root { 144 | --chatboxH: 45px; 145 | --headerH: 55px; 146 | --paddingF: 10px; 147 | } 148 | 149 | .textA { 150 | position: relative; 151 | width: 100%; 152 | height: 100%; 153 | } 154 | 155 | #message { 156 | box-sizing: border-box; 157 | resize: none; 158 | padding: 8px 10px; 159 | border-radius: 20px; 160 | border: none; 161 | background: rgb(230, 227, 255); 162 | outline: none; 163 | color: #502a75; 164 | width: 100%; 165 | font-size: 17px; 166 | position: absolute; 167 | width: 100%; 168 | bottom: 0; 169 | } 170 | 171 | .button-s1 { 172 | width: 40px; 173 | padding: 5px; 174 | border-radius: 6rem; 175 | border: none; 176 | background: transparent; 177 | outline: none; 178 | color: #502a75; 179 | height: 40px; 180 | display: flex; 181 | align-items: center; 182 | justify-content: center; 183 | } 184 | 185 | .button-s1 span { 186 | font-size: calc(40px / 1.5) !important; 187 | } 188 | 189 | .container::-webkit-scrollbar { 190 | width: 6px; 191 | } 192 | 193 | .container::-webkit-scrollbar-track { 194 | background-color: white; 195 | border-radius: 100px; 196 | } 197 | 198 | .container::-webkit-scrollbar-thumb { 199 | background-color: #502a75; 200 | border-radius: 100px; 201 | } 202 | 203 | .box { 204 | background-color: #f4f4f4; 205 | /* border: 2px solid black; */ 206 | border-radius: 10px; 207 | height: 61px; 208 | box-sizing: border-box; 209 | padding: 13px; 210 | margin: 2px; 211 | box-shadow: 0px 1px 5px rgba(114, 112, 112, 0.3); 212 | } 213 | 214 | @media (max-width: 530px) { 215 | .container { 216 | grid-template-columns: repeat(1, 1fr); 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /src/Dashboard.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect, useContext } from "react"; 2 | import { Link, useLocation } from "react-router-dom"; 3 | import { auth, db } from "../src/firebase"; 4 | import { useHistory } from "react-router-dom"; 5 | import { signOut } from "firebase/auth"; 6 | import { updateDoc, doc } from "firebase/firestore"; 7 | import { AuthContext } from "./auth"; 8 | import { collection, query, where, onSnapshot } from "firebase/firestore"; 9 | import { usePageVisibility } from "react-page-visibility"; 10 | import "./Dashboard.css"; 11 | 12 | export default function Dashboard() { 13 | const isVisible = usePageVisibility(); 14 | 15 | const { user } = useContext(AuthContext); 16 | 17 | const history = useHistory(); 18 | 19 | const location = useLocation(); 20 | 21 | const handleSignOut = async () => { 22 | await updateDoc(doc(db, "users", user.uid), { 23 | isOnline: false 24 | }); 25 | await signOut(auth); 26 | history.push("/"); 27 | }; 28 | 29 | const [users, setUsers] = useState([]); 30 | const [myuser, setMyUser] = useState([]); 31 | 32 | useEffect(() => { 33 | const usersRef = collection(db, "users"); 34 | 35 | // create query object 36 | const myname = query(usersRef, where("uid", "in", [auth.currentUser.uid])); 37 | 38 | console.log("hi--", myname); 39 | const q = query(usersRef); 40 | //execute query 41 | const unsub = onSnapshot(q, (querySnapshot) => { 42 | let users = []; 43 | querySnapshot.forEach((doc) => { 44 | users.push(doc.data()); 45 | }); 46 | setMyUser(users.find((e) => e.uid === auth.currentUser.uid)); 47 | setUsers(users.filter((e) => e.uid != auth.currentUser.uid)); 48 | }); 49 | return () => unsub(); 50 | }, []); 51 | 52 | console.log(myuser); 53 | 54 | return ( 55 |
56 |
57 | 58 | 70 | 71 | 72 | 73 | 74 |

82 | Web Chat Pingo! 83 |

84 |
85 |
86 |
87 | 88 | handleSignOut()} 102 | > 103 | {" "} 109 | 110 | 111 |
112 |

117 | Available User 118 |

119 |
120 | 121 |
122 |
134 | {users.map((user) => { 135 | return ( 136 | 143 |
144 |

145 | {user.name} 146 | 147 | {user.isOnline ? ( 148 | 149 | ) : ( 150 | 151 | )} 152 |

153 |

{user.email}

154 |
155 | 156 | ); 157 | })} 158 |
159 |
160 |
161 |
162 |
163 | ); 164 | } 165 | -------------------------------------------------------------------------------- /src/Home.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useContext } from "react"; 2 | import { Link } from "react-router-dom"; 3 | import { signInWithEmailAndPassword } from "firebase/auth"; 4 | import { auth, db } from "../src/firebase"; 5 | import { updateDoc, doc } from "firebase/firestore"; 6 | import { useHistory } from "react-router-dom"; 7 | import "./Home.css"; 8 | const Home = () => { 9 | const history = useHistory(); 10 | const [data, setData] = useState({ 11 | email: "", 12 | password: "", 13 | error: null, 14 | loading: false 15 | }); 16 | const { email, password, error, loading } = data; 17 | const handleChange = (e) => { 18 | setData({ ...data, [e.target.name]: e.target.value }); 19 | }; 20 | 21 | const handleSubmit = async (e) => { 22 | e.preventDefault(); 23 | 24 | setData({ ...data, error: null, loading: true }); 25 | console.log(data); 26 | if (!data.email || !data.password) { 27 | setData({ ...data, error: "All fields are required!" }); 28 | return; 29 | } 30 | try { 31 | const result = await signInWithEmailAndPassword(auth, email, password); 32 | await updateDoc(doc(db, "users", result.user.uid), { 33 | isOnline: true 34 | }); 35 | 36 | history.push("/dashboard"); 37 | } catch (err) { 38 | console.log(err.message); 39 | setData({ ...data, error: err.message, loading: false }); 40 | } 41 | }; 42 | return ( 43 |
44 |
45 | 46 | 58 | 59 | 60 | 61 | 62 |

70 | Web Chat Pingo! 71 |

72 |
73 | 74 |
75 |
76 |

84 | Login 85 |

86 | 87 |
88 |
89 |
90 | 91 | 103 | 108 | 109 | 110 | 117 |
118 | 119 |
120 | 121 | 133 | 138 | 139 | 140 | 147 |
148 | 149 | 150 |
151 | {loading ? "Please Wait..." : ""} 152 |
153 |
{error ? error : ""}
154 |
155 |
Don't have an Account?
156 | Register Here 157 |
158 |
159 |
160 |
161 |
162 |
163 | ); 164 | }; 165 | 166 | export default Home; 167 | -------------------------------------------------------------------------------- /src/Register.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { Link } from "react-router-dom"; 3 | import { createUserWithEmailAndPassword } from "firebase/auth"; 4 | import { auth, db } from "../src/firebase"; 5 | import { setDoc, doc, Timestamp } from "firebase/firestore"; 6 | import { useHistory } from "react-router-dom"; 7 | import "./Register.css"; 8 | 9 | const Register = () => { 10 | const history = useHistory(); 11 | const [data, setData] = useState({ 12 | name: "", 13 | email: "", 14 | password: "", 15 | error: null, 16 | loading: false 17 | }); 18 | const { name, email, password, error, loading } = data; 19 | const handleChange = (e) => { 20 | setData({ ...data, [e.target.name]: e.target.value }); 21 | }; 22 | 23 | const handleSubmit = async (e) => { 24 | e.preventDefault(); 25 | console.log(data); 26 | setData({ ...data, error: null, loading: true }); 27 | if (!data.name || !data.email || !data.password) { 28 | setData({ ...data, error: "All fields are required!" }); 29 | return; 30 | } 31 | try { 32 | const result = await createUserWithEmailAndPassword( 33 | auth, 34 | email, 35 | password 36 | ); 37 | console.log(result.user); 38 | await setDoc(doc(db, "users", result.user.uid), { 39 | uid: result.user.uid, 40 | name, 41 | email, 42 | createdAt: Timestamp.fromDate(new Date()), 43 | isOnline: true 44 | }); 45 | setData({ 46 | name: "", 47 | email: "", 48 | password: "", 49 | error: null, 50 | loading: false 51 | }); 52 | history.push("/dashboard", name); 53 | } catch (err) { 54 | console.log(err.message); 55 | setData({ ...data, error: err.message, loading: false }); 56 | } 57 | }; 58 | return ( 59 |
60 |
61 | 62 | 74 | 75 | 76 | 77 | 78 |

86 | Web Chat Pingo! 87 |

88 |
89 | 90 |
91 |
92 |

100 | Register 101 |

102 |
103 |
104 |
105 | 106 | {" "} 107 | 119 | 120 | 125 | 126 | 127 | 134 |
135 |
136 | 137 | 149 | 154 | 155 | 156 | 163 |
164 |
165 | 166 | 178 | 183 | 184 | 185 | 192 |
193 | 194 | 197 | 198 |
199 | {loading ? "Please Wait..." : ""} 200 |
201 | 202 |
{data.error ? data.error : ""}
203 |
204 |
Already have an account?
205 | Login Here 206 |
207 |
208 |
209 |
210 |
211 |
212 | ); 213 | }; 214 | 215 | export default Register; 216 | -------------------------------------------------------------------------------- /src/SelectedUser.js: -------------------------------------------------------------------------------- 1 | import { useRef, useState, useContext, useEffect } from "react"; 2 | import { Link, useLocation } from "react-router-dom"; 3 | import { auth, db, storage } from "../src/firebase"; 4 | import FileUpload from "./FileUpload"; 5 | import { AuthContext } from "./auth"; 6 | import "./SelectedUser.css"; 7 | import SendIcon from "@mui/icons-material/Send"; 8 | import UploadIcon from "@mui/icons-material/Upload"; 9 | import { 10 | collection, 11 | query, 12 | where, 13 | orderBy, 14 | onSnapshot, 15 | addDoc, 16 | Timestamp, 17 | QuerySnapshot, 18 | setDoc, 19 | doc, 20 | updateDoc 21 | } from "firebase/firestore"; 22 | import { 23 | ref, 24 | getDownloadURL, 25 | uploadBytes, 26 | deleteObject 27 | } from "firebase/storage"; 28 | import Messages from "./Messages"; 29 | import { usePageVisibility } from "react-page-visibility"; 30 | 31 | export default function SelectedUser() { 32 | const { user } = useContext(AuthContext); 33 | const user1id = user.uid; 34 | console.log(user1id); 35 | let location = useLocation(); 36 | const user2 = location.state; 37 | var isVisible = usePageVisibility(); 38 | 39 | const [text, setText] = useState(""); 40 | const [img, setImg] = useState(""); 41 | const [msgs, setMsgs] = useState([]); 42 | const [data, setData] = useState(""); 43 | const [uploading, setUploading] = useState(false); 44 | 45 | const user2uid = user2.uid; 46 | const id = 47 | user1id > user2uid ? `${user1id + user2uid}` : `${user2uid + user1id}`; 48 | const addDocMsg = async (url) => { 49 | await addDoc(collection(db, "messages", id, "chat"), { 50 | text, 51 | from: user1id, 52 | to: user2uid, 53 | createdAt: Timestamp.fromDate(new Date()), 54 | media: url || "" 55 | }); 56 | await setDoc(doc(db, "lastMsg", id), { 57 | text, 58 | from: user1id, 59 | to: user2uid, 60 | createdAt: Timestamp.fromDate(new Date()), 61 | media: url || "", 62 | unread: true 63 | }); 64 | setText(""); 65 | setImg(""); 66 | }; 67 | 68 | const handleSubmit = async (e) => { 69 | e.preventDefault(); 70 | 71 | let url; 72 | if (img) { 73 | setUploading(true); 74 | const imgRef = ref( 75 | storage, 76 | `images/${new Date().getTime()} - ${img.name}` 77 | ); 78 | const snap = await uploadBytes(imgRef, img); 79 | const dlUrl = await getDownloadURL(ref(storage, snap.ref.fullPath)); 80 | url = dlUrl; 81 | 82 | addDocMsg(url); 83 | setUploading(false); 84 | return; 85 | } 86 | addDocMsg(); 87 | }; 88 | 89 | useEffect(() => { 90 | const msgRef = collection(db, "messages", id, "chat"); 91 | const _q = query(msgRef, orderBy("createdAt", "asc")); 92 | onSnapshot(_q, (querySnapshot) => { 93 | let msgs = []; 94 | querySnapshot.forEach((doc) => { 95 | msgs.push(doc.data()); 96 | }); 97 | setMsgs(msgs); 98 | }); 99 | let unsub = onSnapshot(doc(db, "lastMsg", id), (doc) => { 100 | setData(doc.data()); 101 | }); 102 | return () => unsub(); 103 | }, []); 104 | useEffect(() => { 105 | console.log("file uploaded"); 106 | }, [setImg]); 107 | 108 | useEffect(async () => { 109 | console.log(isVisible); 110 | await updateDoc(doc(db, "users", user.uid), { 111 | isOnline: isVisible 112 | }); 113 | user2.isOnline = isVisible; 114 | }, [isVisible]); 115 | console.log(data); 116 | return ( 117 |
118 |
119 | 120 | 132 | 133 | 134 | 135 | 136 |

144 | Web Chat Pingo! 145 |

146 |
147 |
148 |
149 | 150 | 163 | 168 | 169 | 170 |
171 |

176 | {user2.name}{" "} 177 | {user2.isOnline ? ( 178 | 179 | ) : ( 180 | 181 | )} 182 |

183 | {user2.email} 184 | 185 |
186 | 187 |
199 | {msgs.length 200 | ? msgs.map((msg, i) => ( 201 | 202 | )) 203 | : null} 204 |
205 | 206 | {/*
214 |
224 | 251 |
252 |
253 | 269 |
270 |
279 | 291 | 296 | 297 |
298 |
*/} 299 |
304 |
305 | setImg(e.target.files[0])} 307 | type="file" 308 | id="upimg" 309 | accept="image/*" 310 | style={{ display: "none" }} 311 | /> 312 | 313 | 326 | 327 |
328 |