├── .github └── ISSUE_TEMPLATE │ ├── BUG_REPORT.md │ └── FEATURE_REQUEST.md ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── backend └── server │ ├── Procfile │ ├── Utils │ └── Rooms.js │ ├── index.js │ ├── package-lock.json │ ├── package.json │ └── rateLimiter.js └── frontend └── syntaxmeets ├── README.md ├── debug.log ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── manifest.json └── siteimages │ └── smlogo.png └── src ├── App.css ├── App.js ├── App.test.js ├── assets └── css │ └── sx-chat.css ├── components ├── About │ ├── About.js │ ├── About.module.css │ └── Card │ │ └── MediaCard.js ├── Footer │ ├── Footer.js │ ├── Footer.module.css │ └── navlogo.png ├── Home │ ├── Home.js │ └── Home.module.css ├── Loader │ ├── Loader.js │ └── Loader.module.css ├── Mobile │ ├── Mobile.js │ └── Mobile.module.css ├── Navbar │ └── Navbar.js ├── SnackBar │ └── Snackbar.js ├── SyntaxChat │ ├── ChatMessage.js │ ├── ParticipantsList.js │ └── SyntaxChat.js ├── SyntaxEditor │ ├── CodeInput.js │ ├── CodeOutput.js │ ├── LanguageData.js │ ├── SyntaxEditor.js │ └── SyntaxEditor.module.css ├── SyntaxPad │ ├── SyntaxPad.js │ └── SyntaxPad.module.css └── SyntaxRoom │ └── SyntaxRoom.js ├── images ├── SyntaxMeets.png ├── akash.jpg ├── dhruv.jpg ├── home_svg.svg ├── homepage.svg ├── mobilehome_svg.svg ├── navlogo.png ├── nishant.jpg └── ogimage.png ├── index.css ├── index.js ├── serviceWorker.js ├── services ├── axios.js └── socket.js ├── setupTests.js ├── store ├── actions │ ├── chatActions.js │ ├── editorActions.js │ ├── roomActions.js │ └── uiActions.js ├── reducers │ ├── chatReducer.js │ ├── editorReducer.js │ ├── roomReducer.js │ └── uiReducer.js ├── store.js └── types.js └── util └── util.js /.github/ISSUE_TEMPLATE/BUG_REPORT.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 🐛 3 | about: Something isn't working as expected? Here is the right place to report. For questions, please use "Discussions". 4 | title: '' 5 | labels: 'type:bug' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | Describe the issue that you're seeing clearly. 12 | 13 | **To Reproduce** 14 | Clear steps describing how to reproduce the issue. 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected result** 21 | What should happen? 22 | 23 | **Actual result** 24 | What happened. 25 | 26 | **Screenshots** 27 | If applicable, add screenshots to help explain your problem. 28 | 29 | **Are you willing to contribute to this issue?** [Yes/No] 30 | 31 | **Desktop (please complete the following information):** 32 | - OS: [e.g. iOS] 33 | - Browser [e.g. chrome, safari] 34 | - Version [e.g. 22] 35 | 36 | **Smartphone (please complete the following information):** 37 | - Device: [e.g. iPhone6] 38 | - OS: [e.g. iOS8.1] 39 | - Browser [e.g. stock browser, safari] 40 | - Version [e.g. 22] 41 | 42 | **Additional context** 43 | Add any other context about the problem here. 44 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 🌟 3 | about: Suggest new ideas for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Motivation** 17 | Please outline the motivation for the proposal. 18 | 19 | **Are you willing to contribute to this issue?** [Yes/No] 20 | 21 | **Describe alternatives you've considered** 22 | A clear and concise description of any alternative solutions or features you've considered. 23 | 24 | **Additional context** 25 | Add any other context or screenshots about the feature request here. 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | .vscode 3 | **/.env 4 | .DS_Store -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at kotharidhruv25@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to SyntaxMeets 2 | 3 | :dizzy::tada: _First of all, thanks for taking the time to contribute!_ :tada::dizzy: 4 | 5 | --- 6 | 7 | ## Contributing Guidelines 📌📌 8 | - Issues will be assigned on a **first come, first serve basis**. You just have to comment on the issue, asking to be assigned, and we'll assign it to you if it's not assigned to anyone else. 9 | 10 | - If you find any **bug** or **feature**, create an issue accordingly, and please wait for it to be **assigned to you**. 11 | - It would be even better if you can visually show a **prototype/animation/design** for any UI improvements you are going to do so that the probability of major changes after writing the code decreases. 12 | - Before creating a Pull Request, please make sure you have **pull the code from origin**, so that **merge conflicts can be prevented.** 13 | - Please remove any **`console logs or errors`** if present in the code(for the sake of debugging), before creating a Pull Request. 14 | - Please write some **comments in the modified section of the code**, so that others can understand you logic and code more easily. 15 | - **Deadlines**: When the issue has been assigned to the user, he/she have to complete the issue within the following assigned time as per the issue level. 16 | - Level 0: 1 Day 17 | - Level 1: 3 Days 18 | - Level 2: 1 Week 19 | - Level 3: 15 Days 20 | --- 21 | 22 | ### ⁉️ Did you find a bug/Issue or an new Feature?, Feel free to submit issues and enhancement requests. 23 | 24 | In general, we follow the "fork-and-pull" Git workflow. 25 | 26 | --- 27 | 28 | ### ✅Following are the steps for Creating Issue - 29 | 30 | ### For creating new issue, select any one of the ISSUE TEMPLATE and describe along with its fields given (\_rather than opening a blank issue*) : 31 | 32 | - Bug Report 🐛 33 | - Feature Request 🌟 34 | 35 | [![issue.png](https://i.postimg.cc/zD6PfwxQ/issue.png)](https://postimg.cc/dDR9HCPR) 36 | 37 | --- 38 | 39 | ### ✅Following are the steps/rules for Contributing in the project - 40 | 41 | 1. _**Fork**_ the repository on GitHub. 42 | 43 | 2. _**Clone**_ the project (forked copy of the project) to your machine : 44 | 45 | `git clone url-of-forked-project` 46 | 47 | 3. _**Navigate**_ to the directory of project : 48 | 49 | `cd SyntaxMeets` 50 | 51 | 4. Set the _**upstream**_ with original repository : 52 | 53 | `git remote add upstream https://github.com/kothariji/SyntaxMeets.git` 54 | 55 | 5. In order to check associated URLs with the project : 56 | 57 | `git remote -v` 58 | 59 | 6. Before commiting any changes, always _**Pull**_ the upstream so that the recents changes in the original repo gets merged (gets updated with recent changes) 60 | 61 | `git pull upstream master` 62 | 63 | 7. Create a _**New**_ Branch (where you need to push the changes done) and switch to the respective branch : 64 | 65 | `git branch your_branch_name` 66 | 67 | `git checkout -b your_branch_name` 68 | 69 | > NOTE : After cloning into project, in order to run it on the local machine, create .env: file as instructed below : 70 | > create a new .env file in the frontend/syntaxmeets folder and add the following variables - 71 | > REACT_APP_SYNTAXMEETS_BACKEND_API = 'http://localhost:4000/' 72 | > REACT_APP_ONLINE_JUDGE_API = YOUR API KEY 73 | 74 | 8. _**Add**_ and _**Commit**_ your changes with relevant message : 75 | 76 | `git add file_name` 77 | 78 | - While commiting, ensure that you follow the below guidelines : 79 | 80 | - Structure of commit message : 81 | `git commit -m "{{ commit heading }}" -m "{{ commit desc }}"` 82 | 83 | - Commit Headings 84 | 85 | - ⚒️[Fix]: used when we do a css-fix, ui-improvement 86 | - 📚[Docs]: used when modifying/updating docs 87 | - ♻️[Refactor]: used when refacting variables, functions, classes 88 | - 🐛 [Bug]: used when commits related to bug fixed/resolve in process 89 | - 🚀[Feat]: used when adding a new feature in application 90 | - 🔏 [API]: when working with API's 91 | 92 | - When commits are in parts Eg: 93 | - 🌘 [1/3] Auth: setup firebase configs 94 | - 🌗 [2/3] Auth: setup auth routes 95 | - 🌕 [3/3] Auth: finishing with auth functionalities 96 | - Commit Body 97 | - In this we can add a brief description of the modification we did in the code. So overall it would look like - 98 | `git commit -m "🔐[API]: Handle CORS policy" -m "Desc: added Cors libraries to access backend from the react, added whitelist 🔥"` 99 | 100 | 9. _**Push**_ the changes to the same working branch (do not push on _origin_ _master_!) 101 | 102 | `git push -u origin your_branch_name` 103 | 104 | 10. To create a _**Pull request**_ ,click on compare and pull requests. 105 | 106 | 11. Add appropriate _**Title**_ and _**Description**_ explaining the work you have done. 107 | 108 | 12. And Done!! 🥳 Wait till your _**PR**_ gets merged after successful submission! 😄 109 | 110 | --- 111 | 112 | ## Happy Coding 🎯! Keep Contributing ! 🚀 113 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Dhruv Kothari 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # SyntaxMeets 4 | ![SM](https://i.imgur.com/O11CKeq.gif) 5 | 6 | 7 | ### Syntaxmeets. Create rooms 🏠 Call your friends 👬🏼 Sip Chai, ☕ Chat, Create, and Code👨‍💻. A coding platform to code simultaneously 🚀 with your friends and design your algorithms on SyntaxPad. And you know what's awesome? It's Free.💫✨ 8 | 9 | ![Vercel](https://therealsujitk-vercel-badge.vercel.app/?app=syntaxmeets) 10 | [ 11 | ![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/kothariji/SyntaxMeets/issues) [![Open Source? Yes!](https://badgen.net/badge/Open%20Source%20%3F/Yes%21/blue?icon=github)](https://github.com/kothariji/SyntaxMeets) 12 | 13 | [![forthebadge](https://forthebadge.com/images/badges/made-with-javascript.svg)](https://github.com/kothariji/SyntaxMeets) [![forthebadge](https://forthebadge.com/images/badges/built-with-love.svg)](https://github.com/kothariji/SyntaxMeets) 14 | 15 | 16 | --- 17 | # Website - [SyntaxMeets](https://syntaxmeets.vercel.app/) 18 | 19 | # Features 20 | - [Create a Room](#Create-a-room-) 21 | - [Join a Room](#Create-a-room-) 22 | - [Syntax Room](#Join-a-room-) 23 | - [SyntaxEditor](#Code-&-Compile-) 24 | - [SyntaxPad](#Syntaxpad-) 25 | - [SyntaxChat](#Code-&-Compile-) 26 | 27 | 28 | ### :rocket:Glimpse :dizzy::dizzy:

![createaroom](https://snipboard.io/eAUFOi.jpg) 29 | 30 | 31 | 32 | 33 | 34 | ### Creators :zap::dizzy: 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 |

Dhruv Kothari
🧠👨‍💻🚀❤️

Akash Salvi
🧘🔭👨‍🎓👽

Nishant Handge
💻📱👨‍💻💥
43 | 44 | ### How to Run Locally 45 | Run the below command to clone the repo locally 46 | ``` 47 | git clone https://github.com/kothariji/SyntaxMeets.git 48 | ``` 49 | 50 | ### To run the server, go to the server folder in the backend directory and Run 51 | ``` 52 | npm install 53 | npm start 54 | ``` 55 | #### this will run your express server in localhost:4000 56 | 57 | --- 58 | ### To run the application, go to the SyntaxMeets folder in the frontend directory 59 | 60 | create a new .env file and add following variables 61 | ``` 62 | REACT_APP_SYNTAXMEETS_BACKEND_API = 'http://localhost:4000/' 63 | REACT_APP_ONLINE_JUDGE_API = YOUR API KEY 64 | ``` 65 | 66 | and Run the application using below commands 67 | ``` 68 | npm install //install all react dependencies 69 | npm start //runserver 70 | ``` 71 | #### this will run your application in localhost:3000 72 | 73 | ### Thank you for Contributing :pray: :dizzy: 74 | ![4](https://contributors-img.web.app/image?repo=kothariji/syntaxmeets) 75 | 76 | 77 | ### If you liked our work do give us a star :star::star::star: It Encourages us to do more :wink: :dizzy: 78 | -------------------------------------------------------------------------------- /backend/server/Procfile: -------------------------------------------------------------------------------- 1 | web: node index.js 2 | -------------------------------------------------------------------------------- /backend/server/Utils/Rooms.js: -------------------------------------------------------------------------------- 1 | // Rooms object will store all clients in each room 2 | // in the form of key-value pairs 3 | // example of rooms object : 4 | // this.rooms = { 5 | // id1: { 6 | // {clientid1: name1}, 7 | // {clientid2: name2}, 8 | // }, 9 | // id2: { 10 | // {clientid3: name3}, 11 | // {clientid4: name4} 12 | // }, 13 | // id3: { 14 | // {clientid5: name5}, 15 | // {clientid6: name6}, 16 | // {clientid7: name7}, 17 | // }, 18 | // }; 19 | 20 | class Rooms { 21 | constructor() { 22 | this.rooms = {}; 23 | } 24 | addUserToRoom(id, name, room) { 25 | if (!this.rooms[room]) { 26 | //Create a new room object 27 | this.rooms[room] = {}; 28 | } 29 | //Add user in the room in the form of key-value pair 30 | this.rooms[room][id] = name; 31 | } 32 | getAllUsers(room) { 33 | //return all users in the room using the roomId 34 | 35 | return this.rooms[room]; 36 | } 37 | deleteUser(room, id) { 38 | //Delete user from the room 39 | if (this.rooms[room]) { 40 | // from the current room delete key-value of client 41 | 42 | delete this.rooms[room][id]; 43 | return id; 44 | } 45 | } 46 | getUser(room) { 47 | let user = {}; 48 | //the room dosen't exist 49 | if (!this.rooms[room]) return user; 50 | const keyArray = Object.keys(this.rooms[room]); 51 | //room exists but no user 52 | if (!keyArray.length) return user; 53 | //send old user 54 | //{id:name} 55 | return { [keyArray[0]]: this.rooms[room][keyArray[0]] }; 56 | } 57 | } 58 | module.exports = Rooms; 59 | -------------------------------------------------------------------------------- /backend/server/index.js: -------------------------------------------------------------------------------- 1 | const app = require("express")(); 2 | const server = require("http").createServer(app); 3 | const cors = require("cors"); 4 | const Rooms = require("./Utils/Rooms"); 5 | const io = require("socket.io")(server, { origins: "*:*" }); 6 | const helmet = require('helmet'); 7 | const rateLimiter = require('./rateLimiter'); 8 | // instantiate a new rooms object to store all clients in the room 9 | const rooms = new Rooms(); 10 | 11 | // to store all the rooms created 12 | const roomsCreated = []; 13 | 14 | app.use(helmet()); 15 | 16 | // io.origins(["http://localhost:3000"]); 17 | app.use(cors()); 18 | 19 | io.on("connection" , (socket) => { 20 | let roomId = 0; 21 | let userName = ""; 22 | let userId = 1; 23 | 24 | //joining in a room 25 | socket.on("joinroom", function ({ name, room }) { 26 | if (!name) return; 27 | 28 | roomId = room; 29 | userName = name; 30 | userId = socket.id; 31 | // pushes new rooms created in the roomsCreated list 32 | if(!roomsCreated.includes(room)) 33 | roomsCreated.push(room); 34 | 35 | socket.join(room); 36 | const oldUser = rooms.getUser(roomId); 37 | rooms.addUserToRoom(socket.id, name, room); 38 | const users = rooms.getAllUsers(room); 39 | // send all the users to only the new User who joined and id of the current user 40 | socket.emit("addusers", { id: socket.id, users }); 41 | // inform everyone (excluding the new User) , that a user has been added 42 | // also send the id of an already existing user to the client so only that one 43 | // will emit the code to update for the new user 44 | // console.log(users); 45 | socket.broadcast 46 | .to(roomId) 47 | .emit("userjoined", { newUser: { [socket.id]: name }, oldUser }); 48 | }); 49 | 50 | socket.on("message", (message) => { 51 | // Send the code in text editor to all the sockets 52 | socket.to(roomId).emit("message", message); 53 | }); 54 | 55 | socket.on("typing", (user) => { 56 | // console.log(user); 57 | // inform everyone (excluding the typing user himself) who is typing 58 | socket.broadcast.to(roomId).emit("typing", user); 59 | }); 60 | socket.on("chatmessage", (data) => { 61 | // send the chat message to all the users 62 | socket.to(roomId).emit("chatmessage", data); 63 | }); 64 | 65 | socket.on("disconnect", function () { 66 | if (!userName) return; 67 | 68 | const returnId = rooms.deleteUser(roomId, socket.id); 69 | 70 | if (returnId) 71 | socket.broadcast 72 | .to(roomId) 73 | .emit("userleft", { id: returnId, name: userName }); 74 | }); 75 | }); 76 | 77 | app.get("/",rateLimiter , (req, res) => { 78 | res.send({ response: "Server is up and Running." }).status(200); 79 | }); 80 | // api call to get rooms created 81 | // this api keeps track of active rooms 82 | app.get('/rooms', function(req, res) { 83 | console.log('got the response'); 84 | res.json(roomsCreated); 85 | }); 86 | 87 | server.listen(process.env.PORT || 4000, function () { 88 | console.log("server is working"); 89 | }); 90 | 91 | -------------------------------------------------------------------------------- /backend/server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "backend for syntaxmeets", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "nodemon index.js" 9 | }, 10 | "author": "kothariji, akash, nishant", 11 | "license": "ISC", 12 | "dependencies": { 13 | "cors": "^2.8.5", 14 | "express": "^4.17.1", 15 | "express-rate-limit": "^5.2.6", 16 | "helmet": "^4.4.1", 17 | "nodemon": "^2.0.6", 18 | "socket.io": "^2.4.1" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /backend/server/rateLimiter.js: -------------------------------------------------------------------------------- 1 | const rateLimit = require('express-rate-limit'); 2 | 3 | const limit = rateLimit({ 4 | windowMs: 1 * 60 * 1000, 5 | max: 100, 6 | message: 7 | "Too many requests created from this IP, please try again after an hour" 8 | }); 9 | 10 | module.exports = limit; -------------------------------------------------------------------------------- /frontend/syntaxmeets/README.md: -------------------------------------------------------------------------------- 1 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 2 | 3 | ## Available Scripts 4 | 5 | In the project directory, you can run: 6 | 7 | ### `npm start` 8 | 9 | Runs the app in the development mode.
10 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 11 | 12 | The page will reload if you make edits.
13 | You will also see any lint errors in the console. 14 | 15 | ### `npm test` 16 | 17 | Launches the test runner in the interactive watch mode.
18 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 19 | 20 | ### `npm run build` 21 | 22 | Builds the app for production to the `build` folder.
23 | It correctly bundles React in production mode and optimizes the build for the best performance. 24 | 25 | The build is minified and the filenames include the hashes.
26 | Your app is ready to be deployed! 27 | 28 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 29 | 30 | ### `npm run eject` 31 | 32 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 33 | 34 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 35 | 36 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 37 | 38 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 39 | 40 | ## Learn More 41 | 42 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 43 | 44 | To learn React, check out the [React documentation](https://reactjs.org/). 45 | 46 | ### Code Splitting 47 | 48 | This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting 49 | 50 | ### Analyzing the Bundle Size 51 | 52 | This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size 53 | 54 | ### Making a Progressive Web App 55 | 56 | This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app 57 | 58 | ### Advanced Configuration 59 | 60 | This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration 61 | 62 | ### Deployment 63 | 64 | This section has moved here: https://facebook.github.io/create-react-app/docs/deployment 65 | 66 | ### `npm run build` fails to minify 67 | 68 | This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify 69 | -------------------------------------------------------------------------------- /frontend/syntaxmeets/debug.log: -------------------------------------------------------------------------------- 1 | [1125/194413.094:ERROR:directory_reader_win.cc(43)] FindFirstFile: The system cannot find the path specified. (0x3) 2 | -------------------------------------------------------------------------------- /frontend/syntaxmeets/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "syntaxmeets", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@material-ui/core": "^4.11.0", 7 | "@material-ui/icons": "^4.9.1", 8 | "@material-ui/lab": "^4.0.0-alpha.56", 9 | "@testing-library/jest-dom": "^4.2.4", 10 | "@testing-library/react": "^9.5.0", 11 | "@testing-library/user-event": "^7.2.1", 12 | "ace-builds": "^1.4.12", 13 | "axios": "^0.21.1", 14 | "bootstrap": "^4.5.3", 15 | "copy-to-clipboard": "^3.3.1", 16 | "emoji-mart": "^3.0.1", 17 | "@emotion/react": "^11.7.0", 18 | "fontsource-poppins": "^3.0.9", 19 | "react": "^16.13.1", 20 | "react-ace": "^9.1.4", 21 | "react-awesome-loaders": "^0.1.37", 22 | "react-bootstrap": "^1.4.0", 23 | "react-bootstrap-sweetalert": "^5.2.0", 24 | "react-canvas-draw": "^1.1.1", 25 | "react-color": "^2.19.3", 26 | "react-dom": "^16.13.1", 27 | "react-draggable": "^4.4.3", 28 | "react-loader-spinner": "^3.1.14", 29 | "react-redux": "^7.2.1", 30 | "react-resize-detector": "^5.2.0", 31 | "react-responsive": "^8.1.1", 32 | "react-router-dom": "^5.2.0", 33 | "react-scripts": "^3.4.4", 34 | "react-skylight": "^0.5.1", 35 | "react-modal": "^3.13.1", 36 | "react-spinners": "^0.11.0", 37 | "reactstrap": "^8.9.0", 38 | "redux": "^4.0.5", 39 | "redux-thunk": "^2.3.0", 40 | "responsive-sketchpad": "^1.2.0", 41 | "save": "^2.4.0", 42 | "socket.io-client": "^2.3.1" 43 | }, 44 | "scripts": { 45 | "start": "react-scripts start", 46 | "build": "react-scripts build", 47 | "test": "react-scripts test", 48 | "eject": "react-scripts eject" 49 | }, 50 | "eslintConfig": { 51 | "extends": "react-app" 52 | }, 53 | "browserslist": { 54 | "production": [ 55 | ">0.2%", 56 | "not dead", 57 | "not op_mini all" 58 | ], 59 | "development": [ 60 | "last 1 chrome version", 61 | "last 1 firefox version", 62 | "last 1 safari version" 63 | ] 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /frontend/syntaxmeets/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kothariji/SyntaxMeets/74f10e80ae3e42908f9943e27ca06e99a0d53530/frontend/syntaxmeets/public/favicon.ico -------------------------------------------------------------------------------- /frontend/syntaxmeets/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 10 | 11 | 12 | 13 | 15 | 16 | 17 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 31 | 35 | 36 | 45 | SyntaxMeets 46 | 47 | 48 | 49 |
50 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /frontend/syntaxmeets/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "SyntaxMeets", 3 | "name": "SyntaxMeets", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "theme_color": "#000000", 7 | "background_color": "#ffffff" 8 | } -------------------------------------------------------------------------------- /frontend/syntaxmeets/public/siteimages/smlogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kothariji/SyntaxMeets/74f10e80ae3e42908f9943e27ca06e99a0d53530/frontend/syntaxmeets/public/siteimages/smlogo.png -------------------------------------------------------------------------------- /frontend/syntaxmeets/src/App.css: -------------------------------------------------------------------------------- 1 | @import "assets/css/sx-chat.css"; 2 | 3 | .ace_print-margin { 4 | display: none; 5 | } 6 | 7 | /* scroll--bar styles */ 8 | ::-webkit-scrollbar { 9 | width: 15px; 10 | height: 4px; 11 | } 12 | 13 | ::-webkit-scrollbar-track { 14 | background: #000a29; 15 | border-left: 5px solid #000a29; 16 | border-right: 5px solid #000a29; 17 | } 18 | 19 | ::-webkit-scrollbar-thumb { 20 | background-image: linear-gradient(#ffd500, #000a29); 21 | border-left: 5px solid #000a29; 22 | border-right: 5px solid #000a29; 23 | border-radius: 30px; 24 | } 25 | -------------------------------------------------------------------------------- /frontend/syntaxmeets/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Suspense } from "react"; 2 | import { BrowserRouter as Router, Switch, Route } from "react-router-dom"; 3 | import "fontsource-poppins"; 4 | import "./App.css"; 5 | import Loader from "./components/Loader/Loader"; 6 | import store from "./store/store"; 7 | import { Provider } from "react-redux"; 8 | import SnackBar from "./components/SnackBar/Snackbar.js" 9 | const Home = React.lazy(() => import("./components/Home/Home")); 10 | const SyntaxRoom = React.lazy(() => 11 | import("./components/SyntaxRoom/SyntaxRoom") 12 | ); 13 | 14 | const App = () => { 15 | return ( 16 | 17 | 18 | }> 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | ); 28 | }; 29 | 30 | export default App; 31 | -------------------------------------------------------------------------------- /frontend/syntaxmeets/src/App.test.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kothariji/SyntaxMeets/74f10e80ae3e42908f9943e27ca06e99a0d53530/frontend/syntaxmeets/src/App.test.js -------------------------------------------------------------------------------- /frontend/syntaxmeets/src/assets/css/sx-chat.css: -------------------------------------------------------------------------------- 1 | .sx-chat-message{ 2 | color: #fff; 3 | overflow-wrap: break-word; 4 | word-wrap: break-word; 5 | hyphens: auto; 6 | } -------------------------------------------------------------------------------- /frontend/syntaxmeets/src/components/About/About.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import MediaCard from "./Card/MediaCard"; 3 | import { Typography } from "@material-ui/core"; 4 | import ArrowBackIcon from '@material-ui/icons/ArrowBack'; 5 | import localclasses from "./About.module.css"; 6 | import DhruvIMG from "../../images/dhruv.jpg"; 7 | import AkashIMG from "../../images/akash.jpg"; 8 | import NishantIMG from "../../images/nishant.jpg"; 9 | 10 | const About = ({handleClose}) => { 11 | return ( 12 |
16 |
17 | 28 | 38 |  SyntaxMeets Creators 39 | 40 |
41 |
42 | 52 | 62 | 72 |
73 |
74 | ); 75 | }; 76 | export default About; 77 | -------------------------------------------------------------------------------- /frontend/syntaxmeets/src/components/About/About.module.css: -------------------------------------------------------------------------------- 1 | .aboutus { 2 | background: "#000a29"; 3 | width: 100%; 4 | } 5 | 6 | .cards { 7 | margin-left: auto; 8 | margin-right: auto; 9 | background-color: "#000a29"; 10 | width: 90%; 11 | padding-top: 5%; 12 | padding-bottom: 5%; 13 | display: flex; 14 | flex-direction: row; 15 | justify-content: space-around; 16 | align-items: inherit; 17 | } 18 | -------------------------------------------------------------------------------- /frontend/syntaxmeets/src/components/About/Card/MediaCard.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { makeStyles } from '@material-ui/core/styles'; 3 | import Card from '@material-ui/core/Card'; 4 | import CardActionArea from '@material-ui/core/CardActionArea'; 5 | import CardActions from '@material-ui/core/CardActions'; 6 | import CardContent from '@material-ui/core/CardContent'; 7 | import CardMedia from '@material-ui/core/CardMedia'; 8 | import Typography from '@material-ui/core/Typography'; 9 | import LinkedInIcon from '@material-ui/icons/LinkedIn'; 10 | import GitHubIcon from '@material-ui/icons/GitHub'; 11 | import EmailIcon from '@material-ui/icons/Email'; 12 | import TwitterIcon from '@material-ui/icons/Twitter'; 13 | import InstagramIcon from '@material-ui/icons/Instagram'; 14 | 15 | const useStyles = makeStyles({ 16 | root: { 17 | maxWidth: 345, 18 | display: "flex", 19 | flexDirection: "column", 20 | justifyContent: "space-between" 21 | }, 22 | media: { 23 | height: 280, 24 | }, 25 | icon_card: { 26 | display: "block", 27 | }, 28 | icons: { 29 | display: "flex", 30 | justifyContent: "space-around" 31 | } 32 | }); 33 | 34 | const MediaCard = (props) => { 35 | const classes = useStyles(); 36 | 37 | 38 | return ( 39 | 40 | 41 | 46 | 47 | 48 | {props.name} 49 | 50 | 51 | {props.desc} 52 | 53 | 54 | 55 | 56 |
57 | {window.open(props.github)}} /> 58 | {window.open(props.twitter)}}/> 59 | {window.open(props.linkedin)}}/> 60 | {window.open(props.gmail)}}/> 61 | {window.open(props.insta)}}/> 62 |
63 |
64 |
65 | ); 66 | } 67 | 68 | export default MediaCard; -------------------------------------------------------------------------------- /frontend/syntaxmeets/src/components/Footer/Footer.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import localclasses from "./Footer.module.css"; 3 | import { Row, Col } from "react-bootstrap"; 4 | import Button from "@material-ui/core/Button"; 5 | import GithubIcon from "@material-ui/icons/GitHub"; 6 | import FavoriteIcon from "@material-ui/icons/Favorite"; 7 | import GroupIcon from "@material-ui/icons/Group"; 8 | import logo from "./navlogo.png"; 9 | import About from "../About/About"; 10 | import { Dialog, Slide } from "@material-ui/core"; 11 | 12 | const Transition = React.forwardRef(function Transition(props, ref) { 13 | return ; 14 | }); 15 | 16 | export default function Footer() { 17 | const [open, setOpen] = React.useState(false); 18 | 19 | const handleClickOpen = () => { 20 | setOpen(true); 21 | }; 22 | const handleClose = () => { 23 | setOpen(false); 24 | }; 25 | 26 | return ( 27 |
28 | 35 | 36 | 37 |
38 | 39 | 40 | syntaxmeets-logo 45 |

46 | 53 | Syntax 54 | 55 | 62 | Meets 63 | 64 |

65 | SyntaxMeets is a real-time, collaborative coding platform to group 66 | with other members and code. A user can create/join a room. A code 67 | editor with 19 language support and 10+ themes, code compiler, 68 | Drawing pad to design algorithm, Chat Box to discuss and code. 69 | 70 | 71 | 72 |
73 | 78 | SyntaxMeets - Create rooms, call friends and code together simultaneously | Product Hunt 83 | 84 |
85 |
86 |
87 |
88 | 89 |
90 | 104 | 110 | 115 | 116 |
117 | 118 |
119 |
120 | 121 |
122 |

123 | Made with 124 | by   125 | 131 | 132 | 133 |   Syntax 134 | 135 | 136 | Meets   137 | 138 | 139 | 140 |

141 |
142 |
143 | ); 144 | } 145 | -------------------------------------------------------------------------------- /frontend/syntaxmeets/src/components/Footer/Footer.module.css: -------------------------------------------------------------------------------- 1 | /* 2 | #000A29, #0077B6,#00B4D8,#FFD500,#F3F7F7,#FFFFFF; 3 | */ 4 | .bgblock { 5 | border-radius: 7px; 6 | background-color: #000a29; 7 | font-family: "Poppins"; 8 | padding: 4px; 9 | } 10 | .bgblock:hover { 11 | background-color: #001c6d; 12 | } 13 | .footer { 14 | background-color: #000a29; 15 | font-family: "Poppins"; 16 | } 17 | .footer_content { 18 | padding: 40px; 19 | color: #dfdddd; 20 | font-size: 0.9em; 21 | margin-right: 0px !important; 22 | } 23 | .footer_content_logo { 24 | padding: 40px; 25 | font-size: 1.3em; 26 | } 27 | .base { 28 | background-color: #f3f7f7; 29 | font-family: "Poppins"; 30 | font-size: 0.9em; 31 | padding-top: 11px; 32 | } 33 | .iconGit { 34 | text-decoration: none; 35 | } 36 | .iconGit:hover { 37 | transition: 0.5s; 38 | -webkit-transition: 0.5s; 39 | -moz-transition: 0.5s; 40 | -ms-transition: 0.5s; 41 | -o-transition: 0.5s; 42 | -webkit-transform: rotate(360deg); 43 | -moz-transform: rotate(360deg); 44 | -o-transform: rotate(360deg); 45 | -ms-transform: rotate(360deg); 46 | transform: rotate(360deg); 47 | } 48 | -------------------------------------------------------------------------------- /frontend/syntaxmeets/src/components/Footer/navlogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kothariji/SyntaxMeets/74f10e80ae3e42908f9943e27ca06e99a0d53530/frontend/syntaxmeets/src/components/Footer/navlogo.png -------------------------------------------------------------------------------- /frontend/syntaxmeets/src/components/Home/Home.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useRef,useEffect} from "react"; 2 | import localclasses from "./Home.module.css"; 3 | import bgimg from "../../images/homepage.svg"; 4 | import Container from "react-bootstrap/Container"; 5 | import Row from "react-bootstrap/Row"; 6 | import Col from "react-bootstrap/Col"; 7 | import { TextField, Button as MUIButton, Typography } from "@material-ui/core"; 8 | import { Link } from "react-router-dom"; 9 | import SkyLight from "react-skylight"; 10 | import PropTypes from "prop-types"; 11 | import { withStyles } from "@material-ui/core/styles"; 12 | import MeetingRoomIcon from "@material-ui/icons/MeetingRoom"; 13 | import GroupAddIcon from "@material-ui/icons/GroupAdd"; 14 | import { connect } from "react-redux"; 15 | import { validateRoomID } from '../../util/util.js' 16 | import * as actions from "../../store/actions/roomActions.js"; 17 | 18 | const styles = { 19 | input: { 20 | color: "#000", 21 | }, 22 | }; 23 | 24 | const Home = (props) => { 25 | const { classes } = props; 26 | const skyLightCreateModal = useRef(SkyLight); 27 | const skyLightJoinModal = useRef(SkyLight); 28 | const [disabledName, setDisabledName] = useState(true); 29 | const [disabledRoomId, setDisabledRoomId] = useState(true); 30 | const createRoomButton = useRef(null); 31 | const joinRoomButton = useRef(null); 32 | 33 | 34 | useEffect(() => { 35 | props.reset(); 36 | },[]); 37 | 38 | const roomModal = { 39 | backgroundImage: 40 | "linear-gradient(to top, #d6d4ee, #e1dff2, #ebe9f6, #f5f4fb, #ffffff)", 41 | width: "30%", 42 | marginTop: "-200px", 43 | marginLeft: "-15%", 44 | }; 45 | 46 | const ModalTitle = (props) => ( 47 | 48 | 56 | {props.start} 57 |  Syntax 58 | Room 59 | 60 | 61 | ); 62 | 63 | return ( 64 |
65 | 66 | 67 | 68 | SyntaxMeets 73 | 74 | 75 | 76 | 77 | } 90 | size="large" 91 | onClick={() => skyLightCreateModal.current.show()} 92 | > 93 | Create a Room 94 | 95 | 96 |
97 |
98 | 99 | } 111 | size="large" 112 | onClick={() => skyLightJoinModal.current.show()} 113 | > 114 | Join a Room 115 | 116 | 117 | 118 | } 123 | > 124 | 125 | 132 | Enter Your Name 133 | 134 | { 143 | props.setName(e.target.value); 144 | e.target.value.length >= 1 145 | ? setDisabledName(false) 146 | : setDisabledName(true); 147 | }} 148 | onKeyPress={(ev) => { 149 | if(ev.key === 'Enter') { 150 | ev.preventDefault(); 151 | if(!disabledName) 152 | { 153 | createRoomButton.current.click(); 154 | } 155 | } 156 | }} 157 | /> 158 | 159 |
160 |
161 | 162 | 163 | { 178 | // it stores the details in localstorage which is used later 179 | localStorage.setItem('roomId',props.joinRoomId); 180 | localStorage.setItem('name',props.name); 181 | // isconnected is used to reconnect 182 | sessionStorage.setItem('isconnected',true); 183 | }} 184 | > 185 | Create Room 186 | 187 | 188 |
189 |
190 | } 195 | > 196 | 197 | 204 | Enter Your Name 205 | 206 | { 215 | props.setName(e.target.value); 216 | e.target.value.length >= 1 217 | ? setDisabledName(false) 218 | : setDisabledName(true); 219 | }} 220 | style={{ color: "#000" }} 221 | /> 222 | 223 | 231 | Enter Room Id 232 | 233 | { 235 | props.setRoomID(event.target.value); 236 | validateRoomID(event.target.value) 237 | ? setDisabledRoomId(false) 238 | : setDisabledRoomId(true); 239 | }} 240 | onKeyPress={(ev) => { 241 | if(ev.key === 'Enter') { 242 | ev.preventDefault(); 243 | if(!disabledName && !disabledRoomId) 244 | { 245 | joinRoomButton.current.click(); 246 | } 247 | } 248 | }} 249 | fullWidth 250 | id="outlined-basic" 251 | className={classes.root} 252 | InputProps={{ className: classes.input }} 253 | label="Enter Room ID" 254 | variant="outlined" 255 | InputLabelProps={{ 256 | shrink: true, 257 | }} 258 | placeholder="xxxx-yyyy-zzzz" 259 | /> 260 | 261 |
262 |
263 | 264 | 265 | { 280 | // it stores the details in localstorage which are later used 281 | localStorage.setItem('roomId',props.joinRoomId) 282 | localStorage.setItem('name',props.name) 283 | sessionStorage.setItem('isconnected',true); 284 | }} 285 | > 286 | Join a Room 287 | 288 | 289 |
290 |
291 |
292 | 293 | 294 |
295 |
296 |
297 | ); 298 | }; 299 | 300 | Home.propTypes = { 301 | classes: PropTypes.object.isRequired, 302 | }; 303 | const mapStateToProps = (state) => { 304 | return { 305 | joinRoomId: state.ROOM.roomId, 306 | name: state.ROOM.name, 307 | }; 308 | }; 309 | 310 | const mapDispatchToProps = (dispatch) => { 311 | return { 312 | setName: (name) => dispatch(actions.setName(name)), 313 | setRoomID: (ID) => dispatch(actions.setRoomID(ID)), 314 | reset: () => dispatch(actions.reset()), 315 | }; 316 | }; 317 | 318 | export default connect( 319 | mapStateToProps, 320 | mapDispatchToProps 321 | )(withStyles(styles)(Home)); -------------------------------------------------------------------------------- /frontend/syntaxmeets/src/components/Home/Home.module.css: -------------------------------------------------------------------------------- 1 | .home { 2 | font-family: "Poppins"; 3 | background-color: #000a29; 4 | width: 100%; 5 | height: 100%; 6 | min-height: 100vh; 7 | padding: 47px 10px 11px 10px; 8 | } 9 | 10 | .home__svg { 11 | width: 85%; 12 | } 13 | 14 | .home__buttons { 15 | margin-top: 64%; 16 | margin-bottom: 82%; 17 | } 18 | 19 | .home__modal__container { 20 | padding: 25px; 21 | } 22 | -------------------------------------------------------------------------------- /frontend/syntaxmeets/src/components/Loader/Loader.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import localclasses from "./Loader.module.css"; 3 | import { XlviLoader } from "react-awesome-loaders"; 4 | function Loader() { 5 | return ( 6 |
7 | 12 |
13 | ); 14 | } 15 | export default Loader; 16 | -------------------------------------------------------------------------------- /frontend/syntaxmeets/src/components/Loader/Loader.module.css: -------------------------------------------------------------------------------- 1 | .load { 2 | background-color: #000a29; 3 | text-align: center; 4 | min-height: 100vh; 5 | display: flex; 6 | flex-direction: column; 7 | align-items: center; 8 | justify-content: center; 9 | } 10 | -------------------------------------------------------------------------------- /frontend/syntaxmeets/src/components/Mobile/Mobile.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import bgimg from "../../images/mobilehome_svg.svg"; 3 | import localclasses from "./Mobile.module.css"; 4 | 5 | const Mobile = () => { 6 | return ( 7 |
8 | SyntaxMeets 13 |
14 | ) 15 | } 16 | 17 | export default Mobile 18 | -------------------------------------------------------------------------------- /frontend/syntaxmeets/src/components/Mobile/Mobile.module.css: -------------------------------------------------------------------------------- 1 | .mobilehome__svg { 2 | height: 100%; 3 | width: 100%; 4 | } 5 | -------------------------------------------------------------------------------- /frontend/syntaxmeets/src/components/Navbar/Navbar.js: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from "react"; 2 | import AppBar from "@material-ui/core/AppBar"; 3 | import { 4 | Toolbar, 5 | Typography, 6 | Button, 7 | Dialog, 8 | } from "@material-ui/core"; 9 | import logo from "../../images/navlogo.png"; 10 | import PersonIcon from "@material-ui/icons/Person"; 11 | import SyntaxChat from "../SyntaxChat/SyntaxChat"; 12 | import copy from "copy-to-clipboard"; 13 | import About from "../About/About.js"; 14 | import Slide from "@material-ui/core/Slide"; 15 | import ParticpantsList from "../SyntaxChat/ParticipantsList"; 16 | import { CallEnd } from "@material-ui/icons"; 17 | import { Link } from "react-router-dom"; 18 | import { connect } from "react-redux"; 19 | import * as UIactions from "../../store/actions/uiActions.js"; 20 | 21 | const Transition = React.forwardRef(function Transition(props, ref) { 22 | return ; 23 | }); 24 | 25 | const Navbar = (props) => { 26 | const Copytext = (value) => { 27 | copy(value); 28 | props.setSnackBar("Room-ID Copied !","success"); 29 | }; 30 | 31 | const [open, setOpen] = React.useState(false); 32 | 33 | const handleClickOpen = () => { 34 | setOpen(true); 35 | }; 36 | const handleClose = () => { 37 | setOpen(false); 38 | }; 39 | 40 | return ( 41 | 42 | 49 | 50 | 51 | 52 | 53 | 54 | SyntaxMeets 59 | 63 |  SyntaxMeets 64 | 65 | 66 | 89 | 94 | 95 | 108 | 109 | 125 | 126 | 127 | 128 | 129 | ); 130 | }; 131 | // const mapStateToProps = (state) => { 132 | // return{ 133 | // roomId:state.ROOM.roomId, 134 | // }; 135 | // }; 136 | const mapDispatchToProps = (dispatch) => { 137 | return { 138 | setSnackBar: (msg,type) => dispatch(UIactions.setSnackBar(msg,type)), 139 | }; 140 | }; 141 | 142 | export default connect(null, mapDispatchToProps)(Navbar); 143 | 144 | 145 | -------------------------------------------------------------------------------- /frontend/syntaxmeets/src/components/SnackBar/Snackbar.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import Snackbar from '@material-ui/core/Snackbar'; 3 | import MuiAlert from '@material-ui/lab/Alert'; 4 | import { connect } from 'react-redux'; 5 | import { Typography } from '@material-ui/core'; 6 | 7 | class SnackBar extends Component { 8 | handleClose = (e) => { 9 | this.props.close() 10 | } 11 | render() { 12 | return ( 13 |
14 | this.handleClose}> 15 | this.props.close()} severity={this.props.type}> 16 | 17 | {this.props.snackbarMessage} 18 | 19 | 20 | 21 |
22 | ) 23 | } 24 | } 25 | function Alert(props) { 26 | return ; 27 | } 28 | const mapStateToProps = state => { 29 | return { 30 | isSnackOpen: state.UI.isSnackBarOpen, 31 | snackbarMessage: state.UI.setSnackBarMessage, 32 | type: state.UI.snackBarType 33 | } 34 | } 35 | const mapDispatchToProps = dispatch => { 36 | return { close: () => dispatch({ type: 'CLOSE_SNACKBAR' }) } 37 | } 38 | export default connect(mapStateToProps, mapDispatchToProps)(SnackBar) -------------------------------------------------------------------------------- /frontend/syntaxmeets/src/components/SyntaxChat/ChatMessage.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { 3 | ListItem, 4 | ListItemText, 5 | ListItemAvatar, 6 | Avatar, 7 | } from "@material-ui/core"; 8 | 9 | const nameGenerator = (name) => 10 | (name[0][0] + (name.length > 1 ? name[1][0] : "")).toUpperCase(); 11 | 12 | export const ChatMessage = (props) => { 13 | return props.messages.map((data) => ( 14 | 21 | 28 | {nameGenerator(data.name.split(" "))} 29 | 30 | 55 | {data.name} 56 | 57 | } 58 | secondary={ 59 | 72 | {data.message} 73 | 74 | } 75 | /> 76 | 77 | )); 78 | }; 79 | -------------------------------------------------------------------------------- /frontend/syntaxmeets/src/components/SyntaxChat/ParticipantsList.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import GroupIcon from "@material-ui/icons/Group"; 3 | import { 4 | ListItem, 5 | ListItemText, 6 | ListItemAvatar, 7 | Avatar, 8 | Button, 9 | Drawer, 10 | List, 11 | } from "@material-ui/core"; 12 | import { makeStyles } from "@material-ui/core/styles"; 13 | import CloseSharpIcon from "@material-ui/icons/CloseSharp"; 14 | import { connect } from "react-redux"; 15 | const useStyles = makeStyles({ 16 | list: { 17 | width: 400, 18 | }, 19 | fullList: { 20 | width: "auto", 21 | }, 22 | }); 23 | const nameGenerator = name => 24 | (name[0][0] + (name.length > 1 ? name[1][0] : "")).toUpperCase(); 25 | 26 | function ParticipantsList(props) { 27 | const classes = useStyles(); 28 | const { users } = props; 29 | const [openList, setOpenList] = useState(false); 30 | 31 | const renderParticipants = () => { 32 | return Object.keys(users).map(elem => { 33 | const name = users[elem]; 34 | return ( 35 | <> 36 | 37 | 38 | 39 | {nameGenerator(name.split(" "))} 40 | 41 | 42 | 57 | 58 | 59 | ); 60 | }); 61 | }; 62 | return ( 63 |
64 | 78 | setOpenList(false)} 82 | > 83 | setOpenList(false)} 86 | /> 87 |
96 | {renderParticipants()} 97 |
98 |
99 |
100 | ); 101 | } 102 | 103 | const mapStateToProps = state => { 104 | return { 105 | users: state.ROOM.users, 106 | }; 107 | }; 108 | 109 | export default connect(mapStateToProps, null)(ParticipantsList); 110 | -------------------------------------------------------------------------------- /frontend/syntaxmeets/src/components/SyntaxChat/SyntaxChat.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useRef } from "react"; 2 | import { makeStyles } from "@material-ui/core/styles"; 3 | import InsertEmoticonIcon from "@material-ui/icons/InsertEmoticon"; 4 | import { 5 | Drawer, 6 | Button, 7 | Divider, 8 | TextField, 9 | Grid, 10 | Typography 11 | } from "@material-ui/core"; 12 | import { connect } from "react-redux"; 13 | import * as actions from "../../store/actions/chatActions.js"; 14 | import * as UIactions from "../../store/actions/uiActions.js"; 15 | import SendIcon from "@material-ui/icons/Send"; 16 | import ForumIcon from "@material-ui/icons/Forum"; 17 | import { ChatMessage } from "./ChatMessage"; 18 | import CloseSharpIcon from "@material-ui/icons/CloseSharp"; 19 | import "emoji-mart/css/emoji-mart.css"; 20 | import { Picker } from "emoji-mart"; 21 | 22 | const useStyles = makeStyles({ 23 | list: { 24 | width: 400 25 | }, 26 | fullList: { 27 | width: "auto" 28 | } 29 | }); 30 | 31 | const SyntaxChat = (props) => { 32 | const classes = useStyles(); 33 | const [emojiPickerState, SetEmojiPicker] = useState(false); 34 | const messagesEndRef = useRef(null); 35 | const [openDrawer, setopenDrawer] = useState(false); 36 | 37 | const handleMessageSubmit = () => { 38 | // this if block is called when page is reloaded 39 | // it ensures that user is connected back to same room to incorporate smooth chating 40 | if (localStorage.getItem("flag") && sessionStorage.getItem("isconnected")) { 41 | if (props.message.trim() === "") return; 42 | SetEmojiPicker(false); 43 | // this data extract info from localStorage 44 | let data = { 45 | name: localStorage.getItem("name"), 46 | roomId: localStorage.getItem("roomId"), 47 | message: props.message 48 | }; 49 | // this is used to connect back the user 50 | props.socket.emit("chatmessage", data); 51 | props.setMessages(data); 52 | props.setMessage(""); 53 | } 54 | // this block is called when the user connects first time 55 | else { 56 | if (props.message.trim() === "") return; 57 | SetEmojiPicker(false); 58 | let data = { 59 | name: props.name, 60 | roomId: props.roomId, 61 | message: props.message 62 | }; 63 | props.socket.emit("chatmessage", data); 64 | 65 | props.setMessages(data); 66 | props.setMessage(""); 67 | } 68 | }; 69 | 70 | useEffect(() => { 71 | props.socket.on("chatmessage", (data) => { 72 | props.setMessages(data); 73 | }); 74 | let timeout; 75 | // recieve the user who is currently typing's data from the backend 76 | props.socket.on("typing", (data) => { 77 | props.whoIsTyping(data.name); 78 | //Remove the timeout(to clear typing message) , if someone has again typed something 79 | clearTimeout(timeout); 80 | timeout = setTimeout(() => { 81 | //Remove the typing message if no one is typing after 500ms 82 | props.whoIsTyping(); 83 | }, 500); 84 | }); 85 | }, []); 86 | 87 | useEffect(()=> { 88 | let lastMessage = props.messages[props.messages.length - 1]; 89 | if (props.name && lastMessage) { 90 | if (props.name !== lastMessage.name) { 91 | console.log("NEW MESSAGE"); 92 | lastMessage.message.length > 20 ? props.setSnackBar(`${lastMessage.name}: ${lastMessage.message.substring(0,20)}...`, "warning") : props.setSnackBar(`${lastMessage.name}: ${lastMessage.message}`, "warning") 93 | } 94 | } 95 | }, [props.messages]); 96 | 97 | const scrollToBottom = () => { 98 | if (messagesEndRef.current) { 99 | messagesEndRef.current.scrollIntoView({ behavior: "smooth" }); 100 | } 101 | }; 102 | 103 | function triggerPicker(event) { 104 | event.preventDefault(); 105 | SetEmojiPicker(!emojiPickerState); 106 | } 107 | useEffect(scrollToBottom, [props.messages]); 108 | let emojiPicker; 109 | if (emojiPickerState) { 110 | emojiPicker = ( 111 | props.setMessage(props.message + emoji.native)} 115 | // style={{width: "90%", display: "flex"}} 116 | /> 117 | ); 118 | } 119 | 120 | const toggleDrawer = (open) => (event) => { 121 | if ( 122 | event.type === "keydown" && 123 | (event.key === "Tab" || event.key === "Shift") 124 | ) { 125 | return; 126 | } 127 | setopenDrawer(open); 128 | }; 129 | 130 | return ( 131 |
132 | 146 | 147 | 151 |
156 |
163 | {} 164 |
165 |
166 |
175 | 176 | 177 | 178 | 179 | 180 | {props.typingUser ? ( 181 | 182 | 183 | 191 | {props.typingUser} is typing... 192 | 193 | 194 | 195 | 196 | ) : undefined} 197 | 198 | 199 | {/* */} 200 | 201 | {/* */} 209 | 210 | 223 | {/* */} 224 | 225 | 226 | 227 | { 235 | props.socket.emit("typing", { 236 | id: props.socket.id, 237 | name: props.name 238 | }); 239 | props.setMessage(e.target.value); 240 | }} 241 | onKeyDown={(e) => { 242 | if (e.key === "Enter") { 243 | handleMessageSubmit(); 244 | } 245 | }} 246 | /> 247 | {/* SetMessage(event.target.value)} 254 | /> */} 255 | 256 | 257 | 273 | 274 | 275 | 276 | 277 | 278 | {emojiPicker} 279 | 280 | 281 |
282 |
283 | 284 |
285 | ); 286 | }; 287 | 288 | const mapStateToProps = (state) => { 289 | return { 290 | typingUser: state.CHAT.typingUser, 291 | message: state.CHAT.message, 292 | messages: state.CHAT.chat 293 | }; 294 | }; 295 | 296 | const mapDispatchToProps = (dispatch) => { 297 | return { 298 | setSnackBar: (msg,type) => dispatch(UIactions.setSnackBar(msg,type)), 299 | setMessage: (msg) => dispatch(actions.setMessage(msg)), 300 | setMessages: (msg) => dispatch(actions.makeMessage(msg)), 301 | whoIsTyping: (user) => dispatch(actions.whoIsTyping(user)) 302 | }; 303 | }; 304 | export default connect(mapStateToProps, mapDispatchToProps)(SyntaxChat); 305 | -------------------------------------------------------------------------------- /frontend/syntaxmeets/src/components/SyntaxEditor/CodeInput.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import AceEditor from "react-ace"; 3 | import { connect } from "react-redux"; 4 | import * as actions from "../../store/actions/editorActions.js"; 5 | const INPUT = (props) => { 6 | const handleIChange = (newValue) => { 7 | props.setCodeInput(newValue); 8 | }; 9 | 10 | return ( 11 | 20 | ); 21 | }; 22 | 23 | const mapStateToProps = (state) => { 24 | return { 25 | codeInput: state.EDITOR.codeInput, 26 | }; 27 | }; 28 | 29 | const mapDispatchToProps = (dispatch) => { 30 | return { 31 | setCodeInput: (input) => dispatch(actions.setCodeInput(input)), 32 | }; 33 | }; 34 | 35 | export default connect(mapStateToProps,mapDispatchToProps)(INPUT); -------------------------------------------------------------------------------- /frontend/syntaxmeets/src/components/SyntaxEditor/CodeOutput.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import AceEditor from "react-ace"; 3 | import { connect } from "react-redux"; 4 | 5 | const OUTPUT = (props) => { 6 | 7 | return ( 8 | 24 | ); 25 | }; 26 | 27 | 28 | const mapStateToProps = (state) => { 29 | return { 30 | codeOutput: state.EDITOR.codeOutput, 31 | }; 32 | }; 33 | export default connect(mapStateToProps)(OUTPUT); -------------------------------------------------------------------------------- /frontend/syntaxmeets/src/components/SyntaxEditor/LanguageData.js: -------------------------------------------------------------------------------- 1 | export const languages = [ 2 | 3 | "c_cpp", 4 | "clojure", 5 | "csharp", 6 | "cobol", 7 | "erlang", 8 | "fortran", 9 | "golang", 10 | "groovy", 11 | "java", 12 | "javascript", 13 | "kotlin", 14 | "php", 15 | "python", 16 | "r", 17 | "ruby", 18 | "sql", 19 | "swift", 20 | "typescript", 21 | ]; 22 | 23 | 24 | export const defaultValue = `#include 25 | #define lli long long int 26 | #define endl "\\n" 27 | #define MAX 1000005 28 | #define MOD 1000000007 29 | using namespace std; 30 | int main() 31 | { 32 | int t; 33 | cin>>t; 34 | 35 | while(t--) 36 | { 37 | //your code 38 | 39 | } 40 | return 0; 41 | }`; 42 | 43 | export const langExtensionDict = { 44 | ".c": "C", 45 | ".cpp": "C++", 46 | ".java": "JAVA", 47 | ".js": "JavaScript", 48 | ".ts": "TypeScript", 49 | ".clj": "Closure", 50 | ".cljs": "Closure", 51 | ".cs": "C#", 52 | ".cbl": "COBOL", 53 | ".cob": "COBOL", 54 | ".cpy": "COBOL", 55 | ".erl": "Erlang", 56 | ".hrl": "Erlang", 57 | ".go": "Go", 58 | ".py": "Python", 59 | ".f90": "FortRan", 60 | ".f95": "FortRan", 61 | ".f03": "FortRan", 62 | ".txt": "", 63 | ".groovy": "Groovy", 64 | ".gvy": "Groovy", 65 | ".gy": "Groovy", 66 | ".gshgsh": "Groovy", 67 | ".kt": "Kotlin", 68 | ".kts": "Kotlin", 69 | ".ktm": "Kotlin", 70 | ".php": "PHP", 71 | ".r": "R", 72 | ".rb": "Ruby", 73 | ".sql": "SQL", 74 | ".swift": "Swift" 75 | }; 76 | 77 | export const LangOptions = [ 78 | "C" , 79 | "C++" , 80 | "Python", 81 | "JAVA", 82 | "JavaScript", 83 | "Kotlin", 84 | "Clojure" , 85 | "C#", 86 | "COBOL", 87 | "Erlang", 88 | "FortRan", 89 | "Go", 90 | "Groovy", 91 | "PHP", 92 | "R", 93 | "Ruby", 94 | "SQL", 95 | "Swift", 96 | "TypeScript" 97 | ] 98 | 99 | export const langId = { 100 | 101 | "C" : 50, 102 | "C++" : 54, 103 | "Clojure" : 86, 104 | "C#": 51, 105 | "COBOL": 77, 106 | "Erlang": 58, 107 | "FortRan": 59, 108 | "Go": 60, 109 | "Groovy": 88, 110 | "JAVA": 62, 111 | "JavaScript": 63, 112 | "Kotlin": 78, 113 | "PHP": 68, 114 | "Python": 71, 115 | "R": 80, 116 | "Ruby": 72, 117 | "SQL": 82, 118 | "Swift": 83, 119 | "TypeScript": 74 120 | 121 | } 122 | 123 | export const langMode = { 124 | 125 | "C": "c_cpp", 126 | "C++": "c_cpp", 127 | "Clojure": "clojure", 128 | "C#": "csharp", 129 | "COBOL": "cobol", 130 | "Erlang": "erlang", 131 | "FortRan": "fortran", 132 | "Go": "golang", 133 | "Groovy": "groovy", 134 | "JAVA": "java", 135 | "JavaScript": "javascript", 136 | "Kotlin": "kotlin", 137 | "PHP": "php", 138 | "Python": "python", 139 | "R": "r", 140 | "Ruby": "ruby", 141 | "SQL": "sql", 142 | "Swift": "swift", 143 | "TypeScript": "typescript", 144 | 145 | } 146 | 147 | 148 | export const themes = [ 149 | "monokai", 150 | "github", 151 | "tomorrow_night", 152 | "tomorrow", 153 | "kuroir", 154 | "twilight", 155 | "xcode", 156 | "textmate", 157 | "solarized_dark", 158 | "solarized_light", 159 | "terminal", 160 | ]; 161 | -------------------------------------------------------------------------------- /frontend/syntaxmeets/src/components/SyntaxEditor/SyntaxEditor.js: -------------------------------------------------------------------------------- 1 | import React, { Fragment, useEffect, useState } from "react"; 2 | import AceEditor from "react-ace"; 3 | import "ace-builds/src-min-noconflict/ext-searchbox"; 4 | import "ace-builds/src-min-noconflict/ext-language_tools"; 5 | import "ace-builds/src-noconflict/mode-jsx"; 6 | import { 7 | AppBar, 8 | FormControl, 9 | InputLabel, 10 | makeStyles, 11 | MenuItem, 12 | Select, 13 | Toolbar, 14 | Typography, 15 | FormControlLabel, 16 | Switch, 17 | Button, 18 | Dialog, 19 | DialogTitle, 20 | DialogActions, 21 | Snackbar, 22 | ButtonGroup, 23 | Tooltip, 24 | Zoom, 25 | DialogContent, 26 | } from "@material-ui/core"; 27 | import Grid from "@material-ui/core/Grid"; 28 | import Alert from "@material-ui/lab/Alert"; 29 | import localClasses from "./SyntaxEditor.module.css"; 30 | import { 31 | languages, 32 | langMode, 33 | LangOptions, 34 | langId, 35 | langExtensionDict, 36 | themes, 37 | } from "./LanguageData"; 38 | import Modal from "react-modal"; 39 | import ShareIcon from "@material-ui/icons/Share"; 40 | import PlayArrowIcon from "@material-ui/icons/PlayArrow"; 41 | import FileCopyIcon from "@material-ui/icons/FileCopy"; 42 | import CloudUploadIcon from "@material-ui/icons/CloudUpload"; 43 | import INPUT from "./CodeInput"; 44 | import OUTPUT from "./CodeOutput"; 45 | import copy from "copy-to-clipboard"; 46 | import { connect } from "react-redux"; 47 | import * as actions from "../../store/actions/editorActions.js"; 48 | import CloudDownloadRounded from "@material-ui/icons/CloudDownloadRounded"; 49 | import FullscreenRounded from "@material-ui/icons/FullscreenRounded"; 50 | import FullscreenExitRounded from "@material-ui/icons/FullscreenExitRounded"; 51 | import CloseIcon from '@material-ui/icons/Close'; 52 | import { getExtensionByLangCode } from "../../util/util"; 53 | import { css } from "@emotion/react"; 54 | import BeatLoader from "react-spinners/BeatLoader"; 55 | 56 | //extracting all the languages recquired 57 | languages.forEach((lang) => { 58 | require(`ace-builds/src-noconflict/mode-${lang}`); 59 | require(`ace-builds/src-noconflict/snippets/${lang}`); 60 | }); 61 | 62 | 63 | // Can be a string as well. Need to ensure each key-value pair ends with ; 64 | const override = css` 65 | display: block; 66 | margin: 0 auto; 67 | border-color: red; 68 | `; 69 | //extracting themes 70 | themes.forEach((theme) => require(`ace-builds/src-noconflict/theme-${theme}`)); 71 | 72 | const useStyles = makeStyles((mutheme) => ({ 73 | formControl: { 74 | margin: mutheme.spacing(1), 75 | minWidth: 120, 76 | }, 77 | selectEmpty: { 78 | marginTop: mutheme.spacing(2), 79 | }, 80 | })); 81 | 82 | Modal.setAppElement('#root') 83 | 84 | const SyntaxEditor = (props) => { 85 | const [theme, setTheme] = useState("monokai"); 86 | const [popup, setPopup] = useState([false, ""]); 87 | const [filePopup, setFilePopup] = useState(false); 88 | const [fileHandleError, setFileHandleError] = useState(""); 89 | const [fullscreen,setFullscreen] = useState(false); // maintain state of screen in syntax Editor 90 | const [modalIsOpen,setModalIsOpen] = useState(false) 91 | const [shareURL,setshareURL] = useState("") 92 | const [isLoading,setIsLoading]=useState(false) 93 | 94 | // This will resend a message to update the code of the newly joined user 95 | useEffect(() => { 96 | let UpdatedCode = props.code; 97 | if (props.previousUser.id === props.id) { 98 | props.socket.emit("message", UpdatedCode); 99 | } 100 | // if the user was connected then over reloading the page this block is called 101 | else if(sessionStorage.getItem('isconnected')) 102 | { 103 | //it used to save the code in sessionStorage when only one user is using there in a room 104 | props.setCode(sessionStorage.getItem('code')); 105 | } 106 | }, [props.previousUser]); 107 | 108 | const classes = useStyles(); 109 | useEffect(() => { 110 | props.socket.on("message", (newValue) => { 111 | props.setCode(newValue); 112 | }); 113 | }, []); 114 | 115 | const handleChange = (newValue) => { 116 | props.setCode(newValue); 117 | sessionStorage.setItem('code',newValue); 118 | props.socket.emit("message", newValue); 119 | }; 120 | 121 | const copyCode = (value) => { 122 | copy(value); 123 | setPopup([true, "Code Copied Sucessfully"]); 124 | }; 125 | 126 | const fetchSharedCodeLink=async (content) =>{ 127 | var response = await fetch("https://dpaste.com/api/v2/", { 128 | method: "POST", 129 | headers: { "Content-Type": "application/x-www-form-urlencoded" }, 130 | body: "content=" + encodeURIComponent(content) 131 | }); 132 | return response.text(); 133 | } 134 | 135 | const shareCode = (value) => { 136 | setModalIsOpen(true) 137 | setIsLoading(true) 138 | fetchSharedCodeLink(value).then(url => {setIsLoading(false);setshareURL(url) }); 139 | } 140 | 141 | const handleCodeRun = () => { 142 | props.executeCode(langId[props.currLang], props.code, props.codeInput); 143 | }; 144 | 145 | const handleCodeDownload = () => { 146 | // download code here... 147 | const element = document.createElement("a"); 148 | const file = new Blob([props.code], { 149 | type: "text/plain;charset=utf-8", 150 | }); 151 | element.href = URL.createObjectURL(file); 152 | element.download = `syntaxmeets-code.${getExtensionByLangCode( 153 | props.currLang 154 | )}`; 155 | document.body.appendChild(element); 156 | element.click(); 157 | }; 158 | 159 | const IONavbar = (props) => { 160 | return ( 161 | 162 | 175 | {props.type} 176 | 177 | 178 | ); 179 | }; 180 | 181 | const uploadFile = () => { 182 | document.querySelector("#upload").click(); 183 | }; 184 | 185 | const checkValidFileExtension = (file) => { 186 | const validExtensions = Object.keys(langExtensionDict); 187 | var name = file.name; 188 | var valid = false; 189 | if (name.length > 0) { 190 | for (var i = 0; i < validExtensions.length; ++i) { 191 | var ext = validExtensions[i]; 192 | if ( 193 | name.substr(name.length - ext.length, ext.length).toLowerCase() == 194 | ext.toLowerCase() 195 | ) { 196 | valid = true; 197 | break; 198 | } 199 | } 200 | } 201 | return valid; 202 | }; 203 | 204 | const handleFileChange = () => { 205 | var file = document.querySelector("#upload").files[0]; 206 | 207 | if (file) { 208 | var reader = new FileReader(); 209 | 210 | reader.onload = function (e) { 211 | if (file.size > 10000) { 212 | setFilePopup(true); 213 | setFileHandleError("Error: File size greater than 10KB!"); 214 | return; 215 | } 216 | 217 | if (!checkValidFileExtension(file)) { 218 | setFilePopup(true); 219 | setFileHandleError("Error: Not a Valid File Extension!"); 220 | return; 221 | } 222 | 223 | handleChange(e.target.result); 224 | const fileNameArr = file.name.split("."); 225 | const ext = `.${fileNameArr[fileNameArr.length - 1]}`; 226 | props.setLanguage(langExtensionDict[ext]); 227 | }; 228 | 229 | reader.onerror = function (e) { 230 | console.error("An error ocurred reading the file", e); 231 | }; 232 | 233 | reader.readAsText(file, "UTF-8"); 234 | } 235 | }; 236 | 237 | // handle fullscreen mode 238 | const handleFullscreen = (props) =>{ 239 | fullscreen ? setFullscreen(false) : setFullscreen(true); 240 | props.toggleFocusMode(); 241 | } 242 | 243 | return ( 244 | 245 | 246 | Compiling ... 247 |
248 |
249 | 250 | 251 | 252 | > 253 |
254 |
255 |
256 | 257 | Oops Error Occured 258 | {props.codeError} 259 | 260 | 268 | 269 | 270 | { 274 | setPopup([false, ""]); 275 | }} 276 | > 277 | { 279 | setPopup([false, ""]); 280 | }} 281 | severity="success" 282 | variant="filled" 283 | > 284 | {popup[1]} 285 | 286 | 287 | { 291 | setFilePopup(false); 292 | }} 293 | > 294 | { 296 | setFilePopup(false); 297 | }} 298 | severity="error" 299 | variant="filled" 300 | > 301 | {fileHandleError} 302 | 303 | 304 | 305 |
306 | 318 |  SyntaxEditor 319 | 320 | 321 | 326 | 330 | Language 331 | 332 | 351 | 352 | 357 | 361 | Theme 362 | 363 | 378 | 379 | 384 | 388 | Font Size 389 | 390 | 405 | 406 | 407 |
408 |
409 | 410 | { 411 | setModalIsOpen(false) 412 | setshareURL("") 413 | }}/> 414 | 415 | Share Your Code 416 | 417 | 418 |
419 | {isLoading ? 420 | : 421 | <> 422 | {shareURL} 423 | 424 | 440 | 441 | 442 | } 443 |
444 |
445 |
446 | 463 | 467 | 468 | { 474 | props.setAutoCompletion(!props.autoCompletion); 475 | }} 476 | name="EnableAutoCompletion" 477 | /> 478 | } 479 | label={ 480 | 481 | Auto-complete 482 | 483 | } 484 | /> 485 | handleFileChange()} 489 | hidden 490 | accept=".c, .cpp, .java, .js, .ts, .clj, .cljs, .cs, .cbl, .cob, .cpy, .erl, .hrl, .go, .py, .f90, .f95, .f03, .txt, .groovy, .gvy, .gy, .gsh, .kt, .kts, .ktm, .php, .r, .rb, .sql, .swift" 491 | /> 492 | 497 | 498 | 511 | 512 | 513 | 526 | 527 | 528 | 541 | 542 | 543 | 556 | 557 | 558 | 574 | 575 | 576 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 |
605 | ); 606 | }; 607 | 608 | const mapStateToProps = (state) => { 609 | return { 610 | code: state.EDITOR.code, 611 | currLang: state.EDITOR.lang, 612 | fontSize: state.EDITOR.fontSize, 613 | autoCompletion: state.EDITOR.autoCompletion, 614 | codeInput: state.EDITOR.codeInput, 615 | codeOutput: state.EDITOR.codeOutput, 616 | isCompiling: state.EDITOR.isCompiling, 617 | isError: state.EDITOR.isError, 618 | codeError: state.EDITOR.codeError, 619 | previousUser: state.ROOM.previousUser, 620 | id: state.ROOM.id, 621 | }; 622 | }; 623 | 624 | const mapDispatchToProps = (dispatch) => { 625 | return { 626 | setCode: (code) => dispatch(actions.setCode(code)), 627 | setLanguage: (lang) => dispatch(actions.setLanguage(lang)), 628 | setFontSize: (size) => dispatch(actions.setFontSize(size)), 629 | setAutoCompletion: (name) => dispatch(actions.setAutoCompletion(name)), 630 | setIsError: (isactive) => dispatch(actions.setIsError(isactive)), 631 | executeCode: (langId, code, input) => 632 | dispatch(actions.executeCode(langId, code, input)), 633 | toggleFocusMode: () => dispatch(actions.toggleFocusMode()), 634 | }; 635 | }; 636 | export default connect(mapStateToProps, mapDispatchToProps)(SyntaxEditor); 637 | -------------------------------------------------------------------------------- /frontend/syntaxmeets/src/components/SyntaxEditor/SyntaxEditor.module.css: -------------------------------------------------------------------------------- 1 | .Editor__navbar { 2 | display: flex; 3 | flex-direction: row; 4 | justify-content: space-between; 5 | padding: auto 5px; 6 | } 7 | 8 | .Menu__options { 9 | font-family: "poppins"; 10 | font-size: 13px; 11 | } 12 | 13 | .loader { 14 | color: #ffd500; 15 | background-color: #000a29; 16 | font-family: Consolas, Menlo, Monaco, monospace; 17 | font-weight: bold; 18 | font-size: 30vh; 19 | opacity: 1; 20 | } 21 | .loader span { 22 | display: inline-block; 23 | animation: pulse 0.4s alternate infinite ease-in-out; 24 | } 25 | .loader span:nth-child(odd) { 26 | animation-delay: 0.4s; 27 | } 28 | .arrow { 29 | font-size: 200px; 30 | } 31 | @keyframes pulse { 32 | to { 33 | transform: scale(0.8); 34 | opacity: 0.5; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /frontend/syntaxmeets/src/components/SyntaxPad/SyntaxPad.js: -------------------------------------------------------------------------------- 1 | import React, {Fragment ,useRef,useState} from "react"; 2 | import { AppBar, Toolbar, Typography, Button } from "@material-ui/core"; 3 | import CanvasDraw from "react-canvas-draw"; 4 | import UndoIcon from "@material-ui/icons/Undo"; 5 | import DeleteIcon from "@material-ui/icons/Delete"; 6 | import localClasses from "./SyntaxPad.module.css"; 7 | import { ChromePicker } from 'react-color'; 8 | import ColorLensIcon from '@material-ui/icons/ColorLens'; 9 | 10 | const SyntaxPad = (props) => { 11 | 12 | const saveableCanvas = useRef(CanvasDraw); 13 | const [displayColorPicker, setdisplayColorPicker] = useState(false) 14 | const [brushColor, setBrushColor] = useState("#000A29") 15 | 16 | const popover = { 17 | position: 'absolute', 18 | zIndex: '100', 19 | left: '-50px' 20 | } 21 | const cover = { 22 | position: 'fixed', 23 | top: '0px', 24 | right: '0px', 25 | bottom: '0px', 26 | left: '0px', 27 | } 28 | 29 | const handleColorOpen = () =>{ 30 | setdisplayColorPicker(true); 31 | } 32 | 33 | const handleColorClose = () =>{ 34 | setdisplayColorPicker(false); 35 | } 36 | 37 | const handleColorChange = (color) => { 38 | setBrushColor(color.hex); 39 | } 40 | 41 | return ( 42 | 43 | 44 |
45 | 49 |  SyntaxPad 50 | 51 | 52 |
53 | 67 | { displayColorPicker ?
68 |
69 | 70 |
: null } 71 |
72 | 73 |
74 | 75 | 76 | 85 | 86 | 87 | 88 | 105 | 121 | 122 | 123 | 124 | ); 125 | 126 | } 127 | 128 | export default SyntaxPad; 129 | -------------------------------------------------------------------------------- /frontend/syntaxmeets/src/components/SyntaxPad/SyntaxPad.module.css: -------------------------------------------------------------------------------- 1 | .Editor__navbar { 2 | display: flex; 3 | flex-direction: row; 4 | justify-content: space-between; 5 | padding: auto 5px; 6 | } 7 | 8 | .Menu__options { 9 | font-family: "poppins"; 10 | font-size: 13px; 11 | color: white; 12 | } 13 | -------------------------------------------------------------------------------- /frontend/syntaxmeets/src/components/SyntaxRoom/SyntaxRoom.js: -------------------------------------------------------------------------------- 1 | import React, { Fragment, useEffect, useState } from "react"; 2 | import Navbar from "../Navbar/Navbar"; 3 | import { 4 | Button, 5 | Dialog, 6 | DialogActions, 7 | DialogTitle, 8 | Grid, 9 | } from "@material-ui/core"; 10 | import SyntaxEditor from "../SyntaxEditor/SyntaxEditor"; 11 | import SyntaxPad from "../SyntaxPad/SyntaxPad"; 12 | import { Redirect } from "react-router-dom"; 13 | import Footer from "../Footer/Footer"; 14 | import { socket } from "../../services/socket"; 15 | import { validateRoomID } from "../../util/util"; 16 | import { connect } from "react-redux"; 17 | import * as actions from "../../store/actions/roomActions.js"; 18 | import * as UIactions from "../../store/actions/uiActions.js"; 19 | 20 | 21 | // use following in case of localhost 22 | //import io from "socket.io-client"; 23 | // to use for localhost 24 | //var socket = io.connect("http://localhost:4000"); 25 | 26 | const SyntaxRoom = (props) => { 27 | const [popup, setPopup] = useState(false); 28 | const [popupMessage, setPopupMessage] = useState(""); 29 | 30 | const roomId = props.roomId; 31 | useEffect(() => { 32 | // If disconnected then connect again to server 33 | // Trigerred when user leaves a room 34 | socket.on("disconnect", (reason) => { 35 | props.reset(); 36 | socket.connect(); 37 | }); 38 | 39 | }); 40 | useEffect(() => { 41 | // fetch the list of active rooms from backend 42 | var roomsList = []; 43 | // all active roomids are there in backend/rooms 44 | const url = `${process.env.REACT_APP_SYNTAXMEETS_BACKEND_API}/rooms`; 45 | // this fetch function fetches the list of active list 46 | fetch(url) 47 | .then(res => { 48 | return res.json(); 49 | }) 50 | .then(rooms => { 51 | console.log(rooms); 52 | for (let i = 0; i < rooms.length; i++) 53 | roomsList.push(rooms[i]); 54 | }); 55 | // roomid gets the roomId from local storage 56 | var roomid = localStorage.getItem('roomId'); 57 | 58 | if (props.Username === undefined || props.Username === "") { 59 | setPopup(true); 60 | setPopupMessage("Name not found"); 61 | } 62 | 63 | 64 | if (props.location.name === undefined || props.location.name === "") { 65 | // If user disconnects and want to connect back to same room 66 | // flag is used to check whether roomid is active or not 67 | var flag = false; 68 | flag = roomsList.includes(roomid); 69 | localStorage.setItem('flag', flag); 70 | // this if statement is called when reload of page takes places 71 | // it just joins the user back to same roomId using socket 72 | if (localStorage.getItem('flag') && sessionStorage.getItem('isconnected')) { 73 | props.location.name = localStorage.getItem('name'); 74 | let data = { 75 | room: localStorage.getItem('roomId'), 76 | name: localStorage.getItem('name'), 77 | } 78 | // join back to same room 79 | socket.emit("joinroom", data); 80 | } 81 | else { 82 | // direct back to home 83 | alert("Please Enter your name"); 84 | props.reset(); 85 | props.setGoToHome(true); 86 | } 87 | } 88 | 89 | if (validateRoomID(roomId) === false || props.location.pathname === "") { 90 | setPopup(true); 91 | setPopupMessage("Invalid Room Id"); 92 | } 93 | // this will send server(backend) the roomId in which the props.socket needs to be joined 94 | //this code will run only once 95 | let data = { 96 | room: roomId, 97 | name: props.Username, 98 | }; 99 | socket.emit("joinroom", data); 100 | localStorage.setItem("my_name", props.Username); 101 | socket.on("addusers", (data) => { 102 | // When a new User joins in the room 103 | // Get all the users from the backend , when the current user joins in the room 104 | // so fetch all users from backend and store in the frontend 105 | // Also pass id of current user from backend 106 | props.setUsers(data.users); 107 | let msg = data.users[data.id] + " , Welcome to Syntax Meets!"; 108 | props.setSnackBar(msg, "success"); 109 | props.setId(data.id); 110 | }); 111 | socket.on("userjoined", (users) => { 112 | // For all other users (excluding the new user) , just get the new user data who entered the room 113 | // and a old user who will emit the code to be updated for new user 114 | // so update in the state 115 | const { newUser, oldUser } = users; 116 | const id = Object.keys(newUser)[0]; 117 | props.setUsers(newUser); 118 | let msg = newUser[id] + " , Welcome to Syntax Meets!"; 119 | props.setSnackBar(msg, "success"); 120 | props.setPreviousUser({ id: Object.keys(oldUser)[0] }); 121 | }); 122 | 123 | socket.on("userleft", (userObject) => { 124 | props.removeUser(userObject); 125 | let msg = userObject.name + " Left the Room."; 126 | props.setSnackBar(msg, "error"); 127 | }); 128 | }, []); 129 | 130 | return ( 131 | 132 | {props.goToHome ? ( 133 | 134 | ) : ( 135 | 136 | 137 | 138 | {popupMessage} 139 | 140 | 141 | 154 | 155 | 156 | 157 |
164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 |
173 |
174 | )} 175 |