├── .firebase ├── firebase.js └── hosting.YnVpbGQ.cache ├── .firebaserc ├── .github └── workflows │ ├── CI CD.yml │ ├── main.yml │ └── npm-publish-github-packages.yml ├── .gitignore ├── Counter.js ├── LICENSE ├── README.md ├── firebase.json ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt ├── src ├── App.css ├── App.js ├── Chat.css ├── Chat.js ├── ChatHeader.css ├── ChatHeader.js ├── Login.css ├── Login.js ├── Message.css ├── Message.js ├── Sidebar.css ├── Sidebar.js ├── SidebarChannel.css ├── SidebarChannel.js ├── app │ └── store.js ├── features │ ├── appSlice.js │ └── userSlice.js ├── firebase.js ├── index.css ├── index.js ├── serviceWorker.js └── static │ ├── .picasa.ini │ ├── .picasaoriginals │ ├── .picasa.ini │ ├── discord1.png │ └── discord2.png │ ├── discord1.jpg │ └── discord2.jpg └── yarn.lock /.firebase/firebase.js: -------------------------------------------------------------------------------- 1 | import firebase from "firebase"; 2 | // For Firebase JS SDK v7.20.0 and later, measurementId is optional 3 | //GET Below Settings from Firebase > Project Overview > Settings > General > Your apps > Firebase SDK snippet > Config 4 | const firebaseConfig = { 5 | apiKey: "AIzaSyDC_O7A0m2570Qgi_lgetjOuWOzD-YG2uw", 6 | authDomain: "alanbinu-discord.firebaseapp.com", 7 | projectId: "alanbinu-discord", 8 | storageBucket: "alanbinu-discord.appspot.com", 9 | messagingSenderId: "18335583725", 10 | appId: "1:18335583725:web:e0ae17ac99468f9390abe4", 11 | measurementId: "G-E01KDMM4CE", 12 | }; 13 | 14 | const firebaseApp = firebase.initializeApp(firebaseConfig); 15 | const db = firebaseApp.firestore(); 16 | const auth = firebaseApp.auth(); 17 | const provider = new firebase.auth.GoogleAuthProvider(); 18 | 19 | export { auth, provider }; 20 | export default db; 21 | -------------------------------------------------------------------------------- /.firebase/hosting.YnVpbGQ.cache: -------------------------------------------------------------------------------- 1 | favicon.ico,1602222537905,87aecafb41d21ea93b7af81398237b980c43ee880676c78630ee2d1e9a6d711f 2 | logo192.png,1602222537905,ebf380e341383bbf65d6a762603f630a9f7610c97f32bd297bc097635bc37ee3 3 | logo512.png,1602222537905,c55d034d9cf7abfac7600919cbf4f4ce2f3ea3478df91d01177e8ff136f58918 4 | manifest.json,1602222537905,5c997de1364b8be939319fa9209abd77f2caf7f8844999a9e2e9173f844e7840 5 | robots.txt,1602222537905,53d2dcecb15666b4f7db4041361d3b6ea115483937dd38b66216deda98adaa4a 6 | asset-manifest.json,1602833173653,f48671a59da0a9a2818351035fab09423b3550bf10c98a0d3a6873d27c7ef4f8 7 | index.html,1602833173653,46cd8ec6f8a07b8df7f856aa7111f5b03f40978b2fe50b90ab361d1a8f097697 8 | precache-manifest.0d14fc2f63a149b0d2e8e163442c44f6.js,1602833173653,f46a23387c4179b23d83990bd5686827ba718a2a83f202a573e3420d97e88c60 9 | static/css/main.31946349.chunk.css,1602833173654,cd93b4830f5961129d62ee1cedfc60035da938ba5d7b0ed9f988111989bb8721 10 | service-worker.js,1602833173653,8774246c1b4ba3b116a1660ec9e64dadef2207c6fd98dd19eb66fd218dec7691 11 | static/css/main.31946349.chunk.css.map,1602833173671,5a96964e336c2050f29e05118ffc03a291bd9c21794e694d1d509e85b069ba5f 12 | static/js/2.52573a50.chunk.js.LICENSE.txt,1602833173671,740a3d67d928b4f4300177a28f340a88651f33f67ab5302fc8db29e8b7d5a5b6 13 | static/js/main.9ffae774.chunk.js,1602833173654,cfbe75066a13e4cae2c352fd006eb966c762482438484236a75343396f5c1ee8 14 | static/js/runtime-main.a881be68.js,1602833173671,36635f8edd36177f4bf13d1bbcda0071393de4bebd6ff43639777461b00bcb0a 15 | static/js/runtime-main.a881be68.js.map,1602833173671,188cb1e3c0d6c014fd9d89b4559642ba2a121a89df35163d2d5930330d2e04a1 16 | static/js/main.9ffae774.chunk.js.map,1602833173671,71e6c7cadfe62833f61ed6081de14930bb396776821d803ca31ac75c71f99739 17 | static/js/2.52573a50.chunk.js,1602833173671,8fe82a3e7cc669a97f6143e07b2a55d4362cfdab00da6c107e5dc2f8131c1840 18 | static/js/2.52573a50.chunk.js.map,1602833173672,07d8e479b0f2f20c2ee9845777cf29567cde8e0081219202629c154a01326004 19 | -------------------------------------------------------------------------------- /.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "discord-clone-rdx" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.github/workflows/CI CD.yml: -------------------------------------------------------------------------------- 1 | name: CI + CD 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | Staging: 8 | runs-on: ubuntu-latest 9 | environment: Staging 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: Run a script 13 | run: echo "Running in Staging" 14 | 15 | Quality_Assurance: 16 | runs-on: ubuntu-latest 17 | environment: Quality_Assurance 18 | needs: Staging 19 | steps: 20 | - uses: actions/checkout@v2 21 | - name: Run a script 22 | run: echo "Running in QA" 23 | 24 | Production: 25 | runs-on: ubuntu-latest 26 | environment: Development 27 | needs: Quality_Assurance 28 | steps: 29 | - uses: actions/checkout@v2 30 | - name: Run a script 31 | run: echo "Running in production" 32 | 33 | Development: 34 | runs-on: ubuntu-latest 35 | environment: Production 36 | needs: Production 37 | steps: 38 | - uses: actions/checkout@v2 39 | - name: Run a script 40 | run: echo "Deployed" 41 | 42 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Node.js Package 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | publish-gpr: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: actions/setup-node@v1 14 | with: 15 | node-version: 12 16 | registry-url: https://npm.pkg.github.com/ 17 | scope: '@AlanBinu007' 18 | - run: npm install 19 | - run: npm publish 20 | env: 21 | NODE_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} 22 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish-github-packages.yml: -------------------------------------------------------------------------------- 1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages 3 | 4 | name: Node.js Package 5 | 6 | on: 7 | release: 8 | types: [created] 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: actions/setup-node@v2 16 | with: 17 | node-version: 16 18 | - run: npm ci 19 | - run: npm test 20 | 21 | publish-gpr: 22 | needs: build 23 | runs-on: ubuntu-latest 24 | permissions: 25 | contents: read 26 | packages: write 27 | steps: 28 | - uses: actions/checkout@v2 29 | - uses: actions/setup-node@v2 30 | with: 31 | node-version: 16 32 | registry-url: https://npm.pkg.github.com/ 33 | - run: npm ci 34 | - run: npm publish 35 | env: 36 | NODE_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} 37 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /Counter.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { useSelector, useDispatch } from 'react-redux'; 3 | import { 4 | decrement, 5 | increment, 6 | incrementByAmount, 7 | incrementAsync, 8 | selectCount, 9 | } from './src/features/counterSlice'; 10 | import styles from './Counter.css'; 11 | 12 | export function Counter() { 13 | const count = useSelector(selectCount); 14 | const dispatch = useDispatch(); 15 | const [incrementAmount, setIncrementAmount] = useState('2'); 16 | 17 | return ( 18 |
19 |
20 | 27 | {count} 28 | 35 |
36 |
37 | setIncrementAmount(e.target.value)} 42 | /> 43 | 51 | 57 |
58 |
59 | ); 60 | } 61 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Rupak Dey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Dicord Clone Using ReactJs 4 | 5 | **Project Link** - ***https://alanbinu-discord.web.app/*** 6 | 7 | ## Tech We Used 8 | 9 | - ReactJS 10 | - Firebase Hosting 11 | - Material UI 12 | - axios API 13 | - React-Dom 14 | - Firebase Cloud Storage 15 | - Google Auth for login 16 | 17 | ## Features 18 | 19 | - Create Group 20 | - Can send message to group and to individuals 21 | - Neat and clean UI 22 | 23 | 24 | ## Steps to run in your machine 25 | 26 | #### RMake the changes 27 | First go to https://console.firebase.google.com/ and create a project and copy your app config to firebase.js 28 | 29 | #### Run the following commands 30 | ``` 31 | npm i 32 | npm run start 33 | ``` 34 | 35 | #### Hope you liked this project, don't forget to ⭐ the repo. 36 | -------------------------------------------------------------------------------- /firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "build", 4 | "ignore": [ 5 | "firebase.json", 6 | "**/.*", 7 | "**/node_modules/**" 8 | ], 9 | "rewrites": [ 10 | { 11 | "source": "**", 12 | "destination": "/index.html" 13 | } 14 | ] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "discord-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 | "@reduxjs/toolkit": "^1.1.0", 9 | "@testing-library/jest-dom": "^4.2.4", 10 | "@testing-library/react": "^9.3.2", 11 | "@testing-library/user-event": "^7.1.2", 12 | "firebase": "^7.23.0", 13 | "react": "^16.13.1", 14 | "react-dom": "^16.13.1", 15 | "react-redux": "^7.1.3", 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 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanBinu007/React-discord-clone/336c8dcba2f1771515a727b0b5d3bf6a199c6cab/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React Redux App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanBinu007/React-discord-clone/336c8dcba2f1771515a727b0b5d3bf6a199c6cab/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanBinu007/React-discord-clone/336c8dcba2f1771515a727b0b5d3bf6a199c6cab/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | .app { 2 | display:flex 3 | } -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React, {useEffect} from 'react'; 2 | 3 | import './App.css'; 4 | 5 | import './Sidebar' 6 | import Sidebar from './Sidebar'; 7 | import Chat from './Chat' 8 | import Login from './Login' 9 | 10 | import { useDispatch, useSelector } from 'react-redux' 11 | import { selectUser } from './features/userSlice' 12 | import { auth } from './firebase' 13 | 14 | import { login, logout } from './features/userSlice' 15 | 16 | function App() { 17 | const dispatch = useDispatch() 18 | const user = useSelector(selectUser) 19 | 20 | useEffect(() => { 21 | auth.onAuthStateChanged((authUser) => { 22 | if (authUser) { 23 | dispatch(login({ 24 | uid : authUser.uid, 25 | photo: authUser.photoURL, 26 | email: authUser.email, 27 | displayName : authUser.displayName, 28 | })) 29 | } 30 | else { 31 | dispatch(logout()) 32 | } 33 | }) 34 | }, [dispatch]) 35 | 36 | return ( 37 |
38 | { user ? ( 39 | <> 40 | 41 | 42 | 43 | ) : ( 44 | 45 | )} 46 | 47 |
48 | ); 49 | } 50 | 51 | export default App; 52 | -------------------------------------------------------------------------------- /src/Chat.css: -------------------------------------------------------------------------------- 1 | .chat { 2 | display: flex; 3 | flex-direction: column; 4 | flex: 0.75; 5 | background-color: #363a3f; 6 | height: 100vh; 7 | } 8 | 9 | .chat__input { 10 | color: lightgray; 11 | display: flex; 12 | align-items: center; 13 | justify-content: space-between; 14 | padding: 15px; 15 | border-radius: 5px; 16 | margin: 20px; 17 | border-top: 1px solid gray; 18 | background-color: #474b53; 19 | } 20 | 21 | .chat__input > form > input { 22 | padding: 15px; 23 | background: transparent; 24 | border: none; 25 | outline-width: 0; 26 | color: white; 27 | font-size: large; 28 | width: 100%; 29 | } 30 | 31 | .chat__input > form { 32 | flex: 1; 33 | } 34 | 35 | .chat__inputButton { 36 | display: none; 37 | } 38 | 39 | .chat__inputIcons > .MuiSvgIcon-root { 40 | padding: 5px; 41 | } 42 | 43 | .chat__messages { 44 | flex: 1; 45 | overflow: scroll; 46 | } 47 | 48 | .chat__messages::-webkit-scrollbar { 49 | display: none; 50 | } 51 | 52 | .chat__messages { 53 | -ms-overflow-style: none; 54 | scrollbar-width: none; 55 | } -------------------------------------------------------------------------------- /src/Chat.js: -------------------------------------------------------------------------------- 1 | import React, {useState, useEffect} from 'react' 2 | import './Chat.css' 3 | import ChatHeader from './ChatHeader' 4 | import Message from './Message' 5 | import AddCircleIcon from '@material-ui/icons/AddCircle'; 6 | import CreditCardIcon from '@material-ui/icons/CreditCard'; 7 | import GifIcon from '@material-ui/icons/Gif'; 8 | import EmojiEmotionsIcon from '@material-ui/icons/EmojiEmotions'; 9 | 10 | import {useSelector} from 'react-redux' 11 | import firebase from 'firebase' 12 | 13 | import {selectChannelId, selectChannelName} from './features/appSlice' 14 | import {selectUser} from './features/userSlice' 15 | import db from './firebase' 16 | 17 | function Chat() { 18 | const user = useSelector(selectUser) 19 | const channelId = useSelector(selectChannelId) 20 | const channelName = useSelector(selectChannelName) 21 | const [input, setInput] = useState("") 22 | const [messages, setMessages] = useState([]) 23 | 24 | useEffect(() => { 25 | if(channelId){ 26 | db.collection("channels").doc(channelId).collection("messages").orderBy('timestamp', 'asc').onSnapshot(snapshot => 27 | setMessages(snapshot.docs.map((doc) => doc.data())) 28 | )} 29 | }, [channelId]) 30 | 31 | const sendMessage = e => { 32 | e.preventDefault() 33 | 34 | db.collection('channels').doc(channelId).collection('messages').add({ 35 | message: input, 36 | user: user, 37 | timestamp: firebase.firestore.FieldValue.serverTimestamp() 38 | }) 39 | 40 | setInput("") 41 | } 42 | 43 | return ( 44 |
45 | 46 | 47 |
48 | {messages.map(message => ( 49 | 54 | ))} 55 |
56 | 57 |
58 | 59 |
60 | setInput(e.target.value)} 64 | type="text" 65 | placeholder={`Send to #${channelName}`} 66 | /> 67 | 75 |
76 | 77 |
78 | 79 | 80 | 81 |
82 |
83 |
84 | ) 85 | } 86 | 87 | export default Chat 88 | -------------------------------------------------------------------------------- /src/ChatHeader.css: -------------------------------------------------------------------------------- 1 | .chatHeader { 2 | display: flex; 3 | justify-content: space-between; 4 | align-items: center; 5 | color: gray; 6 | padding: 10px; 7 | } 8 | 9 | .chatHeader__hash { 10 | color: gray; 11 | font-size: 30px; 12 | padding: 10px; 13 | } 14 | 15 | .chatHeader__left > h3 { 16 | display: flex; 17 | align-items: center; 18 | color: white; 19 | } 20 | 21 | .chatHeader__right { 22 | display: flex; 23 | align-items: center; 24 | flex: 0.5; 25 | justify-content: space-between; 26 | } 27 | 28 | .chatHeader__right > .MuiSvgIcon-root { 29 | padding: 5px; 30 | cursor: pointer; 31 | } 32 | 33 | .chatHeader__right > .MuiSvgIcon-root:hover { 34 | color: white; 35 | } 36 | 37 | .chatHeader__search { 38 | display: flex; 39 | align-items: center; 40 | color: gray; 41 | background-color: #2f3135; 42 | border-radius: 3px; 43 | padding: 3px; 44 | } 45 | 46 | .chatHeader__search > input { 47 | background: transparent; 48 | outline-width: 0; 49 | color: white; 50 | border: none; 51 | } -------------------------------------------------------------------------------- /src/ChatHeader.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import './ChatHeader.css' 3 | 4 | import NotificationsIcon from '@material-ui/icons/Notifications'; 5 | import LocationOnIcon from '@material-ui/icons/LocationOn'; 6 | import PeopleAltIcon from '@material-ui/icons/PeopleAlt'; 7 | import SearchIcon from '@material-ui/icons/Search'; 8 | import SendIcon from '@material-ui/icons/Send'; 9 | import HelpIcon from '@material-ui/icons/Help'; 10 | 11 | function ChatHeader({channelName}) { 12 | return ( 13 |
14 |
15 |

16 | 17 | # 18 | 19 | {channelName} 20 |

21 |
22 | 23 |
24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
32 | 33 | 34 | 35 |
36 |
37 | ) 38 | } 39 | 40 | export default ChatHeader 41 | -------------------------------------------------------------------------------- /src/Login.css: -------------------------------------------------------------------------------- 1 | .login { 2 | display: grid; 3 | place-items: center; 4 | height: 100vh; 5 | width: 100%; 6 | } 7 | 8 | .login > button { 9 | width: 300px; 10 | background-color: #738adb; 11 | color: #eff2f5; 12 | font-weight: 800; 13 | } 14 | 15 | .login > button:hover { 16 | background-color: black; 17 | color: #738adb; 18 | } 19 | 20 | .login > div > img { 21 | object-fit: contain; 22 | height: 150px; 23 | } -------------------------------------------------------------------------------- /src/Login.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import './Login.css' 3 | import { auth, provider } from './firebase' 4 | 5 | import { Button } from '@material-ui/core' 6 | 7 | function Login() { 8 | 9 | const signIn = () => { 10 | auth.signInWithPopup(provider).catch((error) => alert(error.message)); 11 | } 12 | 13 | return ( 14 |
15 | 16 |
17 | 18 |
19 | 20 | 21 | 22 |
23 | ) 24 | } 25 | 26 | export default Login 27 | -------------------------------------------------------------------------------- /src/Message.css: -------------------------------------------------------------------------------- 1 | .message { 2 | display: flex; 3 | align-items: center; 4 | padding: 20px; 5 | color: white; 6 | } 7 | 8 | .message__info { 9 | margin-left: 20px; 10 | } 11 | 12 | .message__timestamp { 13 | color: gray; 14 | margin-left: 20px; 15 | font-size: x-small; 16 | } 17 | 18 | -------------------------------------------------------------------------------- /src/Message.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import './Message.css' 3 | import { Avatar } from '@material-ui/core' 4 | 5 | function Message({timestamp, user, message}) { 6 | return ( 7 |
8 | 9 |
10 |

11 | {user.displayName} 12 | 13 | {new Date(timestamp?.toDate()).toUTCString()} 14 | 15 |

16 | 17 |

{message}

18 |
19 |
20 | ) 21 | } 22 | 23 | export default Message 24 | -------------------------------------------------------------------------------- /src/Sidebar.css: -------------------------------------------------------------------------------- 1 | .sidebar { 2 | display: flex; 3 | flex-direction: column; 4 | flex: 0.25; 5 | height: 100vh; 6 | background-color: #2f3135; 7 | overflow: scroll; 8 | -ms-overflow-style: none; 9 | scrollbar-width: none; 10 | } 11 | 12 | .sidebar::-webkit-scrollbar { 13 | display: none; 14 | } 15 | 16 | .sidebar__top { 17 | display: flex; 18 | justify-content: space-between; 19 | align-items: center; 20 | padding: 20px; 21 | background-color: #2f3135; 22 | color: white; 23 | border-bottom: 3px solid #26282c; 24 | } 25 | 26 | .sidebar__channels { 27 | flex: 1; 28 | 29 | } 30 | 31 | .sidebar__addChannel:hover { 32 | color: white; 33 | } 34 | 35 | .sidebar__channelsHeader { 36 | display: flex; 37 | justify-content: space-between; 38 | align-items: center; 39 | padding: 10px; 40 | background-color: #2f3135; 41 | color: gray; 42 | } 43 | 44 | .sidebar__header { 45 | display: flex; 46 | align-items: center; 47 | } 48 | 49 | .sidebar__voice, .sidebar__profile { 50 | display: flex; 51 | justify-content: space-between; 52 | align-items: center; 53 | color: gray; 54 | background-color: #2f3135; 55 | padding: 10px; 56 | border-top: 1px solid gray; 57 | } 58 | 59 | .sidebar__voiceInfo { 60 | flex: 1; 61 | padding: 10px; 62 | } 63 | 64 | .sidebar__voiceIcon { 65 | color: #4fb185; 66 | } 67 | 68 | .sidebar__voiceInfo > h3 { 69 | color: #4fb185; 70 | } 71 | 72 | .sidebar__voiceInfo> p { 73 | font-size: smaller; 74 | } 75 | 76 | .sidebar__voiceIcon > .MuiSvgIcon-root { 77 | padding: 10px; 78 | } 79 | 80 | .sidebar__profileInfo { 81 | flex: 1; 82 | padding: 10px; 83 | } 84 | 85 | .sidebar__profileInfo> h3 { 86 | color: white; 87 | } 88 | 89 | .sidebar__profileIcons> .MuiSvgIcon-root { 90 | padding: 10px; 91 | } -------------------------------------------------------------------------------- /src/Sidebar.js: -------------------------------------------------------------------------------- 1 | import React, {useState, useEffect} from 'react' 2 | import './Sidebar.css' 3 | 4 | import Avatar from "@material-ui/core/Avatar" 5 | import ExpandMoreIcon from "@material-ui/icons/ExpandMore"; 6 | import AddIcon from "@material-ui/icons/Add"; 7 | import CallIcon from "@material-ui/icons/Call"; 8 | import MicIcon from "@material-ui/icons/Mic"; 9 | import SettingsIcon from '@material-ui/icons/Settings'; 10 | import { Headset, InfoOutlined, SignalCellularAlt } from '@material-ui/icons'; 11 | 12 | import SidebarChannel from './SidebarChannel' 13 | 14 | import { useSelector } from 'react-redux' 15 | import { selectUser } from './features/userSlice' 16 | import db, { auth } from './firebase' 17 | 18 | function Sidebar() { 19 | 20 | const user = useSelector(selectUser) 21 | const [channels, setChannels] = useState([]) 22 | useEffect(() => { 23 | db.collection('channels').onSnapshot(snapshot => ( 24 | setChannels(snapshot.docs.map(doc => ({ 25 | id: doc.id, 26 | channel: doc.data(), 27 | }))) 28 | ) 29 | ) 30 | }, []) 31 | 32 | const handleAddChannel = () => { 33 | const channelName = prompt("Enter Channel Name"); 34 | 35 | if (channelName) { 36 | db.collection('channels').add({ 37 | channelName 38 | }) 39 | } 40 | } 41 | 42 | return ( 43 |
44 |
45 |

CloneServer

46 | 47 |
48 | 49 |
50 |
51 |
52 | 53 |

Text Channels

54 |
55 | 56 | 57 |
58 | 59 |
60 | 61 | {channels.map(({id, channel}) => ( 62 | 63 | ))} 64 | 65 |
66 |
67 | 68 |
69 | 73 |
74 |

Voice Connected

75 |

Stream

76 |
77 |
78 | 79 | 80 |
81 |
82 | 83 |
84 | auth.signOut()} src={user.photo}/> 85 |
86 |

{user.displayName}

87 |

#{user.uid.substring(0,5)}

88 |
89 | 90 |
91 | 92 | 93 | 94 |
95 |
96 |
97 | ); 98 | } 99 | 100 | export default Sidebar 101 | -------------------------------------------------------------------------------- /src/SidebarChannel.css: -------------------------------------------------------------------------------- 1 | .sidebarChannel:hover > h4 { 2 | color: white; 3 | background-color: #40464b; 4 | } 5 | 6 | .sidebarChannel > h4 { 7 | display: flex; 8 | padding-left: 15px; 9 | align-items: center; 10 | background-color: #2f3135; 11 | color: gray; 12 | cursor: pointer; 13 | } 14 | 15 | .sidebarChannel__hash { 16 | font-size: 30px; 17 | padding: 8px; 18 | } -------------------------------------------------------------------------------- /src/SidebarChannel.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import './SidebarChannel.css' 3 | import {useDispatch} from 'react-redux' 4 | import {setChannelInfo} from './features/appSlice' 5 | 6 | function SidebarChannel({ id, channelName }) { 7 | 8 | const dispatch = useDispatch(); 9 | 10 | return ( 11 |
dispatch(setChannelInfo({ 12 | channelId: id, 13 | channelName 14 | }))}> 15 |

16 | # 17 | {channelName} 18 |

19 |
20 | ) 21 | } 22 | 23 | 24 | export default SidebarChannel 25 | -------------------------------------------------------------------------------- /src/app/store.js: -------------------------------------------------------------------------------- 1 | import { configureStore } from '@reduxjs/toolkit'; 2 | import userReducer from '../features/userSlice'; 3 | import appReducer from '../features/appSlice'; 4 | 5 | export default configureStore({ 6 | reducer: { 7 | user: userReducer, 8 | app: appReducer, 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /src/features/appSlice.js: -------------------------------------------------------------------------------- 1 | import { createSlice } from '@reduxjs/toolkit'; 2 | 3 | export const appSlice = createSlice({ 4 | name: 'app', 5 | initialState: { 6 | channelId: null, 7 | channelName: null, 8 | }, 9 | reducers: { 10 | setChannelInfo: ( state, action ) => { 11 | state.channelId = action.payload.channelId 12 | state.channelName = action.payload.channelName 13 | }, 14 | }, 15 | }); 16 | 17 | export const { setChannelInfo } = appSlice.actions; 18 | 19 | export const selectChannelId = state => state.app.channelId; 20 | export const selectChannelName = state => state.app.channelName; 21 | 22 | export default appSlice.reducer; 23 | -------------------------------------------------------------------------------- /src/features/userSlice.js: -------------------------------------------------------------------------------- 1 | import { createSlice } from '@reduxjs/toolkit'; 2 | 3 | export const userSlice = createSlice({ 4 | name: 'user', 5 | initialState: { 6 | user: null, 7 | }, 8 | reducers: { 9 | login: ( state, action ) => { 10 | state.user = action.payload; 11 | }, 12 | 13 | logout: ( state ) => { 14 | state.user = null; 15 | }, 16 | }, 17 | }); 18 | 19 | export const { login, logout } = userSlice.actions; 20 | 21 | export const selectUser = state => state.user.user; 22 | 23 | export default userSlice.reducer; 24 | -------------------------------------------------------------------------------- /src/firebase.js: -------------------------------------------------------------------------------- 1 | import firebase from "firebase"; 2 | // For Firebase JS SDK v7.20.0 and later, measurementId is optional 3 | //GET Below Settings from Firebase > Project Overview > Settings > General > Your apps > Firebase SDK snippet > Config 4 | const firebaseConfig = { 5 | //your app config 6 | }; 7 | 8 | const firebaseApp = firebase.initializeApp(firebaseConfig); 9 | const db = firebaseApp.firestore(); 10 | const auth = firebaseApp.auth(); 11 | const provider = new firebase.auth.GoogleAuthProvider(); 12 | 13 | export { auth, provider }; 14 | export default db; 15 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0 3 | } 4 | 5 | body { 6 | margin: 0; 7 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 8 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 9 | sans-serif; 10 | -webkit-font-smoothing: antialiased; 11 | -moz-osx-font-smoothing: grayscale; 12 | } 13 | 14 | code { 15 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 16 | monospace; 17 | } 18 | -------------------------------------------------------------------------------- /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 store from './app/store'; 6 | import { Provider } from 'react-redux'; 7 | import * as serviceWorker from './serviceWorker'; 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/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.then(registration => { 134 | registration.unregister(); 135 | }); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/static/.picasa.ini: -------------------------------------------------------------------------------- 1 | [discord2.png.jpg] 2 | backuphash=53518 3 | [discord1.png.jpg] 4 | backuphash=57515 5 | -------------------------------------------------------------------------------- /src/static/.picasaoriginals/.picasa.ini: -------------------------------------------------------------------------------- 1 | [discord2.png] 2 | filters=crop64=1,9f5fffff4db; 3 | crop=rect64(9f5fffff4db) 4 | moddate=3c5550888ea3d601 5 | width=1920 6 | height=1080 7 | textactive=0 8 | [discord1.png] 9 | filters=crop64=1,a31fffff3e8; 10 | crop=rect64(a31fffff3e8) 11 | moddate=03d2198a8ea3d601 12 | width=1920 13 | height=1080 14 | textactive=0 15 | -------------------------------------------------------------------------------- /src/static/.picasaoriginals/discord1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanBinu007/React-discord-clone/336c8dcba2f1771515a727b0b5d3bf6a199c6cab/src/static/.picasaoriginals/discord1.png -------------------------------------------------------------------------------- /src/static/.picasaoriginals/discord2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanBinu007/React-discord-clone/336c8dcba2f1771515a727b0b5d3bf6a199c6cab/src/static/.picasaoriginals/discord2.png -------------------------------------------------------------------------------- /src/static/discord1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanBinu007/React-discord-clone/336c8dcba2f1771515a727b0b5d3bf6a199c6cab/src/static/discord1.jpg -------------------------------------------------------------------------------- /src/static/discord2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanBinu007/React-discord-clone/336c8dcba2f1771515a727b0b5d3bf6a199c6cab/src/static/discord2.jpg --------------------------------------------------------------------------------