Initial Commit
This commit is contained in:
47
public/index.html
Normal file
47
public/index.html
Normal file
@@ -0,0 +1,47 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
|
||||
<title>Dogeller</title>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css?family=Montserrat:300,400,500,700&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.3.0/socket.io.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header class="header">
|
||||
<div class="logo-container">
|
||||
<img src="./img/doge.png" alt="doge logo" class="logo-img" />
|
||||
<h1 class="logo-text">
|
||||
Doge<span class="logo-highlight">ller</span>
|
||||
</h1>
|
||||
</div>
|
||||
</header>
|
||||
<div class="content-container">
|
||||
<div class="active-users-panel" id="active-user-container">
|
||||
<h3 class="panel-title">Active Users:</h3>
|
||||
</div>
|
||||
<div class="video-chat-container">
|
||||
<h2 class="talk-info" id="talking-with-info">
|
||||
Select active user on the left menu.
|
||||
</h2>
|
||||
<div class="video-container">
|
||||
<video autoplay class="remote-video" id="remote-video"></video>
|
||||
<video autoplay muted class="local-video" id="local-video"></video>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.socket.io/4.8.1/socket.io.min.js" integrity="sha384-mkQ3/7FUtcGyoppY6bz/PORYoGqOl7/aSUMn2ymDOJcapfS6PHqxhRTMh1RR0Q6+" crossorigin="anonymous"></script>
|
||||
|
||||
|
||||
|
||||
<script src="./scripts/index.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
149
public/scripts/index.js
Normal file
149
public/scripts/index.js
Normal file
@@ -0,0 +1,149 @@
|
||||
let isAlreadyCalling = false;
|
||||
let getCalled = false;
|
||||
|
||||
const existingCalls = [];
|
||||
|
||||
const { RTCPeerConnection, RTCSessionDescription } = window;
|
||||
|
||||
const peerConnection = new RTCPeerConnection();
|
||||
|
||||
function unselectUsersFromList() {
|
||||
const alreadySelectedUser = document.querySelectorAll(
|
||||
".active-user.active-user--selected"
|
||||
);
|
||||
|
||||
alreadySelectedUser.forEach(el => {
|
||||
el.setAttribute("class", "active-user");
|
||||
});
|
||||
}
|
||||
|
||||
function createUserItemContainer(socketId) {
|
||||
const userContainerEl = document.createElement("div");
|
||||
|
||||
const usernameEl = document.createElement("p");
|
||||
|
||||
userContainerEl.setAttribute("class", "active-user");
|
||||
userContainerEl.setAttribute("id", socketId);
|
||||
usernameEl.setAttribute("class", "username");
|
||||
usernameEl.innerHTML = `Socket: ${socketId}`;
|
||||
|
||||
userContainerEl.appendChild(usernameEl);
|
||||
|
||||
userContainerEl.addEventListener("click", () => {
|
||||
unselectUsersFromList();
|
||||
userContainerEl.setAttribute("class", "active-user active-user--selected");
|
||||
const talkingWithInfo = document.getElementById("talking-with-info");
|
||||
talkingWithInfo.innerHTML = `Talking with: "Socket: ${socketId}"`;
|
||||
console.log('calling socket id [%o]', socketId);
|
||||
callUser(socketId);
|
||||
});
|
||||
|
||||
return userContainerEl;
|
||||
}
|
||||
|
||||
async function callUser(socketId) {
|
||||
const offer = await peerConnection.createOffer();
|
||||
await peerConnection.setLocalDescription(new RTCSessionDescription(offer));
|
||||
|
||||
socket.emit("call-user", {
|
||||
offer,
|
||||
to: socketId
|
||||
});
|
||||
}
|
||||
|
||||
function updateUserList(socketIds) {
|
||||
const activeUserContainer = document.getElementById("active-user-container");
|
||||
|
||||
socketIds.forEach(socketId => {
|
||||
if (socketId === socket.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const alreadyExistingUser = document.getElementById(socketId);
|
||||
if (!alreadyExistingUser) {
|
||||
const userContainerEl = createUserItemContainer(socketId);
|
||||
|
||||
activeUserContainer.appendChild(userContainerEl);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const socket = io.connect("localhost:5555");
|
||||
|
||||
socket.on("update-user-list", ({ users }) => {
|
||||
updateUserList(users);
|
||||
});
|
||||
|
||||
socket.on("remove-user", ({ socketId }) => {
|
||||
const elToRemove = document.getElementById(socketId);
|
||||
|
||||
if (elToRemove) {
|
||||
elToRemove.remove();
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("call-made", async data => {
|
||||
if (getCalled) {
|
||||
const confirmed = confirm(
|
||||
`User "Socket: ${data.socket}" wants to call you. Do accept this call?`
|
||||
);
|
||||
|
||||
if (!confirmed) {
|
||||
socket.emit("reject-call", {
|
||||
from: data.socket
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await peerConnection.setRemoteDescription(
|
||||
new RTCSessionDescription(data.offer)
|
||||
);
|
||||
const answer = await peerConnection.createAnswer();
|
||||
await peerConnection.setLocalDescription(new RTCSessionDescription(answer));
|
||||
|
||||
socket.emit("make-answer", {
|
||||
answer,
|
||||
to: data.socket
|
||||
});
|
||||
getCalled = true;
|
||||
});
|
||||
|
||||
socket.on("answer-made", async data => {
|
||||
await peerConnection.setRemoteDescription(
|
||||
new RTCSessionDescription(data.answer)
|
||||
);
|
||||
|
||||
if (!isAlreadyCalling) {
|
||||
callUser(data.socket);
|
||||
isAlreadyCalling = true;
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("call-rejected", data => {
|
||||
alert(`User: "Socket: ${data.socket}" rejected your call.`);
|
||||
unselectUsersFromList();
|
||||
});
|
||||
|
||||
peerConnection.ontrack = function({ streams: [stream] }) {
|
||||
const remoteVideo = document.getElementById("remote-video");
|
||||
if (remoteVideo) {
|
||||
remoteVideo.srcObject = stream;
|
||||
}
|
||||
};
|
||||
|
||||
navigator.getUserMedia(
|
||||
{ video: true, audio: true },
|
||||
stream => {
|
||||
const localVideo = document.getElementById("local-video");
|
||||
if (localVideo) {
|
||||
localVideo.srcObject = stream;
|
||||
}
|
||||
|
||||
stream.getTracks().forEach(track => peerConnection.addTrack(track, stream));
|
||||
},
|
||||
error => {
|
||||
console.warn(error.message);
|
||||
}
|
||||
);
|
||||
105
public/styles.css
Normal file
105
public/styles.css
Normal file
@@ -0,0 +1,105 @@
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: "Montserrat", sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
background-color: #f9fafc;
|
||||
color: #595354;
|
||||
}
|
||||
|
||||
.header {
|
||||
background-color: #ffffff;
|
||||
padding: 10px 40px;
|
||||
box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.header > .logo-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header > .logo-container > .logo-img {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.header > .logo-container > .logo-text {
|
||||
font-size: 26px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.header > .logo-container > .logo-text > .logo-highlight {
|
||||
color: #65a9e5;
|
||||
}
|
||||
|
||||
.content-container {
|
||||
width: 100%;
|
||||
height: calc(100vh - 89px);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.active-users-panel {
|
||||
width: 300px;
|
||||
height: 100%;
|
||||
border-right: 1px solid #cddfe7;
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
margin: 10px 0 0 0;
|
||||
padding-left: 30px;
|
||||
font-weight: 500;
|
||||
font-size: 18px;
|
||||
border-bottom: 1px solid #cddfe7;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.active-user {
|
||||
padding: 10px 30px;
|
||||
border-bottom: 1px solid #cddfe7;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.active-user:hover {
|
||||
background-color: #e8e9eb;
|
||||
transition: background-color 0.5s ease;
|
||||
}
|
||||
|
||||
.active-user--selected {
|
||||
background-color: #fff;
|
||||
border-right: 5px solid #65a9e5;
|
||||
font-weight: 500;
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
.video-chat-container {
|
||||
padding: 0 20px;
|
||||
flex: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.talk-info {
|
||||
font-weight: 500;
|
||||
font-size: 21px;
|
||||
}
|
||||
|
||||
.remote-video {
|
||||
border: 1px solid #cddfe7;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.local-video {
|
||||
position: absolute;
|
||||
border: 1px solid #cddfe7;
|
||||
bottom: 60px;
|
||||
right: 40px;
|
||||
box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.2);
|
||||
border-radius: 5px;
|
||||
width: 300px;
|
||||
}
|
||||
Reference in New Issue
Block a user