diff --git a/README.md b/README.md index 20d74b3..f526596 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,18 @@ npm start --- +## One-Click Call Between Two Users + +Allows a user to `join` the room as a `user1` + +[http://localhost:8000/join?user=user1](http://localhost:8000/join?user=user1) + +Lets the `user2 join` the room and initiate a `call` to the `user1` + +[http://localhost:8000/join?user=user2&call=user1](http://localhost:8000/join?user=user2&call=user1) + +--- + ## API Get all connected users diff --git a/app/server.js b/app/server.js index fe35031..a359117 100755 --- a/app/server.js +++ b/app/server.js @@ -15,6 +15,9 @@ const packageJson = require('../package.json'); // Public directory location const PUBLIC_DIR = path.join(__dirname, '../', 'public'); +// Home page/client +const HOME = path.join(PUBLIC_DIR, '/index.html'); + // Map to store connected users const users = new Map(); @@ -101,7 +104,7 @@ app.use(config.apiBasePath + '/docs', swaggerUi.serve, swaggerUi.setup(config.sw // Logs requests app.use((req, res, next) => { console.log('New request', { - headers: req.headers, + //headers: req.headers, body: req.body, method: req.method, path: req.originalUrl, @@ -111,7 +114,27 @@ app.use((req, res, next) => { // Set up route to serve the main HTML file app.get('/', (req, res) => { - res.sendFile(path.join(PUBLIC_DIR, '/index.html')); + res.sendFile(HOME); +}); + +// Direct Join room +app.get('/join/', (req, res) => { + if (Object.keys(req.query).length > 0) { + console.log('Request query', req.query); + // http://localhost:8000/join?user=user1 + // http://localhost:8000/join?user=user2&call=user1 + const { user, call } = req.query; + if (user || (user && call)) { + return res.sendFile(HOME); + } + return notFound(res); + } + return notFound(res); +}); + +// Page not found +app.get('*', (req, res) => { + return notFound(res); }); // Axios API requests @@ -131,6 +154,11 @@ app.get(`${config.apiBasePath}/users`, (req, res) => { return res.json({ users }); }); +// Page not found +function notFound(res) { + res.json({ data: '404 not found' }); +} + // Function to handle individual WebSocket connections function handleConnection(socket) { console.log('User connected:', socket.id); @@ -227,12 +255,21 @@ function handleConnection(socket) { // Function to handle signaling messages (offer, answer, candidate, leave) function handleSignalingMessage(data) { - const { name } = data; + const { type, name } = data; const recipientSocket = users.get(name); - if (recipientSocket) { - sendMsgTo(recipientSocket, { ...data, name: socket.username }); - } else { - sendError(socket, `User ${name} not found`); + + switch (type) { + case 'leave': + if (recipientSocket !== undefined) { + console.log('Leave room', socket.username); + sendMsgTo(recipientSocket, { type: 'leave' }); + } + break; + default: + if (recipientSocket !== undefined) { + sendMsgTo(recipientSocket, { ...data, name: socket.username }); + } + break; } } @@ -241,10 +278,6 @@ function handleConnection(socket) { if (socket.username) { console.log('User disconnected:', socket.username); users.delete(socket.username); - const recipientSocket = users.get(socket.recipient); - if (recipientSocket) { - sendMsgTo(recipientSocket, { type: 'leave' }); - } console.log('Connected Users', getConnectedUsers()); } } diff --git a/package.json b/package.json index 3412661..e35eebf 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "call-me", - "version": "1.0.10", + "version": "1.0.12", "description": "Your Go-To for Instant Video Calls", "author": "Miroslav Pejic - miroslav.pejic.85@gmail.com", "license": "AGPLv3", diff --git a/public/client.js b/public/client.js index 2366c4a..8c61481 100755 --- a/public/client.js +++ b/public/client.js @@ -33,16 +33,47 @@ let stream; // Hide room page initially roomPage.style.display = 'none'; +// On html page loaded... document.addEventListener('DOMContentLoaded', function () { - // Initialize tooltips + handleToolTip(); + handleLocalStorage(); + handleDirectJoin(); +}); + +// Initialize tooltips +function handleToolTip() { const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-toggle="tooltip"]')); const tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) { return new bootstrap.Tooltip(tooltipTriggerEl); }); - // Handle localStorage data +} + +// Handle localStorage data +function handleLocalStorage() { usernameIn.value = localStorage.callMeUsername ? localStorage.callMeUsername : ''; callUsernameIn.value = localStorage.callMeUsernameToCall ? localStorage.callMeUsernameToCall : ''; -}); +} + +// Handle Room direct join +function handleDirectJoin() { + const usp = new URLSearchParams(window.location.search); + const user = usp.get('user'); + const call = usp.get('call'); + + console.log('Direct Join detected', { user, call }); + + if (user) { + // SignIn + usernameIn.value = user; + handleSignInClick(); + + if (call) { + // Call user if call is provided + callUsernameIn.value = call; + handleCallClick(); + } + } +} // WebSocket event listeners socket.on('connect', handleSocketConnect); @@ -147,7 +178,7 @@ function toggleLocalVideo() { // Handle hang-up button click function handleHangUpClick() { - sendMsg({ type: 'leave' }); + sendMsg({ type: 'leave', name: socket.recipient }); handleLeave(); } @@ -178,6 +209,7 @@ function handleSignIn(data) { const { success } = data; if (!success) { handleError('Username already in use. Try a different username.'); + setTimeout(handleHangUpClick, 2000); } else { githubDiv.style.display = 'none'; signInPage.style.display = 'none'; @@ -277,6 +309,7 @@ function offerAccept(data) { callUsernameIn.style.display = 'none'; callBtn.style.display = 'none'; data.type = 'offerCreate'; + socket.recipient = data.from; } else { data.type = 'offerDecline'; } @@ -336,12 +369,23 @@ async function sound(name) { // Handle leaving the room function handleLeave() { - connectedUser = null; - remoteVideo.srcObject = null; + // Stop local video tracks + if (localVideo.srcObject != null) { + localVideo.srcObject.getTracks().forEach((track) => track.stop()); + localVideo.srcObject = null; + } + // Stop remote video tracks + if (remoteVideo.srcObject != null) { + remoteVideo.srcObject.getTracks().forEach((track) => track.stop()); + remoteVideo.srcObject = null; + } + // Disconnect from server if (thisConnection) { thisConnection.close(); thisConnection = null; } + // GoTo homepage + connectedUser = null; window.location.href = '/'; }