├── .github └── pull_request_template.md ├── .gitignore ├── CONTRIBUTORS.md ├── Designs ├── VineethKumarM.json ├── kaneki-ken260.json ├── template.json └── varunKT001.json ├── Dockerfile ├── Procfile ├── README.md ├── app.js ├── models ├── message.js └── user.js ├── package-lock.json ├── package.json ├── public ├── avtars │ ├── 01.png │ ├── 02.png │ ├── 03.png │ ├── 04.png │ ├── 05.png │ ├── 06.png │ ├── 07.png │ ├── 08.png │ ├── 09.png │ ├── 10.png │ ├── 11.png │ └── 12.png ├── js │ ├── avatar.js │ ├── dark-mode.js │ ├── gradient.js │ ├── index.js │ ├── lastEncounter.js │ └── roomMembers.js ├── scss │ ├── index.scss │ └── main.scss ├── sounds │ ├── beep1.mp3 │ ├── beep2.wav │ └── beep3.mp3 └── uploads │ └── .gitkeep ├── routes ├── image.js └── index.js ├── utils ├── message.js ├── roomMembersCount.js └── users.js └── views ├── index.html └── main.html /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | Issue: ISSUENUMBER 2 | 3 | 4 | 5 | 6 | #### Short description of what this resolves: 7 | 8 | 9 | 10 | #### Changes proposed in this pull request and/or Screenshots of changes: 11 | 12 | - 13 | - 14 | - -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | .DS_Store 9 | 10 | # Diagnostic reports (https://nodejs.org/api/report.html) 11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | *.lcov 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # Bower dependency directory (https://bower.io/) 33 | bower_components 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (https://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directories 42 | node_modules/ 43 | jspm_packages/ 44 | # Snowpack dependency directory (https://snowpack.dev/) 45 | web_modules/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | .parcel-cache 78 | 79 | # Next.js build output 80 | .next 81 | out 82 | 83 | # Nuxt.js build / generate output 84 | .nuxt 85 | dist 86 | 87 | # Gatsby files 88 | .cache/ 89 | # Comment in the public line in if your project uses Gatsby and not Next.js 90 | # https://nextjs.org/blog/next-9-1#public-directory-support 91 | # public 92 | 93 | # vuepress build output 94 | .vuepress/dist 95 | 96 | # Serverless directories 97 | .serverless/ 98 | 99 | # FuseBox cache 100 | .fusebox/ 101 | 102 | # DynamoDB Local files 103 | .dynamodb/ 104 | 105 | # TernJS port file 106 | .tern-port 107 | 108 | # Stores VSCode versions used for testing VSCode extensions 109 | .vscode-test 110 | 111 | # yarn v2 112 | .yarn/cache 113 | .yarn/unplugged 114 | .yarn/build-state.yml 115 | .yarn/install-state.gz 116 | .pnp.* 117 | 118 | public/uploads/* 119 | !public/uploads/.gitkeep -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | # List of Contributors for GeekChat! 2 | 3 | 5 | 6 | - [Aditi Yadav](https://github.com/adtoria) 7 | - [Prakhar Awasthi](https://github.com/prakhar011) 8 | - [Mallik Prabhanjan](https://github.com/vemulapandu) 9 | - [Varun Tiwari](https://github.com/varunKT001) 10 | - [Vineeth Kumar Munigyala](https://github.com/VineethKumarM) 11 | - [Aman Utkarsh](https://github.com/mitrya) 12 | - [HariPrasath R](https://github.com/HariPrasath-3) 13 | - [Amitvikram Dwivedi](https://github.com/batflarrow) 14 | - [Shailesh Tiwari](https://github.com/shlesh) 15 | - [Agrim Verma](https://github.com/AgrimVerma) 16 | - [Kirti Singla](https://github.com/codesanta142) 17 | - [sai suman kumar](https://github.com/saisumanthkumar) 18 | - [Prasanta Barman](https://github.com/prasanta352) 19 | - [Vijay Karanjkar](https://github.com/Vijay-K-2003) 20 | - [Revathi Ari](https://github.com/Revathi2306) 21 | - [Rohit Yadav](https://github.com/yadav-rohit) 22 | - [Anjali Sahu](https://github.com/99anjali) 23 | - [Danish Ahmed Mirza](https://github.com/try-catch-stack) 24 | - [Raghav Goel](https://github.com/raghavgoel25) 25 | - [Akanksha Gupta](https://github.com/Akanksha2391) 26 | - [Gautam Misra](https://github.com/Darkknight131714) 27 | - [Shantanu](https://github.com/underoot-iota) 28 | - [Puja Saraf](https://github.com/Puja-Saraf) 29 | - [Sauahrd Srivastava](https://github.com/sauhard22) 30 | - [Ramanpreet Singh](https://github.com/raman5911) 31 | - [Srishti Dhir](https://github.com/srishtayy) 32 | - [Divyansh Gupta](https://github.com/The-Divyansh) 33 | - [Prakhar jalan](https://github.com/jalanprakhar) 34 | - [Aashita Chouhan](https://github.com/aashitachouhan) 35 | - [Palak Ahuja](https://github.com/Palak-2109) 36 | - [Janhavi Bawaskar](https://github.com/janhaviiii) 37 | - [Shadaan Hussain](https://github.com/shadaanhussain) 38 | - [Saloni Doshi](https://github.com/saloni33) 39 | - [Pranavi Tadivalasa](https://github.com/Ms-Error) 40 | - [Tanushree](https://github.com/Tanushree-coder) 41 | - [Aaryan Gupta](https://github.com/Aaryan0424) 42 | - [Janhavi Bawaskar](https://github.com/janhaviiii) 43 | - [Sathwik Kuppam](https://github.com/Ksathwik03) 44 | - [Harsh Gupta](https://github.com/Harshgupta5901) 45 | - [Siddharth S](https://github.com/Siddharth1002) 46 | - [Sarthak](https://github.com/daniel-aracquine) 47 | - [Pranav Raj](https://github.com/rajpranav63) 48 | - [Gude Lakshmi](https://github.com/gudelakshmi) 49 | - [Nitheesh](https://github.com/nitheesh96) 50 | - [Rankit](https://github.com/rankit2001) 51 | - [Siddhant](https://github.com/siddhantsingh186) 52 | - [Pratik Raghuvanshi](https://github.com/prxtikk-18) 53 | - [Neeraj Gupta](https://github.com/codelord09) 54 | - [Sravya Kaviti](https://github.com/sravyakaviti) 55 | - [Md Atif](https://github.com/Atif0604) 56 | - [Diya Waghmare](https://github.com/diyawaghmare) 57 | - [Monikinderjit Singh](https://github.com/Monik09) 58 | - [Akshitha Dasa](https://github.com/AKI35429) 59 | - [Kajal Kaushal](https://github.com/Kajal-7) 60 | - [Botte Shreya](https://github.com/shreya-0508) 61 | - [Aditya Singh Machhaiya](https://github.com/asingh1601) 62 | - [Shubhi Pandey](https://github.com/Shubhi2002) 63 | - [Naman Yadav](https://github.com/Naman-11) 64 | - [Shivanshu Singh](https://github.com/Shivanshu97i) 65 | - [Anushtha Bageria](https://github.com/anu-phoenix) 66 | - [BOTTE SHREYA](https://github.com/shreya-0508) 67 | - [Manthan Jain](https://github.com/Manthanjain) 68 | - [Anushtha Bageria](https://github.com/anu-phoenix) 69 | - [Swapnil Manke](https://github.com/mankeswapnil0) 70 | - [Sneh Shah](https://github.com/Sneh16Shah) 71 | - [Neha Singh](https://github.com/neha3001-singh) 72 | - [Karan Chhabra](https://github.com/karankc23) 73 | - [Niyoj Oli](https://github.com/niyoj) 74 | - [Shivansh Dwivedi](https://github.com/shivansh2741) 75 | - [Mohammad Tabish Malik](https://github.com/Dark-Hope-tech) 76 | - [Abhay Parihar](https://github.com/A-Spiral-Forge) 77 | - [Naman Kaushal](https://github.com/naman-c) 78 | - [Pradumn Kumar](https://github.com/kumarpradumn) 79 | - [Himanshu Yadav](https://github.com/SeriousConjurer) 80 | - [Vimal Vinayak](https://github.com/BlaZe-001) 81 | - [Medha Tiwari](https://github.com/medhatiwari) 82 | - [Anushka Kalwale](https://github.com/anu-1989) 83 | - [Anuj Jain](https://github.com/anuj046) 84 | - [Mukilan S](https://github.com/MUKILAN-2003) 85 | - [Isha Rawat](https://github.com/isharawat) 86 | - [Shalini Agrawal](https://github.com/shaliniagrawal2512) 87 | - [Shashwat Mittal](https://github.com/shashwat-mittal) 88 | - [Karus Manisha](https://github.com/sanjay395) 89 | - [Mayank Kumar](https://github.com/mayank-kr) 90 | - [Abhinav Singh](https://github.com/Abhisin90) 91 | - [Abhi Singh](https://github.com/Abhijeet452) 92 | 93 | 94 | -------------------------------------------------------------------------------- /Designs/VineethKumarM.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"Vineeth Kumar Munigyala", 3 | "github-Username": "VineethKumarM", 4 | "figma-link":"https://www.figma.com/file/6vF0LscrKRvzr6r6OpuB7N/geekchat?node-id=0%3A1" 5 | } -------------------------------------------------------------------------------- /Designs/kaneki-ken260.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"Ritwik Rajput", 3 | "github-Username": "kaneki-ken260", 4 | "figma-link":"https://www.figma.com/file/rKgyJEXMLxa1t11xEQvP0U/GeekChat?node-id=0%3A1" 5 | } 6 | -------------------------------------------------------------------------------- /Designs/template.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"", 3 | "github-Username": "", 4 | "figma-link":"" 5 | } -------------------------------------------------------------------------------- /Designs/varunKT001.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"Varun Tiwari", 3 | "github-Username": "varunKT001", 4 | "figma-link":"https://www.figma.com/file/vNGQo55LWnTYR3Dt4ow9BD/geekchat_ui?node-id=0%3A1" 5 | } -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:14-alpine 2 | ENV NODE_ENV=production 3 | WORKDIR /app 4 | COPY ["package.json", "package-lock.json*", "./"] 5 | RUN npm install --production 6 | COPY . . 7 | CMD ["node", "app.js"] -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: node app.js 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GeekChat 2 | 3 | ## What is GeekChat? 4 | 5 | GeekChat is a simple bi-directional communication app where users can join rooms and have live text messaging with other users present in the room. It works on the functionalities of Web Sockets and simple HTTPS requests.[Visit here](https://oc-geekchat.herokuapp.com/) 6 | 7 | And don't forget to star ⭐ the repo, it helps ^^. 8 | ### Tech Stack 9 | * Nodejs 10 | * Express 11 | * Socket.io 12 | * HTML 13 | * CSS(SCSS) 14 | * Javascript 15 | 16 | 17 | ## Local Setup 18 | 19 | ### Pre-Requisites 20 | * [Git](https://git-scm.com/downloads) 21 | * [Node.js](https://nodejs.org/en/) 22 | * [SCSS Live Compiler](https://marketplace.visualstudio.com/items?itemName=ritwickdey.live-sass)(Only if you are changing the styling of the website) 23 | 24 | ### How to Setup? 25 | 26 | * Fork the repo. 27 | * Clone it to your local setup by using the command `git clone ` 28 | * Run the following command in the root directory of the project `npm install` 29 | * After the process is completed run the command `node app.js` 30 | * The website will be live on [localhost:3000](https://localhost:3000) 31 | 32 | ## Brief Project Structure 33 | 34 | ``` 35 | / 36 | |-- public/ 37 | |-- css/ #Contains the scss styling files 38 | |-- images/ #Contains images used in the project 39 | |-- js/ #Contains client side js files 40 | |-- index.html #First webpage of the project 41 | |-- main.html #Main chatting page of the website 42 | | 43 | |-- utils/ 44 | |-- message.js #Contains all functions and variables concerning Chat Messages. 45 | |-- users.js #Contains all functions and variables concerning Users 46 | | 47 | |-- app.js #Main Server file that setups and runs the node server. 48 | 49 | ``` 50 | 51 | ## Guidelines 52 | 53 | ### Please help us follow the best practice to make it easy for the reviewer as well as the contributor. We want to focus on the code quality more than on managing pull request ethics. 54 | 55 | * Single commit per pull request and name the commit as something meaningful, example: Adding <-your-name-> in students/mentors section. 56 | 57 | * Reference the issue numbers in the commit message if it resolves an open issue.**Follow the PR template strictly.** 58 | 59 | * In case you want to change the styling of the website do not change the .css files instead change the .scss files and then compile them.[More details](https://ritwickdey.github.io/vscode-live-sass-compiler/docs/faqs.html) 60 | 61 | * Do not use unneccesary variables or functions and follow a easy and understandable code structure with proper comments. 62 | 63 | * Provide the link to live heroku pages from your forked repository or relevant screenshot for easier review. 64 | 65 | * Pull Request older than 3 days with no response from the contributor shall be marked closed. 66 | 67 | * Do not make PR which is not related to any issues. You can create an issue and solve it once we approve them. 68 | 69 | * Avoid duplicate PRs, if need be comment on the older PR with the PR number of the follow-up (new PR) and close the obsolete PR yourself. 70 | 71 | * Be polite: Be polite to other community members. 72 | 73 | ## Communicate 74 | 75 | Whether you are working on a new feature or facing a doubt please feel free to ask us on our [discord](https://discord.gg/WxZhCNNN) channel. We will be happy to help you out. 76 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const http = require("http"); 3 | const express = require("express"); 4 | const socketio = require("socket.io"); 5 | const cors = require("cors"); //make requests from one website to another website in the browser 6 | const moment = require("moment"); 7 | const fileUpload = require('express-fileupload') 8 | const indexRoutes= require("./routes/index") 9 | const imageRoutes= require("./routes/image") 10 | const app = express(); 11 | const server = http.createServer(app); 12 | const io = socketio(server); 13 | const compileSass = require('compile-sass'); 14 | 15 | const { sanitizeAndRenderMessage } = require("./utils/message"); 16 | const { formatMessage } = require("./models/message"); 17 | 18 | const { usersArr, newUser, currentUserData } = require("./utils/users"); 19 | const { roomMembersCount } = require("./utils/roomMembersCount"); 20 | 21 | // Json 22 | app.use(express.json()); 23 | app.use( 24 | express.urlencoded({ 25 | extended: true, 26 | }) 27 | ); 28 | 29 | app.use( 30 | "/css/:cssName", 31 | compileSass.setup({ 32 | sassFilePath: path.join(__dirname, "public/scss/"), 33 | sassFileExt: "scss", 34 | embedSrcMapInProd: true, 35 | resolveTildes: true, 36 | nodeSassOptions: { 37 | errLogToConsole: true, 38 | noCache: true, 39 | force: true, 40 | }, 41 | }) 42 | ); 43 | 44 | app.use(fileUpload()) 45 | app.use(cors()); 46 | app.use(express.static(path.join(__dirname, "public"))); 47 | 48 | //routes 49 | app.use("/", indexRoutes); 50 | app.use("/", imageRoutes); 51 | 52 | const roomDetails = io.of("/roomMembers"); 53 | roomDetails.on("connection", (socket) => { 54 | roomDetails.emit("roomMembersCount", roomMembersCount); 55 | }); 56 | 57 | io.on("connection", (socket) => { 58 | //Validating user 59 | //Connecting the user to the room 60 | //Greeting message in the room user has joined 61 | 62 | let { usrnm, room, profilePhoto } = currentUserData; 63 | 64 | if (usrnm != undefined && room != undefined) { 65 | roomMembersCount[room]++; 66 | roomDetails.emit("countUpdate", { 67 | room, 68 | count: roomMembersCount[room], 69 | }); 70 | 71 | newUser(usrnm, room, socket.id, profilePhoto); 72 | 73 | socket.join(room); 74 | socket.emit("roomJoined", { 75 | room, 76 | username: usrnm 77 | }); 78 | } 79 | 80 | socket.emit( 81 | "message", 82 | formatMessage("", "GeekChat Bot", "*Welcome to GeekChat!*", "") 83 | ); 84 | 85 | socket 86 | .to(room) 87 | .emit( 88 | "message", 89 | formatMessage("", "GeekChat Bot", `**${usrnm}** entered the chat!`, "", profilePhoto) 90 | ); 91 | 92 | let userList = usersArr.filter((ob) => ob.room === room); 93 | socket.emit("userList", userList); 94 | 95 | socket.to(room).emit("userJoined", { 96 | id: socket.id, 97 | username: usrnm, 98 | profilePhoto:profilePhoto 99 | }); 100 | 101 | //When a user leaves the room 102 | //Disconnecting the user from the room 103 | //Message in the room user has disconnected 104 | socket.on("disconnect", () => { 105 | roomMembersCount[room]--; 106 | roomDetails.emit("countUpdate", { 107 | room, 108 | count: roomMembersCount[room], 109 | }); 110 | 111 | io.in(room).emit("userLeft", { 112 | id: socket.id, 113 | username: usrnm, 114 | }); 115 | 116 | let userIndex = -1, user; 117 | 118 | for (const [index, userObj] of usersArr.entries()) { 119 | if (userObj.session_id === socket.id) { 120 | userIndex = index; 121 | user = userObj; 122 | break; 123 | } 124 | } 125 | 126 | if (userIndex !== -1) { 127 | usersArr.splice(userIndex, 1); 128 | io.in(user.room).emit( 129 | "message", 130 | formatMessage("", "GeekChat Bot", `**${user.name}** disconnected.`, "", user.profilePhoto) 131 | ); 132 | } 133 | }); 134 | 135 | socket.on("chatMessage", (messageObject) => { 136 | // When a user sends a chat message 137 | // An id is created for the message 138 | // Validating user and sending the message in the room 139 | let user = usersArr.find((ob) => ob.session_id === socket.id); 140 | let messageID = user.name + "_" + new Date().getTime(); 141 | if (user) { 142 | io.in(user.room).emit( 143 | "message", 144 | formatMessage( 145 | messageID, 146 | user.name, 147 | messageObject.msg, 148 | messageObject.userID, 149 | user.profilePhoto, 150 | ) 151 | ); 152 | } 153 | }); 154 | socket.on("deleteChatMsg", (msgId) => { 155 | // When a user deletes a message 156 | // Message id is verified 157 | // Message is deleted 158 | if (msgId == null || msgId == undefined) { 159 | return; 160 | } 161 | let user = usersArr.find((ob) => ob.session_id === socket.id); 162 | if (user) { 163 | io.in(user.room).emit("deleteMsgFromChat", msgId); 164 | } 165 | }); 166 | 167 | //Event listner for typing 168 | socket.on("typing", (info) => { 169 | if (info.name !== undefined || info.name !== null) { 170 | socket.broadcast.to(info.room).emit("typing", info.name); 171 | } 172 | }); 173 | 174 | //event listner for edit msg 175 | socket.on("edited-msg", (info) => { 176 | //find user 177 | let user = usersArr.find((ob) => ob.session_id === socket.id); 178 | //check user 179 | if (user != null || user != undefined) { 180 | if (info.text !== undefined || info.id !== undefined) { 181 | info = { id: info.id, text: sanitizeAndRenderMessage(info.text, true), time: moment().valueOf() }; 182 | io.in(user.room).emit("edit-msg", info); 183 | } 184 | } 185 | }); 186 | }); 187 | 188 | const port = process.env.PORT || 3000; 189 | server.listen(port, () => console.log(`Server running on port ${port}`)); 190 | -------------------------------------------------------------------------------- /models/message.js: -------------------------------------------------------------------------------- 1 | const moment = require("moment"); 2 | const {sanitizeAndRenderMessage} = require("../utils/message") 3 | function formatMessage(id, username, text, userID, profilePhoto="") { 4 | return { 5 | id, 6 | username, 7 | text: sanitizeAndRenderMessage(text, !text.includes("replied-msg-container")), 8 | userID, 9 | time: moment().valueOf(), 10 | profilePhoto, 11 | }; 12 | } 13 | 14 | module.exports = { 15 | formatMessage 16 | }; 17 | -------------------------------------------------------------------------------- /models/user.js: -------------------------------------------------------------------------------- 1 | function CreateUser(name, room, session_id, profilePhoto){ 2 | return { 3 | name, 4 | room, 5 | session_id, 6 | profilePhoto 7 | }; 8 | } 9 | 10 | module.exports = { CreateUser }; 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chatapp", 3 | "version": "1.0.0", 4 | "description": "A real time chatting app build on socket.io", 5 | "main": "app.js", 6 | "scripts": { 7 | "start": "node app.js", 8 | "dev": "nodemon app.js" 9 | }, 10 | "engines": { 11 | "node": "14.16.1" 12 | }, 13 | "author": "Aditya Verma", 14 | "license": "ISC", 15 | "dependencies": { 16 | "bad-words": "^3.0.4", 17 | "compile-sass": "^1.1.3", 18 | "cors": "^2.8.5", 19 | "express": "^4.17.1", 20 | "express-fileupload": "^1.2.1", 21 | "html-entities": "^2.3.2", 22 | "marked": "^3.0.7", 23 | "moment": "^2.29.1", 24 | "node-fetch": "^2.6.0", 25 | "querystring": "^0.2.1", 26 | "socket.io": "^4.2.0" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /public/avtars/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opencodeiiita/Geek-Chat/32861b069aaf927a65f7ea1fdae56e76d04beb93/public/avtars/01.png -------------------------------------------------------------------------------- /public/avtars/02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opencodeiiita/Geek-Chat/32861b069aaf927a65f7ea1fdae56e76d04beb93/public/avtars/02.png -------------------------------------------------------------------------------- /public/avtars/03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opencodeiiita/Geek-Chat/32861b069aaf927a65f7ea1fdae56e76d04beb93/public/avtars/03.png -------------------------------------------------------------------------------- /public/avtars/04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opencodeiiita/Geek-Chat/32861b069aaf927a65f7ea1fdae56e76d04beb93/public/avtars/04.png -------------------------------------------------------------------------------- /public/avtars/05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opencodeiiita/Geek-Chat/32861b069aaf927a65f7ea1fdae56e76d04beb93/public/avtars/05.png -------------------------------------------------------------------------------- /public/avtars/06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opencodeiiita/Geek-Chat/32861b069aaf927a65f7ea1fdae56e76d04beb93/public/avtars/06.png -------------------------------------------------------------------------------- /public/avtars/07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opencodeiiita/Geek-Chat/32861b069aaf927a65f7ea1fdae56e76d04beb93/public/avtars/07.png -------------------------------------------------------------------------------- /public/avtars/08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opencodeiiita/Geek-Chat/32861b069aaf927a65f7ea1fdae56e76d04beb93/public/avtars/08.png -------------------------------------------------------------------------------- /public/avtars/09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opencodeiiita/Geek-Chat/32861b069aaf927a65f7ea1fdae56e76d04beb93/public/avtars/09.png -------------------------------------------------------------------------------- /public/avtars/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opencodeiiita/Geek-Chat/32861b069aaf927a65f7ea1fdae56e76d04beb93/public/avtars/10.png -------------------------------------------------------------------------------- /public/avtars/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opencodeiiita/Geek-Chat/32861b069aaf927a65f7ea1fdae56e76d04beb93/public/avtars/11.png -------------------------------------------------------------------------------- /public/avtars/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opencodeiiita/Geek-Chat/32861b069aaf927a65f7ea1fdae56e76d04beb93/public/avtars/12.png -------------------------------------------------------------------------------- /public/js/avatar.js: -------------------------------------------------------------------------------- 1 | const profile = document.querySelector("#profile_avatars"); 2 | 3 | /* 4 | =================================== 5 | ******Modal close******** 6 | =================================== 7 | */ 8 | 9 | const modalDiv = document.querySelector(".modal_div"); 10 | const overlayDiv = document.querySelector(".overlay_div "); 11 | const btnCloseModal = document.querySelector(".btn--close-modal"); 12 | const btnOpenModal = document.querySelector(".btn--show-modal"); 13 | 14 | /* 15 | =========================================== 16 | ********Function to Open Modal******** 17 | =========================================== 18 | */ 19 | 20 | const OpenModal = () => { 21 | modalDiv.classList.remove("hidden"); 22 | overlayDiv.classList.remove("overlay_hidden"); 23 | }; 24 | 25 | /* 26 | =========================================== 27 | ********Function to Close Modal******** 28 | =========================================== 29 | */ 30 | 31 | const CloseModal = () => { 32 | modalDiv.classList.add("hidden"); 33 | overlayDiv.classList.add("overlay_hidden"); 34 | }; 35 | btnOpenModal.addEventListener("click", OpenModal); 36 | btnCloseModal.addEventListener("click", CloseModal); 37 | 38 | /* 39 | ================================================== 40 | ******Modal close with Escape Key******** 41 | ================================================== 42 | */ 43 | document.addEventListener("keydown", (e) => { 44 | if (e.key === "Escape" && !modalDiv.classList.contains("hidden")) { 45 | CloseModal(); 46 | } 47 | }); 48 | overlayDiv.addEventListener("click", () => { 49 | if (!modalDiv.classList.contains("hidden")) { 50 | CloseModal(); 51 | } 52 | }); 53 | 54 | const profileImg = document.querySelectorAll(".ProfileImg"); 55 | const profileUrl = document.querySelector("#profile_avatars"); 56 | const profileValue = document.querySelector("#profilePhoto"); 57 | /* 58 | =========================== 59 | ***Random Profile****** 60 | =========================== 61 | */ 62 | 63 | var PhotoNumber; 64 | if (localStorage.PhotoNumber) { 65 | PhotoNumber = localStorage.PhotoNumber; 66 | } else { 67 | var randNumber; 68 | randNumber = Math.trunc(Math.random() * 12 + 1); 69 | PhotoNumber = `0${randNumber}`.slice(-2); 70 | localStorage.PhotoNumber = PhotoNumber; 71 | } 72 | if (localStorage.customAvt) { 73 | profileUrl.src = `./avtars/${localStorage.customAvt}`; 74 | profileValue.value = `./avtars/${localStorage.customAvt}`; 75 | } else { 76 | profileUrl.src = `./avtars/${PhotoNumber}.png`; 77 | profileValue.value = `./avtars/${PhotoNumber}.png`; 78 | } 79 | 80 | /* 81 | =========================== 82 | ***Initital SliderIndex****** 83 | =========================== 84 | */ 85 | 86 | if (localStorage.customAvt) { 87 | var slideIndex = 1; 88 | } else { 89 | var slideIndex = Number(profileUrl.src.slice(-6, -4)); 90 | } 91 | const numberChange = document.querySelector(".numberChange"); 92 | const slides = document.querySelectorAll(".mySlides"); 93 | const maxSlide = slides.length; 94 | const BtnRight = document.querySelector(".next"); 95 | const BtnLeft = document.querySelector(".prev"); 96 | const dotBox = document.querySelector('.dotBox'); 97 | /* 98 | =========================== 99 | ***Creating dots****** 100 | =========================== 101 | */ 102 | const createDots = function (){ 103 | slides.forEach(function(s, i){ 104 | dotBox.insertAdjacentHTML('beforeend', `` ); 105 | }) 106 | } 107 | 108 | createDots(); 109 | 110 | 111 | /* 112 | =========================== 113 | ***Left Right Button*** 114 | =========================== 115 | */ 116 | 117 | const showSlides = (n) => { 118 | slides.forEach((slide, i) => { 119 | slide.style.transform = `translateX(${100 * (i - (n - 1))}%)`; 120 | }); 121 | activeDot(n) 122 | }; 123 | const activeDot = (slideIndex) => { 124 | var dots = document.getElementsByClassName("dot"); 125 | for (i = 0; i < dots.length; i++) { 126 | dots[i].className = dots[i].className.replace(" active", ""); 127 | } 128 | numberChange.innerHTML = slideIndex; 129 | dots[slideIndex - 1].className += " active"; 130 | }; 131 | showSlides(slideIndex); 132 | BtnRight.addEventListener("click", () => { 133 | if (slideIndex === maxSlide) { 134 | slideIndex = 1; 135 | } else { 136 | slideIndex++; 137 | } 138 | showSlides(slideIndex); 139 | activeDot(slideIndex); 140 | }); 141 | BtnLeft.addEventListener("click", () => { 142 | if (slideIndex === 1) { 143 | slideIndex = maxSlide; 144 | } else { 145 | slideIndex--; 146 | } 147 | showSlides(slideIndex); 148 | activeDot(slideIndex); 149 | }); 150 | 151 | /* 152 | ================================================== 153 | ******Profile Image Selection******** 154 | ================================================== 155 | */ 156 | const Dots = document.querySelectorAll(".dot"); 157 | var curntSelected = Number(PhotoNumber); 158 | const checkIcon = document.querySelectorAll('.icon_check'); 159 | if (curntSelected != 13) { 160 | Dots[curntSelected - 1].className += " selectDot"; 161 | checkIcon[curntSelected - 1].classList.remove('hidden1'); 162 | } 163 | profileImg.forEach((profile) => { 164 | profile.addEventListener("click", () => { 165 | var srcNumber = profile.src.slice(-6, -4); 166 | slideIndex = Number(srcNumber); 167 | localStorage.PhotoNumber = srcNumber; //storing in local storage current profile Image 168 | PhotoNumber = srcNumber; //current Profile Number 169 | profileUrl.src = `./avtars/${PhotoNumber}.png`; // profile src 170 | profileValue.value = `./avtars/${PhotoNumber}.png`; // profile value to send in chat box 171 | Dots.forEach((Dot, i) => { 172 | if(checkIcon[i]) { 173 | if (Dot.classList.contains("selectDot")) { 174 | Dot.classList.remove("selectDot"); 175 | 176 | } 177 | if(!checkIcon[i].classList.contains('hidden1')) 178 | {checkIcon[i].classList.add('hidden1'); 179 | } 180 | } 181 | }); 182 | Dots[slideIndex - 1].className += " selectDot"; 183 | checkIcon[slideIndex - 1].classList.remove('hidden1'); 184 | }); 185 | }); 186 | 187 | 188 | /* 189 | ================================================== 190 | ******Dot activation Selection******** 191 | ================================================== 192 | */ 193 | dotBox.addEventListener('click', function(e){ 194 | if(e.target.classList.contains('dot')){ 195 | const slide = e.target.dataset.slide; 196 | // console.log(slide) 197 | showSlides(Number(slide)+1); 198 | slideIndex=Number(slide)+1; 199 | } 200 | }) 201 | 202 | 203 | /* 204 | ================================================== 205 | ******Custom Avatar******** 206 | ================================================== 207 | */ 208 | const avtarForm = document.querySelector('#avatarForm') 209 | const upldBtn = document.querySelector('#upload') 210 | const Input = document.querySelector('#fileInp') 211 | 212 | avtarForm.addEventListener('submit', (e) => { 213 | e.preventDefault(); 214 | const some = Input.value.split('.'); 215 | const extn = some[some.length -1]; 216 | if (extn == 'png' || extn == 'jpg' || extn == 'jpeg' || extn == 'svg') { 217 | let xhr = new XMLHttpRequest(); 218 | 219 | xhr.open('POST', '/newAvatar'); 220 | let formData = new FormData(avtarForm); 221 | xhr.send(formData); 222 | 223 | xhr.onreadystatechange = function() { 224 | if (xhr.readyState === xhr.DONE) { 225 | // request finished 226 | Input.value = ""; 227 | localStorage.setItem('customAvt',xhr.response); 228 | window.location.reload(false); 229 | console.log(some); 230 | } 231 | }; 232 | } 233 | else { 234 | alert('supported formats png,jpg,jpeg') 235 | } 236 | 237 | 238 | 239 | }) -------------------------------------------------------------------------------- /public/js/dark-mode.js: -------------------------------------------------------------------------------- 1 | $(document).ready(() => { 2 | var body = document.querySelector("body"); 3 | var toggleButton = document.getElementById("toggle"); 4 | if (localStorage.dark == true) { 5 | body.classList.add("dark-mode"); 6 | toggleButton.classList.toggle("active"); 7 | } else { 8 | toggleButton.classList.toggle("toggle"); 9 | } 10 | 11 | toggleButton.onclick = function () { 12 | toggleButton.classList.toggle("active"); 13 | body.classList.toggle("dark-mode"); 14 | localStorage.dark ^= true; 15 | }; 16 | 17 | }); 18 | -------------------------------------------------------------------------------- /public/js/gradient.js: -------------------------------------------------------------------------------- 1 | $(document).ready(() => { 2 | 3 | document.querySelector('.new-room').value = document.querySelector('select').value; 4 | 5 | document.querySelector('input[type="text"]').addEventListener('focus', (e) => { 6 | const out = document.querySelectorAll('.border-gradient')[0]; 7 | out.style.background = 'linear-gradient(45deg, var(--one), var(--two))'; 8 | }) 9 | document.querySelector('input[type="text"]').addEventListener('blur', (e) => { 10 | const out = document.querySelectorAll('.border-gradient')[0]; 11 | out.style.background = ''; 12 | }) 13 | document.querySelector('select').addEventListener('change', (e) => { 14 | document.querySelector('.new-room').value = e.target.value; 15 | }) 16 | document.querySelector('.new-room').addEventListener('focus', (e) => { 17 | const out = document.querySelectorAll('.border-gradient')[2]; 18 | out.style.background = 'linear-gradient(45deg, var(--one), var(--two))'; 19 | }) 20 | document.querySelector('.new-room').addEventListener('blur', (e) => { 21 | const out = document.querySelectorAll('.border-gradient')[2]; 22 | out.style.background = ''; 23 | }) 24 | document.querySelector('select').addEventListener('focus', (e) => { 25 | const out = document.querySelectorAll('.border-gradient')[1]; 26 | out.style.background = 'linear-gradient(45deg, var(--one), var(--two))'; 27 | }) 28 | document.querySelector('select').addEventListener('blur', (e) => { 29 | const out = document.querySelectorAll('.border-gradient')[1]; 30 | out.style.background = ''; 31 | }) 32 | 33 | }); 34 | -------------------------------------------------------------------------------- /public/js/index.js: -------------------------------------------------------------------------------- 1 | const socket = io(); 2 | const roomName = document.querySelector("#room-name"); 3 | const users = document.querySelector("#users"); 4 | const roomNameDiv = document.querySelector(".room-name-container"); 5 | const userMap = new Map(); 6 | const CURRENT_USER = sessionStorage.getItem("current_user"); 7 | const colors = ["F4C430","E02401","F037A5","A9333A","800080","FFA400","D8345F"] 8 | var room; 9 | var cntr=1, rCntr=1; 10 | function randColor() { 11 | const c = Math.floor(Math.random() * 7); 12 | return colors[c]; 13 | } 14 | //function to display truncated text 15 | function readMore(num) { 16 | document.getElementById(`s${num}`).classList.add("extra") 17 | document.getElementById(`${num}xtra`).className = ''; 18 | document.getElementById(`${num}link`).classList.add("extra"); 19 | 20 | scrollToBottom(); 21 | } 22 | 23 | socket.on("roomJoined", (connectionObj) => { 24 | room = connectionObj.room; 25 | roomName.innerHTML = connectionObj.room; 26 | roomNameDiv.classList.remove("animate"); 27 | }); 28 | socket.on("message", (message) => { 29 | if ( 30 | document.querySelector(".typing") != null || 31 | document.querySelector(".typing") != undefined 32 | ) { 33 | document.querySelector(".typing").remove(); 34 | TYPING_USERS = []; 35 | } 36 | let userID = socket.id; 37 | outputMessage({ message, userID }); 38 | }); 39 | 40 | function outputMessage(msg) { 41 | var values = Object.values(msg); 42 | // console.log(values[0].text); 43 | var color = sessionStorage.getItem(values[0].username); 44 | const div = document.createElement("div"); 45 | const mssgProfilePhoto = document.createElement("img"); 46 | const div1 = document.createElement("div"); 47 | mssgProfilePhoto.src = values[0].profilePhoto; 48 | mssgProfilePhoto.classList.add("userAvatar1"); 49 | div.setAttribute("id", values[0].id); 50 | div1.setAttribute("id", `${values[0].id}-topDiv`); 51 | let trnc = false; 52 | if (values[0].userID === values[1]) { 53 | div.classList.add("author"); 54 | div1.classList.add("profileRight"); 55 | div.innerHTML += `
58 |
`; 61 | playSound("send"); 62 | } else { 63 | div.classList.add("message"); 64 | div1.classList.add("profileLeft"); 65 | if (values[0].username === "GeekChat Bot") { 66 | playSound("bot"); 67 | } else { 68 | playSound("recieve"); 69 | } 70 | } 71 | if (values[0].username === "GeekChat Bot") { 72 | div.classList.add("bot"); 73 | if (values[0].profilePhoto !== "") { 74 | div.appendChild(mssgProfilePhoto); 75 | div.classList.add("profileAdded"); 76 | } 77 | div.innerHTML += `

${values[0].username} ${moment( 78 | values[0].time 79 | ).format("h:mm a")}

80 |
81 | ${values[0]["text"]} 82 |
`; 83 | playSound("bot"); 84 | } else { 85 | 86 | if (div.classList.contains("author")){ 87 | color= "000000"; 88 | } 89 | var temp = document.createElement("div"); 90 | temp.innerHTML = values[0].text; 91 | div.innerHTML += ` 92 | 97 |

${values[0].username} ${moment( 98 | values[0].time 99 | ).format("h:mm a")}

` 100 | //check if its a replied message 101 | if(temp.children.length < 2 ) { 102 | //if more than 50 characters , we truncate the message 103 | var lgMsg = values[0].text; 104 | let pText = document.createElement('p'); 105 | pText.innerHTML = lgMsg.substring(3,lgMsg.length-5); 106 | if (pText.innerText.length <= 50) { 107 | div.innerHTML += `
108 | ${values[0].text} 109 |
`; 110 | } else { 111 | trnc = true; 112 | var seenMsg = pText.innerText.substring(0,49); 113 | // var extraMsg = pText.innerText.substring(49,); 114 | div.innerHTML += `

115 | ${seenMsg}${pText.innerHTML}
116 | Read more

117 |
` 118 | ++cntr; 119 | // } 120 | } 121 | } else { 122 | var msgCntr = temp.children[0].outerHTML; 123 | if (temp.children[1].innerText.length <= 50) { 124 | div.innerHTML += `
${msgCntr}

125 | ${temp.children[1].innerText}

126 |
`; 127 | } else { 128 | trnc = true; 129 | var longMsg = temp.children[1].innerText; 130 | var seenMsg = longMsg.substring(0,49); 131 | div.innerHTML += `
${msgCntr}

132 | ${seenMsg}${temp.children[1].innerText}
133 | Read more

134 |
` 135 | ++cntr; 136 | } 137 | } 138 | div.innerHTML += `${ 139 | values[0].userID === values[1] 140 | ? ` 141 | more_vert 142 | ` 143 | : `` 144 | }`; 145 | } 146 | if(trnc) { 147 | div.querySelector(".text").querySelector('p').classList.add('trnc'); 148 | } 149 | let repliedMsgCheck = div 150 | .querySelector(".text") 151 | .querySelector(".replied-msg-container"); 152 | if (repliedMsgCheck != null || repliedMsgCheck != undefined) { 153 | if ( 154 | repliedMsgCheck.querySelector(".replied-msg") != null || 155 | repliedMsgCheck.querySelector(".replied-msg") != undefined 156 | ) { 157 | repliedMsgCheck 158 | .querySelector(".replied-msg") 159 | .classList.remove("replied-msg"); 160 | } 161 | } 162 | div1.appendChild(mssgProfilePhoto); 163 | div1.appendChild(div); 164 | if (values[0].username === "GeekChat Bot") 165 | document.querySelector(".chat-messages").appendChild(div); 166 | else document.querySelector(".chat-messages").appendChild(div1); 167 | scrollToBottom(); 168 | } 169 | 170 | const form = document.getElementById("chat-form"); 171 | form.addEventListener("submit", (e) => { 172 | e.preventDefault(); 173 | 174 | let allList = document.querySelectorAll(".btn-container"); 175 | allList.forEach((item) => { 176 | // item.style['display'] = 'none'; 177 | item.classList.add("hide"); 178 | }); 179 | 180 | if (isEditing.status) { 181 | return emitEditedText(e); 182 | } 183 | if (isReplying.status) { 184 | return emitReplyMsg(e); 185 | } 186 | 187 | const msg = e.target.elements.msg.value; 188 | let userID = socket.id; 189 | if (msg.trim() == "") { 190 | } else { 191 | socket.emit("chatMessage", { msg, userID }); 192 | } 193 | 194 | e.target.elements.msg.value = ""; 195 | e.target.elements.msg.focus(); 196 | scrollToBottom(); 197 | }); 198 | 199 | function scrollToBottom() { 200 | document.querySelector(".chat-messages").scrollTop = 201 | document.querySelector(".chat-messages").scrollHeight; 202 | } 203 | 204 | socket.on("userList", (userList) => { 205 | // console.log(userList) 206 | for (let { name, session_id, profilePhoto } of userList) { 207 | userMap.set(session_id, name); 208 | let user = document.createElement("li"); 209 | user.classList.add("fade-in"); 210 | user.dataset.id = session_id; 211 | user.innerHTML = name; 212 | const profileImage = document.createElement("img"); // created an img element to add profile photo of each entering new user 213 | profileImage.src = profilePhoto; 214 | profileImage.classList.add("userAvatar"); 215 | user.appendChild(profileImage); 216 | users.appendChild(user); 217 | } 218 | }); 219 | socket.on("userJoined", ({ id, username, profilePhoto }) => { 220 | userMap.set(id, username); 221 | const user = document.createElement("li"); 222 | user.classList.add("fade-in"); 223 | user.dataset.id = id; 224 | user.innerHTML = username; 225 | sessionStorage.setItem(username, randColor()); 226 | const profileImage = document.createElement("img"); // created an img element to add profile photo of each entering new user 227 | profileImage.src = profilePhoto; 228 | profileImage.classList.add("userAvatar"); 229 | user.appendChild(profileImage); 230 | users.appendChild(user); 231 | }); 232 | 233 | socket.on("userLeft", ({ id, username }) => { 234 | const user = document.querySelector(`[data-id="${id}"]`); 235 | if (user) user.classList.add("fade-out"); 236 | setTimeout(() => { 237 | user.remove(); 238 | }, 1500); 239 | userMap.delete(id, username); 240 | }); 241 | 242 | socket.on("deleteMsgFromChat", (msgId) => { 243 | if (msgId == null || msgId == undefined) { 244 | return; 245 | } 246 | document.getElementById(msgId).remove(); 247 | document.getElementById(`${msgId}-topDiv`).remove(); 248 | }); 249 | 250 | function deleteMsg(info) { 251 | let [name, id] = info.split("_"); 252 | if (name === CURRENT_USER) { 253 | socket.emit("deleteChatMsg", info); 254 | } 255 | } 256 | 257 | const beep1 = document.getElementById("beep1"); 258 | const beep2 = document.getElementById("beep2"); 259 | const beep3 = document.getElementById("beep3"); 260 | const playSound = (beep) => { 261 | if (beep === "send") { 262 | return beep1.play(); 263 | } 264 | if (beep === "bot") { 265 | return beep3.play(); 266 | } 267 | return beep2.play(); 268 | }; 269 | 270 | //timeout for removing typing element from dom 271 | var timeout; 272 | 273 | //event for typing 274 | socket.on("typing", (typingUser) => { 275 | //push user to typingusers array 276 | TYPING_USERS.push(typingUser); 277 | 278 | //remove duplicates 279 | TYPING_USERS = [...new Set(TYPING_USERS)]; 280 | 281 | //append typing event msg 282 | appendTyping(); 283 | 284 | //timeout to delete typing event 285 | if (timeout !== undefined) { 286 | clearTimeout(timeout); 287 | } 288 | if ( 289 | document.querySelector(".typing") != null || 290 | document.querySelector(".typing") != undefined 291 | ) { 292 | timeout = setTimeout(() => { 293 | document.querySelector(".typing").remove(); 294 | TYPING_USERS = []; 295 | }, 3000); 296 | } 297 | }); 298 | 299 | //list of typing users 300 | var TYPING_USERS = []; 301 | 302 | const emitTyping = () => { 303 | socket.emit("typing", { name: CURRENT_USER, room }); 304 | }; 305 | 306 | //function to append typing msg 307 | const appendTyping = () => { 308 | //checking for existing typing element 309 | if ( 310 | document.querySelector(".typing") != null || 311 | document.querySelector(".typing") != undefined 312 | ) { 313 | let divEx = document.querySelector(".typing"); 314 | 315 | //if found then changing its content 316 | if (TYPING_USERS.length > 2) { 317 | divEx.innerHTML = `

many people typing

`; 318 | } else if (TYPING_USERS.length > 1) { 319 | divEx.innerHTML = `

${TYPING_USERS.join( 320 | ", " 321 | )} are typing

`; 322 | } else if (TYPING_USERS.length <= 1) { 323 | divEx.innerHTML = `

${TYPING_USERS.join( 324 | ", " 325 | )} is typing

`; 326 | } 327 | 328 | return; 329 | } 330 | 331 | //create div 332 | let div = document.createElement("div"); 333 | 334 | //add class 335 | div.classList.add("message", "typing"); 336 | 337 | //check no. of users typing 338 | if (TYPING_USERS.length > 2) { 339 | div.innerHTML = `

many people typing

`; 340 | } else if (TYPING_USERS.length > 1) { 341 | div.innerHTML = `

${TYPING_USERS.join( 342 | ", " 343 | )} are typing

`; 344 | } else if (TYPING_USERS.length <= 1) { 345 | div.innerHTML = `

${TYPING_USERS.join( 346 | ", " 347 | )} is typing

`; 348 | } 349 | 350 | //apend element to dom 351 | document.querySelector(".chat-messages").appendChild(div); 352 | scrollToBottom(); 353 | }; 354 | 355 | /* EDITING MSG FEATURE */ 356 | var isEditing = { status: false, id: null }; 357 | 358 | const editMsg = (id) => { 359 | //set editing mode to true 360 | isEditing = { status: true, id: id }; 361 | let isRepliedMsg = false; 362 | let prevMsgText = ''; 363 | //get old text 364 | if ( 365 | document 366 | .getElementById(id) 367 | .querySelector(".text") 368 | .querySelector(".replied-msg") != undefined || 369 | document 370 | .getElementById(id) 371 | .querySelector(".text") 372 | .querySelector(".replied-msg") != null 373 | ) { 374 | isRepliedMsg = true; 375 | } 376 | const prevMsgPara = document 377 | .getElementById(id) 378 | .querySelector(".text") 379 | .querySelector(`${isRepliedMsg ? ".replied-msg" : "p"}`); 380 | 381 | if (prevMsgPara.classList.contains("trnc")) { 382 | prevMsgText = prevMsgPara.children[1].innerText; 383 | } else { 384 | prevMsgText = prevMsgPara.innerText; 385 | } 386 | //select input and put old text in input 387 | const inputEle = document.getElementById("msg"); 388 | inputEle.value = prevMsgText; 389 | //set popup to inform user that he is editing 390 | const formContainer = document.querySelector(".chat-form-container"); 391 | formContainer.classList.add("editing-form-container"); 392 | //close btn 393 | let span = document.createElement("span"); 394 | span.classList.add("material-icons", "replying-close-btn"); 395 | span.innerText = "cancel"; 396 | span.setAttribute("onclick", "cancelEdit()"); 397 | //prepend 398 | formContainer.prepend(span); 399 | //focus 400 | inputEle.focus(); 401 | }; 402 | 403 | const emitEditedText = (e) => { 404 | //get msg text 405 | const msg = e.target.elements.msg.value; 406 | //check msg and emit 407 | if (msg.trim() == "") { 408 | isEditing = { status: false, id: null }; 409 | } else { 410 | socket.emit("edited-msg", { text: msg, id: isEditing.id }); 411 | isEditing = { status: false, id: null }; 412 | } 413 | //remove popup 414 | const formContainer = document.querySelector(".chat-form-container"); 415 | formContainer.classList.remove("editing-form-container"); 416 | //scroll 417 | e.target.elements.msg.value = ""; 418 | e.target.elements.msg.focus(); 419 | scrollToBottom(); 420 | }; 421 | 422 | socket.on("edit-msg", ({ text, id, time }) => { 423 | let isRepliedMsg = false; 424 | // console.log(text); 425 | if ( 426 | document 427 | .getElementById(id) 428 | .querySelector(".text") 429 | .querySelector(".replied-msg") != undefined || 430 | document 431 | .getElementById(id) 432 | .querySelector(".text") 433 | .querySelector(".replied-msg") != null 434 | ) { 435 | isRepliedMsg = true; 436 | document 437 | .getElementById(id) 438 | .querySelector(".text") 439 | .querySelector(".replied-msg") 440 | .remove(); 441 | } 442 | //get msg div with id 443 | const msgDiv = document.getElementById(id); 444 | //get text msg div 445 | const textDiv = msgDiv.querySelector(".text"); 446 | console.log(textDiv.outerHTML); 447 | let eText = document.createElement('p'); 448 | eText.innerHTML = text.substring(3,text.length - 5) 449 | //insert new text 450 | if (!isRepliedMsg) { 451 | //normal msg directly edited to division 452 | if (text.length <= 57) { 453 | textDiv.innerHTML = text; 454 | } else { 455 | // var lgMsg = eText.innerText; 456 | // var longMsg = lgMsg.substring(0,lgMsg.length-5); 457 | var seenMsg = eText.innerText.substring(0,49); 458 | // var extraMsg = eText.innerText.substring(49,); 459 | textDiv.innerHTML = `

460 | ${seenMsg}${text.substring(3,text.length - 5)}
461 | Read more

` 462 | ++cntr; 463 | } 464 | } else { 465 | //replied msg 466 | //alter the para and add to div 467 | var editpara = document.createElement('p'); 468 | var tempPara = document.createElement('p'); 469 | if (text.length <= 57) { 470 | editpara.innerHTML = text.substring(3,text.length-5); 471 | } else { 472 | tempPara.innerHTML = text.substring(3,text.length-5); 473 | // var longMsg = lgMsg.substring(3,lgMsg.length-5); 474 | var seenMsg = tempPara.innerText.substring(0,49); 475 | // var extraMsg = longMsg.substring(49,); 476 | // console.log(textDiv.outerHTML); 477 | editpara.innerHTML = ` 478 | ${seenMsg}${tempPara.innerHTML}
479 | Read more

` 480 | ++cntr; 481 | } 482 | editpara.classList.add('replied-msg'); 483 | textDiv.appendChild(editpara); 484 | } 485 | 486 | //change time 487 | const timeSpan = msgDiv.querySelector(".meta").querySelector("span"); 488 | timeSpan.innerHTML = moment(time).format("h:mm a"); 489 | //close menu 490 | msgDiv.querySelector(".btn-container").classList.add("hide"); 491 | //change classes 492 | msgDiv.classList.add("edited-msg"); 493 | let allList = document.querySelectorAll(".btn-container"); 494 | allList.forEach((item) => { 495 | // item.style['display'] = 'none'; 496 | item.classList.add("hide"); 497 | }); 498 | }); 499 | 500 | /* Replying msg feature */ 501 | var isReplying = { status: false, info: { name: "", text: "" } }; 502 | var repliedDiv; 503 | const replyMsg = (id) => { 504 | let isRepliedMsg = false; 505 | if ( 506 | document 507 | .getElementById(id) 508 | .querySelector(".text") 509 | .querySelector(".replied-msg") != undefined || 510 | document 511 | .getElementById(id) 512 | .querySelector(".text") 513 | .querySelector(".replied-msg") != null 514 | ) { 515 | isRepliedMsg = true; 516 | } 517 | //find author name and text 518 | repliedDiv = document.getElementById(id); 519 | const authorDetails = repliedDiv.querySelector(".meta"); 520 | const authorName = authorDetails.innerText; 521 | let msgText; 522 | if (isRepliedMsg) { 523 | msgText = repliedDiv.querySelector(".text").querySelector(".replied-msg"); 524 | } else { 525 | msgText = repliedDiv.querySelector(".text").querySelector("p"); 526 | } 527 | //set replying mode 528 | isReplying = { 529 | status: true, 530 | info: { name: authorDetails.outerHTML, text: msgText.outerHTML }, 531 | }; 532 | //set popup 533 | const formContainer = document.querySelector(".chat-form-container"); 534 | formContainer.classList.add("replying-form-container"); 535 | document 536 | .querySelector(".replying-form-container") 537 | .setAttribute("to", `Replying to : ${authorName.split(" ")[0]}`); 538 | //create close button 539 | let span = document.createElement("span"); 540 | span.classList.add("material-icons", "replying-close-btn"); 541 | span.innerText = "cancel"; 542 | span.setAttribute("onclick", "cancelReply()"); 543 | //prepend 544 | formContainer.prepend(span); 545 | 546 | const inputEle = document.getElementById("msg"); 547 | inputEle.focus(); 548 | }; 549 | 550 | const emitReplyMsg = (e) => { 551 | //get msg text 552 | const repMsg = e.target.elements.msg.value; 553 | //check msg and emit 554 | let isRepliedMsg = false; 555 | if ( 556 | repliedDiv 557 | .querySelector(".text") 558 | .querySelector(".replied-msg") != undefined || 559 | repliedDiv 560 | .querySelector(".text") 561 | .querySelector(".replied-msg") != null 562 | ) { 563 | isRepliedMsg = true; 564 | } 565 | var rplyText; 566 | var rplyPara = repliedDiv 567 | .querySelector(".text") 568 | .querySelector(`${isRepliedMsg ? ".replied-msg" : "p"}`); 569 | if(rplyPara.innerText.length <=50) { 570 | rplyText = rplyPara.outerHTML; 571 | } else { 572 | var temp = rplyPara.querySelector('.seen').innerText; 573 | rplyText = '

' + temp + '...

'; 574 | } 575 | 576 | let userID = socket.id; 577 | if (repMsg.trim() == "") { 578 | isReplying = { status: false, info: { name: "", text: "" } }; 579 | } else { 580 | const msg = `
${isReplying.info.name}${rplyText}

${repMsg}

`; 581 | socket.emit("chatMessage", { msg, userID }); 582 | isReplying = { status: false, info: { name: "", text: "" } }; 583 | } 584 | //remove popup 585 | const formContainer = document.querySelector(".chat-form-container"); 586 | formContainer.classList.remove("replying-form-container"); 587 | //remove close btn 588 | formContainer.querySelector(".replying-close-btn").remove(); 589 | // //scroll 590 | let allList = document.querySelectorAll(".btn-container"); 591 | allList.forEach((item) => { 592 | // item.style['display'] = 'none'; 593 | item.classList.add("hide"); 594 | }); 595 | e.target.elements.msg.value = ""; 596 | e.target.elements.msg.focus(); 597 | scrollToBottom(); 598 | }; 599 | 600 | const cancelReply = () => { 601 | //exit replying 602 | isReplying = { status: false, info: { name: "", text: "" } }; 603 | //remove popup 604 | const formContainer = document.querySelector(".chat-form-container"); 605 | formContainer.classList.remove("replying-form-container"); 606 | //remove close btn 607 | formContainer.querySelector(".replying-close-btn").remove(); 608 | // //scroll 609 | formContainer.querySelector("#msg").focus(); 610 | scrollToBottom(); 611 | }; 612 | 613 | const cancelEdit = () => { 614 | //exit replying 615 | isEditing = { status: false, id: null }; 616 | //remove popup 617 | const formContainer = document.querySelector(".chat-form-container"); 618 | formContainer.classList.remove("editing-form-container"); 619 | //remove close btn 620 | formContainer.querySelector(".replying-close-btn").remove(); 621 | // //scroll 622 | //remove popup 623 | let allList = document.querySelectorAll(".btn-container"); 624 | allList.forEach((item) => { 625 | // item.style['display'] = 'none'; 626 | item.classList.add("hide"); 627 | }); 628 | formContainer.querySelector("#msg").focus(); 629 | scrollToBottom(); 630 | }; 631 | 632 | var isMenuOpen = false; 633 | const menuOpen = (id) => { 634 | let allList = document.querySelectorAll(".btn-container"); 635 | allList.forEach((item) => { 636 | // item.style['display'] = 'none'; 637 | item.classList.add("hide"); 638 | }); 639 | 640 | const msgDiv = document.getElementById(id); 641 | const btnContainer = msgDiv.querySelector(".btn-container"); 642 | if (!isMenuOpen) { 643 | isMenuOpen = true; 644 | // btnContainer.style['display'] = 'flex'; 645 | btnContainer.classList.remove("hide"); 646 | } else { 647 | isMenuOpen = false; 648 | // btnContainer.style['display'] = 'none'; 649 | btnContainer.classList.add("hide"); 650 | } 651 | }; 652 | 653 | isSideBarOpen = false; 654 | const toggleSideBar = () => { 655 | if (isSideBarOpen) { 656 | document.querySelector(".chat-sidebar").classList.remove("sidemenu-open"); 657 | document.querySelector(".hamburger").classList.remove("is-active"); 658 | isSideBarOpen = false; 659 | } else { 660 | document.querySelector(".chat-sidebar").classList.add("sidemenu-open"); 661 | document.querySelector(".hamburger").classList.add("is-active"); 662 | isSideBarOpen = true; 663 | } 664 | }; 665 | 666 | //image upload 667 | const imageUpload = async () => { 668 | const file = document.getElementById('image-input').files[0]; 669 | const formData = new FormData() 670 | formData.append('image', file) 671 | console.log(formData) 672 | const res = await fetch('/image', { 673 | method: 'post', 674 | body: formData 675 | }) 676 | const imgURL = await res.json(); 677 | document.getElementById('msg').value = `![img](${imgURL.link})` 678 | document.getElementById('image-input').value = ''; 679 | } 680 | -------------------------------------------------------------------------------- /public/js/lastEncounter.js: -------------------------------------------------------------------------------- 1 | const username = document.querySelector("#exampleFormControlInput1"); 2 | const roomSelector = document.querySelector("#exampleFormControlSelect1"); 3 | const joinChatFrom = document.querySelector("#joinChatForm"); 4 | 5 | const lastUsedUserName = localStorage.getItem("last_used_username"); 6 | const lastJoinedRoom = localStorage.getItem("last_joined_room"); 7 | if (lastUsedUserName !== null) username.value = lastUsedUserName; 8 | if (lastJoinedRoom !== null) roomSelector.value = lastJoinedRoom; 9 | 10 | joinChatFrom.addEventListener("submit", () => { 11 | localStorage.setItem("last_used_username", username.value); 12 | localStorage.setItem("last_joined_room", roomSelector.value); 13 | sessionStorage.setItem("current_user", username.value); 14 | }); 15 | -------------------------------------------------------------------------------- /public/js/roomMembers.js: -------------------------------------------------------------------------------- 1 | const socket = io('/roomMembers'); 2 | socket.on('roomMembersCount', (roomMembersCount) => { 3 | for (let room in roomMembersCount) { 4 | const roomOption = document.querySelector(`option[value="${room}"]`); 5 | roomOption.innerHTML = `${room} (${roomMembersCount[room]})`; 6 | } 7 | }); 8 | socket.on('countUpdate', ({ room, count }) => { 9 | const roomOption = document.querySelector(`option[value="${room}"]`); 10 | roomOption.innerHTML = `${room} (${count})`; 11 | }); 12 | 13 | -------------------------------------------------------------------------------- /public/scss/index.scss: -------------------------------------------------------------------------------- 1 | $primary-color: #63b4b8; 2 | @import url("https://fonts.googleapis.com/css2?family=Gluten:wght@500&display=swap"); 3 | body { 4 | background: linear-gradient(90deg, rgb(0, 40, 70), rgb(255, 115, 115), rgb(255, 175, 123)); 5 | text-align: center; 6 | position: relative; 7 | min-height: 100vh; 8 | 9 | /* dark-mode toggle switch */ 10 | #toggle { 11 | position: absolute; 12 | width: 50px; 13 | height: 50px; 14 | top: 10px; 15 | right: 10px; 16 | cursor: pointer; 17 | } 18 | 19 | #toggle svg { 20 | aspect-ratio: 1; 21 | object-fit: contain; 22 | } 23 | 24 | #toggle .outer-star { 25 | transition: transform 600ms cubic-bezier(0.59, 0.02, 0, 1.26) 0s; 26 | } 27 | 28 | #toggle .inner-dot { 29 | transition: transform 600ms ease 0s; 30 | transform: matrix(1, 0, 0, 1.5, -3, 0); 31 | } 32 | 33 | #toggle.active .outer-star { 34 | transform: rotate(180deg); 35 | } 36 | 37 | #toggle.active .inner-dot { 38 | transform: matrix(1, 0, 0, 1, 0, 0); 39 | } 40 | 41 | .heading { 42 | display: flex; 43 | align-items: center; 44 | justify-content: center; 45 | height: 30vh; 46 | padding: 3%; 47 | iframe { 48 | height: 100%; 49 | pointer-events: none; 50 | width: 10%; 51 | } 52 | h1 { 53 | font-family: "Gluten", cursive; 54 | font-size: 4rem; 55 | font-weight: 600; 56 | transition: all 1s ease-in-out 0s; 57 | display: inline; 58 | } 59 | } 60 | .container-fluid { 61 | padding-bottom: 5rem !important; 62 | } 63 | .main-div { 64 | min-height: 60vh; 65 | max-width: 500px; 66 | margin: auto; 67 | border-radius: 20px; 68 | background:rgba(255,255,255,0.25); 69 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); 70 | backdrop-filter: blur(2px); 71 | padding: 1rem 2rem; 72 | form { 73 | .form-group { 74 | padding: 0; 75 | margin: 1rem auto; 76 | display:flex; 77 | flex-direction:column; 78 | } 79 | label { 80 | font-size: 1rem; 81 | padding: 1%; 82 | margin-left: 3px; 83 | text-align: left; 84 | } 85 | #username{ 86 | padding-right: 15px; 87 | text-align: left; 88 | } 89 | .form-control{ 90 | padding-top: 12px; 91 | padding-bottom: 12px; 92 | } 93 | .border-gradient { 94 | --one: #0098c1; 95 | --two: #00d721; 96 | width: 100%; 97 | padding:5px; 98 | border-radius: 15px; 99 | } 100 | input:focus{ 101 | border:none; 102 | outline:none; 103 | background:white; 104 | } 105 | input { 106 | position: relative; 107 | width: 100%; 108 | padding: 0.65rem; 109 | border-radius: 5px; 110 | border:none; 111 | box-shadow: 1px 1px 8px rgba(0,0,0,0.15); 112 | background: rgba( 255, 255, 255, 0.3 ); 113 | box-shadow: 1px 1px 10px 0 rgba( 31, 38, 135, 0.15 ); 114 | backdrop-filter: blur( 4.5px ); 115 | -webkit-backdrop-filter: blur( 4.5px ); 116 | border-radius: 10px; 117 | border: 1px solid rgba( 255, 255, 255, 0.18 ); 118 | } 119 | 120 | select { 121 | position: relative; 122 | width: 100%; 123 | padding: 0.65rem; 124 | border-radius: 5px; 125 | border:none; 126 | box-shadow: 1px 1px 8px rgba(0,0,0,0.15); 127 | background: rgba( 255, 255, 255, 0.3 ); 128 | box-shadow: 1px 1px 10px 0 rgba( 31, 38, 135, 0.15 ); 129 | backdrop-filter: blur( 4.5px ); 130 | -webkit-backdrop-filter: blur( 4.5px ); 131 | border-radius: 10px; 132 | border: 1px solid rgba( 255, 255, 255, 0.18 ); 133 | } 134 | select:focus { 135 | border:none; 136 | outline:none; 137 | background:white; 138 | } 139 | button { 140 | font-size: 15px; 141 | width: 70%; 142 | font-weight: bold; 143 | background: linear-gradient(45deg, #4daaff,#0cf076); 144 | padding: 10px 30px; 145 | color: #3d3d3d; 146 | transition: color 1s ease 0s; 147 | border: 2px solid hsl(193, 100%, 50%); 148 | border-radius: 10px; 149 | cursor: pointer; 150 | box-shadow: 0 1px 8px rgba(0, 0, 0, 0.1); 151 | -webkit-transition-duration: 0.05s; 152 | transition-duration: 0.05s; 153 | -webkit-transition-property: box-shadow, tranform; 154 | transition-property: box-shadow, transform; 155 | } 156 | button:hover, 157 | button:focus, 158 | button:active { 159 | -webkit-transform: scale(1.1); 160 | transform: scale(1.1); 161 | } 162 | } 163 | } 164 | } 165 | body::before { 166 | content: ""; 167 | position: absolute; 168 | inset: 0; 169 | background: linear-gradient(90deg, #00c9ff, #92fe9e); 170 | opacity: 1; 171 | z-index: -1; 172 | transition: all 1s ease-in-out 0.5s; 173 | } 174 | #joinChat { 175 | margin-top: 10px; 176 | margin-bottom: 20px; 177 | transition: all 1s ease 0s; 178 | } 179 | .form-group { 180 | transition: all 1s ease-in-out 0s; 181 | } 182 | .dark-mode { 183 | color: white; 184 | h1 { 185 | color: white; 186 | } 187 | .form-group { 188 | color:black; 189 | } 190 | .form-group input{ 191 | color:black; 192 | } 193 | .form-group select{ 194 | color:black; 195 | } 196 | select { 197 | color: white; 198 | border: 3px solid white; 199 | } 200 | #joinChat { 201 | background: linear-gradient(45deg, #7e4c5b, #ff7f33); 202 | border: 2px solid #8b5858; 203 | } 204 | input { 205 | color: white; 206 | } 207 | select:focus { 208 | border: 3px solid white; 209 | } 210 | #joinChat { 211 | color: white; 212 | } 213 | .border-gradient { 214 | --one: #a80000 !important; 215 | --two: #ff7631 !important; 216 | } 217 | } 218 | .dark-mode::before { 219 | opacity: 0; 220 | } 221 | /* 222 | =================================== 223 | ***Avatars*** 224 | =================================== 225 | */ 226 | 227 | .profile { 228 | width: 124px; 229 | height: 124px; 230 | margin: 30px auto; 231 | border-radius: 50%; 232 | img { 233 | width: 120px; 234 | height: 120px; 235 | border-radius: 50%; 236 | vertical-align: middle; 237 | box-shadow: rgba(50, 50, 93, 0.25) 0px 50px 100px -20px, rgba(0, 0, 0, 0.3) 0px 30px 60px -30px, 238 | rgba(10, 37, 64, 0.35) 0px -2px 6px 0px inset; 239 | } 240 | &:hover { 241 | cursor: pointer; 242 | } 243 | } 244 | /* 245 | ============================================= 246 | ******Modal Related SCSS******** 247 | ============================================ 248 | */ 249 | .modal_div { 250 | position: fixed; 251 | top: 60%; 252 | left: 50%; 253 | transform: translate(-50%, -50%); 254 | width: 400px; 255 | height: 400px; 256 | // padding: 20px; 257 | display: flex; 258 | align-items: flex-end; 259 | justify-content: center; 260 | flex-direction: column; 261 | border-radius: 10px; 262 | background-color: #fee2f8; 263 | background-image: linear-gradient(315deg, #fee2f8 0%, #dcf8ef 74%); 264 | box-shadow: 0 4rem 6rem rgba(0, 0, 0, 0.3); 265 | z-index: 1000; 266 | opacity: 1; 267 | visibility: visible; 268 | transition: all 0.7s ease 0s; 269 | p.btn--close-modal { 270 | display: block; 271 | text-align: right; 272 | margin: 0 10px; 273 | font-family: "Times New Roman", Times, serif; 274 | cursor: pointer; 275 | font-size: 3rem; 276 | font-weight: bold; 277 | } 278 | } 279 | .modal_box { 280 | width: 400px; 281 | height: 340px; 282 | } 283 | /* 284 | ============================================= 285 | ******Slider Related SCSS******** 286 | ============================================ 287 | */ 288 | 289 | .slideshow-container { 290 | position: relative; 291 | overflow: hidden; 292 | .mySlides { 293 | width: 400px; 294 | height: 240px; 295 | margin: 0 auto; 296 | 297 | 298 | } 299 | } 300 | 301 | .mySlides { 302 | width: 100%; 303 | height: 100%; 304 | display: block; 305 | margin-top: 20px; 306 | position: absolute; 307 | top: 0px; 308 | transition: transform 1s; 309 | 310 | img { 311 | // vertical-align: middle; 312 | width: 240px; 313 | height: 240px; 314 | border-radius: 50%; 315 | cursor: pointer; 316 | } 317 | } 318 | /* Next & previous buttons */ 319 | .prev, 320 | .next { 321 | cursor: pointer; 322 | position: absolute; 323 | top: 33%; 324 | width: auto; 325 | padding: 16px; 326 | margin-top: -22px; 327 | color: grey; 328 | font-weight: bold; 329 | font-size: 22px; 330 | transition: 0.6s ease; 331 | border-radius: 0 3px 3px 0; 332 | user-select: none; 333 | text-decoration: none; 334 | } 335 | 336 | /* Position the "next button" to the right */ 337 | .next { 338 | right: 10px; 339 | border-radius: 3px 0 0 3px; 340 | } 341 | .prev { 342 | left: 10px; 343 | border-radius: 3px 0 0 3px; 344 | } 345 | 346 | /* On hover, add a black background color with a little bit see-through */ 347 | .prev:hover, 348 | .next:hover { 349 | background-color: rgba(41, 129, 151, 0.8); 350 | } 351 | 352 | a.next:hover { 353 | color: white; 354 | } 355 | a.prev:hover { 356 | color: white; 357 | } 358 | /* Caption text */ 359 | .text { 360 | color: grey; 361 | font-size: 15px; 362 | padding: 8px 12px; 363 | position: absolute; 364 | bottom: 8px; 365 | width: 100%; 366 | text-align: center; 367 | } 368 | 369 | /* Number text (1/3 etc) */ 370 | .numbertext { 371 | color: grey; 372 | font-size: 12px; 373 | padding: 12px 16px; 374 | position: absolute; 375 | display: block; 376 | top: 0px; 377 | } 378 | .dotBox { 379 | position: absolute; 380 | bottom: 10px; 381 | left: 10px; 382 | right: 10px; 383 | 384 | .active, 385 | .dot:hover { 386 | background-color: #717171; 387 | } 388 | } 389 | .dot { 390 | cursor: pointer; 391 | height: 15px; 392 | width: 15px; 393 | margin: 0 2px; 394 | background-color: #bbb; 395 | border-radius: 50%; 396 | display: inline-block; 397 | transition: background-color 0.6s ease; 398 | transition: all 0.3s; 399 | } 400 | .selectDot { 401 | background-color: #13b8ff !important; 402 | width: 20px; 403 | height: 20px; 404 | // transition: all 0.6s; 405 | 406 | } 407 | 408 | /* 409 | ============================================= 410 | ******Overlay Related Scss******** 411 | ============================================ 412 | */ 413 | .overlay_div { 414 | position: fixed; 415 | left: 0; 416 | top: 0; 417 | width: 100%; 418 | height: 100%; 419 | background-color: black; 420 | z-index: 100; 421 | transition: all 0.7s; 422 | opacity: 0.5; 423 | visibility: visible; 424 | } 425 | .overlay_hidden { 426 | opacity: 0; 427 | visibility: hidden; 428 | } 429 | img { 430 | transition: all 1s; 431 | } 432 | 433 | i.fa.fa-check.icon_check{ 434 | position: absolute; 435 | font-weight: lighter; 436 | font-weight: 50; 437 | top: 174px; 438 | left: 64%; 439 | font-size: 40px; 440 | color: white; 441 | border-radius: 50%; 442 | width: 50px; 443 | height: 50px; 444 | display: flex; 445 | align-items: center; 446 | justify-content: center; 447 | background-color: #00a6ff; 448 | } 449 | i.fa.fa-check.icon_check.hidden1{ 450 | display: none; 451 | } 452 | /* 453 | 454 | ============================================= 455 | ******Dark MODE OF modal Scss******** 456 | ============================================ 457 | */ 458 | .dark-mode { 459 | .darkModal { 460 | background-color: #d9e4f5; 461 | background-image: linear-gradient(315deg, #d9e4f5 0%, #f5e3e6 74%); 462 | } 463 | .darkClose { 464 | background-color: #d7a385b3; 465 | } 466 | } 467 | @media (max-height: 768px) { 468 | body { 469 | .main-div { 470 | min-height: 55vh; 471 | } 472 | } 473 | } 474 | @media (max-width: 768px) { 475 | body { 476 | .main-div { 477 | width: 95%; 478 | padding: 5% 5% 7% 5%; 479 | } 480 | .heading { 481 | h1 { 482 | font-size: 3rem; 483 | } 484 | } 485 | .main-div { 486 | form { 487 | .form-group { 488 | display: flex; 489 | flex-direction: column; 490 | justify-content: start; 491 | } 492 | #username{ 493 | padding: 2.5px; 494 | } 495 | input { 496 | width: 100%; 497 | margin: 0; 498 | padding: 0.5rem 1rem; 499 | } 500 | select { 501 | padding: auto 1rem; 502 | width: 100%; 503 | } 504 | select:focus { 505 | width: 85%; 506 | } 507 | } 508 | } 509 | } 510 | } 511 | 512 | @media (max-width: 576px) { 513 | body { 514 | .heading { 515 | h1 { 516 | font-size: 2.75rem; 517 | } 518 | iframe { 519 | width: 15%; 520 | } 521 | } 522 | .profile { 523 | width: 84px; 524 | height: 84px; 525 | img { 526 | width: 80px; 527 | height: 80px; 528 | } 529 | } 530 | } 531 | .modal_div { 532 | width: 280px; 533 | height: 280px; 534 | p.btn--close-modal { 535 | font-size: 2rem; 536 | } 537 | } 538 | .modal_box { 539 | width: 280px; 540 | height: 250px; 541 | } 542 | .mySlides { 543 | img { 544 | width: 160px; 545 | height: 160px; 546 | } 547 | } 548 | .prev, 549 | .next, 550 | .text { 551 | font-size: 14px; 552 | } 553 | .prev { 554 | left: 5px; 555 | 556 | } 557 | .next { 558 | right: 5px; 559 | 560 | } 561 | .selectDot { 562 | background-color: #13b8ff !important; 563 | width: 15px; 564 | height: 15px; 565 | // transition: all 0.6s; 566 | 567 | } 568 | 569 | .dot { 570 | width: 10px; 571 | height: 10px; 572 | } 573 | .slideshow-container { 574 | .mySlides { 575 | width: 280px; 576 | height: 240px; 577 | } 578 | height: 240px !important; 579 | } 580 | i.fa.fa-check.icon_check{ 581 | top: 118px; 582 | font-size: 22px; 583 | width: 35px; 584 | height: 35px; 585 | } 586 | 587 | 588 | } 589 | 590 | @media (max-width: 800px) { 591 | } 592 | .hidden { 593 | opacity: 0; 594 | visibility: hidden; 595 | } 596 | 597 | 598 | //avatar form scss 599 | #addAvatar { 600 | text-align: center; 601 | margin: 20px 0; 602 | h5{ 603 | font-weight: 500; 604 | color: #000000; 605 | } 606 | #fileInp { 607 | margin: 20px auto; 608 | position: relative; 609 | display: block; 610 | background: #000000; 611 | color: #5bc0de; 612 | border-radius: 8px; 613 | } 614 | 615 | #upload{ 616 | display: block; 617 | margin: 20px auto; 618 | width: 80px; 619 | height: max-content; 620 | padding: 8px 16px; 621 | font-size: 16px; 622 | font-weight: 500; 623 | border: none; 624 | outline: none; 625 | color: black; 626 | background: #5bc0de; 627 | border-radius: 8px; 628 | cursor: pointer; 629 | } 630 | } 631 | -------------------------------------------------------------------------------- /public/scss/main.scss: -------------------------------------------------------------------------------- 1 | @import url("https://fonts.googleapis.com/css?family=Roboto&display=swap"); 2 | @import url("https://fonts.googleapis.com/css2?family=Gluten:wght@500&display=swap"); 3 | 4 | $dark-color-a: #00adb5; 5 | $dark-color-b: #393e46; 6 | $light-color: #eeeeee; 7 | $light-color-b: #eeeeeed0; 8 | $success-color: #222831; 9 | $error-color: #d9534f; 10 | $transitionchange: all 1s ease 0s; 11 | 12 | * { 13 | box-sizing: border-box; 14 | margin: 0; 15 | padding: 0; 16 | } 17 | 18 | body { 19 | font-family: "Roboto", sans-serif; 20 | font-size: 16px; 21 | // background-color: #FFDEE9; 22 | // background-image: linear-gradient(0deg, #FFDEE9 0%, #B5FFFC 100%); 23 | background-image: linear-gradient(90deg, rgb(0, 40, 70), rgb(255, 115, 115), rgb(255, 175, 123)); 24 | background-position: center; 25 | background-repeat: no-repeat; 26 | background-size: cover; 27 | height: 100vh; 28 | overflow: hidden; 29 | // background: $light-color; 30 | margin: 20px; 31 | } 32 | body * { 33 | transition: all 0.5s linear; 34 | } 35 | body::before { 36 | content: ""; 37 | position: absolute; 38 | inset: 0; 39 | background-image: linear-gradient(90deg, #01cafe 0%, #60ecbf 100%); 40 | opacity: 1; 41 | z-index: -1; 42 | transition: all 0.5s linear; 43 | } 44 | 45 | /* dark-mode toggle switch */ 46 | #toggle { 47 | position: absolute; 48 | width: 50px; 49 | height: 50px; 50 | top: 10px; 51 | right: 10px; 52 | cursor: pointer; 53 | } 54 | 55 | #toggle svg { 56 | aspect-ratio: 1; 57 | object-fit: contain; 58 | } 59 | 60 | #toggle .outer-star { 61 | transition: transform 600ms cubic-bezier(0.59, 0.02, 0, 1.26) 0s; 62 | } 63 | 64 | #toggle .inner-dot { 65 | transition: transform 600ms ease 0s; 66 | transform: matrix(1, 0, 0, 1.5, -3, 0); 67 | } 68 | 69 | #toggle.active .outer-star { 70 | transform: rotate(180deg); 71 | } 72 | 73 | #toggle.active .inner-dot { 74 | transform: matrix(1, 0, 0, 1, 0, 0); 75 | } 76 | 77 | ul { 78 | list-style: none; 79 | } 80 | 81 | a { 82 | text-decoration: none; 83 | } 84 | 85 | .btn { 86 | cursor: pointer; 87 | padding: 5px 15px; 88 | background: $light-color; 89 | color: $dark-color-a; 90 | border: 0; 91 | font-size: 17px; 92 | } 93 | 94 | /* Chat Page */ 95 | 96 | .chat-container { 97 | border-radius: 20px; 98 | max-width: 1100px; 99 | background: #fff; 100 | margin: 40px auto; 101 | overflow: hidden; 102 | opacity: 1; 103 | box-shadow: 10px 10px 58px 3px rgba(0, 0, 0, 0.75); 104 | border: 1px solid #393e46; 105 | } 106 | 107 | .chat-header { 108 | background: #00adb5; 109 | color: #fff; 110 | border-top-left-radius: 5px; 111 | border-top-right-radius: 5px; 112 | padding: 15px; 113 | display: flex; 114 | align-items: center; 115 | 116 | justify-content: space-between; 117 | .main-heading { 118 | font-family: "Gluten", cursive; 119 | } 120 | } 121 | 122 | .chat-main { 123 | display: grid; 124 | min-height: 60vh; 125 | // margin-bottom: 5vh; 126 | grid-template-columns: 1fr 3fr; 127 | } 128 | 129 | .chat-sidebar { 130 | background: $dark-color-b; 131 | color: #fff; 132 | padding: 20px 20px 60px; 133 | overflow-y: scroll; 134 | min-height: 60vh; 135 | } 136 | 137 | .chat-sidebar h2 { 138 | font-size: 20px; 139 | padding: 10px; 140 | } 141 | .room-name-container { 142 | height: 45px; 143 | margin-bottom: 25px; 144 | background: rgba(0, 0, 0, 0.1); 145 | } 146 | .chat-sidebar h3 { 147 | margin-bottom: 15px; 148 | } 149 | 150 | .chat-sidebar ul li { 151 | padding: 5px 0px; 152 | display: flex; 153 | align-items: center; 154 | justify-content: space-between; 155 | } 156 | 157 | .chat-main, 158 | .chat-messages, 159 | .chat-sidebar { 160 | max-height: 64vh; 161 | } 162 | .chat-messages { 163 | padding: 30px; 164 | max-height: 64vh; 165 | overflow-y: scroll; 166 | display: flex; 167 | flex-direction: column; 168 | } 169 | 170 | .chat-messages .message { 171 | position: relative; 172 | box-sizing: border-box; 173 | padding: 0.5rem 1rem; 174 | margin: 1rem auto 1rem 1rem; 175 | color: black; 176 | background: #e5e5ea; 177 | border-radius: 1.125rem 1.125rem 1.125rem 0; 178 | animation: scale-up-tl 700ms; 179 | } 180 | 181 | .chat-messages .author { 182 | position: relative; 183 | margin: 1rem 1rem 1rem auto; 184 | border-radius: 1.125rem 1.125rem 0 1.125rem; 185 | color: white; 186 | background: #0b93f6; 187 | box-sizing: border-box; 188 | padding: 0.5rem 1rem; 189 | animation: scale-up-br 700ms; 190 | } 191 | 192 | .message.bot { 193 | position: relative; 194 | margin: 1rem auto 1rem auto; 195 | padding: 10px 10px 15px; 196 | color: white; 197 | background: #948e8e; 198 | border-radius: 5px; 199 | opacity: 0.8; 200 | box-shadow: $light-color-b 5px 5px; 201 | width: 75%; 202 | height: 11%; 203 | text-align: center; 204 | vertical-align: middle; 205 | animation: fadeDown 700ms ease-out 0s 1 normal none; 206 | } 207 | .message.bot p { 208 | max-width: none !important; 209 | } 210 | .three-dots-menu { 211 | position: relative; 212 | float: right; 213 | margin-bottom: -2px; 214 | transform: rotate(90deg); 215 | cursor: pointer; 216 | } 217 | .chat-messages .author:hover .btn-danger { 218 | opacity: 1; 219 | } 220 | .chat-messages .message:hover .btn-danger { 221 | opacity: 1; 222 | } 223 | 224 | .chat-messages .message .meta { 225 | display: inline-block; 226 | font-size: 15px; 227 | font-weight: bold; 228 | color: $dark-color-b; 229 | opacity: 0.7; 230 | margin-bottom: 7px; 231 | } 232 | .chat-messages .message .replied-msg-container { 233 | background-color: #a9a5a5 !important; 234 | color: white !important; 235 | } 236 | .chat-messages .message .replied-msg-container .meta { 237 | color: white; 238 | } 239 | .chat-messages .message .replied-msg-container .meta span{ 240 | color: white; 241 | } 242 | .chat-messages .message .meta span { 243 | color: #777; 244 | padding-left: 0.6em; 245 | } 246 | .chat-messages .message p { 247 | max-width: 300px; 248 | word-break: break-all; 249 | } 250 | .chat-messages .author .meta { 251 | display: inline-block; 252 | font-size: 15px; 253 | font-weight: bold; 254 | color: $light-color; 255 | opacity: 0.7; 256 | margin-bottom: 7px; 257 | } 258 | .chat-messages .author p { 259 | max-width: 300px; 260 | word-break: break-all; 261 | } 262 | .chat-messages .author .meta span { 263 | color: $light-color-b; 264 | padding-left: 0.6em; 265 | opacity: 1; 266 | } 267 | .chat-messages .author .text img { 268 | width: 100px; 269 | } 270 | .chat-messages .message .text img { 271 | width: 100px; 272 | } 273 | .message.bot .meta { 274 | font-size: 15px; 275 | font-weight: bold; 276 | color: black; 277 | margin-bottom: 7px; 278 | display: none; 279 | } 280 | 281 | .message.bot .meta span { 282 | color: rgb(82, 80, 80); 283 | padding-left: 0.6em; 284 | } 285 | 286 | .chat-form-container { 287 | padding: 20px 30px; 288 | background: #00adb5; 289 | } 290 | 291 | .editing-form-container { 292 | position: relative; 293 | } 294 | .editing-form-container::before { 295 | position: absolute; 296 | content: "Editing a message"; 297 | top: -20px; 298 | left: 65px; 299 | padding: 0.5rem; 300 | background: navajowhite; 301 | font-weight: 800; 302 | border-radius: 10px; 303 | box-shadow: 1px 1px 5px black; 304 | } 305 | .replying-form-container { 306 | position: relative; 307 | } 308 | .replying-form-container::before { 309 | position: absolute; 310 | content: attr(to); 311 | top: -20px; 312 | left: 65px; 313 | padding: 0.5rem; 314 | background: navajowhite; 315 | font-weight: 800; 316 | border-radius: 10px; 317 | box-shadow: 1px 1px 5px black; 318 | } 319 | .replying-close-btn { 320 | position: absolute; 321 | top: -15px; 322 | color: navajowhite; 323 | cursor: pointer; 324 | } 325 | .chat-form-container form { 326 | display: flex; 327 | } 328 | 329 | .chat-form-container input[type="text"] { 330 | position: relative; 331 | font-size: 16px; 332 | padding: 5px 15px; 333 | height: 50px; 334 | flex: 1; 335 | } 336 | 337 | .message_input { 338 | overflow: hidden; 339 | padding: 15px; 340 | // border: solid blue 1px; 341 | border: none; 342 | border-radius: 1.5em; 343 | box-shadow: 0 3px 4px rgb(41, 39, 39); 344 | margin-top: 2.5px; 345 | outline: none; 346 | } 347 | #image-input { 348 | display: none; 349 | } 350 | .message_send_btn { 351 | background: rgba(0, 255, 149, 0.349); 352 | } 353 | 354 | /* Join Page */ 355 | .join-container { 356 | max-width: 500px; 357 | margin: 80px auto; 358 | color: #fff; 359 | transition: $transitionchange; 360 | } 361 | 362 | .join-header { 363 | text-align: center; 364 | padding: 20px; 365 | background: $dark-color-a; 366 | border-top-left-radius: 5px; 367 | border-top-right-radius: 5px; 368 | } 369 | 370 | .join-main { 371 | padding: 30px 40px; 372 | background: $dark-color-b; 373 | } 374 | 375 | .join-main p { 376 | margin-bottom: 20px; 377 | } 378 | 379 | .join-main .form-control { 380 | margin-bottom: 20px; 381 | } 382 | 383 | .join-main label { 384 | display: block; 385 | margin-bottom: 5px; 386 | } 387 | 388 | .join-main input[type="text"] { 389 | font-size: 16px; 390 | padding: 5px; 391 | height: 40px; 392 | width: 100%; 393 | } 394 | 395 | .join-main select { 396 | font-size: 16px; 397 | padding: 5px; 398 | height: 40px; 399 | width: 100%; 400 | } 401 | 402 | .join-main .btn { 403 | margin-top: 20px; 404 | width: 100%; 405 | } 406 | .dark-mode { 407 | background-image: linear-gradient(90deg, rgb(0, 40, 70), rgb(255, 115, 115), rgb(255, 175, 123)); 408 | .chat-header { 409 | background: rgb(0, 40, 70); 410 | transition: $transitionchange; 411 | } 412 | .chat-sidebar { 413 | background: rgb(255, 115, 115); 414 | transition: $transitionchange; 415 | } 416 | .chat-form-container { 417 | background: rgb(0, 40, 70); 418 | transition: $transitionchange; 419 | } 420 | .btn { 421 | color: rgb(0, 40, 70); 422 | transition: $transitionchange; 423 | } 424 | .chat-main { 425 | background: rgba(0, 0, 0, 0.849); 426 | transition: $transitionchange; 427 | } 428 | .message_send_btn, .image-add-btn { 429 | background: rgb(119, 0, 255); 430 | } 431 | .btn-danger, .btn-danger2 { 432 | background-color: #272727 !important; 433 | color: white !important; 434 | } 435 | .btn-danger span { 436 | color: white; 437 | } 438 | .btn-danger-reply:hover { 439 | background-color: #6c6c6c !important; 440 | } 441 | .btn-danger2:hover { 442 | background-color: #6c6c6c !important; 443 | } 444 | .btn-container { 445 | background-color: #272727; 446 | border: 1px solid white; 447 | } 448 | .hamburger-icon { 449 | color: #ffffff; 450 | } 451 | .hamburger .hamburger-inner{ 452 | background: #ffffff; 453 | } 454 | .hamburger .hamburger-inner::after{ 455 | background: #ffffff; 456 | } 457 | .hamburger .hamburger-inner::before{ 458 | background: #ffffff; 459 | } 460 | .hamburger--spin.is-active .hamburger-inner{ 461 | background: #ffffff; 462 | } 463 | .hamburger--spin.is-active .hamburger-inner::after{ 464 | background: #ffffff; 465 | } 466 | .hamburger--spin.is-active .hamburger-inner::before{ 467 | background: #ffffff; 468 | } 469 | ::-webkit-scrollbar-thumb { 470 | background: white; 471 | border-radius: 10px; 472 | } 473 | } 474 | .dark-mode::before { 475 | opacity: 0; 476 | } 477 | .btn-container { 478 | position: absolute; 479 | right: 6px; 480 | bottom: -85px; 481 | z-index: 5; 482 | display: flex; 483 | flex-direction: column; 484 | background-color: white; 485 | border-radius: 10px; 486 | border: 1px solid #393e46; 487 | transition: transform 125ms ease-in; 488 | } 489 | .hide { 490 | transform: scale(0); 491 | } 492 | .btn-danger { 493 | position: absolute; 494 | top: 50%; 495 | left: 70%; 496 | transform: translate(-50%, -50%); 497 | padding: 0.5rem; 498 | outline: none; 499 | border: none; 500 | border-radius: 0.5rem; 501 | color: #ffffff; 502 | background-color: white; 503 | opacity: 0; 504 | } 505 | .msg-head { 506 | display: flex; 507 | justify-content: space-between; 508 | } 509 | .btn-danger2 { 510 | padding: 0.5rem; 511 | outline: none; 512 | border: none; 513 | border-radius: 10px; 514 | background-color: white; 515 | } 516 | .btn-danger2:hover { 517 | background-color: #cacaca; 518 | } 519 | .btn-danger span { 520 | color: black; 521 | } 522 | .btn-danger-edit { 523 | left: 30%; 524 | } 525 | 526 | .author .btn-danger-reply { 527 | left: -25px; 528 | background: none; 529 | color: black; 530 | } 531 | .author .btn-danger-reply:hover { 532 | background-color: #00000080; 533 | color: white; 534 | } 535 | .extra { 536 | display: none; 537 | } 538 | .message .btn-danger-reply { 539 | left: calc(100% + 25px); 540 | background: none !important; 541 | color: black; 542 | } 543 | .message .btn-danger-reply:hover { 544 | background-color: #00000080 !important; 545 | color: white; 546 | } 547 | 548 | .btn-danger:hover { 549 | background: #a20900; 550 | } 551 | 552 | .replied-msg-container { 553 | background-color: #a8cce7; 554 | // opacity: 0.3; 555 | padding: 0.5rem; 556 | margin: 10px -2px; 557 | border-radius: 10px; 558 | } 559 | 560 | #beep1, 561 | #beep2 { 562 | display: none; 563 | } 564 | 565 | .dot-animation { 566 | position: relative; 567 | margin-right: 0.5rem; 568 | } 569 | .dot-animation::after { 570 | position: absolute; 571 | content: ""; 572 | animation: dotdot 1s infinite; 573 | } 574 | .edited-msg { 575 | position: relative; 576 | margin-right: 0.5rem; 577 | } 578 | .edited-msg::after { 579 | content: "(edited)"; 580 | font-size: 0.75rem; 581 | font-family: monospace; 582 | font-weight: 300; 583 | opacity: 0.75; 584 | } 585 | .hamburger-icon { 586 | display: none !important; 587 | } 588 | .hamburger { 589 | display: none !important; 590 | } 591 | @keyframes dotdot { 592 | 0% { 593 | content: "."; 594 | } 595 | 50% { 596 | content: ".."; 597 | } 598 | 100% { 599 | content: "..."; 600 | } 601 | } 602 | @media (max-width: 1200px) { 603 | .chat-container { 604 | position: relative; 605 | margin: 60px auto; 606 | } 607 | } 608 | @media (max-width: 700px) { 609 | .chat-main { 610 | display: block; 611 | max-height: 60vh; 612 | } 613 | .chat-messages { 614 | height: 60vh; 615 | } 616 | .chat-sidebar { 617 | position: absolute; 618 | z-index: 10; 619 | transform: translateX(-100%); 620 | } 621 | .sidemenu-open { 622 | transform: translateX(0); 623 | } 624 | .hamburger-icon { 625 | display: block !important; 626 | position: absolute; 627 | top: 15px; 628 | left: 25px; 629 | transform: scale(1.5); 630 | } 631 | .hamburger { 632 | display: inline-block !important; 633 | } 634 | .top-nav { 635 | display: flex; 636 | flex-direction: row; 637 | justify-content: space-between; 638 | } 639 | } 640 | /* ---------------------------------------------- 641 | * Generated by Animista on 2021-9-30 12:47:32 642 | * Licensed under FreeBSD License. 643 | * See http://animista.net/license for more info. 644 | * w: http://animista.net, t: @cssanimista 645 | * ---------------------------------------------- */ 646 | 647 | /** 648 | * ---------------------------------------- 649 | * animation bounce-in-bottom 650 | * ---------------------------------------- 651 | */ 652 | /* ---------------------------------------------- 653 | * Generated by Animista on 2021-9-30 12:51:35 654 | * Licensed under FreeBSD License. 655 | * See http://animista.net/license for more info. 656 | * w: http://animista.net, t: @cssanimista 657 | * ---------------------------------------------- */ 658 | 659 | /** 660 | * ---------------------------------------- 661 | * animation swing-in-left-bck 662 | * ---------------------------------------- 663 | */ 664 | /* ---------------------------------------------- 665 | * Generated by Animista on 2021-10-16 10:42:35 666 | * Licensed under FreeBSD License. 667 | * See http://animista.net/license for more info. 668 | * w: http://animista.net, t: @cssanimista 669 | * ---------------------------------------------- */ 670 | 671 | /** 672 | * ---------------------------------------- 673 | * animation scale-up-br 674 | * ---------------------------------------- 675 | */ 676 | /* ---------------------------------------------- 677 | * Generated by Animista on 2021-10-16 10:46:11 678 | * Licensed under FreeBSD License. 679 | * See http://animista.net/license for more info. 680 | * w: http://animista.net, t: @cssanimista 681 | * ---------------------------------------------- */ 682 | 683 | /** 684 | * ---------------------------------------- 685 | * animation scale-up-tl 686 | * ---------------------------------------- 687 | */ 688 | @-webkit-keyframes scale-up-tl { 689 | 0% { 690 | -webkit-transform: scale(0.5); 691 | transform: scale(0.5); 692 | -webkit-transform-origin: 0% 0%; 693 | transform-origin: 0% 0%; 694 | } 695 | 100% { 696 | -webkit-transform: scale(1); 697 | transform: scale(1); 698 | -webkit-transform-origin: 0% 0%; 699 | transform-origin: 0% 0%; 700 | } 701 | } 702 | @keyframes scale-up-tl { 703 | 0% { 704 | -webkit-transform: scale(0.5); 705 | transform: scale(0.5); 706 | -webkit-transform-origin: 0% 0%; 707 | transform-origin: 0% 0%; 708 | } 709 | 100% { 710 | -webkit-transform: scale(1); 711 | transform: scale(1); 712 | -webkit-transform-origin: 0% 0%; 713 | transform-origin: 0% 0%; 714 | } 715 | } 716 | 717 | @-webkit-keyframes scale-up-br { 718 | 0% { 719 | -webkit-transform: scale(0.5); 720 | transform: scale(0.5); 721 | -webkit-transform-origin: 100% 100%; 722 | transform-origin: 100% 100%; 723 | } 724 | 100% { 725 | -webkit-transform: scale(1); 726 | transform: scale(1); 727 | -webkit-transform-origin: 100% 100%; 728 | transform-origin: 100% 100%; 729 | } 730 | } 731 | @keyframes scale-up-br { 732 | 0% { 733 | -webkit-transform: scale(0.5); 734 | transform: scale(0.5); 735 | -webkit-transform-origin: 100% 100%; 736 | transform-origin: 100% 100%; 737 | } 738 | 100% { 739 | -webkit-transform: scale(1); 740 | transform: scale(1); 741 | -webkit-transform-origin: 100% 100%; 742 | transform-origin: 100% 100%; 743 | } 744 | } 745 | 746 | @-webkit-keyframes swing-in-left-bck { 747 | 0% { 748 | -webkit-transform: rotateY(-70deg); 749 | transform: rotateY(-70deg); 750 | -webkit-transform-origin: left; 751 | transform-origin: left; 752 | opacity: 0; 753 | } 754 | 100% { 755 | -webkit-transform: rotateY(0); 756 | transform: rotateY(0); 757 | -webkit-transform-origin: left; 758 | transform-origin: left; 759 | opacity: 1; 760 | } 761 | } 762 | ::-webkit-scrollbar { 763 | width: 15px; 764 | } 765 | ::-webkit-scrollbar-track { 766 | box-shadow: inset 0 0 5px grey; 767 | border-radius: 10px; 768 | } 769 | 770 | /* Handle */ 771 | ::-webkit-scrollbar-thumb { 772 | background: #00adb5; 773 | border-radius: 10px; 774 | } 775 | 776 | /* Handle on hover */ 777 | ::-webkit-scrollbar-thumb:hover { 778 | background: #bbbbbb; 779 | } 780 | ::-webkit-scrollbar-button:single-button { 781 | background-color: #bbbbbb; 782 | display: block; 783 | border-style: solid; 784 | height: 13px; 785 | width: 16px; 786 | } 787 | /* Up */ 788 | ::-webkit-scrollbar-button:single-button:vertical:decrement { 789 | border-width: 0 8px 8px 8px; 790 | border-color: transparent transparent #555555 transparent; 791 | } 792 | 793 | ::-webkit-scrollbar-button:single-button:vertical:decrement:hover { 794 | border-color: transparent transparent #777777 transparent; 795 | } 796 | /* Down */ 797 | ::-webkit-scrollbar-button:single-button:vertical:increment { 798 | border-width: 8px 8px 0 8px; 799 | border-color: #555555 transparent transparent transparent; 800 | } 801 | 802 | ::-webkit-scrollbar-button:vertical:single-button:increment:hover { 803 | border-color: #777777 transparent transparent transparent; 804 | } 805 | @keyframes swing-in-left-bck { 806 | 0% { 807 | -webkit-transform: rotateY(-70deg); 808 | transform: rotateY(-70deg); 809 | -webkit-transform-origin: left; 810 | transform-origin: left; 811 | opacity: 0; 812 | } 813 | 100% { 814 | -webkit-transform: rotateY(0); 815 | transform: rotateY(0); 816 | -webkit-transform-origin: left; 817 | transform-origin: left; 818 | opacity: 1; 819 | } 820 | } 821 | 822 | .animate { 823 | animation: shimmer 15s infinite linear; 824 | background: linear-gradient(to right, rgba(0, 0, 0, 0.05) 4%, rgba(0, 0, 0, 0.01) 35%, rgba(0, 0, 0, 0.1) 55%); 825 | background-size: 1000px 100%; 826 | } 827 | 828 | .fade-in { 829 | animation: fadeIn linear 1s; 830 | -webkit-animation: fadeIn linear 1s; 831 | -moz-animation: fadeIn linear 1s; 832 | -o-animation: fadeIn linear 1s; 833 | -ms-animation: fadeIn linear 1s; 834 | } 835 | 836 | /* 837 | ============================================== 838 | *********User List avtar related css******** 839 | ============================================== 840 | */ 841 | .users { 842 | li { 843 | display: flex; 844 | align-items: flex-end; 845 | justify-content: center; 846 | } 847 | } 848 | .userAvatar { 849 | width: 35px; 850 | height: 35px; 851 | border-radius: 50%; 852 | box-shadow: rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.23) 0px 3px 6px; 853 | } 854 | .userAvatar1 { 855 | width: 55px; 856 | height: 55px; 857 | border-radius: 50%; 858 | margin-bottom: 15px; 859 | box-shadow: rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.23) 0px 3px 6px; 860 | } 861 | .profileAdded{ 862 | display: flex; 863 | align-items: center; 864 | justify-content: center; 865 | img{ 866 | margin: 0 5px; 867 | width: 40px; 868 | height: 40px; 869 | box-shadow: rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.23) 0px 3px 6px; 870 | } 871 | } 872 | 873 | /* 874 | ============================================== 875 | *********Message related profiles******** 876 | ============================================== 877 | */ 878 | .profileLeft{ 879 | display: flex; 880 | // flex-direction: row-reverse; 881 | align-items: flex-end; 882 | } 883 | .profileRight{ 884 | display: flex; 885 | flex-direction: row-reverse; 886 | align-items: flex-end; 887 | } 888 | 889 | @keyframes shimmer { 890 | 0% { 891 | background-position: -1000px 0; 892 | } 893 | 100% { 894 | background-position: 1000px 0; 895 | } 896 | } 897 | 898 | @keyframes fadeIn { 899 | 0% { 900 | opacity: 0; 901 | } 902 | 100% { 903 | opacity: 1; 904 | } 905 | } 906 | 907 | @-moz-keyframes fadeIn { 908 | 0% { 909 | opacity: 0; 910 | } 911 | 100% { 912 | opacity: 1; 913 | } 914 | } 915 | 916 | @-webkit-keyframes fadeIn { 917 | 0% { 918 | opacity: 0; 919 | } 920 | 100% { 921 | opacity: 1; 922 | } 923 | } 924 | 925 | @-o-keyframes fadeIn { 926 | 0% { 927 | opacity: 0; 928 | } 929 | 100% { 930 | opacity: 1; 931 | } 932 | } 933 | 934 | @-ms-keyframes fadeIn { 935 | 0% { 936 | opacity: 0; 937 | } 938 | 100% { 939 | opacity: 1; 940 | } 941 | } 942 | 943 | .fade-out { 944 | animation: fadeOut ease 1.5s; 945 | -webkit-animation: fadeOut ease 1.5s; 946 | -moz-animation: fadeOut ease 1.5s; 947 | -o-animation: fadeOut ease 1.5s; 948 | -ms-animation: fadeOut ease 1.5s; 949 | } 950 | 951 | @keyframes fadeOut { 952 | 0% { 953 | opacity: 1; 954 | } 955 | 100% { 956 | opacity: 0; 957 | } 958 | } 959 | 960 | @-moz-keyframes fadeOut { 961 | 0% { 962 | opacity: 1; 963 | } 964 | 100% { 965 | opacity: 0; 966 | } 967 | } 968 | 969 | @-webkit-keyframes fadeOut { 970 | 0% { 971 | opacity: 1; 972 | } 973 | 100% { 974 | opacity: 0; 975 | } 976 | } 977 | 978 | @-o-keyframes fadeOut { 979 | 0% { 980 | opacity: 1; 981 | } 982 | 100% { 983 | opacity: 0; 984 | } 985 | } 986 | 987 | @-ms-keyframes fadeOut { 988 | 0% { 989 | opacity: 1; 990 | } 991 | 100% { 992 | opacity: 0; 993 | } 994 | } 995 | @-webkit-keyframes fadeDown { 996 | 0% { 997 | opacity: 0; 998 | -webkit-transform: translateY(-15px); 999 | } 1000 | 100% { 1001 | opacity: 0.8; 1002 | -webkit-transform: translateY(0); 1003 | } 1004 | } 1005 | 1006 | @keyframes fadeDown { 1007 | 0% { 1008 | opacity: 0; 1009 | transform: translateY(-15px); 1010 | } 1011 | 100% { 1012 | opacity: 0.8; 1013 | transform: translateY(0); 1014 | } 1015 | } 1016 | 1017 | .hamburger { 1018 | position: absolute; 1019 | top: 0; 1020 | left: 10px; 1021 | padding: 15px 15px; 1022 | display: inline-block; 1023 | cursor: pointer; 1024 | transition-property: opacity, filter; 1025 | transition-duration: 0.15s; 1026 | transition-timing-function: linear; 1027 | font: inherit; 1028 | color: inherit; 1029 | text-transform: none; 1030 | background-color: transparent; 1031 | border: 0; 1032 | margin: 0; 1033 | overflow: visible; } 1034 | .hamburger:hover { 1035 | opacity: 0.7; } 1036 | .hamburger.is-active:hover { 1037 | opacity: 0.7; } 1038 | .hamburger.is-active .hamburger-inner, 1039 | .hamburger.is-active .hamburger-inner::before, 1040 | .hamburger.is-active .hamburger-inner::after { 1041 | background-color: #000; } 1042 | 1043 | .hamburger-box { 1044 | width: 40px; 1045 | height: 24px; 1046 | display: inline-block; 1047 | position: relative; } 1048 | 1049 | .hamburger-inner { 1050 | display: block; 1051 | top: 50%; 1052 | margin-top: -2px; } 1053 | .hamburger-inner, .hamburger-inner::before, .hamburger-inner::after { 1054 | width: 40px; 1055 | height: 4px; 1056 | background-color: #000; 1057 | border-radius: 4px; 1058 | position: absolute; 1059 | transition-property: transform; 1060 | transition-duration: 0.15s; 1061 | transition-timing-function: ease; } 1062 | .hamburger-inner::before, .hamburger-inner::after { 1063 | content: ""; 1064 | display: block; } 1065 | .hamburger-inner::before { 1066 | top: -10px; } 1067 | .hamburger-inner::after { 1068 | bottom: -10px; } 1069 | 1070 | .hamburger--spin .hamburger-inner { 1071 | transition-duration: 0.22s; 1072 | transition-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); } 1073 | .hamburger--spin .hamburger-inner::before { 1074 | transition: top 0.1s 0.25s ease-in, opacity 0.1s ease-in; } 1075 | .hamburger--spin .hamburger-inner::after { 1076 | transition: bottom 0.1s 0.25s ease-in, transform 0.22s cubic-bezier(0.55, 0.055, 0.675, 0.19); } 1077 | 1078 | .hamburger--spin.is-active .hamburger-inner { 1079 | transform: rotate(225deg); 1080 | transition-delay: 0.12s; 1081 | transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); } 1082 | .hamburger--spin.is-active .hamburger-inner::before { 1083 | top: 0; 1084 | opacity: 0; 1085 | transition: top 0.1s ease-out, opacity 0.1s 0.12s ease-out; } 1086 | .hamburger--spin.is-active .hamburger-inner::after { 1087 | bottom: 0; 1088 | transform: rotate(-90deg); 1089 | transition: bottom 0.1s ease-out, transform 0.22s 0.12s cubic-bezier(0.215, 0.61, 0.355, 1); } 1090 | -------------------------------------------------------------------------------- /public/sounds/beep1.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opencodeiiita/Geek-Chat/32861b069aaf927a65f7ea1fdae56e76d04beb93/public/sounds/beep1.mp3 -------------------------------------------------------------------------------- /public/sounds/beep2.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opencodeiiita/Geek-Chat/32861b069aaf927a65f7ea1fdae56e76d04beb93/public/sounds/beep2.wav -------------------------------------------------------------------------------- /public/sounds/beep3.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opencodeiiita/Geek-Chat/32861b069aaf927a65f7ea1fdae56e76d04beb93/public/sounds/beep3.mp3 -------------------------------------------------------------------------------- /public/uploads/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opencodeiiita/Geek-Chat/32861b069aaf927a65f7ea1fdae56e76d04beb93/public/uploads/.gitkeep -------------------------------------------------------------------------------- /routes/image.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const router = express.Router(); 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | 6 | const avatarsDirectory = path.join(__dirname, '../public/avtars'), 7 | uploadsDirectory = path.join(__dirname, '../public/uploads'); 8 | 9 | function getAvatarIndex() { 10 | return fs.readdirSync(avatarsDirectory).length + 1; 11 | } 12 | 13 | router.post('/image', (req, res) => { 14 | if (!req.files || !req.files.image) return res.json({ success: false }); 15 | 16 | const image = req.files.image; 17 | const pieces = image.name.split('.'); 18 | const filename = `${new Date().getTime()}.${pieces[pieces.length - 1]}`; 19 | 20 | image.mv(path.join(uploadsDirectory, filename)); 21 | return res.json({ success: true, link: `/uploads/${filename}` }); 22 | }); 23 | 24 | router.post('/newAvatar', (req, res) => { 25 | if (!req.files || !req.files.avatar) return res.status(400).send(null); 26 | 27 | const avatar = req.files.avatar; 28 | const pieces = avatar.name.split('.'); 29 | const filename = `${getAvatarIndex()}.${pieces[pieces.length - 1]}`; 30 | 31 | avatar.mv(path.join(avatarsDirectory, filename)); 32 | res.send(filename); 33 | }); 34 | 35 | module.exports = router; 36 | -------------------------------------------------------------------------------- /routes/index.js: -------------------------------------------------------------------------------- 1 | const express= require("express") 2 | const router = express.Router(); 3 | const path = require("path"); 4 | const { stringify } = require('querystring'); 5 | const fetch = require('node-fetch'); 6 | const { usersArr, currentUserData } = require("../utils/users"); 7 | 8 | router.get("/", (req, res) => { 9 | // rendering main.html 10 | res.sendFile(path.join(__dirname , "../views/index.html")); 11 | }); 12 | 13 | router.post("/", async(req, res) => { 14 | // RECAPTCHA SERVER SIDE 15 | if (!req.body['g-recaptcha-response']) 16 | return res.sendFile(path.join(__dirname , "../views/index.html")); 17 | // Secret key 18 | const secretKey = '6Lc20uYcAAAAANeXi5yv3q_YTMsN3J8NTHUcpmD5'; 19 | // Verify URL 20 | const query = stringify({ 21 | secret: secretKey, 22 | response: req.body['g-recaptcha-response'], 23 | remoteip: req.connection.remoteAddress 24 | }); 25 | const verifyURL = `https://google.com/recaptcha/api/siteverify?${query}`; 26 | // Make a request to verifyURL 27 | const body = await fetch(verifyURL).then(resp => resp.json()); 28 | // If not successful 29 | if (body.success !== undefined && !body.success) return res.sendFile(path.join(__dirname, " ../views/index.html")); 30 | // If successful 31 | // return res.json({ success: true, msg: 'Captcha passed' }); 32 | 33 | currentUserData.usrnm = req.body.usrnm; 34 | currentUserData.room = req.body.newroom; 35 | currentUserData.profilePhoto = req.body.imageUrl; 36 | 37 | let { usrnm, room, profilePhoto } = currentUserData; 38 | 39 | if ( 40 | usersArr.find((user) => { 41 | if (currentUserData.usrnm === user.name && currentUserData.room === user.room) return true; 42 | }) 43 | ) { 44 | console.log('here'); 45 | return res.sendFile(path.join(__dirname , "../views/index.html")); 46 | } 47 | if (/\s/g.test(usrnm)) { 48 | return res.sendFile(path.join(__dirname , "../views/index.html")); 49 | } 50 | res.sendFile(path.join(__dirname , "../views/main.html")); 51 | }); 52 | 53 | module.exports=router; -------------------------------------------------------------------------------- /utils/message.js: -------------------------------------------------------------------------------- 1 | const encode = require("html-entities")["encode"]; 2 | const marked = require("marked"); 3 | 4 | const Filter = require("bad-words"); 5 | 6 | const filter = new Filter({ placeHolder: "x" }); 7 | const profanityWarning = marked("**Message blocked**: profanity not allowed!"); 8 | 9 | function sanitizeAndRenderMessage(message, encodeMessage) { 10 | if (encodeMessage) 11 | message = encode(message); 12 | 13 | const fiterMessage = filter.clean(message); 14 | const markdown = marked(fiterMessage); 15 | const reducedString = markdown.replace( /(<([^>]+)>)/ig, ""); 16 | 17 | if (filter.isProfane(reducedString)) 18 | return profanityWarning; 19 | 20 | return markdown; 21 | } 22 | 23 | module.exports = { 24 | sanitizeAndRenderMessage 25 | }; 26 | -------------------------------------------------------------------------------- /utils/roomMembersCount.js: -------------------------------------------------------------------------------- 1 | const roomMembersCount = { 2 | "Web Development": 0, 3 | "App Development": 0, 4 | "FOSS": 0, 5 | "Design": 0, 6 | "Competitive Programming": 0, 7 | "Cyber Security": 0, 8 | "Artificial Intelligence": 0 9 | }; 10 | 11 | 12 | module.exports = { roomMembersCount }; 13 | -------------------------------------------------------------------------------- /utils/users.js: -------------------------------------------------------------------------------- 1 | const { CreateUser } = require('../models/user'); 2 | 3 | var usersArr = []; 4 | var currentUserData = { usrnm: null, room: null, profilePhoto: null }; 5 | 6 | function newUser(name, room, session_id, profilePhoto) { 7 | return usersArr.push(CreateUser(name, room, session_id, profilePhoto)); 8 | } 9 | 10 | module.exports = { usersArr, newUser, currentUserData }; 11 | -------------------------------------------------------------------------------- /views/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 13 | 18 | 19 | 20 | 25 | 26 | 27 | 28 | 29 | GeekChat 30 | 31 | 32 | 33 |
34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 |
58 | 59 |
60 |

GeekChat!

61 | 62 |
63 | 133 |
134 |
135 |
136 | Avatars 143 |
144 |
145 | 151 |
152 | 153 |
154 | 162 |
163 |
164 |
165 | 166 |
167 | 189 |
190 |
191 |

OR

192 |
193 | 194 |
195 | 205 |
206 |
207 |
208 |
209 | 210 |
211 |
212 |
213 |
214 | 215 | 216 | 217 | 218 | 219 | 220 | 240 | 241 | -------------------------------------------------------------------------------- /views/main.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 16 | 17 | 22 | 23 | 27 | 28 | 29 | 30 | GeekChat! 31 | 32 | 33 | 34 |
35 |
36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 |
60 | 61 | 66 |
67 |
68 |
69 |

GeekChat

70 | 71 | 76 | 77 |
78 |
79 |
80 |

Room Name:

81 |
82 |

83 |
84 |

Users

85 |
    86 |
    87 |
    88 |
    89 |
    90 |
    91 | 101 | 112 | 113 | 122 |
    123 |
    124 |
    125 | 126 | 127 | 128 | 129 | 130 | 139 | 140 | 141 | 142 | 143 | 144 | --------------------------------------------------------------------------------