├── .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 |
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
--------------------------------------------------------------------------------