Skip to content

Commit bfae6e7

Browse files
committed
initial commit
0 parents  commit bfae6e7

File tree

7 files changed

+1493
-0
lines changed

7 files changed

+1493
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
server/node_modules/

client/index.html

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<title>Document</title>
8+
<link rel="stylesheet" href="style.css">
9+
</head>
10+
<body>
11+
<!-- A better approach would be to dynamically generate these elements -->
12+
<div class="register-box">
13+
<h2 class="heading">Register yourself</h2>
14+
<div style="width: 70%; margin: 0 auto">
15+
<div class="chatbox" style="margin-bottom: 1.4rem">
16+
<label for="username">Enter Username</label>
17+
<input type="text" name="username" id="username" />
18+
</div>
19+
<button class="regBtn">Submit</button>
20+
</div>
21+
</div>
22+
<div class="section-2" style="opacity: 0">
23+
<div class="heading">Let's Chat</div>
24+
<div class="container">
25+
<div class="online-users">
26+
<h2>Online-Users</h2>
27+
<ul class="users-list"></ul>
28+
</div>
29+
<div class="chatbox-container">
30+
<div class="chatbox">
31+
<label for="message"
32+
>Type Message:<span id="typingUsers"></span
33+
></label>
34+
<input type="text" name="message" id="message" />
35+
<button disabled class="send">Send</button>
36+
</div>
37+
<div class="allMessages"></div>
38+
</div>
39+
</div>
40+
</div>
41+
42+
<script src="./index.js"></script>
43+
</body>
44+
</html>

client/index.js

+192
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
document.addEventListener("DOMContentLoaded", init);
2+
3+
function init() {
4+
// dom references
5+
const messageBox = document.querySelector("#message");
6+
const sendBtn = document.querySelector(".send");
7+
const regBtn = document.querySelector(".regBtn");
8+
const username = document.querySelector("#username");
9+
const registerBox = document.querySelector(".register-box");
10+
const section2 = document.querySelector(".section-2");
11+
const userList = document.querySelector(".users-list");
12+
const messages = document.querySelector(".allMessages");
13+
const typingUsers = document.querySelector("#typingUsers");
14+
15+
let isTyping = false;
16+
17+
// animations
18+
const animations = {
19+
fadeOut: [{ opacity: 1 }, { opacity: 0 }],
20+
fadeIn: [{ opacity: 0 }, { opacity: 1 }],
21+
};
22+
23+
// some constant values
24+
const url = "ws://localhost:3004";
25+
26+
// adding some functionalities to the WebSocket instance
27+
WebSocket.prototype.emit = function (event, data) {
28+
this.send(JSON.stringify({ event, data }));
29+
};
30+
31+
WebSocket.prototype.listen = function (eventName, callback) {
32+
this._socketEvents = this._socketEvents || {};
33+
this._socketEvents[eventName] = this._socketEvents[eventName] || [];
34+
this._socketEvents[eventName].push(callback);
35+
};
36+
37+
const websocket = new WebSocket(url);
38+
39+
function handleRegister(ev) {
40+
const msg = username.value;
41+
if (!msg) {
42+
alert("enter some username");
43+
return;
44+
}
45+
46+
websocket.emit("user", { msg });
47+
48+
registerBox.animate(animations.fadeOut, {
49+
duration: 300,
50+
easing: "linear",
51+
}).onfinish = (ev) => {
52+
ev.target.effect.target.remove();
53+
};
54+
55+
section2.animate(animations.fadeIn, {
56+
duration: 600,
57+
easing: "linear",
58+
fill: "forwards",
59+
delay: 300,
60+
});
61+
62+
username.value = "";
63+
}
64+
65+
function handleKeyPress(ev) {
66+
const message = ev.currentTarget.value;
67+
if (message) {
68+
sendBtn.disabled = false;
69+
if (!isTyping) {
70+
websocket.emit("addTyping");
71+
isTyping = true;
72+
}
73+
} else {
74+
sendBtn.disabled = true;
75+
websocket.emit("removeTyping");
76+
isTyping = false;
77+
}
78+
}
79+
80+
function handleVisibilityChange() {
81+
if (document.visibilityState === "visible") {
82+
websocket.emit("updateUserStatus", { msg: "green" });
83+
} else {
84+
websocket.emit("updateUserStatus", { msg: "yellow" });
85+
}
86+
}
87+
88+
function sendMessage() {
89+
const msg = messageBox.value;
90+
if (msg) {
91+
websocket.emit("oldMessage", { msg });
92+
websocket.emit("removeTyping");
93+
isTyping = false;
94+
messageBox.value = "";
95+
sendBtn.disabled = true;
96+
}
97+
}
98+
99+
function onOpen() {
100+
console.log("web socket connection is open");
101+
messageBox.addEventListener("keyup", handleKeyPress);
102+
sendBtn.addEventListener("click", sendMessage);
103+
regBtn.addEventListener("click", handleRegister);
104+
document.addEventListener("visibilitychange", handleVisibilityChange);
105+
106+
// adding some events and their listeners
107+
108+
websocket.listen("newMessage", (messageArray) => {
109+
// clear the old html
110+
messages.innerHTML = "";
111+
const fragment = document.createDocumentFragment();
112+
113+
messageArray.forEach((message) => {
114+
const p = document.createElement("p");
115+
p.textContent = `${message.username}: ${message.msg}`;
116+
fragment.appendChild(p);
117+
});
118+
119+
messages.appendChild(fragment);
120+
});
121+
122+
websocket.listen("allUsers", (users) => {
123+
// clear the old html
124+
userList.innerHTML = "";
125+
126+
const fragment = document.createDocumentFragment();
127+
users.forEach((user) => {
128+
if (user) {
129+
const li = document.createElement("li");
130+
li.textContent = `${user.username}`;
131+
li.classList.add(user.status);
132+
fragment.appendChild(li);
133+
}
134+
});
135+
136+
userList.appendChild(fragment);
137+
});
138+
139+
websocket.listen("typingUsers", (users) => {
140+
console.log(users.length);
141+
let str = "";
142+
if (!users.length) {
143+
// clear the old content
144+
typingUsers.textContent = "";
145+
return;
146+
};
147+
148+
if (users.length === 1) {
149+
str = `${users[0]} is typing...`;
150+
} else {
151+
str = "".concat(
152+
users[0],
153+
" and ",
154+
users.length - 1,
155+
" more are typing..."
156+
);
157+
}
158+
159+
typingUsers.textContent = str;
160+
});
161+
}
162+
163+
function onMessage(ev) {
164+
try {
165+
const { event, data } = JSON.parse(ev.data);
166+
console.log(event, data);
167+
websocket._socketEvents[event].forEach((callback) => {
168+
callback(data);
169+
});
170+
} catch (err) {
171+
// ignore errors due to some string text or binary data from server
172+
// those data not needed for this app
173+
}
174+
}
175+
176+
function onClose() {
177+
console.log("closed");
178+
messageBox.removeEventListener("keyup", handleKeyPress);
179+
sendBtn.removeEventListener("click", sendMessage);
180+
regBtn.removeEventListener("click", handleRegister);
181+
document.removeEventListener("visibilitychange", handleVisibilityChange);
182+
}
183+
184+
function onError(err) {
185+
console.log(err);
186+
}
187+
188+
websocket.addEventListener("open", onOpen);
189+
websocket.addEventListener("error", onError);
190+
websocket.addEventListener("close", onClose);
191+
websocket.addEventListener("message", onMessage);
192+
}

client/style.css

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
body {
2+
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
3+
min-height: 100vh;
4+
color: white;
5+
background: linear-gradient(132deg, rgb(253, 54, 133) 0.00%, rgb(254, 207, 215) 100.00%);
6+
background-size: 300% 100%;
7+
animation: animateBg 5s linear 0ms infinite alternate;
8+
}
9+
10+
@keyframes animateBg {
11+
0% {
12+
background-position: 0 0;
13+
}
14+
100% {
15+
background-position: 100% 0;
16+
}
17+
}
18+
19+
button {
20+
color: white;
21+
background-color: rgb(11, 92, 143);
22+
padding: 0.8rem 1.8rem;
23+
border: none;
24+
font-size: 1rem;
25+
}
26+
27+
ul {
28+
list-style-type: none;
29+
}
30+
31+
.container {
32+
display: flex;
33+
gap: 1rem;
34+
justify-content: center;
35+
}
36+
37+
.heading {
38+
display: flex;
39+
justify-content: center;
40+
font-size: 2.4rem;
41+
font-weight: bolder;
42+
margin-bottom: 1.6rem;
43+
}
44+
45+
.online-users {
46+
flex-basis: 27%;
47+
}
48+
49+
.chatbox-container {
50+
width: 45%;
51+
}
52+
53+
.chatbox {
54+
display: flex;
55+
flex-direction: column;
56+
gap: 0.7rem;
57+
}
58+
59+
.chatbox > * {
60+
font-size: 1.2rem;
61+
}
62+
63+
.register-box.hide {
64+
opacity: 0;
65+
animation: fade-out 300ms linear 0s;
66+
}
67+
68+
.users-list .green::after {
69+
content: "";
70+
background-color: green;
71+
border-radius: 50%;
72+
width: 8px;
73+
height: 8px;
74+
display: inline-block;
75+
margin-left: 5px;
76+
}
77+
78+
.users-list .yellow::after {
79+
content: "";
80+
background-color: yellow;
81+
border-radius: 50%;
82+
width: 8px;
83+
height: 8px;
84+
display: inline-block;
85+
margin-left: 5px;
86+
}
87+
88+
.allMessages {
89+
max-height: 50vh;
90+
margin-top: 14px;
91+
overflow: auto;
92+
}

0 commit comments

Comments
 (0)