Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: apsinghdev/drawRTC
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v1.0.0
Choose a base ref
...
head repository: apsinghdev/drawRTC
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: main
Choose a head ref

Commits on Aug 20, 2024

  1. remove chat button for now

    apsinghdev committed Aug 20, 2024
    Copy the full SHA
    bc35420 View commit details

Commits on Aug 22, 2024

  1. remove unnecessary code

    apsinghdev committed Aug 22, 2024
    Copy the full SHA
    8400ff0 View commit details

Commits on Sep 2, 2024

  1. install uuid

    apsinghdev committed Sep 2, 2024
    Copy the full SHA
    adf14dd View commit details
  2. Copy the full SHA
    4fe444d View commit details

Commits on Sep 3, 2024

  1. Copy the full SHA
    2648d9f View commit details
  2. Copy the full SHA
    36c8a44 View commit details
  3. Copy the full SHA
    66759c9 View commit details
  4. feat: add rooms feature

    apsinghdev committed Sep 3, 2024
    Copy the full SHA
    f411c1d View commit details
  5. Copy the full SHA
    b2c793d View commit details
  6. Copy the full SHA
    b21192d View commit details
  7. Copy the full SHA
    25954e4 View commit details
  8. fix: store the socket in socket variable and change the collaboration…

    … flag when collaboration is on
    apsinghdev committed Sep 3, 2024
    Copy the full SHA
    f6291eb View commit details

Commits on Sep 4, 2024

  1. Copy the full SHA
    52294fc View commit details
  2. Copy the full SHA
    b8532a1 View commit details
  3. Copy the full SHA
    450d99e View commit details
  4. Copy the full SHA
    2f5eef7 View commit details
  5. Copy the full SHA
    60e5d34 View commit details
  6. Copy the full SHA
    97c2db0 View commit details
  7. Copy the full SHA
    a6e5820 View commit details
  8. Merge pull request #62 from apsinghdev/feat/implement-collaboration

    feat: implement the functionality of the collaboration workflow
    apsinghdev authored Sep 4, 2024
    Copy the full SHA
    23581e6 View commit details
  9. Copy the full SHA
    bf16964 View commit details
  10. Copy the full SHA
    bdf72e6 View commit details
  11. Copy the full SHA
    f1aa04b View commit details
  12. Copy the full SHA
    1c36341 View commit details
  13. docs: add note heading

    apsinghdev committed Sep 4, 2024
    Copy the full SHA
    de37930 View commit details
  14. Copy the full SHA
    41b86c7 View commit details
  15. Copy the full SHA
    013ce46 View commit details
  16. Merge pull request #64 from apsinghdev/feat/handle-vercel-case

    feat: handle vercel case
    apsinghdev authored Sep 4, 2024
    Copy the full SHA
    73ee85a View commit details
  17. Copy the full SHA
    167a40b View commit details
  18. Merge pull request #65 from apsinghdev/fix/hostname-checker

    fix: remove the trailling slashes
    apsinghdev authored Sep 4, 2024
    Copy the full SHA
    9168dd8 View commit details
  19. Copy the full SHA
    8aa702f View commit details
  20. Merge pull request #66 from apsinghdev/fix/setMsg

    fix: replace setShowMenu with setShowMsg
    apsinghdev authored Sep 4, 2024
    Copy the full SHA
    246e72f View commit details

Commits on Sep 5, 2024

  1. Copy the full SHA
    02d06ab View commit details
  2. Copy the full SHA
    0383f14 View commit details
  3. Merge pull request #68 from apsinghdev/fix/hide-menu-on-click

    fix: hide menu when user click anywhere in canvas
    apsinghdev authored Sep 5, 2024
    Copy the full SHA
    b79acd7 View commit details
  4. chore: remove console log

    apsinghdev committed Sep 5, 2024
    Copy the full SHA
    4b18fe3 View commit details
  5. Copy the full SHA
    7534e9f View commit details
  6. Copy the full SHA
    115ec92 View commit details
  7. Copy the full SHA
    017defd View commit details
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -2,12 +2,16 @@

A real-time collaboration project to draw with your friends.

![](./drawRTC.png)
![Collaboration Demo](./collaboration.gif)

## Demo
(click on the thumbnail below)

[![Demo video](https://img.youtube.com/vi/74T8SncLnE8/maxresdefault.jpg)](https://youtu.be/74T8SncLnE8?si=T-uwfenkqrkfhn9q?autoplay=1)
[![Demo video](./demoVideoThumbnail.jpeg)](https://youtu.be/HuAL1eiMnSw)

### Note

> Unfortunately, Vercel's free plan does not support WebSocket functionality, so I was unable to deploy the backend to production. To use the collaborative features of the app, you'll need to set up the application locally. The setup steps are provided below, and I've also included a demonstration video of the local setup process above. Thank you!
## Instructions

20 changes: 20 additions & 0 deletions api/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Ignore node_modules
node_modules

# Ignore logs
npm-debug.log
yarn-debug.log
yarn-error.log

# Ignore environment files
.env


# Ignore git files
.git
.gitignore

# Ignore build artifacts
dist
build
coverage
28 changes: 21 additions & 7 deletions api/index.js
Original file line number Diff line number Diff line change
@@ -27,26 +27,40 @@ app.get('/test', (res) => {
})

io.on('connection', (socket) => {
console.log('user connected socket')

socket.on('joinRoom', ({room_id}) => {
socket.join(room_id);
console.log(`User ${socket.id} joined room ${room_id}`);
})

socket.on('draw', (data)=>{
socket.broadcast.emit('draw', data);
const room = data.room_id;
socket.to(room).emit('draw', data);
})

socket.on('clear', () => {
io.emit('clear');
socket.on('clear', (data) => {
const room = data.room_id;
socket.to(room).emit('clear');
})

socket.on('open-text-editor', data => {
socket.broadcast.emit("open-text-editor", data);
const room = data.room_id;
socket.to(room).emit("open-text-editor", data);
})

socket.on('close-text-editor', data => {
socket.broadcast.emit("close-text-editor", data);
const room = data.room_id;
socket.to(room).emit("close-text-editor", data);
})

socket.on("text-updated", (data) => {
socket.broadcast.emit("text-updated", data);
const room = data.room_id;
socket.to(room).emit("text-updated", data);
});

socket.on("disconnect", () => {
console.log(`${socket.id} disconnected`);
})
})

server.listen(PORT, ()=>{
20 changes: 20 additions & 0 deletions client/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Ignore node_modules
node_modules

# Ignore logs
npm-debug.log
yarn-debug.log
yarn-error.log

# Ignore environment files
.env


# Ignore git files
.git
.gitignore

# Ignore build artifacts
dist
build
coverage
1 change: 0 additions & 1 deletion client/index.html
Original file line number Diff line number Diff line change
@@ -9,6 +9,5 @@
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
<script src="./src/socket.js"></script>
</body>
</html>
14 changes: 14 additions & 0 deletions client/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions client/package.json
Original file line number Diff line number Diff line change
@@ -16,6 +16,7 @@
"react-icons": "^5.2.0",
"recoil": "^0.7.7",
"socket.io-client": "^4.7.4",
"uuid": "^10.0.0",
"y-socket.io": "^1.1.3",
"yjs": "^13.6.15"
},
159 changes: 131 additions & 28 deletions client/src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,29 @@
/* eslint-disable react-hooks/exhaustive-deps */
import { useEffect, useRef, useState } from "react";
import { io } from "socket.io-client";
import "./App.css";
import socket from "./socket";

import InfoMsg from "./components/InfoMsg";
import Sidebar from "./components/Sidebar";
import Canvas from "./components/Canvas";
import Menu from "./components/Menu";
import EraserCursor from "./components/EraserCursor";
import TextEditor from "./components/TextEditor";
import { useRecoilValue, useRecoilState } from "recoil";
import { useRecoilValue, useRecoilState, useSetRecoilState } from "recoil";
import {
eraserState,
cursorPosition,
canvasColors,
canvasState,
showMenuState,
showTextEditor,
collaborationStarted,
showMsg,
roomIdAtom,
messageTxtAtom
} from "./atoms";
import { useSocket } from "./Context";

socket.connect();
const PORT = "http://localhost:8000";

function App() {
const [showMenu, setShowMenu] = useRecoilState(showMenuState);
@@ -32,7 +37,13 @@ function App() {
const canvasColor = useRecoilValue(canvasColors);
const [currentCanvas, setCanvas] = useRecoilState(canvasState);
const textEditor = useRecoilValue(showTextEditor);

const setTextEditor = useSetRecoilState(showTextEditor);
const hasCollaborationStarted = useRecoilValue(collaborationStarted);
const setCollaborationFlag = useSetRecoilState(collaborationStarted);
const [showMessage, setShowMsg] = useRecoilState(showMsg);
const [roomId, setRoomId] = useRecoilState(roomIdAtom);
const { socket, setSocket } = useSocket();
const [ messageText, setMessageText ] = useRecoilState(messageTxtAtom);

function toggleMenu() {
setShowMenu(!showMenu);
@@ -76,7 +87,9 @@ function App() {
const endX = e.clientX - canvas.getBoundingClientRect().left;
const endY = e.clientY - canvas.getBoundingClientRect().top;
drawLine(startX, startY, endX, endY, penColor);
socket.emit("draw", { startX, startY, endX, endY, penColor, lineWidth });
if (hasCollaborationStarted && socket) {
socket.emit("draw", { startX, startY, endX, endY, penColor, lineWidth, room_id: roomId });
}
setStartX(endX);
setStartY(endY);
}
@@ -104,29 +117,31 @@ function App() {
canvas.removeEventListener("mousedown", handleMousedown);
canvas.removeEventListener("mouseup", handleMouseup);
};
}, [penColor, eraserMode, position, ctx, isDrawing, startX, startY]);
}, [socket, penColor, eraserMode, position, ctx, isDrawing, startX, startY]);

useEffect(() => {
socket.on("draw", (data) => {
drawLine(
data.startX,
data.startY,
data.endX,
data.endY,
data.penColor,
data.lineWidth
);
});

socket.on("clear", () => {
clearRect();
});

return () => {
socket.off("draw");
socket.off("clear");
};
}, [socket, ctx]);
if (hasCollaborationStarted && socket) {
socket.on("draw", (data) => {
drawLine(
data.startX,
data.startY,
data.endX,
data.endY,
data.penColor,
data.lineWidth
);
});

socket.on("clear", () => {
clearRect();
});

return () => {
socket.off("draw");
socket.off("clear");
};
}
}, [socket, ctx, hasCollaborationStarted]);

function clearRect() {
if (ctx) {
@@ -136,7 +151,10 @@ function App() {

function clearOnClick() {
clearRect();
socket.emit("clear");
if (hasCollaborationStarted && socket) {
const data = {room_id: roomId};
socket.emit("clear", data);
}
}

function addStroke(e) {
@@ -158,6 +176,90 @@ function App() {
}
}

const closeMsg = () => {
setMessageText(null);
setShowMsg(false);
}

useEffect(() => {
const urlParams = new URLSearchParams(window.location.search);
const roomID = urlParams.get("roomID");
const RETRIES = 5;
const DELAY_DURATION = 2000;
let attempts = 0;

const closeConnection = (socket) => {
if (attempts >= RETRIES) {
socket.disconnect();
setCollaborationFlag(false);
console.log("Max socket calls exceeded. Please try connecting again.")
}
}

if (roomID) {
const collaborationLink = window.location.href;
setRoomId(roomID);
setCollaborationFlag(true);
const newSocket = io(PORT);

try {
newSocket.on("connect", () => {
console.log("connected");
try {
newSocket.emit("joinRoom", { room_id: roomID });
console.log(`joined room ${roomID}`);
setSocket(newSocket);
setMessageText(`Collaboration Link : ${collaborationLink}`);
setShowMsg(true);
} catch (error) {
console.log("Can't join the room", error);
}
});

newSocket.on("connect_error", (error) => {
attempts++;
console.log("Failed to connect to the socket server. Retrying...")
setTimeout(() => closeConnection(newSocket), DELAY_DURATION);
})

} catch (error) {
console.log("Can't connect", error);
}
}
}, []);

// Hook to listen the events emitted by the server
useEffect(() => {
if (hasCollaborationStarted && socket) {
socket.on("open-text-editor", () => {
setTextEditor(true);
});

return () => {
socket.off("open-text-editor");
};
}
}, [showTextEditor, socket, hasCollaborationStarted]);

// Hook to display a message when someone opens the app through vercel's url
useEffect(() => {
const isVercel = window.location.hostname === "drawrtcbyajeet.vercel.app"
if (isVercel) {
setMessageText("Collaborative features require local installation. For more information, please visit our GitHub repository (linked in the menu).");
setShowMsg(true);
}
}, []);

// Hook to hide the menu when user click elsewhere on the screen
useEffect(() => {
const canvas = document.getElementById("canvas");
canvas.addEventListener("click", () => {
if (showMenu) {
setShowMenu(false);
}
})
}, [showMenu]);

return (
<div id="container">
<Sidebar
@@ -172,6 +274,7 @@ function App() {
{eraserMode && <EraserCursor></EraserCursor>}
{showMenu && <Menu></Menu>}
{textEditor && <TextEditor></TextEditor>}
{showMessage && <InfoMsg message={messageText} clickHandler={closeMsg}></InfoMsg>}
</div>
);
}
20 changes: 20 additions & 0 deletions client/src/Context.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React, { createContext, useContext, useState } from 'react';

// Create a Context for the socket
const SocketContext = createContext(null);

// Create a custom hook to use the SocketContext
export const useSocket = () => {
return useContext(SocketContext);
};

// Create a provider component
export const SocketProvider = ({ children }) => {
const [socket, setSocket] = useState(null);

return (
<SocketContext.Provider value={{ socket, setSocket }}>
{children}
</SocketContext.Provider>
);
};
Loading