├── Procfile
├── client
├── src
│ ├── assets
│ │ ├── meeting.png
│ │ ├── online-interview.png
│ │ ├── heart.png
│ │ ├── home.png
│ │ ├── home2.png
│ │ ├── note.png
│ │ ├── note1.png
│ │ ├── note2.png
│ │ ├── github.png
│ │ ├── linkedin.png
│ │ ├── homeVideo.mp4
│ │ ├── homeVideo1.mp4
│ │ ├── interview.png
│ │ ├── video-call.png
│ │ ├── styles
│ │ │ └── _mixins.scss
│ │ └── video-call.svg
│ ├── common
│ │ ├── spin.gif
│ │ ├── spin1.gif
│ │ ├── Spinner.js
│ │ └── AntSwitch.js
│ ├── constants.js
│ ├── setupTests.js
│ ├── App.test.js
│ ├── index.css
│ ├── reportWebVitals.js
│ ├── index.js
│ ├── Components
│ │ ├── Messages
│ │ │ ├── Message.js
│ │ │ ├── Messages.css
│ │ │ ├── Messages.scss
│ │ │ └── Messages.js
│ │ ├── Notes
│ │ │ ├── Notes.scss
│ │ │ ├── Notes.css
│ │ │ └── Notes.js
│ │ ├── Footer
│ │ │ ├── Footer.css
│ │ │ ├── Footer.scss
│ │ │ └── Footer.js
│ │ ├── Editor
│ │ │ ├── Editor.css
│ │ │ ├── Editor.scss
│ │ │ └── Editor.js
│ │ ├── Join
│ │ │ ├── Join.css
│ │ │ ├── Join.scss
│ │ │ └── Join.js
│ │ ├── Navbar
│ │ │ ├── Navbar.js
│ │ │ ├── Navbar.css
│ │ │ └── Navbar.scss
│ │ ├── Options
│ │ │ ├── Options.css
│ │ │ ├── Options.scss
│ │ │ └── Options.js
│ │ ├── Home
│ │ │ ├── Home.js
│ │ │ ├── Home.css
│ │ │ └── Home.scss
│ │ └── Meet
│ │ │ ├── Meet.js
│ │ │ ├── Meet.css
│ │ │ └── Meet.scss
│ ├── App.js
│ ├── App.css
│ ├── logo.svg
│ └── SocketContext.js
├── public
│ ├── robots.txt
│ ├── home.png
│ ├── favicon.ico
│ ├── home2.png
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── index.html
├── .firebaserc
├── firebase.json
├── .gitignore
├── package.json
└── .firebase
│ └── hosting.YnVpbGQ.cache
├── .gitignore
├── package.json
├── .github
└── workflows
│ ├── Deployment.yml
│ ├── main.yml
│ ├── docker-publish.yml
│ └── codeql-analysis.yml
├── README.md
└── server.js
/Procfile:
--------------------------------------------------------------------------------
1 | web: node server.js
--------------------------------------------------------------------------------
/client/src/assets/meeting.png:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/src/assets/online-interview.png:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/client/.firebaserc:
--------------------------------------------------------------------------------
1 | {
2 | "projects": {
3 | "default": "alanbinu-video-conference"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/client/public/home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanBinu007/Live-Video-Chat-Application-for-Interview-purposes/HEAD/client/public/home.png
--------------------------------------------------------------------------------
/client/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanBinu007/Live-Video-Chat-Application-for-Interview-purposes/HEAD/client/public/favicon.ico
--------------------------------------------------------------------------------
/client/public/home2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanBinu007/Live-Video-Chat-Application-for-Interview-purposes/HEAD/client/public/home2.png
--------------------------------------------------------------------------------
/client/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanBinu007/Live-Video-Chat-Application-for-Interview-purposes/HEAD/client/public/logo192.png
--------------------------------------------------------------------------------
/client/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanBinu007/Live-Video-Chat-Application-for-Interview-purposes/HEAD/client/public/logo512.png
--------------------------------------------------------------------------------
/client/src/assets/heart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanBinu007/Live-Video-Chat-Application-for-Interview-purposes/HEAD/client/src/assets/heart.png
--------------------------------------------------------------------------------
/client/src/assets/home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanBinu007/Live-Video-Chat-Application-for-Interview-purposes/HEAD/client/src/assets/home.png
--------------------------------------------------------------------------------
/client/src/assets/home2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanBinu007/Live-Video-Chat-Application-for-Interview-purposes/HEAD/client/src/assets/home2.png
--------------------------------------------------------------------------------
/client/src/assets/note.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanBinu007/Live-Video-Chat-Application-for-Interview-purposes/HEAD/client/src/assets/note.png
--------------------------------------------------------------------------------
/client/src/assets/note1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanBinu007/Live-Video-Chat-Application-for-Interview-purposes/HEAD/client/src/assets/note1.png
--------------------------------------------------------------------------------
/client/src/assets/note2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanBinu007/Live-Video-Chat-Application-for-Interview-purposes/HEAD/client/src/assets/note2.png
--------------------------------------------------------------------------------
/client/src/common/spin.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanBinu007/Live-Video-Chat-Application-for-Interview-purposes/HEAD/client/src/common/spin.gif
--------------------------------------------------------------------------------
/client/src/common/spin1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanBinu007/Live-Video-Chat-Application-for-Interview-purposes/HEAD/client/src/common/spin1.gif
--------------------------------------------------------------------------------
/client/src/assets/github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanBinu007/Live-Video-Chat-Application-for-Interview-purposes/HEAD/client/src/assets/github.png
--------------------------------------------------------------------------------
/client/src/assets/linkedin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanBinu007/Live-Video-Chat-Application-for-Interview-purposes/HEAD/client/src/assets/linkedin.png
--------------------------------------------------------------------------------
/client/src/assets/homeVideo.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanBinu007/Live-Video-Chat-Application-for-Interview-purposes/HEAD/client/src/assets/homeVideo.mp4
--------------------------------------------------------------------------------
/client/src/assets/homeVideo1.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanBinu007/Live-Video-Chat-Application-for-Interview-purposes/HEAD/client/src/assets/homeVideo1.mp4
--------------------------------------------------------------------------------
/client/src/assets/interview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanBinu007/Live-Video-Chat-Application-for-Interview-purposes/HEAD/client/src/assets/interview.png
--------------------------------------------------------------------------------
/client/src/assets/video-call.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanBinu007/Live-Video-Chat-Application-for-Interview-purposes/HEAD/client/src/assets/video-call.png
--------------------------------------------------------------------------------
/client/src/constants.js:
--------------------------------------------------------------------------------
1 | export const APP_NAME = 'Merge';
2 | export const APP_URL = 'https://alanbinu-video-conference.web.app';
3 | export const BACKEND_URL = 'https://video-chat-meet-backend.herokuapp.com';
4 | // export const BACKEND_URL='http://localhost:5000';
--------------------------------------------------------------------------------
/client/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/client/src/App.test.js:
--------------------------------------------------------------------------------
1 | import { render, screen } from '@testing-library/react';
2 | import App from './App';
3 |
4 | test('renders learn react link', () => {
5 | render();
6 | const linkElement = screen.getByText(/learn react/i);
7 | expect(linkElement).toBeInTheDocument();
8 | });
9 |
--------------------------------------------------------------------------------
/client/firebase.json:
--------------------------------------------------------------------------------
1 | {
2 | "hosting": {
3 | "public": "build",
4 | "ignore": [
5 | "firebase.json",
6 | "**/.*",
7 | "**/node_modules/**"
8 | ],
9 | "rewrites": [
10 | {
11 | "source": "**",
12 | "destination": "/index.html"
13 | }
14 | ]
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/client/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/client/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/client/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 | # /client
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
25 | # Local Netlify folder
26 | .netlify
--------------------------------------------------------------------------------
/client/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import "antd/dist/antd.css";
5 | import App from './App';
6 | import reportWebVitals from './reportWebVitals';
7 |
8 | ReactDOM.render(, document.getElementById('root'));
9 |
10 | // If you want to start measuring performance in your app, pass a function
11 | // to log results (for example: reportWebVitals(console.log))
12 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
13 | reportWebVitals();
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "video-chat-app",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "server.js",
6 | "scripts": {
7 | "start": "node server.js",
8 | "server": "nodemon server.js",
9 | "test": "echo \"Error: no test specified\" && exit 1"
10 | },
11 | "keywords": [],
12 | "author": "",
13 | "license": "ISC",
14 | "dependencies": {
15 | "cors": "^2.8.5",
16 | "express": "^4.17.1",
17 | "socket.io": "^4.1.2"
18 | },
19 | "devDependencies": {
20 | "nodemon": "^2.0.7"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/client/src/Components/Messages/Message.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { SocketContext } from '../../SocketContext';
3 | import './Messages.css';
4 |
5 | const Message = (props) => {
6 | const { me } = useContext(SocketContext);
7 |
8 | return (
9 |
12 |
{props.message.text}
13 |
14 | );
15 | };
16 |
17 | export default Message;
18 |
--------------------------------------------------------------------------------
/client/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "I-Meet",
3 | "name": "I-Meet",
4 | "icons": [
5 | {
6 | "src": "home.png",
7 | "type": "image/png",
8 | "sizes": "192x192"
9 | },
10 | {
11 | "src": "home2.png",
12 | "type": "image/png",
13 | "sizes": "512x512"
14 | },
15 | {
16 | "src": "home1.png",
17 | "sizes": "64x64 32x32 24x24 16x16",
18 | "type": "image/png"
19 | }
20 | ],
21 | "start_url": "/",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/client/src/assets/styles/_mixins.scss:
--------------------------------------------------------------------------------
1 | $phone-width: 576px;
2 | $tablet-width: 767px;
3 | $desktop-width: 992px;
4 | $xl-width: 1200px;
5 |
6 | @mixin mobile {
7 | @media (max-width: $phone-width) {
8 | @content;
9 | }
10 | }
11 |
12 | @mixin landscape {
13 | @media (max-width: $tablet-width) {
14 | @content;
15 | }
16 | }
17 |
18 | @mixin tablet {
19 | @media (max-width: $desktop-width) {
20 | @content;
21 | }
22 | }
23 |
24 | @mixin desktop {
25 | @media (min-width: $desktop-width) {
26 | @content;
27 | }
28 | }
29 |
30 | @mixin xl-desktop {
31 | @media (min-width: $xl-width) {
32 | @content;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/.github/workflows/Deployment.yml:
--------------------------------------------------------------------------------
1 |
2 | name: CI + CD
3 |
4 | on:
5 | workflow_dispatch:
6 |
7 | jobs:
8 | Production:
9 | runs-on: ubuntu-latest
10 | environment: Development
11 | steps:
12 | - uses: actions/checkout@v2
13 | - name: Run a script
14 | run: firebase login ${{scerets.FIREBASE_ID}} ${{scerets.FIREBASE_PASS}}
15 | firebase deploy
16 |
17 |
18 | Development:
19 | runs-on: ubuntu-latest
20 | environment: Production
21 | needs: Production
22 | steps:
23 | - uses: actions/checkout@v2
24 | - name: Run a script
25 | run: echo "Deployed"
26 |
27 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # Merge - Video Chat for Online Interviews
3 |
4 | **Project Link** - ***https://alanbinu-video-conference.web.app/***
5 |
6 | ## Features
7 |
8 | - 1:1 Video chat
9 | - Live Editor(Whiteboard) with syntax highlighting for interviews
10 | - Notes (downloadable as PDF)
11 | - Instantly join a call with a meeting link
12 | - Real time chat
13 | - Chat Notifications
14 | - Instant notifications of ongoing meeting
15 | - Mute audio/video
16 | - Neat and clean UI
17 |
18 | ## Steps to run in your machine
19 |
20 | #### Run the following commands
21 | ```
22 | npm i
23 | npm run server
24 | cd client
25 | npm i
26 | npm run start
27 | ```
28 |
29 |
30 |
31 |
32 | #### Hope you liked this project, dont forget to ⭐ the repo.
33 |
--------------------------------------------------------------------------------
/client/src/Components/Notes/Notes.scss:
--------------------------------------------------------------------------------
1 | .notes {
2 | height: 50vh;
3 | width: 40vw;
4 | outline: none;
5 | .btn-div {
6 | display: flex;
7 | justify-content: space-between;
8 | h3 {
9 | padding: 0 10px;
10 | }
11 | .flex-btns-div {
12 | width: 15%;
13 | display: flex;
14 | justify-content: space-between;
15 | padding-right:10px ;
16 | button {
17 | background: none;
18 | border: none;
19 | cursor: pointer;
20 | outline: none;
21 | }
22 | }
23 | }
24 | textarea {
25 | border: none;
26 | border-top: 0.5px solid rgb(220, 220, 220);
27 | padding: 10px;
28 | height: 90%;
29 | width: 100%;
30 | outline: none;
31 | resize: none;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/client/src/Components/Footer/Footer.css:
--------------------------------------------------------------------------------
1 | .footer {
2 | height: 5vh;
3 | display: flex;
4 | justify-content: center;
5 | align-items: center;
6 | margin: auto;
7 | }
8 |
9 | .footer p {
10 | margin: auto 0;
11 | font-size: 17px;
12 | font-weight: 500;
13 | }
14 |
15 | .footer p svg {
16 | fill: #f80303;
17 | }
18 |
19 | .footer img {
20 | height: 17px;
21 | width: 17px;
22 | z-index: 1;
23 | }
24 |
25 | .footer svg {
26 | z-index: 1;
27 | margin-top: 0px;
28 | margin-left: 30px;
29 | padding: 0px;
30 | float: right;
31 | }
32 |
33 | .footer .heartIcon span svg {
34 | font-size: 21px;
35 | fill: red;
36 | }
37 |
38 | .footer .linkIcon svg {
39 | font-size: 21px;
40 | fill: #0e76a8;
41 | }
42 |
43 | .footer .githubIcon svg {
44 | font-size: 20px;
45 | fill: black;
46 | }
47 |
--------------------------------------------------------------------------------
/client/src/common/Spinner.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from 'react';
2 | import spinner from './spin1.gif';
3 | const Spinner = (props) => {
4 | return (
5 |
6 |

20 |
21 | {props.starting ? 'Starting your meeting...' : 'Loading your video...'}
22 |
23 |
24 | );
25 | };
26 |
27 | export default Spinner;
28 |
--------------------------------------------------------------------------------
/client/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react';
2 | import './App.css';
3 | import Meet from './Components/Meet/Meet';
4 | import Join from './Components/Join/Join';
5 | import Home from './Components/Home/Home';
6 | import { ContextProvider } from './SocketContext';
7 | import {
8 | BrowserRouter as Router,
9 | Switch,
10 | Route,
11 | } from 'react-router-dom';
12 |
13 | function App() {
14 |
15 | useEffect(() => {
16 | if (!navigator.onLine) alert('Connect to internet!');
17 | }, [navigator]);
18 |
19 | return (
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | );
30 | }
31 |
32 | export default App;
33 |
--------------------------------------------------------------------------------
/client/src/Components/Notes/Notes.css:
--------------------------------------------------------------------------------
1 | .notes {
2 | height: 50vh;
3 | width: 40vw;
4 | outline: none;
5 | }
6 |
7 | .notes .btn-div {
8 | display: flex;
9 | justify-content: space-between;
10 | }
11 |
12 | .notes .btn-div h3 {
13 | padding: 0 10px;
14 | }
15 |
16 | .notes .btn-div .flex-btns-div {
17 | width: 15%;
18 | display: flex;
19 | justify-content: space-between;
20 | padding-right: 10px;
21 | }
22 |
23 | .notes .btn-div .flex-btns-div button {
24 | background: none;
25 | border: none;
26 | cursor: pointer;
27 | outline: none;
28 | }
29 |
30 | .notes textarea {
31 | border: none;
32 | border-top: 0.5px solid gainsboro;
33 | padding: 10px;
34 | height: 90%;
35 | width: 100%;
36 | outline: none;
37 | resize: none;
38 | }
39 |
40 | .notesesed textareaese {
41 | border: none;
42 | border-top: 0.5px solid gainsboro;
43 | padding: 10px;
44 | height: 90%;
45 | width: 100%;
46 | outline: none;
47 | resize: none;
48 | }
49 |
--------------------------------------------------------------------------------
/client/src/Components/Footer/Footer.scss:
--------------------------------------------------------------------------------
1 | .footer {
2 | height: 5vh;
3 | // z-index: 1;
4 | // background-color: black;
5 | display: flex;
6 | justify-content: center;
7 | align-items: center;
8 | // color: white;
9 | margin: auto;
10 | p {
11 | margin: auto 0;
12 | font-size: 17px;
13 | font-weight: 500;
14 | svg {
15 | fill: rgb(248, 3, 3);
16 | }
17 | }
18 | img {
19 | height: 17px;
20 | width: 17px;
21 | z-index: 1;
22 | }
23 | svg {
24 | z-index: 1;
25 | margin-top: 0px;
26 | margin-left: 30px;
27 | padding: 0px;
28 | float: right;
29 | }
30 | .heartIcon {
31 | span {
32 | svg {
33 | font-size: 21px;
34 | fill: red;
35 | }
36 | }
37 | }
38 | .linkIcon {
39 | svg {
40 | font-size: 21px;
41 | fill: #0e76a8;
42 | }
43 | }
44 | .githubIcon {
45 | svg {
46 | font-size: 20px;
47 | fill: black;
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/client/src/Components/Footer/Footer.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import './Footer.css';
3 | import heartIcon from '../../assets/heart.png';
4 | import githubIcon from '../../assets/github.png';
5 | import linkedIcon from '../../assets/linkedin.png';
6 | import GitHubIcon from '@material-ui/icons/GitHub';
7 | import LinkedInIcon from '@material-ui/icons/LinkedIn';
8 | import FavoriteIcon from '@material-ui/icons/Favorite';
9 | const Footer = () => {
10 | return (
11 |
12 |
13 | Made with
by Alan
14 |
{' '}
15 |
23 |
24 | );
25 | };
26 | export default Footer;
27 |
--------------------------------------------------------------------------------
/client/src/Components/Editor/Editor.css:
--------------------------------------------------------------------------------
1 | *,
2 | *::after,
3 | *::before {
4 | box-sizing: border-box;
5 | margin: 0;
6 | }
7 |
8 | body {
9 | background-color: #fafafa;
10 | margin: 0;
11 | }
12 |
13 | .editor {
14 | height: 92vh;
15 | overflow-y: scroll;
16 | overflow-x: hidden;
17 | }
18 |
19 | .editor .ql-editor {
20 | width: 9.5in;
21 | min-height: 20in;
22 | box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.2);
23 | padding: 0.4in;
24 | margin: 1rem;
25 | background-color: white;
26 | }
27 |
28 | .editor .ql-container.ql-snow {
29 | display: flex;
30 | justify-content: center;
31 | border: none;
32 | }
33 |
34 | .editor .ql-toolbar.ql-snow {
35 | display: flex;
36 | justify-content: center;
37 | position: sticky;
38 | top: 0;
39 | z-index: 1;
40 | background-color: #fafafa;
41 | box-shadow: rgba(99, 99, 99, 0.2) 0px 2px 8px 0px;
42 | }
43 |
44 | .ql-snow .ql-editor pre.ql-syntax {
45 | background-color: #f2f2f2;
46 | color: black;
47 | font-size: 16px;
48 | }
49 |
50 | @page {
51 | margin: 1in;
52 | }
53 |
--------------------------------------------------------------------------------
/client/src/Components/Editor/Editor.scss:
--------------------------------------------------------------------------------
1 | *,
2 | *::after,
3 | *::before {
4 | box-sizing: border-box;
5 | margin: 0;
6 | }
7 | body {
8 | background-color: #fafafa;
9 | margin: 0;
10 | }
11 | .editor {
12 | height: 92vh;
13 | overflow-y: scroll;
14 | overflow-x: hidden;
15 | }
16 |
17 | .editor .ql-editor {
18 | width: 9.5in;
19 | min-height: 20in;
20 | // height: auto;
21 | box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.2);
22 | padding: 0.4in;
23 | margin: 1rem;
24 | background-color: white;
25 | }
26 | .editor .ql-container.ql-snow {
27 | display: flex;
28 | justify-content: center;
29 | border: none;
30 | }
31 |
32 | .editor .ql-toolbar.ql-snow {
33 | display: flex;
34 | justify-content: center;
35 | position: sticky;
36 | top: 0;
37 | z-index: 1;
38 | background-color: #fafafa;
39 | box-shadow: rgba(99, 99, 99, 0.2) 0px 2px 8px 0px;
40 | }
41 |
42 | .ql-snow .ql-editor pre.ql-syntax {
43 | background-color: #f2f2f2;
44 | color: black;
45 | font-size: 16px;
46 | }
47 |
48 | @page {
49 | margin: 1in;
50 | }
51 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: CI + CD
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | jobs:
7 | Staging:
8 | runs-on: ubuntu-latest
9 | environment: Staging
10 | steps:
11 | - uses: actions/checkout@v2
12 | - name: Run a script
13 | run: echo "Running in Staging"
14 |
15 | Quality_Assurance:
16 | runs-on: ubuntu-latest
17 | environment: Quality_Assurance
18 | needs: Staging
19 | steps:
20 | - uses: actions/checkout@v2
21 | - name: Run a script
22 | run: echo "Running in QA"
23 |
24 | Production:
25 | runs-on: ubuntu-latest
26 | environment: Development
27 | needs: Quality_Assurance
28 | steps:
29 | - uses: actions/checkout@v2
30 | - name: Run a script
31 | run: echo "Running in production"
32 |
33 | Development:
34 | runs-on: ubuntu-latest
35 | environment: Production
36 | needs: Production
37 | steps:
38 | - uses: actions/checkout@v2
39 | - name: Run a script
40 | run: echo "Deployed"
41 |
42 |
43 |
--------------------------------------------------------------------------------
/client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "video-chat",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@material-ui/core": "^4.11.4",
7 | "@material-ui/icons": "^4.11.2",
8 | "@testing-library/jest-dom": "^5.12.0",
9 | "@testing-library/react": "^11.2.7",
10 | "@testing-library/user-event": "^12.8.3",
11 | "antd": "^4.16.0",
12 | "file-saver": "^2.0.5",
13 | "highlight.js": "^11.0.1",
14 | "jspdf": "^2.3.1",
15 | "netlify-cli": "^3.35.2",
16 | "quill": "^1.3.7",
17 | "quill-to-pdf": "^1.0.7",
18 | "react": "^17.0.2",
19 | "react-copy-to-clipboard": "^5.0.3",
20 | "react-dom": "^17.0.2",
21 | "react-router-dom": "^5.2.0",
22 | "react-scripts": "4.0.3",
23 | "simple-peer": "^9.11.0",
24 | "socket.io-client": "^4.1.2",
25 | "web-vitals": "^1.1.2"
26 | },
27 | "scripts": {
28 | "start": "SET PORT=443 && react-scripts start",
29 | "build": "react-scripts build",
30 | "test": "react-scripts test",
31 | "eject": "react-scripts eject"
32 | },
33 | "eslintConfig": {
34 | "extends": [
35 | "react-app",
36 | "react-app/jest"
37 | ]
38 | },
39 | "browserslist": {
40 | "production": [
41 | ">0.2%",
42 | "not dead",
43 | "not op_mini all"
44 | ],
45 | "development": [
46 | "last 1 chrome version",
47 | "last 1 firefox version",
48 | "last 1 safari version"
49 | ]
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/client/src/Components/Join/Join.css:
--------------------------------------------------------------------------------
1 | .join-page {
2 | padding-top: 5%;
3 | display: flex;
4 | justify-content: center;
5 | align-items: center;
6 | place-content: center;
7 | margin: auto;
8 | text-align: center;
9 | background-color: white;
10 | height: 90vh;
11 | }
12 |
13 | .join-page .video-div video {
14 | width: 50%;
15 | height: 40%;
16 | }
17 |
18 | .join-page input {
19 | margin-top: 5%;
20 | padding: 0.5rem;
21 | border: 2px solid #d1d1d1;
22 | outline: none;
23 | border-radius: 10px;
24 | width: 70%;
25 | font-size: 18px;
26 | transition-duration: 0.4s;
27 | z-index: 100;
28 | }
29 |
30 | .join-page input:focus {
31 | border-color: #2cbdc7;
32 | }
33 |
34 | .join-page .join-btns-div {
35 | width: 40%;
36 | display: flex;
37 | justify-content: space-between;
38 | text-align: center;
39 | margin: auto;
40 | padding: 1rem 0.5rem;
41 | }
42 |
43 | .join-page .join-btns-div .btn {
44 | margin: auto;
45 | padding: 0.5rem 1rem;
46 | border-radius: 10px;
47 | border: none;
48 | cursor: pointer;
49 | font-size: 17px;
50 | border: 2px solid #16c071;
51 | background-color: #0d4b85;
52 | color: white;
53 | transition-duration: 0.4s;
54 | }
55 |
56 | .join-page .join-btns-div .btn:hover {
57 | background-color: #1b8299;
58 | color: white;
59 | }
60 |
61 | @media (max-width: 576px) {
62 | .join-page .join-btns-div {
63 | width: 100%;
64 | }
65 | }
66 |
67 | @media (max-width: 576px) {
68 | .join-page .video-div video {
69 | width: 70%;
70 | height: 50%;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/client/src/Components/Navbar/Navbar.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import homeIcon from '../../assets/video-call.png';
3 | import './Navbar.css';
4 | import StarIcon from '@material-ui/icons/Star';
5 |
6 | const Navbar = () => {
7 | return (
8 |
42 | );
43 | };
44 |
45 | export default Navbar;
46 |
--------------------------------------------------------------------------------
/client/src/assets/video-call.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/src/Components/Join/Join.scss:
--------------------------------------------------------------------------------
1 | @import '../../assets/styles/_mixins.scss';
2 |
3 |
4 | .join-page {
5 | padding-top: 5%;
6 | display: flex;
7 | justify-content: center;
8 | align-items: center;
9 | place-content: center;
10 | margin: auto;
11 | text-align: center;
12 | background-color: white;
13 | height: 90vh;
14 | .video-div {
15 | video {
16 | width: 50%;
17 | height: 40%;
18 | }
19 | }
20 | input {
21 | margin-top: 5%;
22 | padding: 0.5rem;
23 | border: 2px solid rgb(209, 209, 209);
24 | outline: none;
25 | border-radius: 10px;
26 | width: 70%;
27 | font-size: 18px;
28 | transition-duration: 0.4s;
29 | &:focus {
30 | border-color: rgb(44, 189, 199);
31 | }
32 | z-index: 100;
33 | }
34 | .join-btns-div {
35 | width: 40%;
36 | display: flex;
37 | justify-content: space-between;
38 | text-align: center;
39 | margin: auto;
40 | padding: 1rem 0.5rem;
41 | .btn {
42 | margin: auto;
43 | padding: 0.5rem 1rem;
44 | border-radius: 10px;
45 | border: none;
46 | cursor: pointer;
47 | font-size: 17px;
48 | border: 2px solid rgb(22, 192, 113);
49 | background-color: rgb(13, 75, 133);
50 | color: white;
51 | transition-duration: 0.4s;
52 | &:hover {
53 | background-color: rgb(27, 130, 153);
54 | color: white;
55 | }
56 | }
57 | @include mobile {
58 | width: 100%;
59 | }
60 | }
61 | @include mobile {
62 | // margin-top: 20%;
63 | .video-div {
64 | video {
65 | width: 70%;
66 | height: 50%;
67 | }
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | const app = require('express')();
2 | const cors = require('cors');
3 | const server = require('http').createServer(app);
4 | const PORT = process.env.PORT || 5000;
5 | const io = require('socket.io')(server, {
6 | cors: {
7 | origin: '*',
8 | methods: ['GET', 'POST'],
9 | },
10 | });
11 |
12 | app.use(cors());
13 |
14 | io.on('connection', (socket) => {
15 | socket.emit('me', socket.id);
16 |
17 | socket.on('updateMyMedia', (data) => {
18 | io.to(data.userToUpdate).emit('updateUserMedia', data.data);
19 | });
20 |
21 | socket.on('calluser', ({ userToCall, from, name, signal, documentId }) => {
22 | io.to(userToCall).emit('calluser', { signal, from, name, documentId });
23 | });
24 |
25 | socket.on('answercall', (data) => {
26 | io.to(data.to).emit('updateUserMedia', {
27 | type: data.type,
28 | mediaStatus: data.mediaStatus,
29 | });
30 | io.to(data.to).emit('callaccepted', data.signal,data.name);
31 | });
32 |
33 | socket.on('send-changes', (delta, userId) => {
34 | // console.log(userId)
35 | io.to(userId).emit('recieve-changes', delta);
36 | });
37 |
38 | socket.on('send-message', (data) => {
39 | io.to(data.userToSend).emit('recieve-message', data.data);
40 | });
41 |
42 | socket.on('callended', (userToUpdate) => {
43 | io.to(userToUpdate).emit('callended');
44 | });
45 |
46 | // socket.on('disconnect', () => {
47 | // socket.broadcast.emit('callended');
48 | // });
49 |
50 | });
51 |
52 | app.get('/', (req, res) => {
53 | res.send('Server is running');
54 | });
55 |
56 | server.listen(PORT, () => {
57 | console.log(`Server is running at port ${PORT}`);
58 | });
59 |
--------------------------------------------------------------------------------
/client/src/Components/Navbar/Navbar.css:
--------------------------------------------------------------------------------
1 | @import url("https://fonts.googleapis.com/css2?family=Jua&display=swap");
2 | @import url("https://fonts.googleapis.com/css2?family=Jua&family=Skranji:wght@700&display=swap");
3 | @import url("https://fonts.googleapis.com/css2?family=Spinnaker&display=swap");
4 | .navbar {
5 | background-color: black;
6 | display: flex;
7 | justify-content: space-between;
8 | height: 8vh;
9 | padding: 0 20px;
10 | }
11 |
12 | .navbar .title-div {
13 | align-items: center;
14 | width: 40%;
15 | display: flex;
16 | justify-content: flex-start;
17 | grid-column-gap: 10px;
18 | }
19 |
20 | .navbar .title-div img {
21 | height: 70%;
22 | }
23 |
24 | .navbar .title-div h3 {
25 | font-family: 'Spinnaker', sans-serif;
26 | font-size: 25px;
27 | font-weight: 400;
28 | margin: auto 0;
29 | color: white;
30 | }
31 |
32 | .navbar .repo {
33 | display: flex;
34 | justify-content: flex-end;
35 | align-items: center;
36 | grid-column-gap: 5px;
37 | width: auto;
38 | background: none;
39 | border: none;
40 | outline: none;
41 | cursor: pointer;
42 | text-decoration: none;
43 | }
44 | .navbar .repo1 {
45 | display: flex;
46 | justify-content: flex-end;
47 | align-items: right;
48 | grid-column-gap: 5px;
49 | width: auto;
50 | background: none;
51 | border: none;
52 | outline: none;
53 | cursor: pointer;
54 | text-decoration: none;
55 | }
56 |
57 | .navbar .repo svg {
58 | fill: #fade0a;
59 | font-size: 30px;
60 | }
61 |
62 | .navbar .repo .repo-text {
63 | text-align: left;
64 | display: block;
65 | }
66 |
67 | .navbar .repo .repo-text .repo-name {
68 | font-size: 11px;
69 | color: white;
70 | padding: 0;
71 | margin: 0;
72 | }
73 |
74 | .navbar .repo .repo-text .github-name {
75 | font-size: 15px;
76 | color: white;
77 | padding: 0;
78 | margin: 0;
79 | }
80 |
--------------------------------------------------------------------------------
/client/src/Components/Navbar/Navbar.scss:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Jua&display=swap');
2 | @import url('https://fonts.googleapis.com/css2?family=Jua&family=Skranji:wght@700&display=swap');
3 | @import url('https://fonts.googleapis.com/css2?family=Spinnaker&display=swap');
4 | .navbar {
5 | // box-shadow: rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.23) 0px 3px 6px;
6 | background-color: black;
7 | display: flex;
8 | justify-content: space-between;
9 | height: 8vh;
10 | padding: 0 20px;
11 | .title-div {
12 | align-items: center;
13 | // padding: 0 20px;
14 | width: 40%;
15 | display: flex;
16 | justify-content: flex-start;
17 | grid-column-gap: 10px;
18 | img {
19 | // width: 70%;
20 | height: 70%;
21 | }
22 | h3 {
23 | // font-family: 'Jua', sans-serif;
24 | // font-family: 'Skranji', cursive;
25 | font-family: 'Spinnaker', sans-serif;
26 | font-size: 25px;
27 | font-weight: 400;
28 | // margin-left: 10px;
29 | margin: auto 0;
30 | color: white;
31 | }
32 | }
33 | .repo {
34 | display: flex;
35 | justify-content: flex-end;
36 | align-items: center;
37 | grid-column-gap: 5px;
38 | width:auto;
39 | background: none;
40 | border: none;
41 | outline: none;
42 | cursor: pointer;
43 | text-decoration: none;
44 | svg {
45 | fill: rgb(250, 222, 10);
46 | font-size: 30px;
47 | // animation: rotation 2s infinite linear;
48 | }
49 | .repo-text {
50 | text-align: left;
51 | display: block;
52 | // flex-direction: column;
53 | .repo-name {
54 | font-size: 11px;
55 | color: white;
56 | padding: 0;
57 | margin: 0;
58 | }
59 | .github-name {
60 | font-size: 15px;
61 | color: white;
62 | padding: 0;
63 | margin: 0;
64 | }
65 | }
66 | }
67 | }
68 |
69 |
--------------------------------------------------------------------------------
/client/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
33 | HAVE FUN - Video chat for Interviews
34 |
35 |
36 |
37 |
38 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/.github/workflows/docker-publish.yml:
--------------------------------------------------------------------------------
1 | name: Docker
2 |
3 | # This workflow uses actions that are not certified by GitHub.
4 | # They are provided by a third-party and are governed by
5 | # separate terms of service, privacy policy, and support
6 | # documentation.
7 |
8 | on:
9 | schedule:
10 | - cron: '41 16 * * *'
11 | push:
12 | branches: [ master ]
13 | # Publish semver tags as releases.
14 | tags: [ 'v*.*.*' ]
15 | pull_request:
16 | branches: [ master ]
17 |
18 | env:
19 | # Use docker.io for Docker Hub if empty
20 | REGISTRY: ghcr.io
21 | # github.repository as /
22 | IMAGE_NAME: ${{ github.repository }}
23 |
24 |
25 | jobs:
26 | build:
27 |
28 | runs-on: ubuntu-latest
29 | permissions:
30 | contents: read
31 | packages: write
32 |
33 | steps:
34 | - name: Checkout repository
35 | uses: actions/checkout@v2
36 |
37 | # Login against a Docker registry except on PR
38 | # https://github.com/docker/login-action
39 | - name: Log into registry ${{ env.REGISTRY }}
40 | if: github.event_name != 'pull_request'
41 | uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c
42 | with:
43 | registry: ${{ env.REGISTRY }}
44 | username: ${{ github.actor }}
45 | password: ${{ secrets.GITHUB_TOKEN }}
46 |
47 | # Extract metadata (tags, labels) for Docker
48 | # https://github.com/docker/metadata-action
49 | - name: Extract Docker metadata
50 | id: meta
51 | uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
52 | with:
53 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
54 |
55 | # Build and push Docker image with Buildx (don't push on PR)
56 | # https://github.com/docker/build-push-action
57 | - name: Build and push Docker image
58 | uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
59 | with:
60 | context: .
61 | push: ${{ github.event_name != 'pull_request' }}
62 | tags: ${{ steps.meta.outputs.tags }}
63 | labels: ${{ steps.meta.outputs.labels }}
64 |
--------------------------------------------------------------------------------
/client/src/App.css:
--------------------------------------------------------------------------------
1 | * {
2 | box-sizing: border-box;
3 | margin: 0;
4 | padding: 0;
5 | }
6 |
7 | body{
8 | /* font-family: 'Raleway', sans-serif; */
9 | }
10 | .editor::-webkit-scrollbar-track {
11 | -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
12 | border-radius: 10px;
13 | background-color: #f5f5f5;
14 | }
15 |
16 | .editor::-webkit-scrollbar {
17 | width: 8px;
18 | background-color: #f5f5f5;
19 | }
20 |
21 | .editor::-webkit-scrollbar-thumb {
22 | border-radius: 10px;
23 | -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
24 | background-color: #b5b5b5;
25 | }
26 | .editor::-webkit-scrollbar-thumb:hover {
27 | background-color: #5c5c5c;
28 | }
29 | .tooltip {
30 | position: relative;
31 | display: inline-block;
32 | border-bottom: 1px dotted black;
33 | }
34 |
35 | .tooltip .tooltiptext {
36 | visibility: hidden;
37 | width: 80px;
38 | background-color: rgb(55, 55, 55);
39 | color: #fff;
40 | text-align: center;
41 | border-radius: 6px;
42 | padding: 5px 0;
43 | position: absolute;
44 | z-index: 1;
45 | bottom: 150%;
46 | left: 50%;
47 | margin-left: -40px;
48 | }
49 |
50 | .tooltip .tooltiptext::after {
51 | content: '';
52 | position: absolute;
53 | top: 100%;
54 | left: 50%;
55 | margin-left: -5px;
56 | border-width: 5px;
57 | border-style: solid;
58 | border-color: black transparent transparent transparent;
59 | }
60 |
61 | .tooltip:hover .tooltiptext {
62 | transition-duration: 1s;
63 | visibility: visible;
64 | }
65 |
66 | .slide-left {
67 | animation: 0.5s slide-left;
68 | }
69 | @keyframes slide-left {
70 | from {
71 | margin-left: 100%;
72 | }
73 | to {
74 | margin-left: 0%;
75 | }
76 | }
77 |
78 | .slide-right {
79 | animation: 1s slide-right;
80 | }
81 | @keyframes slide-right {
82 | from {
83 | margin-left: -100%;
84 | }
85 | to {
86 | margin-left: 0%;
87 | }
88 | }
89 |
90 | .rotate {
91 | animation: rotation 5s infinite linear;
92 | }
93 |
94 | @keyframes rotation {
95 | from {
96 | transform: rotate(0deg);
97 | }
98 | to {
99 | transform: rotate(359deg);
100 | }
101 | }
--------------------------------------------------------------------------------
/client/src/Components/Messages/Messages.css:
--------------------------------------------------------------------------------
1 | .outer-div {
2 | height: 40vh;
3 | width: 30vw;
4 | }
5 |
6 | @media (max-width: 576px) {
7 | .outer-div {
8 | height: 60vh;
9 | width: 70vw;
10 | }
11 | }
12 |
13 | .btn-div {
14 | display: flex;
15 | justify-content: space-between;
16 | }
17 |
18 | .btn-div h3 {
19 | padding: 0 10px;
20 | }
21 |
22 | .btn-div button {
23 | background-color: white;
24 | border: none;
25 | cursor: pointer;
26 | }
27 |
28 | .messages {
29 | overflow-y: scroll;
30 | padding: 0 10px;
31 | width: 100%;
32 | height: 100%;
33 | flex: auto;
34 | overflow: auto;
35 | }
36 |
37 | .messages h3 {
38 | text-align: center;
39 | }
40 |
41 | .message-div {
42 | display: flex;
43 | margin-top: 10px;
44 | word-wrap: break-word;
45 | }
46 |
47 | .message {
48 | display: flex;
49 | max-width: 60%;
50 | padding: 3px 10px;
51 | border-radius: 10px;
52 | word-wrap: break-word;
53 | overflow-x: hidden;
54 | border: 1.5px solid #888888;
55 | font-weight: 500;
56 | }
57 |
58 | .bg-dark {
59 | color: white;
60 | background-color: #33b0dd;
61 | }
62 |
63 | .tr {
64 | justify-content: flex-end;
65 | }
66 |
67 | .tl {
68 | justify-content: flex-start;
69 | }
70 |
71 | .inputs {
72 | display: flex;
73 | justify-content: space-between;
74 | align-items: center;
75 | margin-top: 20px;
76 | }
77 |
78 | .inputs input {
79 | width: 80%;
80 | border: 1px solid grey;
81 | outline: none;
82 | border-radius: 10px;
83 | padding: 5px;
84 | }
85 |
86 | @media (max-width: 576px) {
87 | .inputs input {
88 | width: 70%;
89 | }
90 | }
91 |
92 | .scrollbar {
93 | overflow: auto;
94 | }
95 |
96 | .scrollbar::-webkit-scrollbar {
97 | width: 7px;
98 | height: 6px;
99 | }
100 |
101 | .scrollbar::-webkit-scrollbar-thumb {
102 | border-radius: 5px;
103 | -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.1);
104 | background: rgba(0, 0, 0, 0.2);
105 | }
106 |
107 | .scrollbar-primary::-webkit-scrollbar {
108 | scroll-margin: auto;
109 | }
110 |
111 | .scrollbar-primary {
112 | scrollbar-color: #76c5f3;
113 | }
114 |
115 | .scrollbar-primary::-webkit-scrollbar-thumb {
116 | border-radius: 10px;
117 | -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.1);
118 | background-color: #5ebdfc;
119 | }
120 |
--------------------------------------------------------------------------------
/client/src/Components/Options/Options.css:
--------------------------------------------------------------------------------
1 | .options {
2 | background-color: black;
3 | height: 10vh;
4 | margin: 0 auto;
5 | padding: 0 10px;
6 | display: flex;
7 | align-items: center;
8 | justify-content: space-around;
9 | outline: none;
10 | border-top-left-radius: 25px;
11 | border-top-right-radius: 25px;
12 | border-bottom-left-radius: 0px;
13 | border-bottom-right-radius: 0px;
14 | }
15 |
16 | .options button {
17 | display: flex;
18 | align-items: center;
19 | border-radius: 50%;
20 | outline: none;
21 | border: none;
22 | padding: 10px;
23 | cursor: pointer;
24 | transition-duration: 0.4s;
25 | box-shadow: rgba(60, 64, 67, 0.3) 0px 1px 2px 0px, rgba(60, 64, 67, 0.15) 0px 1px 3px 1px;
26 | }
27 |
28 | .options .bg-grey {
29 | background-color: #8f8f8f;
30 | }
31 |
32 | .options .red-btn {
33 | border-radius: 35%;
34 | padding: 7px 15px;
35 | background-color: #c20505;
36 | }
37 |
38 | .options .red-btn svg {
39 | fill: white;
40 | }
41 |
42 | .options .red-btn:hover {
43 | background-color: #e44646;
44 | }
45 |
46 | @media (max-width: 576px) {
47 | .options {
48 | position: fixed;
49 | bottom: 0;
50 | }
51 | }
52 |
53 | .options-menu {
54 | padding: 0 1rem;
55 | display: block;
56 | text-align: center;
57 | outline: none;
58 | }
59 |
60 | .options-menu div {
61 | padding: 0.5rem;
62 | }
63 |
64 | .options-menu input {
65 | border-radius: 10px;
66 | border: 1px solid #9e9e9e;
67 | outline: none;
68 | padding: 10px;
69 | }
70 |
71 | .options-menu .btn-div {
72 | display: flex;
73 | justify-content: space-between;
74 | }
75 |
76 | .options-menu .btn-div h3 {
77 | padding: 0 10px;
78 | }
79 |
80 | .options-menu .btn-div button {
81 | background-color: white;
82 | border: none;
83 | cursor: pointer;
84 | }
85 |
86 | .w100 {
87 | border-radius: 25px;
88 | width: 45%;
89 | }
90 |
91 | @media (max-width: 576px) {
92 | .w100 {
93 | width: 100%;
94 | border-top-left-radius: 25px;
95 | border-top-right-radius: 25px;
96 | border-bottom-left-radius: 0px;
97 | border-bottom-right-radius: 0px;
98 | }
99 | }
100 |
101 | .call-div p {
102 | font-size: 20px;
103 | }
104 |
105 | .call-div .flex {
106 | display: flex;
107 | justify-content: space-between;
108 | align-items: center;
109 | }
110 |
--------------------------------------------------------------------------------
/client/src/common/AntSwitch.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { withStyles } from '@material-ui/core/styles';
3 | import Switch from '@material-ui/core/Switch';
4 |
5 | const AntSwitch = withStyles((theme) => ({
6 | root: {
7 | width: 44,
8 | height: 24,
9 | padding: 0,
10 | margin: theme.spacing(1),
11 | },
12 | switchBase: {
13 | padding: 1,
14 | '&$checked': {
15 | transform: 'translateX(20px)',
16 | color: theme.palette.common.white,
17 | '& + $track': {
18 | backgroundColor: '#52d869',
19 | opacity: 1,
20 | border: 'none',
21 | },
22 | },
23 | '&$focusVisible $thumb': {
24 | color: '#52d869',
25 | border: '6px solid #fff',
26 | },
27 | },
28 | thumb: {
29 | width: 22,
30 | height: 22,
31 | },
32 | track: {
33 | borderRadius: 24 / 2,
34 | border: `1px solid ${theme.palette.grey[400]}`,
35 | backgroundColor: theme.palette.grey[80],
36 | opacity: 1,
37 | transition: theme.transitions.create(['background-color', 'border']),
38 | },
39 | checked: {},
40 | focusVisible: {},
41 | }))(({ classes, ...props }) => {
42 | return (
43 |
55 | );
56 | });
57 | // const AntSwitch = withStyles((theme) => ({
58 | // root: {
59 | // width: 42,
60 | // height: 26,
61 | // padding: 0,
62 | // display: 'flex',
63 | // },
64 | // switchBase: {
65 | // padding: 1,
66 | // color: theme.palette.grey[500],
67 | // '&$checked': {
68 | // transform: 'translateX(12px)',
69 | // color: theme.palette.common.white,
70 | // '& + $track': {
71 | // opacity: 1,
72 | // backgroundColor: theme.palette.primary.main,
73 | // borderColor: theme.palette.primary.main,
74 | // },
75 | // },
76 | // },
77 | // thumb: {
78 | // width: 24,
79 | // height: 24,
80 | // boxShadow: 'none',
81 | // },
82 | // track: {
83 | // border: `1px solid ${theme.palette.grey[500]}`,
84 | // borderRadius: 26 / 2,
85 | // opacity: 1,
86 | // backgroundColor: theme.palette.common.white,
87 | // },
88 | // checked: {},
89 | // }))(Switch);
90 |
91 | export default AntSwitch;
92 |
--------------------------------------------------------------------------------
/client/src/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | #
12 | name: "CodeQL"
13 |
14 | on:
15 | push:
16 | branches: [ master ]
17 | pull_request:
18 | # The branches below must be a subset of the branches above
19 | branches: [ master ]
20 | schedule:
21 | - cron: '16 6 * * 0'
22 |
23 | jobs:
24 | analyze:
25 | name: Analyze
26 | runs-on: ubuntu-latest
27 | permissions:
28 | actions: read
29 | contents: read
30 | security-events: write
31 |
32 | strategy:
33 | fail-fast: false
34 | matrix:
35 | language: [ 'javascript' ]
36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
37 | # Learn more about CodeQL language support at https://git.io/codeql-language-support
38 |
39 | steps:
40 | - name: Checkout repository
41 | uses: actions/checkout@v2
42 |
43 | # Initializes the CodeQL tools for scanning.
44 | - name: Initialize CodeQL
45 | uses: github/codeql-action/init@v1
46 | with:
47 | languages: ${{ matrix.language }}
48 | # If you wish to specify custom queries, you can do so here or in a config file.
49 | # By default, queries listed here will override any specified in a config file.
50 | # Prefix the list here with "+" to use these queries and those in the config file.
51 | # queries: ./path/to/local/query, your-org/your-repo/queries@main
52 |
53 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
54 | # If this step fails, then you should remove it and run the build manually (see below)
55 | - name: Autobuild
56 | uses: github/codeql-action/autobuild@v1
57 |
58 | # ℹ️ Command-line programs to run using the OS shell.
59 | # 📚 https://git.io/JvXDl
60 |
61 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
62 | # and modify them (or add more) to build your code if your project
63 | # uses a compiled language
64 |
65 | #- run: |
66 | # make bootstrap
67 | # make release
68 |
69 | - name: Perform CodeQL Analysis
70 | uses: github/codeql-action/analyze@v1
71 |
--------------------------------------------------------------------------------
/client/src/Components/Options/Options.scss:
--------------------------------------------------------------------------------
1 | @import '../../assets/styles/_mixins.scss';
2 | .options {
3 | background-color: rgb(0, 0, 0);
4 | // width: 30%;
5 | height: 10vh;
6 | margin: 0 auto;
7 | // border-radius: 10px;
8 | padding: 0 10px;
9 | display: flex;
10 | align-items: center;
11 | justify-content: space-around;
12 | // position: fixed;
13 | // bottom: 0;
14 | outline: none;
15 | border-top-left-radius: 25px;
16 | border-top-right-radius: 25px;
17 | border-bottom-left-radius: 0px;
18 | border-bottom-right-radius: 0px;
19 | button {
20 | display: flex;
21 | align-items: center;
22 | border-radius: 50%;
23 | outline: none;
24 | border: none;
25 | padding: 10px;
26 | cursor: pointer;
27 | transition-duration: 0.4s;
28 | box-shadow: rgba(60, 64, 67, 0.3) 0px 1px 2px 0px,
29 | rgba(60, 64, 67, 0.15) 0px 1px 3px 1px;
30 | // &:hover {
31 | // background-color: rgb(221, 221, 221);
32 | // }
33 | }
34 | .bg-white {
35 | // background-color: rgb(221, 221, 221);
36 | }
37 | .bg-grey {
38 | background-color: rgb(143, 143, 143);
39 | }
40 | .red-btn {
41 | border-radius: 35%;
42 | padding: 7px 15px;
43 | background-color: rgb(194, 5, 5);
44 | svg {
45 | fill: white;
46 | }
47 | &:hover {
48 | background-color: rgb(228, 70, 70);
49 | }
50 | }
51 | @include mobile {
52 | position: fixed;
53 | bottom: 0;
54 | }
55 | }
56 | .options-menu {
57 | padding: 0 1rem;
58 | display: block;
59 | text-align: center;
60 | outline: none;
61 | div {
62 | padding: 0.5rem;
63 | }
64 | input {
65 | border-radius: 10px;
66 | border: 1px solid rgb(158, 158, 158);
67 | outline: none;
68 | padding: 10px;
69 | }
70 | .btn-div {
71 | display: flex;
72 | justify-content: space-between;
73 | h3 {
74 | padding: 0 10px;
75 | }
76 | button {
77 | background-color: white;
78 | border: none;
79 | cursor: pointer;
80 | // box-shadow: rgba(100, 100, 111, 0.2) 0px 7px 29px 0px;
81 | }
82 | }
83 | }
84 | .w100 {
85 | border-radius: 25px;
86 |
87 | width: 45%;
88 | @include mobile {
89 | width: 100%;
90 | border-top-left-radius: 25px;
91 | border-top-right-radius: 25px;
92 | border-bottom-left-radius: 0px;
93 | border-bottom-right-radius: 0px;
94 | }
95 | }
96 |
97 | .call-div {
98 | p {
99 | font-size: 20px;
100 | }
101 | .flex {
102 | display: flex;
103 | justify-content: space-between;
104 | align-items: center;
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/client/src/Components/Notes/Notes.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect, useCallback, useContext } from 'react';
2 | import { message } from 'antd';
3 | import { SocketContext } from '../../SocketContext';
4 | import EventNoteIcon from '@material-ui/icons/EventNote';
5 | import './Notes.css';
6 | import CloseIcon from '@material-ui/icons/Close';
7 | import GetAppIcon from '@material-ui/icons/GetApp';
8 | import Menu from '@material-ui/core/Menu';
9 | import { jsPDF } from 'jspdf';
10 |
11 | const Notes = () => {
12 | const { notes, setNotes } = useContext(SocketContext);
13 | const [anchorEl, setAnchorEl] = useState(null);
14 | const [mobileView, setMobileView] = useState(false);
15 |
16 | const resize = () => {
17 | setMobileView(window.innerWidth <= 600);
18 | };
19 |
20 | useEffect(() => {
21 | resize();
22 | window.addEventListener('resize', resize);
23 | }, []);
24 |
25 | const handleClick = (event) => {
26 | setAnchorEl(event.currentTarget);
27 | };
28 |
29 | const handleClose = () => {
30 | setAnchorEl(null);
31 | };
32 | const downLoadAsPdf = () => {
33 | if (notes.trim().length == 0) {
34 | message.error('Please write some text to download');
35 | return;
36 | }
37 | const pdfDoc = new jsPDF();
38 | pdfDoc.text(notes, 10, 10);
39 | message.success('Your notes is downloading');
40 | pdfDoc.save('meet notes.pdf');
41 | };
42 |
43 | return (
44 | <>
45 | {!mobileView && (
46 |
50 | )}
51 |
83 | >
84 | );
85 | };
86 |
87 | export default Notes;
88 |
--------------------------------------------------------------------------------
/client/src/Components/Messages/Messages.scss:
--------------------------------------------------------------------------------
1 | @import '../../assets/styles/_mixins.scss';
2 | .outer-div {
3 | height: 40vh;
4 | width: 30vw;
5 | // background-color: black;
6 | @include mobile {
7 | height: 60vh;
8 | width: 70vw;
9 | }
10 | }
11 | .btn-div {
12 | display: flex;
13 | justify-content: space-between;
14 | h3 {
15 | padding: 0 10px;
16 | }
17 | button {
18 | background-color: white;
19 | border: none;
20 | cursor: pointer;
21 | // box-shadow: rgba(100, 100, 111, 0.2) 0px 7px 29px 0px;
22 | }
23 | }
24 | .messages {
25 | overflow-y: scroll;
26 | // background-color: black;
27 | // display: flex;
28 | // align-items: center;
29 | // justify-content: center;
30 | padding: 0 10px;
31 | width: 100%;
32 | height: 100%;
33 | h3 {
34 | text-align: center;
35 | }
36 | flex: auto;
37 | overflow: auto;
38 | }
39 | .message-div {
40 | display: flex;
41 | margin-top: 10px;
42 | word-wrap: break-word;
43 | }
44 | .message {
45 | display: flex;
46 | max-width: 60%;
47 | // background-color: rgb(14, 144, 184);
48 | // color: white;
49 | padding: 3px 10px;
50 | border-radius: 10px;
51 | word-wrap: break-word;
52 | overflow-x: hidden;
53 | border: 1.5px solid rgb(136, 136, 136);
54 | font-weight: 500;
55 | }
56 | .bg-dark {
57 | color: white;
58 | background-color: rgb(51, 176, 221);
59 | }
60 | .tr {
61 | // text-align: right;
62 | justify-content: flex-end;
63 | }
64 | .tl {
65 | // text-align: left;
66 |
67 | justify-content: flex-start;
68 | }
69 | .inputs {
70 | // width: 30vw;
71 | display: flex;
72 | justify-content: space-between;
73 | align-items: center;
74 | margin-top: 20px;
75 | input {
76 | width: 80%;
77 | border: 1px solid grey;
78 | outline: none;
79 | border-radius: 10px;
80 | padding: 5px;
81 | }
82 | // Button {
83 | // margin-left: 1vw;
84 | // }
85 | // background-color: black;
86 | @include mobile {
87 | input {
88 | width: 70%;
89 | }
90 | // Button {
91 | // margin-left: 2vw;
92 | // }
93 | }
94 | }
95 | .scrollbar {
96 | overflow: auto;
97 | }
98 |
99 | .scrollbar::-webkit-scrollbar {
100 | width: 7px;
101 | height: 6px;
102 | }
103 |
104 | .scrollbar::-webkit-scrollbar-thumb {
105 | border-radius: 5px;
106 | -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.1);
107 | background: rgba(0, 0, 0, 0.2);
108 | }
109 |
110 | .scrollbar-primary::-webkit-scrollbar {
111 | scroll-margin: auto;
112 | }
113 | .scrollbar-primary {
114 | scrollbar-color: #76c5f3;
115 | }
116 | .scrollbar-primary::-webkit-scrollbar-thumb {
117 | border-radius: 10px;
118 | -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.1);
119 | background-color: #5ebdfc;
120 | }
121 |
--------------------------------------------------------------------------------
/client/src/Components/Editor/Editor.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect, useCallback, useContext } from 'react';
2 | import 'quill/dist/quill.snow.css';
3 | import Quill from 'quill';
4 | import './Editor.css';
5 | import { SocketContext } from '../../SocketContext';
6 | import hljs from 'highlight.js';
7 | import 'highlight.js/styles/github.css';
8 |
9 | var toolbarOptions = [
10 | ['bold', 'italic', 'underline', 'strike'],
11 | ['blockquote', 'code-block'],
12 | [{ header: 1 }, { header: 2 }],
13 | [{ list: 'ordered' }, { list: 'bullet' }],
14 | [{ script: 'sub' }, { script: 'super' }],
15 | [{ indent: '-1' }, { indent: '+1' }],
16 | [{ direction: 'rtl' }],
17 | [{ size: ['small', false, 'large', 'huge'] }],
18 | [{ header: [1, 2, 3, 4, 5, 6, false] }],
19 | [{ color: [] }, { background: [] }],
20 | [{ font: [] }],
21 | [{ align: [] }],
22 | ['clean'],
23 | ];
24 |
25 | hljs.configure({
26 | languages: [
27 | 'c++',
28 | 'java',
29 | 'javascript',
30 | 'ruby',
31 | 'python',
32 | 'swift',
33 | 'golang',
34 | 'html',
35 | ],
36 | });
37 |
38 | const Editor = () => {
39 | const {
40 | otherUser,
41 | socketState: socket,
42 | quill,
43 | setQuill,
44 | } = useContext(SocketContext);
45 |
46 | useEffect(() => {
47 | if (!socket || !quill || !otherUser) return;
48 | const handler = (delta, oldDelta, source) => {
49 | if (source !== 'user') return;
50 | socket.emit('send-changes', delta, otherUser);
51 | };
52 |
53 | quill.on('text-change', handler);
54 | return () => {
55 | quill.off('text-change', handler);
56 | };
57 | }, [socket, quill]);
58 |
59 | useEffect(() => {
60 | if (!socket || !quill) return;
61 | const handler = (delta) => {
62 | // console.log(delta)
63 | quill.updateContents(delta);
64 | };
65 |
66 | socket.on('recieve-changes', handler);
67 | return () => {
68 | socket.off('recieve-changes', handler);
69 | };
70 | }, [socket, quill]);
71 |
72 | const editorRef = useCallback((editorWrapper) => {
73 | if (editorWrapper === null) return;
74 |
75 | editorWrapper.innerHTML = '';
76 | const editor = document.createElement('div');
77 | editorWrapper.append(editor);
78 |
79 | const q = new Quill(editor, {
80 | theme: 'snow',
81 | modules: {
82 | syntax: {
83 | highlight: (text) => hljs.highlightAuto(text).value,
84 | },
85 | toolbar: toolbarOptions,
86 | },
87 | });
88 | if (quill) q.setContents(quill.getContents());
89 | setQuill(q);
90 | }, []);
91 |
92 | return (
93 | <>
94 |
95 | >
96 | );
97 | };
98 |
99 | export default Editor;
100 |
--------------------------------------------------------------------------------
/client/src/Components/Messages/Messages.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect, useRef, useContext } from 'react';
2 | import { Button, message, notification } from 'antd';
3 | import { SocketContext } from '../../SocketContext';
4 | import Message from './Message';
5 | import DialogContent from '@material-ui/core/DialogContent';
6 | import DialogTitle from '@material-ui/core/DialogTitle';
7 | import Dialog from '@material-ui/core/Dialog';
8 | import { UserOutlined, MessageOutlined } from '@ant-design/icons';
9 | import CloseIcon from '@material-ui/icons/Close';
10 | import './Messages.css';
11 |
12 | const Messages = () => {
13 | const {
14 | me,
15 | otherUser,
16 | socketState: socket,
17 | showChatBox,
18 | setShowChatBox,
19 | messages,
20 | setMessages,
21 | otherUserName,
22 | } = useContext(SocketContext);
23 |
24 | const [newMessage, setNewMessage] = useState('');
25 | const msgRef = useRef();
26 |
27 | useEffect(() => {
28 | if (msgRef.current) msgRef.current.scrollIntoView({ behavior: 'smooth' });
29 | }, [messages, showChatBox]);
30 |
31 | useEffect(() => {
32 | socket.on('recieve-message', (data) => {
33 | setMessages((messages) => [...messages, data]);
34 | notification.open({
35 | message: `New Message`,
36 | description: data.text,
37 | icon: ,
38 | });
39 | });
40 | }, []);
41 |
42 | const sendMessage = () => {
43 | if (newMessage.trim().length <= 0) {
44 | message.error('Enter some message');
45 | return;
46 | }
47 |
48 | let tempMessage = { text: newMessage.trim(), user: me };
49 | socket.emit('send-message', {
50 | data: tempMessage,
51 | userToSend: otherUser,
52 | });
53 | setMessages((messages) => [...messages, tempMessage]);
54 | setNewMessage('');
55 | };
56 |
57 | const handleClose = () => {
58 | setShowChatBox(!showChatBox);
59 | };
60 |
61 | const handleKeypress = (e) => {
62 | if (e.key === 'Enter') {
63 | sendMessage();
64 | }
65 | };
66 |
67 | return (
68 |
116 | );
117 | };
118 |
119 | export default Messages;
120 |
--------------------------------------------------------------------------------
/client/src/Components/Join/Join.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useEffect, useRef, useParams } from 'react';
2 | import { SocketContext } from '../../SocketContext';
3 | import './Join.css';
4 | import homeIcon from '../../assets/video-call.png';
5 | import { message } from 'antd';
6 | import Spinner from '../../common/Spinner';
7 | import Navbar from '../Navbar/Navbar';
8 |
9 | const Join = (props) => {
10 | const {
11 | callAccepted,
12 | name,
13 | setName,
14 | stream,
15 | setStream,
16 | callUser,
17 | meetingCode,
18 | setMeetingCode,
19 | newMeet,
20 | } = useContext(SocketContext);
21 |
22 | const myPreviewVideo = useRef();
23 |
24 | useEffect(() => {
25 | if (!newMeet && meetingCode.length === 0) {
26 | props.history.push('/');
27 | window.location.reload();
28 | return;
29 | }
30 | // if (stream) return;
31 | navigator.mediaDevices
32 | .getUserMedia({ video: true, audio: true })
33 | .then((res) => {
34 | res.getAudioTracks()[0].enabled = false;
35 | setStream(res);
36 | myPreviewVideo.current.srcObject = res;
37 | });
38 | }, []);
39 |
40 | useEffect(() => {
41 | if (callAccepted) props.history.push('meet');
42 | }, [callAccepted]);
43 |
44 | return (
45 | <>
46 |
47 |
48 |
49 |
50 | {stream ? (
51 |
59 | ) : (
60 |
61 | )}
62 |
63 | {stream && (
64 | <>
65 |
{
70 | setName(e.target.value);
71 | }}
72 | />
73 |
74 | {newMeet ? (
75 |
87 | ) : (
88 |
101 | )}
102 |
112 |
113 | >
114 | )}
115 |
116 |
117 | >
118 | );
119 | };
120 |
121 | export default Join;
122 |
--------------------------------------------------------------------------------
/client/.firebase/hosting.YnVpbGQ.cache:
--------------------------------------------------------------------------------
1 | favicon.ico,1624087441114,eae62e993eb980ec8a25058c39d5a51feab118bd2100c4deebb2a9c158ec11f9
2 | home.png,1624091115711,f49fe8f2057688117206217c187e7e5e0d9f4861b2271d447f3e77a762452728
3 | home2.png,1624090948989,c801d57a778f77d5ed3e7b04ff5f44ad3d9a2278d2e56a1bf59025aef1b1414c
4 | logo192.png,1624087441116,3ee59515172ee198f3be375979df15ac5345183e656720a381b8872b2a39dc8b
5 | logo512.png,1624087441117,ee7e2f3fdb8209c4b6fd7bef6ba50d1b9dba30a25bb5c3126df057e1cb6f5331
6 | manifest.json,1624087441117,77352fa742d2351ca24581f9edcd64ee3baaf6dd64439ecfe33fda8b293beb54
7 | robots.txt,1624087441117,b2090cf9761ef60aa06e4fab97679bd43dfa5e5df073701ead5879d7c68f1ec5
8 | asset-manifest.json,1624095319782,9f767d766cbb8ba9c57cf01541f2bf14ccc5a9667eacac4e9fd1e13dc7dcd230
9 | index.html,1624095319777,15613e99befc850c7940475ffaa0fe257bd189e43d55ee8208e3a24cdaa8b88a
10 | static/css/main.c8284328.chunk.css,1624095319629,2bed5b0b5f564a28132648537d48f5b8215733b774a0556a78afdfc6d2f219e7
11 | static/css/main.c8284328.chunk.css.map,1624095319782,f207ebd4bea8dbe5dd69aad187843a7208a2f98bb5b7bec4e5bc075f17fd1a6b
12 | static/js/2.959319c6.chunk.js.LICENSE.txt,1624095319698,2156f4f4f335b7aa81dd6b9da6d7f3ac6e9e0234fd6ebab58153c31a77161a0f
13 | static/js/3.4bcb59e0.chunk.js.LICENSE.txt,1624095319696,032941c73baec853976fe02a909679d0c41df718ee09c9eb999c0c49194e7a99
14 | static/js/4.e010d17e.chunk.js,1624095319696,14176053753787c4a515490b4aac5d58e5ec4b50bc5a6d119c79130249da43fd
15 | static/js/4.e010d17e.chunk.js.LICENSE.txt,1624095319702,d3e1d351b31973e51a901c7ea2d4a4a4433ea7ca26d26932b5764f978f862608
16 | static/js/5.cd28be83.chunk.js.LICENSE.txt,1624095319777,2c3fbd65f07d4843d4df16f571044f77371ac610e00da02f5ea1364dd089f79b
17 | static/js/6.3ad96b1b.chunk.js,1624095319696,6830d00ccda5728e2321eb60ee120800a63b018fa79441992a25cae2761e01f0
18 | static/js/6.3ad96b1b.chunk.js.map,1624095320138,1bf45966ef154bebfa923bfc69eefbe150cabf1323fdb7ccf8acc9b3880ba766
19 | static/js/runtime-main.7fa185e7.js,1624095319692,554ccc5a173403d900af84cb5b49caacdb7ac5716312e8aa56ce3a733fd3f2fc
20 | static/js/runtime-main.7fa185e7.js.map,1624095319782,5fdbdeb844fb9f677fd2b568a5080d87f506c1d7e594a3a101e190431e2aceec
21 | static/js/main.01b07354.chunk.js,1624095319630,be4c7952152a2229ab1a1bd6eee9da500575d1c6b7f511e24bb1d768e5fc40ac
22 | static/media/spin1.86646140.gif,1624095319603,bbd4735fa9a16121fa3f54cb2e7b007a40e4679355f3db473c1c8113bf7144c0
23 | static/media/video-call.7f003a75.png,1624095319692,c801d57a778f77d5ed3e7b04ff5f44ad3d9a2278d2e56a1bf59025aef1b1414c
24 | static/js/4.e010d17e.chunk.js.map,1624095319790,612b7ca04eaf980ef4fe7addf3b39602a8a2f23f6848c5e25c7baccae6c1ebd9
25 | static/media/home.5bb1b273.png,1624095319692,f49fe8f2057688117206217c187e7e5e0d9f4861b2271d447f3e77a762452728
26 | static/js/main.01b07354.chunk.js.map,1624095319782,ba98fa8dc27463832b3e47fcae05b5b9938aa76e133178e4b532d6f45f302c9a
27 | static/js/5.cd28be83.chunk.js,1624095319698,80b71efcd7ad5ee0ea4d9236e7e402e184243545537ef92a27d32e88d360f332
28 | static/js/3.4bcb59e0.chunk.js,1624095319693,423abc36ac476d823daf74c3568b37978e842c12a3adaed5b85bca0931d499e4
29 | static/css/2.51adceda.chunk.css,1624095319692,50b05019ae731fa3e63e74d75cf164d10551a9b027b49821de1028bcc23dd28c
30 | static/js/3.4bcb59e0.chunk.js.map,1624095319789,f95ea1abee4e89f5be6cc16b819f86ae1a10d02671aa95409bb610ba63726650
31 | static/js/5.cd28be83.chunk.js.map,1624095320138,79eb7155b1d2bce1c315d9301c1875c8c359064b4fe27dfda2c4e48222606a06
32 | static/media/homeVideo1.ac66ac2f.mp4,1624095319703,3543e48ff26e8749f0bbb8bacef438ba2ca856796feccaf414da2b386be2a67e
33 | static/css/2.51adceda.chunk.css.map,1624095319783,279d30715d3a6bff164454b9edbac6c9a51bea116d23bbacf90bb539dc26be62
34 | static/js/2.959319c6.chunk.js,1624095319697,b0e704a901c3ebca8959ad9ef0df7bafe58ab56abf131720f5517e273178aa44
35 | static/js/2.959319c6.chunk.js.map,1624095319789,4a1466732fb278008223a6c197b9b6a2762b549f2f50d7ff2542cf132d7241f9
36 |
--------------------------------------------------------------------------------
/client/src/Components/Home/Home.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useState, useEffect } from 'react';
2 | import { SocketContext } from '../../SocketContext';
3 | import './Home.css';
4 | import Footer from '../Footer/Footer';
5 | import homeIcon from '../../assets/video-call.png';
6 | import homeIcon1 from '../../assets/home.png';
7 | import noteIcon from '../../assets/note2.png';
8 | import homeVideo from '../../assets/homeVideo1.mp4';
9 | import ChatIcon from '@material-ui/icons/Chat';
10 | import EventNoteIcon from '@material-ui/icons/EventNote';
11 | import SurroundSoundIcon from '@material-ui/icons/SurroundSound';
12 | import DuoIcon from '@material-ui/icons/Duo';
13 | import { Link } from 'react-router-dom';
14 | import { message } from 'antd';
15 | import Navbar from '../Navbar/Navbar';
16 |
17 | const Home = (props) => {
18 | const paramsCode = props.location.search;
19 |
20 | const { meetingCode, setMeetingCode, setNewMeet } = useContext(SocketContext);
21 |
22 | useEffect(() => {
23 | if (paramsCode.length) {
24 | if (paramsCode.substring(0, 5) == '?ref=') return; // for product hunt ref
25 | setMeetingCode(paramsCode.substring(1));
26 | }
27 | setNewMeet(null);
28 | }, []);
29 |
30 | return (
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
Welcome Friends
40 |
Its Free and Open to all
41 |
42 | {
46 | setNewMeet(true);
47 | }}
48 | >
49 | Start Meeting
50 |
51 |
52 |
53 |
54 | {
59 | setMeetingCode(e.target.value);
60 | }}
61 | />
62 |
74 |
75 |
76 |
Features
77 |
78 |
79 |
1:1 Video chat
80 |
81 |
82 |
83 |
Live Editor for interview
84 |
85 |
89 |
90 |
91 |
Real time Chat
92 |
93 |
94 |

95 |

96 |
97 |
98 |
99 | {/*

*/}
100 |
108 |
109 |
110 |
111 |
112 |
113 | );
114 | };
115 |
116 | export default Home;
117 |
--------------------------------------------------------------------------------
/client/src/Components/Home/Home.css:
--------------------------------------------------------------------------------
1 | .home {
2 | position: relative;
3 | overflow: hidden;
4 | background-color: white;
5 | }
6 |
7 | .home .body-div {
8 | overflow: hidden;
9 | height: 87vh;
10 | }
11 |
12 | .home .body-div .flex-box {
13 | display: flex;
14 | }
15 |
16 | .home .body-div .flex-box .left-div {
17 | display: flex;
18 | align-items: center;
19 | justify-content: center;
20 | width: 40%;
21 | align-items: center;
22 | }
23 |
24 | .home .body-div .flex-box .left-div .contents {
25 | position: absolute;
26 | width: 40%;
27 | margin-left: 30%;
28 | align-items: center;
29 | }
30 |
31 | .home .body-div .flex-box .left-div .contents .chat-img {
32 | width: 100%;
33 | height: 110%;
34 | opacity: 0.15;
35 | top: 28%;
36 | right: 55%;
37 | position: absolute;
38 | }
39 |
40 | .home .body-div .flex-box .left-div .contents .note-img {
41 | width: 60%;
42 | height: 70%;
43 | opacity: 0.15;
44 | top: 80%;
45 | left: 115%;
46 | position: absolute;
47 | transform: rotate(-30deg);
48 | }
49 |
50 | .home .body-div .flex-box .left-div input {
51 | padding: 0.5rem;
52 | border: 2px solid #d1d1d1;
53 | outline: none;
54 | border-radius: 10px;
55 | width: 70%;
56 | font-size: 18px;
57 | transition-duration: 0.4s;
58 | z-index: 100;
59 | }
60 |
61 | .home .body-div .flex-box .left-div input:focus {
62 | border-color: #2cbdc7;
63 | }
64 |
65 | .home .body-div .flex-box .left-div .home-btn {
66 | margin: auto;
67 | padding: 0.5rem 1rem;
68 | border-radius: 25px;
69 | border: none;
70 | cursor: pointer;
71 | font-size: 17px;
72 | border: 2px solid #16c071;
73 | background-color: #0d4b85;
74 | color: white;
75 | transition-duration: 0.4s;
76 | z-index: 1;
77 | }
78 |
79 | .home .body-div .flex-box .left-div .home-btn:hover {
80 | background-color: #1b8299;
81 | color: white;
82 | }
83 |
84 | .home .body-div .flex-box .left-div .start-meet {
85 | padding: 1rem 0;
86 | }
87 |
88 | .home .body-div .flex-box .left-div .join-meet {
89 | width: 90%;
90 | padding: 1rem 0;
91 | display: flex;
92 | justify-content: space-between;
93 | align-items: center;
94 | }
95 |
96 | .home .body-div .flex-box .left-div .features {
97 | margin: auto;
98 | padding: 1rem;
99 | margin-left: 4rem;
100 | }
101 |
102 | .home .body-div .flex-box .left-div .features .grid-div {
103 | width: 70%;
104 | display: flex;
105 | justify-content: flex-start;
106 | align-items: center;
107 | padding: 10px;
108 | grid-column-gap: 10px;
109 | margin-left: 10%;
110 | }
111 |
112 | .home .body-div .flex-box .left-div .features .grid-div p {
113 | font-size: 20px;
114 | }
115 |
116 | .home .body-div .flex-box .left-div .features .grid-div p {
117 | margin: auto 0;
118 | }
119 |
120 | .home .body-div .flex-box .left-div .features .grid-div svg {
121 | font-size: 30px;
122 | }
123 |
124 | .home .body-div .flex-box .right-div {
125 | width: 60%;
126 | }
127 |
128 | .home .body-div .flex-box .right-div video {
129 | width: 100%;
130 | }
131 |
132 | @media (max-width: 576px) {
133 | .home .body-div {
134 | height: 87vh;
135 | }
136 | .home .body-div .flex-box {
137 | display: block;
138 | }
139 | .home .body-div .flex-box .left-div {
140 | z-index: 1;
141 | width: 100%;
142 | }
143 | .home .body-div .flex-box .left-div .contents {
144 | width: 100%;
145 | margin-left: 10%;
146 | top: 25%;
147 | }
148 | .home .body-div .flex-box .left-div .contents .note-img {
149 | width: 50%;
150 | height: 50%;
151 | opacity: 0.15;
152 | top: 40%;
153 | left: 65%;
154 | position: absolute;
155 | transform: rotate(-30deg);
156 | }
157 | .home .body-div .flex-box .left-div .contents .chat-img {
158 | width: 90%;
159 | height: 70%;
160 | opacity: 0.15;
161 | top: 60%;
162 | right: 55%;
163 | position: absolute;
164 | }
165 | .home .body-div .flex-box .left-div input {
166 | padding: 0.5rem;
167 | border: 2px solid #d1d1d1;
168 | outline: none;
169 | border-radius: 10px;
170 | width: 55%;
171 | font-size: 18px;
172 | transition-duration: 0.4s;
173 | }
174 | .home .body-div .flex-box .left-div input:focus {
175 | border-color: #2cbdc7;
176 | }
177 | .home .body-div .flex-box .left-div .home-btn {
178 | position: relative;
179 | padding: 0.5rem;
180 | z-index: 1;
181 | }
182 | .home .body-div .flex-box .left-div .home-btn:hover {
183 | background-color: #1b8299;
184 | color: white;
185 | }
186 | .home .body-div .flex-box .left-div .start-meet {
187 | padding: 2rem 1rem;
188 | }
189 | .home .body-div .flex-box .left-div .join-meet {
190 | width: 100%;
191 | padding: 0.5rem 0;
192 | display: flex;
193 | justify-content: space-between;
194 | align-items: center;
195 | }
196 | .home .body-div .flex-box .left-div .features {
197 | margin: auto;
198 | padding: 1rem;
199 | margin-left: 1rem;
200 | }
201 | .home .body-div .flex-box .left-div .features .grid-div {
202 | width: 90%;
203 | margin-left: 10%;
204 | }
205 | .home .body-div .flex-box .right-div {
206 | width: 100%;
207 | }
208 | .home .body-div .flex-box .right-div video {
209 | z-index: -1;
210 | width: 100%;
211 | }
212 | }
213 |
--------------------------------------------------------------------------------
/client/src/Components/Meet/Meet.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useState, useEffect } from 'react';
2 | import { SocketContext } from '../../SocketContext';
3 | import Editor from '../Editor/Editor';
4 | import Options from '../Options/Options';
5 | import MicIcon from '@material-ui/icons/Mic';
6 | import MicOffIcon from '@material-ui/icons/MicOff';
7 | import './Meet.css';
8 | import homeIcon1 from '../../assets/video-call.png';
9 | import noteIcon from '../../assets/note2.png';
10 | import Spinner from '../../common/Spinner';
11 | import Navbar from '../Navbar/Navbar';
12 | import saveAs from 'file-saver';
13 | import { pdfExporter } from 'quill-to-pdf';
14 | import { message } from 'antd';
15 | import GetAppIcon from '@material-ui/icons/GetApp';
16 |
17 | const Meet = (props) => {
18 | const {
19 | me,
20 | call,
21 | callAccepted,
22 | callEnded,
23 | name,
24 | myVideo,
25 | userVideo,
26 | stream,
27 | setStream,
28 | myVideoStatus,
29 | myMicStatus,
30 | userVideoStatus,
31 | userMicStatus,
32 | showEditor,
33 | otherUserStream,
34 | otherUser,
35 | otherUserName,
36 | quill,
37 | setQuill,
38 | } = useContext(SocketContext);
39 |
40 | const [mobileView, setMobileView] = useState(false);
41 | const [loading, setLoading] = useState(true);
42 |
43 | const resize = () => {
44 | setMobileView(window.innerWidth <= 600);
45 | };
46 |
47 | useEffect(() => {
48 | resize();
49 | window.addEventListener('resize', resize);
50 | }, []);
51 | // console.log(me,otherUser)
52 |
53 | useEffect(() => {
54 | if (loading) {
55 | setTimeout(() => {
56 | setLoading(false);
57 | }, 2000);
58 | }
59 | }, [loading]);
60 |
61 | useEffect(() => {
62 | if (loading) return;
63 | if (stream) {
64 | myVideo.current.srcObject = stream;
65 | return;
66 | }
67 | navigator.mediaDevices
68 | .getUserMedia({ video: true, audio: true })
69 | .then((res) => {
70 | res.getAudioTracks()[0].enabled = false;
71 | setStream(res);
72 | myVideo.current.srcObject = res;
73 | });
74 | }, [loading]);
75 |
76 | useEffect(() => {
77 | if (myVideo.current) myVideo.current.srcObject = stream;
78 | }, [myVideoStatus]);
79 |
80 | useEffect(() => {
81 | if (userVideo.current) userVideo.current.srcObject = otherUserStream;
82 | // console.log(otherUserStream)
83 | }, [otherUserStream, userVideoStatus, loading]);
84 |
85 | const downloadPdf = async () => {
86 | const delta = quill.getContents();
87 | const pdfAsBlob = await pdfExporter.generatePdf(delta);
88 | message.success('Downloading your whiteboard');
89 | saveAs(pdfAsBlob, `Merge-whiteboard.pdf`);
90 | };
91 |
92 | if (loading) {
93 | return (
94 |
101 |
102 |
103 | );
104 | }
105 | return (
106 |
107 |
108 |
109 |
110 | {' '}
111 |
114 |
115 | {stream ? (
116 | <>
117 | {myMicStatus ?
:
}
118 | {myVideoStatus ? (
119 |
128 | ) : (
129 |
130 |

131 |
132 | )}
133 |
{name} (you)
134 | >
135 | ) : (
136 |
137 | )}
138 |
139 |
140 | {callAccepted && (
141 |
142 | {userMicStatus ?
:
}
143 | {userVideoStatus ? (
144 |
153 | ) : (
154 |
155 |

156 |
157 | )}
158 |
{otherUserName}
159 |
160 | )}
161 |
162 |
163 |
164 |
165 |
166 |
167 | {!mobileView && showEditor && (
168 |
169 |
170 |
171 |
172 |

173 |
Whiteboard
174 |
175 |
178 |
179 |
180 |
181 |
182 | )}
183 |
184 | );
185 | };
186 |
187 | export default Meet;
188 |
--------------------------------------------------------------------------------
/client/src/Components/Meet/Meet.css:
--------------------------------------------------------------------------------
1 | .flex-div {
2 | display: flex;
3 | height: 100vh;
4 | background-color: white;
5 | }
6 |
7 | .flex-div .left {
8 | height: 9vh;
9 | width: 30%;
10 | }
11 |
12 | .flex-div .left .video-div {
13 | grid-row-gap: 1rem;
14 | display: flex;
15 | align-items: center;
16 | justify-content: center;
17 | height: 82vh;
18 | overflow: auto;
19 | }
20 |
21 | .flex-div .left .video-div .video-frames {
22 | display: flex;
23 | flex-direction: column;
24 | align-items: center;
25 | justify-content: center;
26 | grid-row-gap: 15px;
27 | margin: auto;
28 | }
29 |
30 | .flex-div .left .video-div .video-frames .video-frame {
31 | display: flex;
32 | flex-direction: column;
33 | align-items: center;
34 | justify-content: center;
35 | }
36 |
37 | .flex-div .left .video-div .video-frames .video-frame svg {
38 | position: absolute;
39 | left: 2%;
40 | }
41 |
42 | .flex-div .left .video-div .video-frames .video-frame .video-ref {
43 | width: 65%;
44 | height: 65%;
45 | }
46 |
47 | .flex-div .left .video-div .video-frames .video-frame .img-bg {
48 | display: flex;
49 | justify-content: center;
50 | align-items: center;
51 | width: 5rem;
52 | height: 5rem;
53 | border-radius: 10px;
54 | }
55 |
56 | .flex-div .left .video-div .video-frames .video-frame .name {
57 | font-size: 15px;
58 | color: white;
59 | background-color: rgba(0, 0, 0, 0.398);
60 | padding: 0px 10px;
61 | border-radius: 10px;
62 | margin-top: 5px;
63 | }
64 |
65 | .flex-div .left .video-div .video-frames .video-frame .hide-img {
66 | display: none;
67 | }
68 |
69 | .flex-div .left .video-div .video-frames .video-frame .show-img {
70 | position: absolute;
71 | bottom: 45%;
72 | }
73 |
74 | @media (max-width: 576px) {
75 | .flex-div .left {
76 | width: 30%;
77 | }
78 | .flex-div .left .video-div {
79 | grid-row-gap: 1rem;
80 | display: flex;
81 | align-items: center;
82 | justify-content: center;
83 | }
84 | .flex-div .left .video-div .video-frames {
85 | margin: auto;
86 | }
87 | .flex-div .left .video-div .video-frames .video-frame {
88 | position: relative;
89 | }
90 | .flex-div .left .video-div .video-frames .video-frame .hide-img {
91 | display: none;
92 | }
93 | .flex-div .left .video-div .video-frames .video-frame .show-img {
94 | position: absolute;
95 | bottom: 50%;
96 | left: 45%;
97 | }
98 | .flex-div .left .video-div .video-frames .video-frame svg {
99 | position: relative;
100 | left: 10%;
101 | }
102 | }
103 |
104 | .flex-div .right {
105 | width: 70%;
106 | background-color: #f3f3f3;
107 | border-left: 1px solid #d5d5d5;
108 | overflow: hidden;
109 | }
110 |
111 | .flex-div .right .editor-div {
112 | overflow: hidden;
113 | }
114 |
115 | .flex-div .right .editor-div .head {
116 | display: flex;
117 | justify-content: space-between;
118 | align-items: center;
119 | background-color: white;
120 | padding: 10px 20px;
121 | grid-column-gap: 5px;
122 | }
123 |
124 | .flex-div .right .editor-div .head .head-title {
125 | display: flex;
126 | background-color: white;
127 | grid-column-gap: 5px;
128 | }
129 |
130 | .flex-div .right .editor-div .head .head-title img {
131 | width: 30px;
132 | }
133 |
134 | .flex-div .right .editor-div .head .head-title h3 {
135 | margin: auto 0;
136 | font-size: 20px;
137 | }
138 |
139 | .flex-div .right .editor-div .head .download {
140 | display: flex;
141 | align-items: center;
142 | background: none;
143 | border: none;
144 | outline: none;
145 | cursor: pointer;
146 | }
147 |
148 | .hide-editor {
149 | display: block;
150 | }
151 |
152 | .hide-editor .left {
153 | width: 100%;
154 | height: 100%;
155 | }
156 |
157 | .hide-editor .left .video-div {
158 | height: 80vh;
159 | }
160 |
161 | .hide-editor .left .video-div .v-size {
162 | width: 75%;
163 | }
164 |
165 | .hide-editor .left .video-div .video-frames {
166 | display: flex;
167 | align-items: center;
168 | flex-direction: row;
169 | justify-content: center;
170 | width: 65%;
171 | }
172 |
173 | .hide-editor .left .video-div .video-frames .video-frame {
174 | text-align: center;
175 | display: flex;
176 | align-items: center;
177 | justify-content: center;
178 | width: 70%;
179 | }
180 |
181 | .hide-editor .left .video-div .video-frames .video-frame svg {
182 | position: relative;
183 | left: 0%;
184 | }
185 |
186 | .hide-editor .left .video-div .video-frames .video-frame img {
187 | width: 50px;
188 | height: 50px;
189 | }
190 |
191 | .hide-editor .left .video-div .video-frames .video-frame .video-ref {
192 | width: 90%;
193 | height: 80%;
194 | }
195 |
196 | .hide-editor .left .video-div .video-frames .video-frame .img-bg {
197 | display: flex;
198 | justify-content: center;
199 | align-items: center;
200 | width: 5rem;
201 | height: 5rem;
202 | border-radius: 10px;
203 | }
204 |
205 | @media (max-width: 576px) {
206 | .hide-editor .left {
207 | width: 100%;
208 | }
209 | .hide-editor .left .video-div {
210 | overflow: scroll;
211 | }
212 | .hide-editor .left .video-div .video-frames {
213 | display: flex;
214 | align-items: center;
215 | flex-direction: column;
216 | justify-content: center;
217 | margin: auto;
218 | width: 100%;
219 | grid-row-gap: 0.2rem;
220 | }
221 | .hide-editor .left .video-div .video-frames .video-frame {
222 | text-align: center;
223 | display: flex;
224 | align-items: center;
225 | justify-content: center;
226 | width: 70%;
227 | }
228 | .hide-editor .left .video-div .video-frames .video-frame svg {
229 | position: relative;
230 | }
231 | .hide-editor .left .video-div .video-frames .video-frame .video-ref {
232 | width: 100%;
233 | height: 100%;
234 | }
235 | .hide-editor .left .video-div .video-frames .video-frame .img-bg {
236 | display: flex;
237 | justify-content: center;
238 | align-items: center;
239 | width: 5rem;
240 | height: 5rem;
241 | border-radius: 10px;
242 | }
243 | }
244 |
--------------------------------------------------------------------------------
/client/src/Components/Home/Home.scss:
--------------------------------------------------------------------------------
1 | @import '../../assets/styles/_mixins.scss';
2 | .home {
3 | position: relative;
4 | overflow: hidden;
5 | background-color: white;
6 |
7 | .body-div {
8 | // position: relative;
9 | overflow: hidden;
10 | height: 87vh;
11 | .flex-box {
12 | display: flex;
13 | .left-div {
14 | // margin: auto;
15 | // text-align: center;
16 | display: flex;
17 | align-items: center;
18 | justify-content: center;
19 | width: 40%;
20 | align-items: center;
21 | // padding: 3rem;
22 |
23 | .contents {
24 | position: absolute;
25 | width: 40%;
26 | margin-left: 30%;
27 | // background-color: whitesmoke;
28 | align-items: center;
29 | .chat-img {
30 | width: 120%;
31 | height: 140%;
32 | opacity: 0.15;
33 | top: 20%;
34 | right: 55%;
35 | position: absolute;
36 | }
37 | .note-img {
38 | width: 60%;
39 | height: 70%;
40 | opacity: 0.15;
41 | top: 80%;
42 | left: 115%;
43 | position: absolute;
44 | transform: rotate(-30deg);
45 | // z-index: -1;
46 | }
47 | }
48 |
49 | input {
50 | padding: 0.5rem;
51 | border: 2px solid rgb(209, 209, 209);
52 | outline: none;
53 | border-radius: 10px;
54 | width: 70%;
55 | font-size: 18px;
56 | transition-duration: 0.4s;
57 | &:focus {
58 | border-color: rgb(44, 189, 199);
59 | }
60 | z-index: 100;
61 | }
62 | .home-btn {
63 | margin: auto;
64 | padding: 0.5rem 1rem;
65 | border-radius: 25px;
66 | border: none;
67 | cursor: pointer;
68 | font-size: 17px;
69 | border: 2px solid rgb(22, 192, 113);
70 | background-color: rgb(13, 75, 133);
71 | color: white;
72 | transition-duration: 0.4s;
73 | &:hover {
74 | background-color: rgb(27, 130, 153);
75 | color: white;
76 | }
77 | z-index: 1;
78 | }
79 | .start-meet {
80 | // display: flex;
81 | // justify-content: flex-start;
82 | // align-items: center;
83 | // margin: left;
84 | padding: 1rem 0;
85 | }
86 | .join-meet {
87 | // margin: auto;
88 | width: 90%;
89 | padding: 1rem 0;
90 | display: flex;
91 | justify-content: space-between;
92 | align-items: center;
93 | }
94 | .features {
95 | margin: auto;
96 | padding: 1rem;
97 | margin-left: 4rem;
98 | .grid-div {
99 | width: 70%;
100 | display: flex;
101 | justify-content: flex-start;
102 | align-items: center;
103 | padding: 10px;
104 | grid-column-gap: 10px;
105 | margin-left: 10%;
106 | // background-color: grey;
107 | p {
108 | font-size: 20px;
109 | }
110 | p {
111 | margin: auto 0;
112 | }
113 | svg {
114 | font-size: 30px;
115 | }
116 | }
117 | }
118 | }
119 | .right-div {
120 | width: 60%;
121 | // img {
122 | // width: 30%;
123 | // height: 30%;
124 | // display: flex;
125 | // align-items: center;
126 | // }
127 | // background-color: whitesmoke;
128 | video {
129 | width: 100%;
130 | // height: 100%;
131 | }
132 | }
133 | }
134 | }
135 | @include mobile {
136 | // overflow-y: scroll;
137 | .body-div {
138 | height: 87vh;
139 | .flex-box {
140 | display: block;
141 | .left-div {
142 | // position: relative;
143 | z-index: 1;
144 | width: 100%;
145 | .contents {
146 | // position: absolute;
147 |
148 | width: 100%;
149 | margin-left: 10%;
150 | top: 25%;
151 |
152 | .note-img {
153 | width: 50%;
154 | height: 50%;
155 | opacity: 0.15;
156 | top: 40%;
157 | left: 65%;
158 | position: absolute;
159 | transform: rotate(-30deg);
160 | // z-index: -1;
161 | }
162 | .chat-img {
163 | width: 90%;
164 | height: 70%;
165 | opacity: 0.15;
166 | top: 60%;
167 | right: 55%;
168 | position: absolute;
169 | }
170 | }
171 |
172 | input {
173 | // z-index: 1;
174 | padding: 0.5rem;
175 | border: 2px solid rgb(209, 209, 209);
176 | outline: none;
177 | border-radius: 10px;
178 | width: 55%;
179 | font-size: 18px;
180 | transition-duration: 0.4s;
181 | &:focus {
182 | border-color: rgb(44, 189, 199);
183 | }
184 | }
185 | .home-btn {
186 | position: relative;
187 | padding: 0.5rem;
188 | &:hover {
189 | background-color: rgb(27, 130, 153);
190 | color: white;
191 | }
192 | z-index: 1;
193 | }
194 | .start-meet {
195 | padding: 2rem 1rem;
196 | }
197 | .join-meet {
198 | // margin: auto;
199 | width: 100%;
200 | padding: 0.5rem 0;
201 | display: flex;
202 | justify-content: space-between;
203 | align-items: center;
204 | }
205 | .features {
206 | margin: auto;
207 | padding: 1rem;
208 | margin-left: 1rem;
209 | .grid-div {
210 | width: 90%;
211 | margin-left: 10%;
212 | }
213 | }
214 | }
215 | .right-div {
216 | width: 100%;
217 | video {
218 | z-index: -1;
219 | width: 100%;
220 | // height: 100%;
221 | }
222 | }
223 | }
224 | }
225 | }
226 | }
227 |
--------------------------------------------------------------------------------
/client/src/SocketContext.js:
--------------------------------------------------------------------------------
1 | import { useEffect, createContext, useState, useRef } from 'react';
2 | import Peer from 'simple-peer';
3 | import { io } from 'socket.io-client';
4 | import { message } from 'antd';
5 | import { BACKEND_URL } from './constants';
6 | const SocketContext = createContext();
7 |
8 | const socket = io(BACKEND_URL);
9 |
10 | const ContextProvider = ({ children }) => {
11 | const [socketState, setSocketState] = useState(socket);
12 | const [me, setMe] = useState('');
13 | const [newMeet, setNewMeet] = useState(false);
14 | const [call, setCall] = useState({});
15 | const [stream, setStream] = useState(null);
16 | const [callAccepted, setCallAccepted] = useState(false);
17 | const [callEnded, setCallEnded] = useState(false);
18 | const [name, setName] = useState('');
19 | const [otherUser, setOtherUser] = useState(null);
20 | const [otherUserName, setOtherUserName] = useState('');
21 | const [myVideoStatus, setMyVideoStatus] = useState(true);
22 | const [userVideoStatus, setUserVideoStatus] = useState(true);
23 | const [myMicStatus, setMyMicStatus] = useState(false);
24 | const [userMicStatus, setUserMicStatus] = useState(false);
25 | const [showEditor, setShowEditor] = useState(false);
26 | const [showChatBox, setShowChatBox] = useState(false);
27 | const [messages, setMessages] = useState([]);
28 | const [notes, setNotes] = useState('');
29 | const [meetingCode, setMeetingCode] = useState('');
30 | const [notesOpen, setNotesOpen] = useState(false);
31 | const [quill, setQuill] = useState(null);
32 | const [otherUserStream, setOtherUserStream] = useState(null);
33 | const myVideo = useRef();
34 | const userVideo = useRef();
35 | const connectionRef = useRef();
36 |
37 | useEffect(() => {
38 | if (!navigator.onLine) alert('Connect to internet!');
39 | }, [navigator]);
40 |
41 | useEffect(() => {
42 | socket.on('me', (id) => {
43 | setMe(id);
44 | });
45 | socket.on('calluser', ({ from, name: callerName, signal }) => {
46 | setCall({
47 | from,
48 | callerName,
49 | signal,
50 | isRecievedCall: true,
51 | });
52 | setOtherUserName(callerName);
53 | });
54 |
55 | socket.on('updateUserMedia', ({ type, mediaStatus }) => {
56 | if (!type || !mediaStatus || !mediaStatus.length) {
57 | return;
58 | }
59 | if (type === 'video') {
60 | message.info(`User turned ${mediaStatus[0] ? 'on' : 'off'} his video`);
61 | setUserVideoStatus(mediaStatus[0]);
62 | return;
63 | }
64 | if (type === 'audio') {
65 | message.info(`User ${mediaStatus[0] ? 'unmuted' : 'muted'} his mic`);
66 | setUserMicStatus(mediaStatus[0]);
67 | return;
68 | }
69 | setUserMicStatus(mediaStatus[0]);
70 | setUserVideoStatus(mediaStatus[1]);
71 | });
72 |
73 | socket.on('callended', () => {
74 | setCall(null);
75 | message.info('User disconnected from call');
76 | setCallAccepted(false);
77 | setCallEnded(true);
78 | });
79 | }, []);
80 |
81 | const answerCall = () => {
82 | setCallAccepted(true);
83 | setOtherUser(call.from);
84 | const peer = new Peer({ initiator: false, trickle: false, stream });
85 |
86 | peer.on('signal', (data) => {
87 | socket.emit('answercall', {
88 | name,
89 | signal: data,
90 | to: call.from,
91 | type: 'both',
92 | mediaStatus: [myMicStatus, myVideoStatus],
93 | });
94 | message.info(`${name} joined with you`);
95 | });
96 |
97 | peer.on('stream', (currentStream) => {
98 | setOtherUserStream(currentStream);
99 | });
100 |
101 | peer.signal(call.signal);
102 | connectionRef.current = peer;
103 | };
104 |
105 | const callUser = (id) => {
106 | message.info(
107 | 'Calling user... Please wait for the other user to accept the call'
108 | );
109 | const peer = new Peer({ initiator: true, trickle: false, stream });
110 | setOtherUser(id);
111 |
112 | peer.on('signal', (data) => {
113 | socket.emit('calluser', {
114 | userToCall: id,
115 | from: me,
116 | signal: data,
117 | name,
118 | });
119 | });
120 | peer.on('stream', (currentStream) => {
121 | setOtherUserStream(currentStream);
122 | });
123 |
124 | socket.on('callaccepted', (signal, userName) => {
125 | socket.emit('updateMyMedia', {
126 | data: {
127 | type: 'both',
128 | mediaStatus: [myMicStatus, myVideoStatus],
129 | },
130 | userToUpdate: id,
131 | });
132 | setOtherUserName(userName);
133 | setCallAccepted(true);
134 | peer.signal(signal);
135 | message.info(`${name} joined with you`);
136 | });
137 |
138 | connectionRef.current = peer;
139 | };
140 |
141 | const endCall = (history) => {
142 | socket.emit('callended', otherUser);
143 | setCallEnded(true);
144 | setCallAccepted(false);
145 | if (connectionRef.current) connectionRef.current.destroy();
146 | history.push('/');
147 | message.success('Meet Ended');
148 | window.location.reload();
149 | };
150 |
151 | const updateVideoStatus = () => {
152 | socket.emit('updateMyMedia', {
153 | data: { type: 'video', mediaStatus: [!myVideoStatus] },
154 | userToUpdate: otherUser,
155 | });
156 |
157 | stream.getVideoTracks()[0].enabled = !myVideoStatus;
158 | setMyVideoStatus(!myVideoStatus);
159 | };
160 |
161 | const updateMicStatus = () => {
162 | socket.emit('updateMyMedia', {
163 | data: { type: 'audio', mediaStatus: [!myMicStatus] },
164 | userToUpdate: otherUser,
165 | });
166 |
167 | stream.getAudioTracks()[0].enabled = !myMicStatus;
168 | setMyMicStatus(!myMicStatus);
169 | };
170 |
171 | return (
172 |
224 | {children}
225 |
226 | );
227 | };
228 | export { SocketContext, ContextProvider };
229 |
--------------------------------------------------------------------------------
/client/src/Components/Meet/Meet.scss:
--------------------------------------------------------------------------------
1 | @import '../../assets/styles/_mixins.scss';
2 |
3 | .flex-div {
4 | display: flex;
5 | height: 100vh;
6 | background-color: white;
7 | .left {
8 | height: 9vh;
9 | width: 30%;
10 | // background-color: grey;
11 | // background-color: white;
12 | // animation: 1s slide-right;
13 | // @keyframes slide-right {
14 | // from {
15 | // margin-left: -100%;
16 | // }
17 | // to {
18 | // margin-left: 0%;
19 | // }
20 | // }
21 |
22 | .video-div {
23 | // display: block;
24 | // align-items: center;
25 | // text-align: center;
26 | // margin-top: 8rem;
27 | grid-row-gap: 1rem;
28 | display: flex;
29 | align-items: center;
30 | justify-content: center;
31 | // background-color: blue;
32 | height: 82vh;
33 | overflow: auto;
34 | .video-frames {
35 | display: flex;
36 | flex-direction: column;
37 | align-items: center;
38 | justify-content: center;
39 | // background-color: rgb(63, 34, 34);
40 | grid-row-gap: 15px;
41 | margin: auto;
42 | .video-frame {
43 | display: flex;
44 | flex-direction: column;
45 | align-items: center;
46 | justify-content: center;
47 | svg {
48 | position: absolute;
49 | // top: 0%;
50 | left: 2%;
51 | }
52 | .video-ref {
53 | // margin-top: 1rem;
54 | width: 65%;
55 | height: 65%;
56 | // border: 2px solid rgb(232, 232, 232);
57 | }
58 | .img-bg {
59 | display: flex;
60 | justify-content: center;
61 | align-items: center;
62 | width: 5rem;
63 | height: 5rem;
64 | // background-color: rgb(235, 235, 235);
65 | border-radius: 10px;
66 | }
67 | .name {
68 | font-size: 15px;
69 | color: white;
70 | background-color: rgba(0, 0, 0, 0.398);
71 | padding: 0px 10px;
72 | border-radius: 10px;
73 | margin-top: 5px;
74 | }
75 | .hide-img {
76 | display: none;
77 | }
78 | .show-img {
79 | position: absolute;
80 | bottom: 45%;
81 | // right: '50%',
82 | }
83 | }
84 | }
85 | }
86 | @include mobile {
87 | width: 30%;
88 | // background-color: rgb(232, 232, 232);
89 | .video-div {
90 | // display: block;
91 | // align-items: center;
92 | // text-align: center;
93 | // margin-top: 8rem;
94 | grid-row-gap: 1rem;
95 | display: flex;
96 | align-items: center;
97 | justify-content: center;
98 | .video-frames {
99 | // display: flex;
100 | // align-items: center;
101 | // justify-content: center;
102 |
103 | margin: auto;
104 | .video-frame {
105 | position: relative;
106 | .hide-img {
107 | display: none;
108 | }
109 | .show-img {
110 | position: absolute;
111 | bottom: 50%;
112 | left: 45%;
113 | }
114 | // border: 1px solid blue;
115 | svg {
116 | position: relative;
117 | left: 10%;
118 | }
119 | .video-ref {
120 | // margin-top: 1rem;
121 | // width: 55%;
122 | // height: 23vh;
123 | // border: 2px solid rgb(232, 232, 232);
124 | }
125 | }
126 | }
127 | }
128 | }
129 | }
130 | .right {
131 | width: 70%;
132 | background-color: rgb(243, 243, 243);
133 | border-left: 1px solid rgb(213, 213, 213);
134 | overflow: hidden;
135 |
136 | .editor-div {
137 | overflow: hidden;
138 | .head {
139 | display: flex;
140 | justify-content: space-between;
141 | align-items: center;
142 | background-color: white;
143 | padding: 10px 20px;
144 | grid-column-gap: 5px;
145 | .head-title {
146 | display: flex;
147 | background-color: white;
148 | grid-column-gap: 5px;
149 | img {
150 | width: 30px;
151 | }
152 | h3 {
153 | margin: auto 0;
154 | // font-weight: 400;
155 | font-size: 20px;
156 | }
157 | }
158 | .download {
159 | display: flex;
160 | align-items: center;
161 | background: none;
162 | border: none;
163 | outline: none;
164 | cursor: pointer;
165 | }
166 | }
167 | }
168 | }
169 | }
170 | .hide-editor {
171 | display: block;
172 | .left {
173 | width: 100%;
174 | // background-color: rgb(232, 232, 232);
175 | height: 100%;
176 | // animation: 0.5s slide-right;
177 | // @keyframes slide-right {
178 | // from {
179 | // margin-left: -70%;
180 | // }
181 | // to {
182 | // margin-left: 0%;
183 | // }
184 | // }
185 | .video-div {
186 | // height: 100%;
187 | height: 80vh;
188 | .v-size {
189 | width: 75%;
190 | }
191 | .video-frames {
192 | display: flex;
193 | align-items: center;
194 | flex-direction: row;
195 | justify-content: center;
196 | // margin: auto;
197 |
198 | width: 65%;
199 | // height: 80%;
200 |
201 | .video-frame {
202 | text-align: center;
203 | // border: 1px solid black;
204 | display: flex;
205 | align-items: center;
206 | // flex-direction: row;
207 | justify-content: center;
208 | svg {
209 | position: relative;
210 | left: 0%;
211 | }
212 | width: 70%;
213 | img {
214 | width: 50px;
215 | height: 50px;
216 | }
217 | .video-ref {
218 | // margin-top: 1rem;
219 | width: 90%;
220 | height: 80%;
221 | // border: 2px solid rgb(232, 232, 232);
222 | }
223 | .img-bg {
224 | display: flex;
225 | justify-content: center;
226 | align-items: center;
227 | width: 5rem;
228 | height: 5rem;
229 | // background-color: rgb(235, 235, 235);
230 | border-radius: 10px;
231 | }
232 | }
233 | }
234 | }
235 | @include mobile {
236 | width: 100%;
237 | // background-color: grey;
238 | // background-color: rgb(232, 232, 232);
239 | // height: 100%;
240 | .video-div {
241 | // height: 100%;
242 | // padding-bottom: 3rem;
243 | // height: auto;
244 | overflow: scroll;
245 | .video-frames {
246 | display: flex;
247 | align-items: center;
248 | flex-direction: column;
249 | justify-content: center;
250 | margin: auto;
251 | width: 100%;
252 | // height: 80%;
253 | grid-row-gap: 0.2rem;
254 | .video-frame {
255 | text-align: center;
256 | // border: 1px solid black;
257 | display: flex;
258 | align-items: center;
259 | // flex-direction: row;
260 | justify-content: center;
261 | // background-color: black;
262 | svg {
263 | position: relative;
264 | // left: 5%;
265 | }
266 | width: 70%;
267 | // img {
268 | // width: 50px;
269 | // height: 50px;
270 | // position: absolute;
271 | // left: 0%;
272 | // }
273 |
274 | .video-ref {
275 | // margin-top: 1rem;
276 | width: 100%;
277 | height: 100%;
278 | // border: 2px solid rgb(232, 232, 232);
279 | }
280 | .img-bg {
281 | display: flex;
282 | justify-content: center;
283 | align-items: center;
284 | width: 5rem;
285 | height: 5rem;
286 | // background-color: rgb(235, 235, 235);
287 | border-radius: 10px;
288 | }
289 | }
290 | }
291 | }
292 | }
293 | }
294 | .right {
295 | // display: none;
296 | }
297 | }
298 |
--------------------------------------------------------------------------------
/client/src/Components/Options/Options.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useState, useEffect } from 'react';
2 | import { SocketContext } from '../../SocketContext';
3 | import { CopyToClipboard } from 'react-copy-to-clipboard';
4 | import './Options.css';
5 | import 'antd/dist/antd.css';
6 | import Menu from '@material-ui/core/Menu';
7 | import VideocamIcon from '@material-ui/icons/Videocam';
8 | import VideocamOffIcon from '@material-ui/icons/VideocamOff';
9 | import MicIcon from '@material-ui/icons/Mic';
10 | import MicOffIcon from '@material-ui/icons/MicOff';
11 | import { Button, message } from 'antd';
12 | import DialogContent from '@material-ui/core/DialogContent';
13 | import DialogTitle from '@material-ui/core/DialogTitle';
14 | import Dialog from '@material-ui/core/Dialog';
15 | import MoreVertIcon from '@material-ui/icons/MoreVert';
16 | import CallEndIcon from '@material-ui/icons/CallEnd';
17 | import ChatIcon from '@material-ui/icons/Chat';
18 | import Messages from '../Messages/Messages';
19 | import Notes from '../Notes/Notes';
20 | import CloseIcon from '@material-ui/icons/Close';
21 | import AntSwitch from '../../common/AntSwitch';
22 | import { APP_URL } from '../../constants';
23 |
24 | const Options = (props) => {
25 | const [callId, setCallId] = useState('');
26 |
27 | const {
28 | me,
29 | call,
30 | callAccepted,
31 | callEnded,
32 | name,
33 | setCall,
34 | setName,
35 | myVideo,
36 | userVideo,
37 | stream,
38 | answerCall,
39 | callUser,
40 | endCall,
41 | myVideoStatus,
42 | myMicStatus,
43 | userVideoStatus,
44 | userMicStatus,
45 | updateMicStatus,
46 | updateVideoStatus,
47 | showEditor,
48 | showChatBox,
49 | setShowChatBox,
50 | setShowEditor,
51 | notesOpen,
52 | setNotesOpen,
53 | } = useContext(SocketContext);
54 |
55 | const [open, setOpen] = useState(true);
56 | const [anchorEl, setAnchorEl] = useState(null);
57 | const [mobileView, setMobileView] = useState(false);
58 | const resize = () => {
59 | setMobileView(window.innerWidth <= 600);
60 | };
61 |
62 | useEffect(() => {
63 | resize();
64 | window.addEventListener('resize', resize);
65 | }, []);
66 |
67 | const handleClick = (event) => {
68 | setAnchorEl(event.currentTarget);
69 | };
70 |
71 | const handleClose = () => {
72 | setAnchorEl(null);
73 | };
74 | useEffect(() => {
75 | if (call && call.isRecievedCall && !callAccepted) {
76 | setOpen(true);
77 | } else {
78 | setOpen(false);
79 | }
80 | }, [call, callEnded]);
81 |
82 | return (
83 | <>
84 |
85 |
89 |
90 |
99 | {/* {callAccepted && !callEnded && ( */}
100 |
110 |
111 | {/* )} */}
112 |
123 |
131 |
132 |
133 |
134 |
225 |
226 | {call && (
227 |
255 | )}
256 |
257 |
258 | >
259 | );
260 | };
261 |
262 | export default Options;
263 |
--------------------------------------------------------------------------------