├── src ├── index.css ├── Assets │ └── login.png ├── setupTests.js ├── App.test.js ├── reportWebVitals.js ├── index.js ├── Firebase │ └── Firebase.js ├── App.css ├── Components │ ├── SignUp.js │ ├── DeleteModal.js │ ├── CreateRoom.js │ ├── EditProfile.js │ ├── Home.js │ ├── Rooms.js │ ├── FileUpload.js │ ├── Chat.js │ ├── Application.js │ └── Messages.js └── App.js ├── .firebaserc ├── Assets └── chatify.png ├── public ├── favicon.ico ├── logo192.png ├── logo512.png ├── robots.txt ├── manifest.json └── index.html ├── firebase.json ├── .gitignore ├── package.json └── README.md /src/index.css: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "chatify-49" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /Assets/chatify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoldStar0103/Chatify/HEAD/Assets/chatify.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoldStar0103/Chatify/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoldStar0103/Chatify/HEAD/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoldStar0103/Chatify/HEAD/public/logo512.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/Assets/login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoldStar0103/Chatify/HEAD/src/Assets/login.png -------------------------------------------------------------------------------- /firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "build", 4 | "ignore": [ 5 | "firebase.json", 6 | "**/.*", 7 | "**/node_modules/**" 8 | ] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /.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 | 25 | .firebase -------------------------------------------------------------------------------- /src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /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 reportWebVitals from './reportWebVitals'; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById('root') 12 | ); 13 | 14 | // If you want to start measuring performance in your app, pass a function 15 | // to log results (for example: reportWebVitals(console.log)) 16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 17 | reportWebVitals(); 18 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/Firebase/Firebase.js: -------------------------------------------------------------------------------- 1 | import firebase from "firebase/app"; 2 | import "firebase/auth"; 3 | import "firebase/firestore"; 4 | import "firebase/storage"; 5 | 6 | const provider = new firebase.auth.GoogleAuthProvider(); 7 | 8 | const firebaseConfig = { 9 | apiKey: "AIzaSyDoN-nSGUo_1h87nkOXSXX2vv4IBXBXey0", 10 | authDomain: "chatify-49.firebaseapp.com", 11 | projectId: "chatify-49", 12 | storageBucket: "chatify-49.appspot.com", 13 | messagingSenderId: "1034185885241", 14 | appId: "1:1034185885241:web:a46af138b7a40d318defe8", 15 | measurementId: "G-EHQ2YBVYY9", 16 | }; 17 | 18 | const firebaseApp = firebase.initializeApp(firebaseConfig); 19 | 20 | const db = firebaseApp.firestore(); 21 | const auth = firebase.auth(); 22 | const storage = firebase.storage(); 23 | 24 | export { db, auth, provider, storage }; 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chatify", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@material-ui/core": "^4.11.3", 7 | "@material-ui/icons": "^4.11.2", 8 | "@testing-library/jest-dom": "^5.11.10", 9 | "@testing-library/react": "^11.2.6", 10 | "@testing-library/user-event": "^12.8.3", 11 | "emoji-mart": "^3.0.1", 12 | "firebase": "^8.4.1", 13 | "react": "^17.0.2", 14 | "react-anchorme": "^2.2.0", 15 | "react-dom": "^17.0.2", 16 | "react-icons": "^4.2.0", 17 | "react-router-dom": "^5.2.0", 18 | "react-scripts": "4.0.3", 19 | "react-scrollable-feed": "^1.3.0", 20 | "web-vitals": "^1.1.1" 21 | }, 22 | "scripts": { 23 | "start": "react-scripts start", 24 | "build": "react-scripts build", 25 | "test": "react-scripts test", 26 | "eject": "react-scripts eject" 27 | }, 28 | "eslintConfig": { 29 | "extends": [ 30 | "react-app", 31 | "react-app/jest" 32 | ] 33 | }, 34 | "browserslist": { 35 | "production": [ 36 | ">0.2%", 37 | "not dead", 38 | "not op_mini all" 39 | ], 40 | "development": [ 41 | "last 1 chrome version", 42 | "last 1 firefox version", 43 | "last 1 safari version" 44 | ] 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | # Chatify 4 | 5 | ### Personal Chat Room or Workspace to share resources and hangout with friends. 6 | 7 | ### https://chatifynew.vercel.app/ 8 | 9 |
10 | Demo 11 |
12 | 13 |
14 | 15 | ## Build With 16 | 17 | - `React.js` 18 | - `Firebase` 19 | - `Material UI` 20 | - `React Icons` 21 | 22 | ## Features: 23 | 24 | - Easy SignIn with Google 25 | - Create Rooms 26 | - Realtime group messaging with image sharing 27 | - Supports Emoji 28 | - Reaction on messages 29 | 30 | ## Installation and Usage 31 | 32 | ### Clone this repository 33 | 34 | `git clone https://github.com/soumyajit4419/Chatify.git`
35 | `cd chatify` 36 | 37 | ### Install Dependencies 38 | 39 | `npm install` 40 | 41 | In the project directory, you can run: 42 | 43 | ### npm start 44 | 45 | Runs the app in the development mode.\ 46 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 47 | 48 | The page will reload if you make edits.\ 49 | You will also see any lint errors in the console. 50 | 51 | ### npm run build 52 | 53 | Builds the app for production to the `build` folder.\ 54 | It correctly bundles React in production mode and optimizes the build for the best performance. 55 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | body { 2 | height: 100vh !important; 3 | overflow: hidden !important; 4 | background-color: #22273b !important; 5 | } 6 | 7 | /* width */ 8 | ::-webkit-scrollbar { 9 | width: 9px; 10 | } 11 | 12 | /* Track */ 13 | ::-webkit-scrollbar-track { 14 | background: #22273b !important; 15 | border-radius: 5px; 16 | } 17 | 18 | /* Handle */ 19 | ::-webkit-scrollbar-thumb { 20 | background: #151b2d !important; 21 | border-radius: 5px; 22 | } 23 | 24 | .emoji-mart { 25 | position: absolute !important; 26 | bottom: 70px !important; 27 | } 28 | 29 | .emoji-mart-preview { 30 | display: none !important; 31 | } 32 | 33 | .MuiInputLabel-outlined { 34 | color: rgba(173, 173, 173, 0.89) !important; 35 | } 36 | 37 | .MuiOutlinedInput-notchedOutline { 38 | border: 0 !important; 39 | } 40 | 41 | .MuiInputBase-root { 42 | color: rgb(245, 245, 245) !important; 43 | } 44 | 45 | .MuiInputBase-root.Mui-disabled { 46 | color: rgb(255 255 255 / 53%) !important; 47 | cursor: default !important; 48 | } 49 | 50 | .MuiDivider-root { 51 | background-color: rgb(0 0 0 / 40%) !important; 52 | } 53 | 54 | .MuiDialog-paper { 55 | overflow-y: visible !important; 56 | background-color: #202035 !important; 57 | color: #dcddde !important; 58 | width: 530px; 59 | } 60 | 61 | .emoji-mart-dark { 62 | color: #fff; 63 | border-color: #22273b !important; 64 | background-color: #121628 !important; 65 | } 66 | 67 | .emoji-mart-dark .emoji-mart-category-label span { 68 | background-color: #121628 !important; 69 | color: #fff; 70 | border-radius: 5px; 71 | } 72 | 73 | .emoji-mart-dark .emoji-mart-search input { 74 | color: #fff; 75 | border-color: #22273b; 76 | background-color: #22273b; 77 | } 78 | 79 | a { 80 | color: #e3a7ff; 81 | } 82 | 83 | .MuiSnackbarContent-root { 84 | background-color: rgb(109 52 125) !important; 85 | } 86 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | Chatify 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/Components/SignUp.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Button from "@material-ui/core/Button"; 3 | import { FcGoogle } from "react-icons/fc"; 4 | import { makeStyles } from "@material-ui/core/styles"; 5 | import Container from "@material-ui/core/Container"; 6 | import loginImg from "../Assets/login.png"; 7 | import Topography from "@material-ui/core/Typography"; 8 | import { auth, provider } from "../Firebase/Firebase"; 9 | 10 | const useStyles = makeStyles((theme) => ({ 11 | root: { 12 | boxShadow: "0 0 15px rgb(7 15 63 / 33%)", 13 | backgroundColor: "#171c30", 14 | color: "white", 15 | }, 16 | paper: { 17 | marginTop: theme.spacing(10), 18 | display: "flex", 19 | flexDirection: "column", 20 | alignItems: "center", 21 | paddingBottom: "25px", 22 | paddingTop: "35px", 23 | }, 24 | mainImg: { 25 | width: "100%", 26 | height: "auto", 27 | }, 28 | submit: { 29 | margin: theme.spacing(3, 0, 2), 30 | color: "#d9d9d9", 31 | }, 32 | })); 33 | 34 | function SignUp() { 35 | const classes = useStyles(); 36 | 37 | const login = () => { 38 | auth 39 | .signInWithPopup(provider) 40 | .then((res) => { 41 | console.log("Success"); 42 | }) 43 | .catch((err) => { 44 | console.log(err); 45 | }); 46 | }; 47 | 48 | return ( 49 | 50 |
51 | signup img 52 | 53 | Sign In To Chatify 54 | 55 | 64 |
65 |
66 | ); 67 | } 68 | 69 | export default SignUp; 70 | -------------------------------------------------------------------------------- /src/Components/DeleteModal.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import Button from "@material-ui/core/Button"; 3 | import Dialog from "@material-ui/core/Dialog"; 4 | import DialogActions from "@material-ui/core/DialogActions"; 5 | import DialogContent from "@material-ui/core/DialogContent"; 6 | import DialogContentText from "@material-ui/core/DialogContentText"; 7 | import DialogTitle from "@material-ui/core/DialogTitle"; 8 | 9 | function DeleteModal({ msgId, text, deleteMsg, handleModal, postImg }) { 10 | const [open, setOpen] = useState(true); 11 | 12 | const handleClose = () => { 13 | setOpen(false); 14 | handleModal(); 15 | }; 16 | 17 | const handleDelete = () => { 18 | deleteMsg(msgId); 19 | handleModal(); 20 | }; 21 | 22 | return ( 23 |
24 | 30 | 31 | {"Are you sure you want to delete the message?"} 32 | 33 | 34 | 38 | {text} 39 | 40 | {postImg ? ( 41 | img 46 | ) : null} 47 | 48 | 49 | 52 | 53 | 61 | 62 | 63 |
64 | ); 65 | } 66 | 67 | export default DeleteModal; 68 | -------------------------------------------------------------------------------- /src/Components/CreateRoom.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import Button from "@material-ui/core/Button"; 3 | import Dialog from "@material-ui/core/Dialog"; 4 | import DialogActions from "@material-ui/core/DialogActions"; 5 | import DialogContent from "@material-ui/core/DialogContent"; 6 | import TextField from "@material-ui/core/TextField"; 7 | import DialogTitle from "@material-ui/core/DialogTitle"; 8 | 9 | function CreateRoom({ create, manage }) { 10 | const [open, setOpen] = useState(true); 11 | const [roomName, setRoomName] = useState(""); 12 | 13 | const handleClose = () => { 14 | setOpen(false); 15 | manage(); 16 | }; 17 | const handleNewRoom = (e) => { 18 | e.preventDefault(); 19 | if (roomName) { 20 | create(roomName); 21 | manage(); 22 | } 23 | }; 24 | 25 | return ( 26 |
27 | 33 | 34 | {"Create A New Channel"} 35 | 36 | 37 |
38 | { 48 | setRoomName(e.target.value); 49 | }} 50 | /> 51 | 52 |
53 | 54 | 61 | 72 | 73 |
74 |
75 | ); 76 | } 77 | 78 | export default CreateRoom; 79 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | import { makeStyles } from "@material-ui/core/styles"; 3 | import Application from "./Components/Application"; 4 | import Chat from "./Components/Chat"; 5 | import Login from "./Components/SignUp"; 6 | import Home from "./Components/Home"; 7 | import { BrowserRouter as Router, Switch, Route } from "react-router-dom"; 8 | import { auth, db } from "./Firebase/Firebase"; 9 | import "./App.css"; 10 | 11 | const useStyles = makeStyles((theme) => ({ 12 | root: { 13 | display: "flex", 14 | }, 15 | toolbar: theme.mixins.toolbar, 16 | content: { 17 | flexGrow: 1, 18 | backgroundColor: "#22273b !important", 19 | height: "100vh", 20 | }, 21 | })); 22 | 23 | function App() { 24 | const classes = useStyles(); 25 | const [user, setUser] = useState(null); 26 | 27 | useEffect(() => { 28 | auth.onAuthStateChanged((user) => { 29 | if (user) { 30 | db.collection("users") 31 | .doc(user.uid) 32 | .get() 33 | .then((doc) => { 34 | if (doc.exists) { 35 | console.log("user exits"); 36 | } else { 37 | const details = { 38 | name: user.displayName, 39 | displayName: user.displayName.split(" ")[0], 40 | photoURL: user.photoURL, 41 | email: user.email, 42 | uid: user.uid, 43 | }; 44 | db.collection("users") 45 | .doc(user.uid) 46 | .set(details) 47 | .then((res) => { 48 | console.log("new user created"); 49 | }) 50 | .catch((err) => { 51 | console.log(err); 52 | }); 53 | } 54 | }) 55 | .catch((err) => { 56 | console.log(err); 57 | }); 58 | 59 | setUser(user.uid); 60 | } else { 61 | setUser(null); 62 | } 63 | }); 64 | }, []); 65 | 66 | return ( 67 |
68 | 69 | {!user ? ( 70 | 71 | ) : ( 72 |
73 | 74 |
75 |
76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 |
85 |
86 | )} 87 |
88 |
89 | ); 90 | } 91 | 92 | export default App; 93 | -------------------------------------------------------------------------------- /src/Components/EditProfile.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import Button from "@material-ui/core/Button"; 3 | import TextField from "@material-ui/core/TextField"; 4 | import Dialog from "@material-ui/core/Dialog"; 5 | import DialogActions from "@material-ui/core/DialogActions"; 6 | import DialogContent from "@material-ui/core/DialogContent"; 7 | import DialogTitle from "@material-ui/core/DialogTitle"; 8 | import { db } from "../Firebase/Firebase"; 9 | 10 | function EditProfile({ toggler, alert }) { 11 | const [open, setOpen] = useState(true); 12 | const [userName, setUserName] = useState(""); 13 | const [displayName, setDisplayName] = useState(""); 14 | const [email, setEmail] = useState(""); 15 | const [uid, setUid] = useState(""); 16 | 17 | const handleClose = () => { 18 | setOpen(false); 19 | toggler(); 20 | }; 21 | 22 | const updateProfile = (e) => { 23 | e.preventDefault(); 24 | db.collection("users") 25 | .doc(uid) 26 | .update({ 27 | displayName: displayName, 28 | }) 29 | .then((res) => { 30 | alert(); 31 | }) 32 | .catch((err) => { 33 | console.log(err); 34 | }); 35 | 36 | setOpen(false); 37 | toggler(); 38 | }; 39 | 40 | useEffect(() => { 41 | const userData = JSON.parse(localStorage.getItem("userDetails")); 42 | setUserName(userData.name); 43 | setDisplayName(userData.displayName); 44 | setEmail(userData.email); 45 | setUid(userData.uid); 46 | }, []); 47 | 48 | return ( 49 |
50 | 55 | Edit User Profile 56 | 57 |
58 | 72 | 86 | 87 | { 99 | setDisplayName(e.target.value); 100 | }} 101 | /> 102 | 103 |
104 | 105 | 108 | 115 | 116 |
117 |
118 | ); 119 | } 120 | 121 | export default EditProfile; 122 | -------------------------------------------------------------------------------- /src/Components/Home.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import { makeStyles } from "@material-ui/core/styles"; 3 | import Grid from "@material-ui/core/Grid"; 4 | import Typography from "@material-ui/core/Typography"; 5 | import Card from "@material-ui/core/Card"; 6 | import CardActionArea from "@material-ui/core/CardActionArea"; 7 | import CardContent from "@material-ui/core/CardContent"; 8 | import Avatar from "@material-ui/core/Avatar"; 9 | import { db } from "../Firebase/Firebase"; 10 | import { useHistory } from "react-router-dom"; 11 | 12 | const useStyles = makeStyles((theme) => ({ 13 | root: { 14 | paddingTop: "50px", 15 | paddingBottom: "25px", 16 | color: "#f0f0f0", 17 | }, 18 | heading: { 19 | fontSize: "2.2em", 20 | fontWeight: "700", 21 | }, 22 | subHeading: { 23 | fontSize: "1.6em", 24 | }, 25 | channelDiv: { 26 | padding: "15px", 27 | }, 28 | channelContent: { 29 | display: "flex", 30 | flexDirection: "column", 31 | textAlign: "center", 32 | padding: "20px", 33 | alignItems: "center", 34 | }, 35 | square: { 36 | height: "80px", 37 | width: "80px", 38 | backgroundColor: "#8fabbd66", 39 | fontSize: "2rem", 40 | }, 41 | rootChannel: { 42 | height: "calc(100vh - 185px)", 43 | position: "relative", 44 | padding: "15px", 45 | overflowY: "scroll", 46 | }, 47 | channelText: { 48 | paddingTop: "10px", 49 | fontSize: "1.2rem", 50 | }, 51 | channelCard: { 52 | backgroundColor: "#1e2439", 53 | boxShadow: 54 | "0px 3px 4px -1px rgb(0 0 0 / 17%), 0px 1px 1px 0px rgb(0 0 0 / 14%), 0px 1px 3px 0px rgb(0 0 0 / 12%)", 55 | color: "rgb(220, 221, 222)", 56 | }, 57 | })); 58 | 59 | function Home() { 60 | const classes = useStyles(); 61 | const [channels, setChannels] = useState([]); 62 | const history = useHistory(); 63 | 64 | useEffect(() => { 65 | db.collection("channels") 66 | .orderBy("channelName", "asc") 67 | .onSnapshot((snapshot) => { 68 | setChannels( 69 | snapshot.docs.map((channel) => ({ 70 | channelName: channel.data().channelName, 71 | id: channel.id, 72 | })) 73 | ); 74 | }); 75 | }, []); 76 | 77 | const goToChannel = (id) => { 78 | history.push(`/channel/${id}`); 79 | }; 80 | 81 | return ( 82 |
83 | 84 | 85 | 86 | Welcome to Chatify 87 | 88 | 89 | Effortless live chat to hangout with friends! 90 | 91 | 92 | 93 | 94 | 95 | {channels.map((channel) => ( 96 | 103 | 104 | goToChannel(channel.id)} 107 | > 108 | 109 | 114 | {channel.channelName.substr(0, 1).toUpperCase()} 115 | 116 | 117 | {channel.channelName} 118 | 119 | 120 | 121 | 122 | 123 | ))} 124 | 125 |
126 | ); 127 | } 128 | 129 | export default Home; 130 | -------------------------------------------------------------------------------- /src/Components/Rooms.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import { makeStyles } from "@material-ui/core/styles"; 3 | import List from "@material-ui/core/List"; 4 | import ListItem from "@material-ui/core/ListItem"; 5 | import ListItemIcon from "@material-ui/core/ListItemIcon"; 6 | import ListItemText from "@material-ui/core/ListItemText"; 7 | import Collapse from "@material-ui/core/Collapse"; 8 | import ExpandLess from "@material-ui/icons/ExpandLess"; 9 | import ExpandMore from "@material-ui/icons/ExpandMore"; 10 | import IconButton from "@material-ui/core/IconButton"; 11 | import Divider from "@material-ui/core/Divider"; 12 | import AddIcon from "@material-ui/icons/Add"; 13 | import { db } from "../Firebase/Firebase"; 14 | import { useHistory } from "react-router-dom"; 15 | import { IoMdChatboxes } from "react-icons/io"; 16 | import { BiHash } from "react-icons/bi"; 17 | import CreateRoom from "./CreateRoom"; 18 | import Fade from "@material-ui/core/Fade"; 19 | import Snackbar from "@material-ui/core/Snackbar"; 20 | import CloseIcon from "@material-ui/icons/Close"; 21 | 22 | const useStyles = makeStyles((theme) => ({ 23 | nested: { 24 | paddingLeft: theme.spacing(4), 25 | }, 26 | iconDesign: { 27 | fontSize: "1.5em", 28 | color: "#cb43fc", 29 | }, 30 | primary: { 31 | color: "#cb43fc", 32 | }, 33 | })); 34 | 35 | function Rooms() { 36 | const classes = useStyles(); 37 | const [open, setOpen] = React.useState(true); 38 | const [channelList, setChannelList] = useState([]); 39 | const [showCreateRoom, setShowCreateRoom] = useState(false); 40 | const history = useHistory(); 41 | const [alert, setAlert] = useState(false); 42 | 43 | useEffect(() => { 44 | db.collection("channels") 45 | .orderBy("channelName", "asc") 46 | .onSnapshot((snapshot) => { 47 | setChannelList( 48 | snapshot.docs.map((channel) => ({ 49 | channelName: channel.data().channelName, 50 | id: channel.id, 51 | })) 52 | ); 53 | }); 54 | }, []); 55 | 56 | const handleClick = () => { 57 | setOpen(!open); 58 | }; 59 | 60 | const goToChannel = (id) => { 61 | history.push(`/channel/${id}`); 62 | }; 63 | 64 | const manageCreateRoomModal = () => { 65 | setShowCreateRoom(!showCreateRoom); 66 | }; 67 | 68 | const handleAlert = () => { 69 | setAlert(!alert); 70 | }; 71 | 72 | const addChannel = (cName) => { 73 | if (cName) { 74 | cName = cName.toLowerCase().trim(); 75 | if (cName === "") { 76 | handleAlert(); 77 | return; 78 | } 79 | 80 | for (var i = 0; i < channelList.length; i++) { 81 | if (cName === channelList[i].channelName) { 82 | handleAlert(); 83 | return; 84 | } 85 | } 86 | 87 | db.collection("channels") 88 | .add({ channelName: cName.toLowerCase() }) 89 | .then((res) => { 90 | console.log("added new channel"); 91 | }) 92 | .then((err) => { 93 | console.log(err); 94 | }); 95 | } 96 | }; 97 | 98 | return ( 99 |
100 | 109 | 110 | 111 | } 112 | /> 113 | 114 | {showCreateRoom ? ( 115 | 116 | ) : null} 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | {open ? ( 132 | 133 | ) : ( 134 | 135 | )} 136 | 137 | 138 | 139 | 140 | {channelList.map((channel) => ( 141 | goToChannel(channel.id)} 146 | > 147 | 148 | 152 | 153 | 161 | 162 | ))} 163 | 164 | 165 | 166 |
167 | ); 168 | } 169 | 170 | export default Rooms; 171 | -------------------------------------------------------------------------------- /src/Components/FileUpload.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { makeStyles } from "@material-ui/core/styles"; 3 | import Button from "@material-ui/core/Button"; 4 | import Dialog from "@material-ui/core/Dialog"; 5 | import DialogActions from "@material-ui/core/DialogActions"; 6 | import DialogContent from "@material-ui/core/DialogContent"; 7 | import DialogTitle from "@material-ui/core/DialogTitle"; 8 | import TextField from "@material-ui/core/TextField"; 9 | import LinearProgress from "@material-ui/core/LinearProgress"; 10 | import Typography from "@material-ui/core/Typography"; 11 | import Box from "@material-ui/core/Box"; 12 | import { storage } from "../Firebase/Firebase"; 13 | import { useParams } from "react-router-dom"; 14 | import firebase from "firebase/app"; 15 | import { db } from "../Firebase/Firebase"; 16 | 17 | const useStyles = makeStyles((theme) => ({ 18 | displayImage: { 19 | height: "105px", 20 | width: "180px", 21 | }, 22 | imageName: { 23 | paddingLeft: "15px", 24 | fontSize: "1.3em", 25 | }, 26 | imageDiv: { 27 | marginLeft: "16px", 28 | marginRight: "16px", 29 | marginTop: "-33px", 30 | }, 31 | })); 32 | 33 | function FileUpload({ setState, file }) { 34 | const params = useParams(); 35 | const classes = useStyles(); 36 | const [open, setOpen] = useState(true); 37 | const [progress, setProgress] = useState(0); 38 | const [progressBar, setProgressBar] = useState({ display: "none" }); 39 | const [message, setMessage] = useState(""); 40 | 41 | const handleClose = () => { 42 | setOpen(false); 43 | setState(); 44 | }; 45 | 46 | const sendMsg = (downloadURL) => { 47 | if (params.id) { 48 | const userData = JSON.parse(localStorage.getItem("userDetails")); 49 | 50 | if (userData) { 51 | const displayName = userData.displayName; 52 | const imgUrl = userData.photoURL; 53 | const uid = userData.uid; 54 | const likeCount = 0; 55 | const likes = {}; 56 | const fireCount = 0; 57 | const fire = {}; 58 | const heartCount = 0; 59 | const heart = {}; 60 | const postImg = downloadURL; 61 | const obj = { 62 | text: message, 63 | timestamp: firebase.firestore.Timestamp.now(), 64 | userImg: imgUrl, 65 | userName: displayName, 66 | uid: uid, 67 | likeCount: likeCount, 68 | likes: likes, 69 | fireCount: fireCount, 70 | fire: fire, 71 | heartCount: heartCount, 72 | heart: heart, 73 | postImg: postImg, 74 | }; 75 | 76 | db.collection("channels") 77 | .doc(params.id) 78 | .collection("messages") 79 | .add(obj) 80 | .then((res) => { 81 | console.log("message sent"); 82 | }) 83 | .catch((err) => { 84 | console.log(err); 85 | }); 86 | } 87 | 88 | setMessage(""); 89 | } 90 | }; 91 | 92 | const fileObj = URL.createObjectURL(file); 93 | 94 | const handleUpload = (e) => { 95 | e.preventDefault(); 96 | setProgressBar({ display: "block" }); 97 | const uploadRef = storage.ref(`images/${file.name}`).put(file); 98 | uploadRef.on( 99 | "state_changed", 100 | (snapshot) => { 101 | // Observe state change events such as progress, pause, and resume 102 | // Get task progress, including the number of bytes uploaded and the total number of bytes to be uploaded 103 | let progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100; 104 | setProgress(progress); 105 | }, 106 | (error) => { 107 | console.log(error); 108 | }, 109 | () => { 110 | // Handle successful uploads on complete 111 | // For instance, get the download URL: https://firebasestorage.googleapis.com/... 112 | uploadRef.snapshot.ref.getDownloadURL().then((downloadURL) => { 113 | sendMsg(downloadURL); 114 | }); 115 | handleClose(); 116 | } 117 | ); 118 | }; 119 | 120 | return ( 121 |
122 | 128 |
129 | {file.name} 130 | {file.name} 131 |
132 | 133 | Upload Image 134 | 135 | 136 |
{ 139 | handleUpload(e); 140 | }} 141 | > 142 | { 153 | setMessage(e.target.value); 154 | }} 155 | /> 156 | 157 | 158 |
159 | 160 | 161 | 162 | 163 | 164 | {Math.round(progress)}% 165 | 166 | 167 |
168 |
169 | 170 | 173 | 182 | 183 |
184 |
185 | ); 186 | } 187 | 188 | export default FileUpload; 189 | -------------------------------------------------------------------------------- /src/Components/Chat.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import TextField from "@material-ui/core/TextField"; 3 | import { makeStyles } from "@material-ui/core/styles"; 4 | import Grid from "@material-ui/core/Grid"; 5 | import Messages from "./Messages"; 6 | import IconButton from "@material-ui/core/IconButton"; 7 | import { useParams } from "react-router-dom"; 8 | import { db } from "../Firebase/Firebase"; 9 | import firebase from "firebase/app"; 10 | import ScrollableFeed from "react-scrollable-feed"; 11 | import { BiHash } from "react-icons/bi"; 12 | import { FiSend } from "react-icons/fi"; 13 | import { GrEmoji } from "react-icons/gr"; 14 | import { Picker } from "emoji-mart"; 15 | import { RiImageAddLine } from "react-icons/ri"; 16 | import FileUpload from "./FileUpload"; 17 | import "emoji-mart/css/emoji-mart.css"; 18 | 19 | const useStyles = makeStyles((theme) => ({ 20 | root: { 21 | flexGrow: 1, 22 | }, 23 | chat: { 24 | position: "relative", 25 | height: "calc(100vh - 200px)", 26 | paddingLeft: "10px", 27 | paddingBottom: "5px", 28 | paddingTop: "5px", 29 | }, 30 | footer: { 31 | paddingRight: "15px", 32 | paddingLeft: "15px", 33 | paddingTop: "10px", 34 | }, 35 | message: { 36 | width: "100%", 37 | color: "white", 38 | }, 39 | roomName: { 40 | border: "1px solid #0000004a", 41 | borderLeft: 0, 42 | borderRight: 0, 43 | padding: "15px", 44 | display: "flex", 45 | color: "#e5e5e5", 46 | }, 47 | roomNameText: { 48 | marginBlockEnd: 0, 49 | marginBlockStart: 0, 50 | paddingLeft: "5px", 51 | }, 52 | iconDesign: { 53 | fontSize: "1.5em", 54 | color: "#e5e5e5", 55 | }, 56 | footerContent: { 57 | display: "flex", 58 | backgroundColor: "#303753", 59 | borderRadius: "5px", 60 | alignItems: "center", 61 | }, 62 | inputFile: { 63 | display: "none", 64 | }, 65 | })); 66 | 67 | function Chat() { 68 | const classes = useStyles(); 69 | const params = useParams(); 70 | const [allMessages, setAllMessages] = useState([]); 71 | const [channelName, setChannelName] = useState(""); 72 | const [userNewMsg, setUserNewMsg] = useState(""); 73 | const [emojiBtn, setEmojiBtn] = useState(false); 74 | const [modalState, setModalState] = useState(false); 75 | const [file, setFileName] = useState(null); 76 | 77 | useEffect(() => { 78 | if (params.id) { 79 | db.collection("channels") 80 | .doc(params.id) 81 | .onSnapshot((snapshot) => { 82 | setChannelName(snapshot.data().channelName); 83 | }); 84 | 85 | db.collection("channels") 86 | .doc(params.id) 87 | .collection("messages") 88 | .orderBy("timestamp", "asc") 89 | .onSnapshot((snapshot) => { 90 | setAllMessages( 91 | snapshot.docs.map((doc) => ({ id: doc.id, data: doc.data() })) 92 | ); 93 | }); 94 | } 95 | }, [params]); 96 | 97 | const sendMsg = (e) => { 98 | e.preventDefault(); 99 | if (userNewMsg && params.id) { 100 | const userData = JSON.parse(localStorage.getItem("userDetails")); 101 | 102 | if (userData) { 103 | const displayName = userData.displayName; 104 | const imgUrl = userData.photoURL; 105 | const uid = userData.uid; 106 | const likeCount = 0; 107 | const likes = {}; 108 | const fireCount = 0; 109 | const fire = {}; 110 | const heartCount = 0; 111 | const heart = {}; 112 | const postImg = null; 113 | const obj = { 114 | text: userNewMsg, 115 | timestamp: firebase.firestore.Timestamp.now(), 116 | userImg: imgUrl, 117 | userName: displayName, 118 | uid: uid, 119 | likeCount: likeCount, 120 | likes: likes, 121 | fireCount: fireCount, 122 | fire: fire, 123 | heartCount: heartCount, 124 | heart: heart, 125 | postImg: postImg, 126 | }; 127 | 128 | db.collection("channels") 129 | .doc(params.id) 130 | .collection("messages") 131 | .add(obj) 132 | .then((res) => { 133 | console.log("message sent"); 134 | }) 135 | .catch((err) => { 136 | console.log(err); 137 | }); 138 | } 139 | 140 | setUserNewMsg(""); 141 | setEmojiBtn(false); 142 | } 143 | }; 144 | 145 | const addEmoji = (e) => { 146 | setUserNewMsg(userNewMsg + e.native); 147 | }; 148 | 149 | const openModal = () => { 150 | setModalState(!modalState); 151 | }; 152 | 153 | const handelFileUpload = (e) => { 154 | e.preventDefault(); 155 | if (e.target.files[0]) { 156 | setFileName(e.target.files[0]); 157 | openModal(); 158 | } 159 | e.target.value = null; 160 | }; 161 | 162 | return ( 163 |
164 | {modalState ? : null} 165 | 166 | 167 |

{channelName}

168 |
169 | 170 | 171 | {allMessages.map((message) => ( 172 | 177 | ))} 178 | 179 | 180 |
181 | 182 | handelFileUpload(e)} 188 | /> 189 | 198 | 199 | setEmojiBtn(!emojiBtn)} 203 | > 204 | 205 | 206 | {emojiBtn ? : null} 207 | 208 |
sendMsg(e)} 212 | > 213 | { 224 | setUserNewMsg(e.target.value); 225 | }} 226 | /> 227 | 228 | 229 | 230 | 231 |
232 |
233 |
234 | ); 235 | } 236 | 237 | export default Chat; 238 | -------------------------------------------------------------------------------- /src/Components/Application.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import AppBar from "@material-ui/core/AppBar"; 3 | import CssBaseline from "@material-ui/core/CssBaseline"; 4 | import Divider from "@material-ui/core/Divider"; 5 | import Drawer from "@material-ui/core/Drawer"; 6 | import Hidden from "@material-ui/core/Hidden"; 7 | import IconButton from "@material-ui/core/IconButton"; 8 | import MenuIcon from "@material-ui/icons/Menu"; 9 | import Toolbar from "@material-ui/core/Toolbar"; 10 | import Typography from "@material-ui/core/Typography"; 11 | import { makeStyles, useTheme, withStyles } from "@material-ui/core/styles"; 12 | import AccountCircle from "@material-ui/icons/AccountCircle"; 13 | import MenuItem from "@material-ui/core/MenuItem"; 14 | import Menu from "@material-ui/core/Menu"; 15 | import Badge from "@material-ui/core/Badge"; 16 | import Avatar from "@material-ui/core/Avatar"; 17 | import { Grid } from "@material-ui/core"; 18 | import { deepPurple } from "@material-ui/core/colors"; 19 | import Rooms from "./Rooms"; 20 | import { GoSignOut } from "react-icons/go"; 21 | import { FaUserEdit } from "react-icons/fa"; 22 | import { auth, db } from "../Firebase/Firebase"; 23 | import { Link } from "react-router-dom"; 24 | import EditProfile from "./EditProfile"; 25 | import Fade from "@material-ui/core/Fade"; 26 | import Snackbar from "@material-ui/core/Snackbar"; 27 | import CloseIcon from "@material-ui/icons/Close"; 28 | 29 | const drawerWidth = 240; 30 | 31 | const StyledBadge = withStyles((theme) => ({ 32 | badge: { 33 | backgroundColor: "#44b700", 34 | color: "#44b700", 35 | boxShadow: `0 0 0 2px ${theme.palette.background.paper}`, 36 | "&::after": { 37 | position: "absolute", 38 | top: 0, 39 | left: 0, 40 | width: "100%", 41 | height: "100%", 42 | borderRadius: "50%", 43 | border: "1px solid currentColor", 44 | content: '""', 45 | }, 46 | }, 47 | "@keyframes ripple": { 48 | "0%": { 49 | transform: "scale(.8)", 50 | opacity: 1, 51 | }, 52 | "100%": { 53 | transform: "scale(2.4)", 54 | opacity: 0, 55 | }, 56 | }, 57 | }))(Badge); 58 | 59 | const useStyles = makeStyles((theme) => ({ 60 | root: { 61 | display: "flex", 62 | }, 63 | avatarGrid: { 64 | paddingTop: "20px", 65 | paddingLeft: "5px", 66 | paddingBottom: "20px", 67 | color: "#dcddde", 68 | }, 69 | avatarIcon: { 70 | display: "flex", 71 | paddingLeft: "10px", 72 | paddingRight: "10px", 73 | }, 74 | avatarName: { 75 | fontSize: "1em", 76 | paddingLeft: "12px", 77 | paddingTop: "8px", 78 | }, 79 | avatarDisplayName: { 80 | alignSelf: "center", 81 | paddingLeft: "10px", 82 | fontWeight: "600", 83 | }, 84 | purple: { 85 | color: theme.palette.getContrastText(deepPurple[500]), 86 | backgroundColor: "#3f51b5", 87 | }, 88 | drawer: { 89 | [theme.breakpoints.up("sm")]: { 90 | width: drawerWidth, 91 | flexShrink: 0, 92 | }, 93 | }, 94 | appBar: { 95 | [theme.breakpoints.up("sm")]: { 96 | width: `calc(100% - ${drawerWidth}px)`, 97 | marginLeft: drawerWidth, 98 | }, 99 | backgroundColor: "#22273b", 100 | color: "#dcddde", 101 | boxShadow: 102 | "0 1px 0 rgba(4,4,5,0.2),0 1.5px 0 rgba(6,6,7,0.05),0 2px 0 rgba(4,4,5,0.05);", 103 | }, 104 | menuButton: { 105 | marginRight: theme.spacing(2), 106 | [theme.breakpoints.up("sm")]: { 107 | display: "none", 108 | }, 109 | }, 110 | // necessary for content to be below app bar 111 | toolbar: theme.mixins.toolbar, 112 | drawerPaper: { 113 | width: drawerWidth, 114 | backgroundColor: "#171c2e", 115 | color: "white", 116 | }, 117 | sideToolBar: { 118 | backgroundColor: "#171c2e", 119 | color: "#fff", 120 | lineHeight: 1.6, 121 | boxShadow: 122 | "0 1px 0 rgba(4,4,5,0.2),0 1.5px 0 rgba(6,6,7,0.05),0 2px 0 rgba(4,4,5,0.05);", 123 | minHeight: "50px", 124 | }, 125 | sideToolBarText: { 126 | letterSpacing: "0.2em", 127 | fontWeight: "900", 128 | }, 129 | title: { 130 | flexGrow: 1, 131 | }, 132 | })); 133 | 134 | function Application(props) { 135 | const { window, uid } = props; 136 | const classes = useStyles(); 137 | const theme = useTheme(); 138 | const [mobileOpen, setMobileOpen] = React.useState(false); 139 | const [anchorEl, setAnchorEl] = React.useState(null); 140 | const [userDetails, setUserDetails] = useState([]); 141 | const [editProfileModal, setEditProfileModal] = useState(false); 142 | const [alert, setAlert] = useState(false); 143 | const open = Boolean(anchorEl); 144 | 145 | useEffect(() => { 146 | db.collection("users") 147 | .doc(uid) 148 | .onSnapshot((doc) => { 149 | setUserDetails(doc.data()); 150 | localStorage.setItem("userDetails", JSON.stringify(doc.data())); 151 | }); 152 | }, [uid]); 153 | 154 | const handleMenu = (event) => { 155 | setAnchorEl(event.currentTarget); 156 | }; 157 | 158 | const handleClose = () => { 159 | setAnchorEl(null); 160 | }; 161 | 162 | const handleDrawerToggle = () => { 163 | setMobileOpen(!mobileOpen); 164 | }; 165 | 166 | const toggleEditProfile = () => { 167 | setEditProfileModal(!editProfileModal); 168 | }; 169 | 170 | const handleAlert = () => { 171 | setAlert(!alert); 172 | }; 173 | 174 | const signOut = () => { 175 | auth 176 | .signOut() 177 | .then(() => { 178 | console.log("signed out"); 179 | localStorage.clear(); 180 | }) 181 | .catch((err) => { 182 | console.log(err); 183 | }); 184 | }; 185 | 186 | const drawer = userDetails && ( 187 |
188 | 189 | 190 | CHATIFY 191 | 192 | 193 | 194 | 195 |
196 | 204 | 209 | 210 | 211 | {userDetails.displayName} 212 | 213 |
214 |
215 | 216 | {userDetails.name} 217 | 218 | 219 | {userDetails.email} 220 | 221 |
222 |
223 | 224 | 225 | 226 |
227 | ); 228 | 229 | const container = 230 | window !== undefined ? () => window().document.body : undefined; 231 | return ( 232 |
233 | 234 | 235 | 244 | 245 | 246 | } 247 | /> 248 | 249 | {editProfileModal ? ( 250 | 251 | ) : null} 252 | 253 | 254 | 255 | 262 | 263 | 264 | 265 | 266 | 267 | Home 268 | 269 | 270 | 271 |
272 | 279 | 280 | 281 | 297 | 298 |   Edit Profile 299 | 300 | 301 | 302 |   Sign Out of Chatify 303 | 304 | 305 |
306 |
307 |
308 | 309 | 341 |
342 | ); 343 | } 344 | 345 | export default Application; 346 | -------------------------------------------------------------------------------- /src/Components/Messages.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import Grid from "@material-ui/core/Grid"; 3 | import { makeStyles } from "@material-ui/core/styles"; 4 | import Avatar from "@material-ui/core/Avatar"; 5 | import { deepPurple } from "@material-ui/core/colors"; 6 | import IconButton from "@material-ui/core/IconButton"; 7 | import { AiFillLike } from "react-icons/ai"; 8 | import { AiFillFire } from "react-icons/ai"; 9 | import { AiFillHeart } from "react-icons/ai"; 10 | import { AiFillDelete } from "react-icons/ai"; 11 | import { db } from "../Firebase/Firebase"; 12 | import { useParams } from "react-router-dom"; 13 | import DeleteModal from "./DeleteModal"; 14 | import { Anchorme } from "react-anchorme"; 15 | 16 | const useStyles = makeStyles((theme) => ({ 17 | root: { 18 | flexGrow: 1, 19 | position: "relative", 20 | padding: "8px", 21 | }, 22 | paper: { 23 | padding: "10px", 24 | "&:hover": { 25 | backgroundColor: "#1f2436", 26 | }, 27 | }, 28 | avatar: { 29 | display: "inline-block", 30 | verticalAlign: "top", 31 | }, 32 | chat: { 33 | display: "inline-block", 34 | paddingLeft: "1rem", 35 | width: "calc(100% - 50px)", 36 | wordBreak: "break-all", 37 | }, 38 | chatHeading: { 39 | marginBlockStart: 0, 40 | marginBlockEnd: 0, 41 | display: "inline-block", 42 | fontSize: "1rem", 43 | fontWeight: "600", 44 | color: "white", 45 | }, 46 | chatTimming: { 47 | marginBlockStart: 0, 48 | marginBlockEnd: 0, 49 | display: "inline-block", 50 | paddingLeft: "0.5em", 51 | color: "white", 52 | }, 53 | chatText: { 54 | color: "#dcddde", 55 | }, 56 | purple: { 57 | color: theme.palette.getContrastText(deepPurple[500]), 58 | backgroundColor: "#3f51b5", 59 | }, 60 | emojiDiv: { 61 | position: "absolute", 62 | right: 0, 63 | top: 0, 64 | }, 65 | emojiDivInner: { 66 | position: "absolute", 67 | right: 0, 68 | padding: "0 35px 0 32px", 69 | }, 70 | emojiBtn: { 71 | fontSize: "1.1rem", 72 | color: "rgb(255 195 54)", 73 | }, 74 | allEmoji: { 75 | backgroundColor: "#2d2e31ba", 76 | borderRadius: "5px", 77 | paddingLeft: "2px", 78 | paddingRight: "2px", 79 | display: "flex", 80 | }, 81 | countEmojiBtn: { 82 | padding: "3px", 83 | borderRadius: "4px", 84 | fontSize: "0.8em", 85 | backgroundColor: "#ffffff4a", 86 | color: "#cacaca", 87 | paddingLeft: "5px", 88 | paddingRight: "5px", 89 | "&:hover": { 90 | backgroundColor: "#ffffff4a", 91 | color: "#e7e7e7", 92 | }, 93 | }, 94 | })); 95 | 96 | function Messages({ values, msgId }) { 97 | const [style, setStyle] = useState({ display: "none" }); 98 | const [deleteModal, setDeleteModal] = useState(false); 99 | const classes = useStyles(); 100 | 101 | const uid = JSON.parse(localStorage.getItem("userDetails")).uid; 102 | const messegerUid = values.uid; 103 | const date = values.timestamp.toDate(); 104 | const day = date.getDate(); 105 | const year = date.getFullYear(); 106 | const month = date.getMonth(); 107 | const hour = date.getHours(); 108 | const minute = date.getMinutes(); 109 | const time = `${day}/${month}/${year} ${hour}:${minute}`; 110 | 111 | const numLikes = values.likeCount; 112 | const numFire = values.fireCount; 113 | const numHeart = values.heartCount; 114 | 115 | const userLiked = values.likes[uid]; 116 | const userFire = values.fire[uid]; 117 | const userHeart = values.heart[uid]; 118 | 119 | const postImg = values.postImg; 120 | 121 | const channelId = useParams().id; 122 | 123 | const selectedLike = userLiked 124 | ? { color: "#8ff879", backgroundColor: "#545454" } 125 | : null; 126 | 127 | const selectedHeart = userHeart 128 | ? { color: "#ff527d", backgroundColor: "#545454" } 129 | : null; 130 | 131 | const selectedFire = userFire 132 | ? { color: "#ffc336", backgroundColor: "#545454" } 133 | : null; 134 | 135 | const showDeleteModal = () => { 136 | setDeleteModal(!deleteModal); 137 | }; 138 | 139 | const heartClick = () => { 140 | const messageDoc = db 141 | .collection("channels") 142 | .doc(channelId) 143 | .collection("messages") 144 | .doc(msgId); 145 | if (userHeart) { 146 | return db 147 | .runTransaction((transaction) => { 148 | // This code may get re-run multiple times if there are conflicts. 149 | return transaction.get(messageDoc).then((doc) => { 150 | if (!doc) { 151 | console.log("doc not found"); 152 | return; 153 | } 154 | 155 | let newHeartCount = doc.data().heartCount - 1; 156 | let newHeart = doc.data().heart ? doc.data().heart : {}; 157 | newHeart[uid] = false; 158 | 159 | transaction.update(messageDoc, { 160 | heartCount: newHeartCount, 161 | heart: newHeart, 162 | }); 163 | }); 164 | }) 165 | .then(() => { 166 | console.log("Disiked"); 167 | }) 168 | .catch((error) => { 169 | console.log(error); 170 | }); 171 | } else { 172 | return db 173 | .runTransaction((transaction) => { 174 | // This code may get re-run multiple times if there are conflicts. 175 | return transaction.get(messageDoc).then((doc) => { 176 | if (!doc) { 177 | console.log("doc not found"); 178 | return; 179 | } 180 | 181 | let newHeartCount = doc.data().heartCount + 1; 182 | let newHeart = doc.data().heart ? doc.data().heart : {}; 183 | newHeart[uid] = true; 184 | 185 | transaction.update(messageDoc, { 186 | heartCount: newHeartCount, 187 | heart: newHeart, 188 | }); 189 | }); 190 | }) 191 | .then(() => { 192 | console.log("Liked"); 193 | }) 194 | .catch((error) => { 195 | console.log(error); 196 | }); 197 | } 198 | }; 199 | 200 | const fireClick = () => { 201 | const messageDoc = db 202 | .collection("channels") 203 | .doc(channelId) 204 | .collection("messages") 205 | .doc(msgId); 206 | if (userFire) { 207 | return db 208 | .runTransaction((transaction) => { 209 | // This code may get re-run multiple times if there are conflicts. 210 | return transaction.get(messageDoc).then((doc) => { 211 | if (!doc) { 212 | console.log("doc not found"); 213 | return; 214 | } 215 | 216 | let newFireCount = doc.data().fireCount - 1; 217 | let newFire = doc.data().fire ? doc.data().fire : {}; 218 | newFire[uid] = false; 219 | 220 | transaction.update(messageDoc, { 221 | fireCount: newFireCount, 222 | fire: newFire, 223 | }); 224 | }); 225 | }) 226 | .then(() => { 227 | console.log("Disiked"); 228 | }) 229 | .catch((error) => { 230 | console.log(error); 231 | }); 232 | } else { 233 | return db 234 | .runTransaction((transaction) => { 235 | // This code may get re-run multiple times if there are conflicts. 236 | return transaction.get(messageDoc).then((doc) => { 237 | if (!doc) { 238 | console.log("doc not found"); 239 | return; 240 | } 241 | 242 | let newFireCount = doc.data().fireCount + 1; 243 | let newFire = doc.data().fire ? doc.data().fire : {}; 244 | newFire[uid] = true; 245 | 246 | transaction.update(messageDoc, { 247 | fireCount: newFireCount, 248 | fire: newFire, 249 | }); 250 | }); 251 | }) 252 | .then(() => { 253 | console.log("Liked"); 254 | }) 255 | .catch((error) => { 256 | console.log(error); 257 | }); 258 | } 259 | }; 260 | 261 | const likeClick = () => { 262 | const messageDoc = db 263 | .collection("channels") 264 | .doc(channelId) 265 | .collection("messages") 266 | .doc(msgId); 267 | if (userLiked) { 268 | return db 269 | .runTransaction((transaction) => { 270 | // This code may get re-run multiple times if there are conflicts. 271 | return transaction.get(messageDoc).then((doc) => { 272 | if (!doc) { 273 | console.log("doc not found"); 274 | return; 275 | } 276 | 277 | let newLikeCount = doc.data().likeCount - 1; 278 | let newLikes = doc.data().likes ? doc.data().likes : {}; 279 | newLikes[uid] = false; 280 | 281 | transaction.update(messageDoc, { 282 | likeCount: newLikeCount, 283 | likes: newLikes, 284 | }); 285 | }); 286 | }) 287 | .then(() => { 288 | console.log("Disiked"); 289 | }) 290 | .catch((error) => { 291 | console.log(error); 292 | }); 293 | } else { 294 | return db 295 | .runTransaction((transaction) => { 296 | // This code may get re-run multiple times if there are conflicts. 297 | return transaction.get(messageDoc).then((doc) => { 298 | if (!doc) { 299 | console.log("doc not found"); 300 | return; 301 | } 302 | 303 | let newLikeCount = doc.data().likeCount + 1; 304 | let newLikes = doc.data().likes ? doc.data().likes : {}; 305 | newLikes[uid] = true; 306 | 307 | transaction.update(messageDoc, { 308 | likeCount: newLikeCount, 309 | likes: newLikes, 310 | }); 311 | }); 312 | }) 313 | .then(() => { 314 | console.log("Liked"); 315 | }) 316 | .catch((error) => { 317 | console.log(error); 318 | }); 319 | } 320 | }; 321 | 322 | const deleteMsg = (id) => { 323 | db.collection("channels") 324 | .doc(channelId) 325 | .collection("messages") 326 | .doc(id) 327 | .delete() 328 | .then((res) => { 329 | console.log("deleted successfully"); 330 | }) 331 | .catch((err) => { 332 | console.log(err); 333 | }); 334 | }; 335 | 336 | return ( 337 | 338 | {deleteModal ? ( 339 | 346 | ) : null} 347 |
{ 350 | setStyle({ display: "block" }); 351 | }} 352 | onMouseLeave={(e) => { 353 | setStyle({ display: "none" }); 354 | }} 355 | > 356 |
357 | 362 |
363 | 364 |
365 |
366 |
{values.userName}
367 |

{time}

368 |
369 | 370 |
371 | {values.text.split("\n").map((txt, idx) => ( 372 |
373 | 374 | {txt} 375 | 376 |
377 | ))} 378 |
379 | 380 | 381 | {postImg ? ( 382 | user 387 | ) : null} 388 | 389 | 390 |
391 | {numLikes > 0 ? ( 392 |
393 | 399 | 400 |
{numLikes}
401 |
402 |
403 | ) : null} 404 | 405 | {numFire > 0 ? ( 406 |
407 | 413 | 414 |
{numFire}
415 |
416 |
417 | ) : null} 418 | 419 | {numHeart > 0 ? ( 420 |
421 | 427 | 428 |
{numHeart}
429 |
430 |
431 | ) : null} 432 |
433 |
434 | 435 |
436 |
437 |
438 | 443 | 444 | 445 | 450 | 451 | 452 | 457 | 458 | 459 | {uid === messegerUid ? ( 460 | 465 | 469 | 470 | ) : null} 471 |
472 |
473 |
474 |
475 |
476 | ); 477 | } 478 | 479 | export default Messages; 480 | --------------------------------------------------------------------------------