First Commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/node_modules
|
||||
2219
package-lock.json
generated
Normal file
2219
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
22
package.json
Normal file
22
package.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "7-chat-app",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "node src/index.js",
|
||||
"dev": "nodemon src/index.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"bad-words": "^3.0.4",
|
||||
"express": "^4.18.2",
|
||||
"moment": "^2.29.4",
|
||||
"socket.io": "^4.7.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^3.0.2"
|
||||
}
|
||||
}
|
||||
68
public/chat.html
Normal file
68
public/chat.html
Normal file
@@ -0,0 +1,68 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Chat App</title>
|
||||
<link rel="icon" href="./img/favicon.png">
|
||||
<link rel="stylesheet" href="./css/styles.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="chat">
|
||||
<div id="sidebar" class="chat__sidebar">
|
||||
|
||||
</div>
|
||||
<div class="chat__main">
|
||||
<div id="messages" class="chat__messages"></div>
|
||||
|
||||
<div class="compose">
|
||||
<!-- <button id="increment">+1</button> -->
|
||||
<form id="message-form">
|
||||
<input type="text" name="message" placeholder="Enter message" autocomplete="off">
|
||||
<button type="submit">Send</button>
|
||||
</form>
|
||||
<button id="send-location">Send Location</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<script id="message-template" type="text/html">
|
||||
<div class="message">
|
||||
<p>
|
||||
<span class="message__name">{{username}}</span>
|
||||
<span class="message__meta">{{createdAt}}</span>
|
||||
</p>
|
||||
<p>{{message}}</p>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script id="locmessage-template" type="text/html">
|
||||
<div class="message">
|
||||
<p>
|
||||
<span class="message__name">{{username}}</span>
|
||||
<span class="message__meta">{{createdAt}}</span>
|
||||
</p>
|
||||
<p><a href="{{url}}" target="_blank" style="color: #7C5CBF;">My Current Location</a></p>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script id="sidebar-template" type="text/html">
|
||||
<h2 class="room-title" >Room: {{room}}</h2>
|
||||
<h3 class="list-title" >Users</h3>
|
||||
<ul class="users" >
|
||||
{{#users}}
|
||||
<li> {{username}} </li>
|
||||
{{/users}}
|
||||
</ul>
|
||||
</script>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/3.0.1/mustache.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/qs/6.6.0/qs.min.js"></script>
|
||||
<script src="/socket.io/socket.io.js"></script>
|
||||
<script src="./js/chat.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
BIN
public/css/.DS_Store
vendored
Normal file
BIN
public/css/.DS_Store
vendored
Normal file
Binary file not shown.
183
public/css/styles.css
Normal file
183
public/css/styles.css
Normal file
@@ -0,0 +1,183 @@
|
||||
/* General Styles */
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
input {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
body {
|
||||
line-height: 1.4;
|
||||
color: #333333;
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
margin-bottom: 8px;
|
||||
color: #777;
|
||||
}
|
||||
|
||||
input {
|
||||
border: 1px solid #eeeeee;
|
||||
padding: 12px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
button {
|
||||
cursor: pointer;
|
||||
padding: 12px;
|
||||
background: #7C5CBF;
|
||||
border: none;
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
transition: background .3s ease;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: #6b47b8;
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
cursor: default;
|
||||
background: #7c5cbf94;
|
||||
}
|
||||
|
||||
/* Join Page Styles */
|
||||
|
||||
.centered-form {
|
||||
background: #333744;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.centered-form__box {
|
||||
box-shadow: 0px 0px 17px 1px #1D1F26;
|
||||
background: #F7F7FA;
|
||||
padding: 24px;
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
.centered-form button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.centered-form input {
|
||||
margin-bottom: 16px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Chat Page Layout */
|
||||
|
||||
.chat {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.chat__sidebar {
|
||||
height: 100vh;
|
||||
color: white;
|
||||
background: #333744;
|
||||
width: 225px;
|
||||
overflow-y: scroll
|
||||
}
|
||||
|
||||
/* Chat styles */
|
||||
|
||||
.chat__main {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-height: 100vh;
|
||||
}
|
||||
|
||||
.chat__messages {
|
||||
flex-grow: 1;
|
||||
padding: 24px 24px 0 24px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
/* Message Styles */
|
||||
|
||||
.message {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.message__name {
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.message__meta {
|
||||
color: #777;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.message a {
|
||||
color: #0070CC;
|
||||
}
|
||||
|
||||
/* Message Composition Styles */
|
||||
|
||||
.compose {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
margin-top: 16px;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.compose form {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.compose input {
|
||||
border: 1px solid #eeeeee;
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
margin: 0 16px 0 0;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.compose button {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* Chat Sidebar Styles */
|
||||
|
||||
.room-title {
|
||||
font-weight: 400;
|
||||
font-size: 22px;
|
||||
background: #2c2f3a;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.list-title {
|
||||
font-weight: 500;
|
||||
font-size: 18px;
|
||||
margin-bottom: 4px;
|
||||
padding: 12px 24px 0 24px;
|
||||
}
|
||||
|
||||
.users {
|
||||
list-style-type: none;
|
||||
font-weight: 300;
|
||||
padding: 12px 24px 0 24px;
|
||||
}
|
||||
169
public/css/styles.min.css
vendored
Normal file
169
public/css/styles.min.css
vendored
Normal file
@@ -0,0 +1,169 @@
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: 16px
|
||||
}
|
||||
|
||||
input {
|
||||
font-size: 14px
|
||||
}
|
||||
|
||||
body {
|
||||
line-height: 1.4;
|
||||
color: #333;
|
||||
font-family: Helvetica, Arial, sans-serif
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-bottom: 16px
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
margin-bottom: 8px;
|
||||
color: #777
|
||||
}
|
||||
|
||||
input {
|
||||
border: 1px solid #eee;
|
||||
padding: 12px;
|
||||
outline: none
|
||||
}
|
||||
|
||||
button {
|
||||
cursor: pointer;
|
||||
padding: 12px;
|
||||
background: #7C5CBF;
|
||||
border: none;
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
transition: background .3s ease
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: #6b47b8
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
cursor: default;
|
||||
background: #7c5cbf94
|
||||
}
|
||||
|
||||
.centered-form {
|
||||
background: #333744;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center
|
||||
}
|
||||
|
||||
.centered-form__box {
|
||||
box-shadow: 0 0 17px 1px #1D1F26;
|
||||
background: #F7F7FA;
|
||||
padding: 24px;
|
||||
width: 250px
|
||||
}
|
||||
|
||||
.centered-form button {
|
||||
width: 100%
|
||||
}
|
||||
|
||||
.centered-form input {
|
||||
margin-bottom: 16px;
|
||||
width: 100%
|
||||
}
|
||||
|
||||
.chat {
|
||||
display: flex
|
||||
}
|
||||
|
||||
.chat__sidebar {
|
||||
height: 100vh;
|
||||
color: #fff;
|
||||
background: #333744;
|
||||
width: 225px;
|
||||
overflow-y: scroll
|
||||
}
|
||||
|
||||
.chat__main {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-height: 100vh
|
||||
}
|
||||
|
||||
.chat__messages {
|
||||
flex-grow: 1;
|
||||
padding: 24px 24px 0;
|
||||
overflow-y: scroll
|
||||
}
|
||||
|
||||
.message {
|
||||
margin-bottom: 16px
|
||||
}
|
||||
|
||||
.message__name {
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
margin-right: 8px
|
||||
}
|
||||
|
||||
.message__meta {
|
||||
color: #777;
|
||||
font-size: 14px
|
||||
}
|
||||
|
||||
.message a {
|
||||
color: #0070CC
|
||||
}
|
||||
|
||||
.compose {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
margin-top: 16px;
|
||||
padding: 24px
|
||||
}
|
||||
|
||||
.compose form {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
margin-right: 16px
|
||||
}
|
||||
|
||||
.compose input {
|
||||
border: 1px solid #eee;
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
margin: 0 16px 0 0;
|
||||
flex-grow: 1
|
||||
}
|
||||
|
||||
.compose button {
|
||||
font-size: 14px
|
||||
}
|
||||
|
||||
.room-title {
|
||||
font-weight: 400;
|
||||
font-size: 22px;
|
||||
background: #2c2f3a;
|
||||
padding: 24px
|
||||
}
|
||||
|
||||
.list-title {
|
||||
font-weight: 500;
|
||||
font-size: 18px;
|
||||
margin-bottom: 4px;
|
||||
padding: 12px 24px 0
|
||||
}
|
||||
|
||||
.users {
|
||||
list-style-type: none;
|
||||
font-weight: 300;
|
||||
padding: 12px 24px 0
|
||||
}
|
||||
BIN
public/img/favicon.png
Normal file
BIN
public/img/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
28
public/index.html
Normal file
28
public/index.html
Normal file
@@ -0,0 +1,28 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Chat App</title>
|
||||
<link rel="icon" href="./img/favicon.png">
|
||||
<link rel="stylesheet" href="./css/styles.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="centered-form">
|
||||
<div class="centered-form__box">
|
||||
<h1>Join</h1>
|
||||
<form action="./chat.html">
|
||||
<label>Display Name</label>
|
||||
<input type="text" name="username" required>
|
||||
<label>Room</label>
|
||||
<input type="text" name="room" required>
|
||||
<button>Join</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
149
public/js/chat.js
Normal file
149
public/js/chat.js
Normal file
@@ -0,0 +1,149 @@
|
||||
const socket = io();
|
||||
|
||||
// Elements
|
||||
const $messageForm = document.querySelector("#message-form");
|
||||
const $messageFormInput = $messageForm.querySelector("input");
|
||||
const $messageFormButton = $messageForm.querySelector("button");
|
||||
const $sendLocationButton = document.querySelector("#send-location");
|
||||
const $messages = document.querySelector("#messages");
|
||||
|
||||
// Templates
|
||||
|
||||
const messageTemplate = document.querySelector("#message-template").innerHTML;
|
||||
const locationTemplate = document.querySelector(
|
||||
"#locmessage-template"
|
||||
).innerHTML;
|
||||
const sidebarTemplate = document.querySelector("#sidebar-template").innerHTML;
|
||||
|
||||
// Options
|
||||
const { username, room } = Qs.parse(location.search, {
|
||||
ignoreQueryPrefix: true,
|
||||
});
|
||||
|
||||
const autoScroll = () => {
|
||||
// New message element
|
||||
const $newMessage = $messages.lastElementChild;
|
||||
|
||||
// hight of the new message
|
||||
const newMessageStyle = getComputedStyle($newMessage);
|
||||
const newMessageMargin = parseInt(newMessageStyle.marginBottom);
|
||||
const newMessageHeight = $newMessage.offsetHeight + newMessageMargin;
|
||||
|
||||
// visible height
|
||||
const visibleHeight = $messages.offsetHeight;
|
||||
|
||||
// height of messages container
|
||||
const containerHeight = $messages.scrollHeight;
|
||||
|
||||
// how far have I scrolled?
|
||||
const scrollOffset = $messages.scrollTop + visibleHeight;
|
||||
|
||||
if (containerHeight - newMessageHeight <= scrollOffset) {
|
||||
$messages.scrollTop = $messages.scrollHeight;
|
||||
}
|
||||
};
|
||||
|
||||
// server (emit) -> client (receive) - countUpdated
|
||||
// client (emit) -> server (receive) - increment
|
||||
|
||||
socket.on("message", (message) => {
|
||||
// console.log(message);
|
||||
const html = Mustache.render(messageTemplate, {
|
||||
username: message.username,
|
||||
message: message.text,
|
||||
createdAt: moment(message.createdAt).format("h:mm a"),
|
||||
});
|
||||
$messages.insertAdjacentHTML("beforeend", html);
|
||||
autoScroll();
|
||||
});
|
||||
|
||||
socket.on("locationMessage", (url) => {
|
||||
// console.log(url.username);
|
||||
const html = Mustache.render(locationTemplate, {
|
||||
username: url.username,
|
||||
url: url.url,
|
||||
createdAt: moment(url.createdAt).format("h:mm a"),
|
||||
});
|
||||
$messages.insertAdjacentHTML("beforeend", html);
|
||||
autoScroll();
|
||||
});
|
||||
|
||||
socket.on("roomData", ({ room, users }) => {
|
||||
const html = Mustache.render(sidebarTemplate, {
|
||||
room: room,
|
||||
users: users,
|
||||
});
|
||||
document.querySelector("#sidebar").innerHTML = html;
|
||||
});
|
||||
|
||||
$messageForm.addEventListener("submit", (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
// disable form after submit
|
||||
$messageFormButton.setAttribute("disabled", "disabled");
|
||||
|
||||
//disable
|
||||
|
||||
const message = $messageFormInput.value;
|
||||
|
||||
if (message === "") {
|
||||
// enable form after submit
|
||||
$messageFormButton.removeAttribute("disabled");
|
||||
return;
|
||||
}
|
||||
socket.emit("sendMessage", message, (error) => {
|
||||
// enable form after submit
|
||||
$messageFormButton.removeAttribute("disabled");
|
||||
// clear input
|
||||
$messageFormInput.value = "";
|
||||
// focus input
|
||||
$messageFormInput.focus();
|
||||
if (error) {
|
||||
return console.log(error);
|
||||
}
|
||||
// console.log("Message Delivered");
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelector("#send-location").addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
if (!navigator.geolocation) {
|
||||
return alert("Geolocation is not supported by your browser");
|
||||
}
|
||||
|
||||
navigator.permissions.query({ name: "geolocation" }).then((res) => {
|
||||
// console.log(res);
|
||||
if (res.state === "denied") {
|
||||
return alert("Please allow permission to send location!");
|
||||
}
|
||||
});
|
||||
|
||||
navigator.geolocation.getCurrentPosition((position) => {
|
||||
// console.log(position);
|
||||
$sendLocationButton.setAttribute("disabled", "disabled");
|
||||
|
||||
socket.emit(
|
||||
"sendLocation",
|
||||
{
|
||||
Latitude: position.coords.latitude,
|
||||
Longitude: position.coords.longitude,
|
||||
},
|
||||
() => {
|
||||
$sendLocationButton.removeAttribute("disabled");
|
||||
// console.log("Location Shared");
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
socket.emit("join", { username, room }, (error) => {
|
||||
if (error) {
|
||||
alert(error);
|
||||
location.href = "/";
|
||||
}
|
||||
});
|
||||
|
||||
// document.querySelector("#increment").addEventListener("click", (e) => {
|
||||
// console.log("clicked");
|
||||
// socket.emit("increment");
|
||||
// });
|
||||
134
src/index.js
Normal file
134
src/index.js
Normal file
@@ -0,0 +1,134 @@
|
||||
// emit events
|
||||
// socket.emit, io.emit, socket.broadcast.emit
|
||||
|
||||
// emit to a specific room
|
||||
// io.to(room).emit, socket.broadcast.to(room).emit
|
||||
|
||||
const express = require("express");
|
||||
const path = require("path");
|
||||
const http = require("http");
|
||||
const socketio = require("socket.io");
|
||||
const Filter = require("bad-words");
|
||||
const {
|
||||
generateMessage,
|
||||
generateLocationMessage,
|
||||
} = require("./utils/messages");
|
||||
|
||||
const {
|
||||
addUser,
|
||||
removeUser,
|
||||
getUser,
|
||||
getUsersInRoom,
|
||||
} = require("./utils/user");
|
||||
|
||||
// initialize express
|
||||
const app = express();
|
||||
// initialize http server
|
||||
const server = http.createServer(app);
|
||||
// initialize socketio
|
||||
const io = socketio(server);
|
||||
|
||||
const port = process.env.PORT || 3000;
|
||||
// define paths for express config
|
||||
const publicDirectoryPath = path.join(__dirname, "../public");
|
||||
|
||||
// setup static directory to serve
|
||||
app.use(express.static(publicDirectoryPath));
|
||||
|
||||
// let count = 0;
|
||||
|
||||
// server (emit) -> client (receive) - countUpdated
|
||||
// client (emit) -> server (receive) - increment
|
||||
|
||||
// let's listen for new connections
|
||||
io.on("connection", (socket) => {
|
||||
console.log("New WebSocket connection");
|
||||
|
||||
// socket.emit("message", generateMessage("Welcome!"));
|
||||
// socket.broadcast.emit("message", "A new user has joined!");
|
||||
|
||||
socket.on("join", ({ username, room }, callback) => {
|
||||
// specifically emit event according to room name eg: no one can check whats going on in another room
|
||||
const { error, user } = addUser({ id: socket.id, username, room });
|
||||
|
||||
if (error) {
|
||||
return callback(error);
|
||||
}
|
||||
|
||||
socket.join(room);
|
||||
socket.emit("message", generateMessage("Admin", "Welcome!"));
|
||||
socket.broadcast
|
||||
.to(user.room)
|
||||
.emit("message", generateMessage("Admin", `${user.username} has joined`));
|
||||
io.to(user.room).emit("roomData", {
|
||||
room: user.room,
|
||||
users: getUsersInRoom(user.room),
|
||||
});
|
||||
callback();
|
||||
});
|
||||
|
||||
socket.on("sendMessage", (message, callback) => {
|
||||
const filter = new Filter();
|
||||
|
||||
const user = getUser(socket.id);
|
||||
|
||||
if (!user) {
|
||||
return callback("You are not authenticated");
|
||||
}
|
||||
|
||||
// if (filter.isProfane(message)) {
|
||||
// return callback("Profanity is not allowed!");
|
||||
// }
|
||||
|
||||
io.to(user.room).emit("message", generateMessage(user.username, message));
|
||||
callback();
|
||||
});
|
||||
|
||||
// socket.emit("countUpdated", count);
|
||||
|
||||
// socket.on("increment", () => {
|
||||
// count++;
|
||||
// notify only the current connection
|
||||
// socket.emit("countUpdated", count);
|
||||
|
||||
// notify all connections
|
||||
// io.emit("countUpdated", count);
|
||||
// });
|
||||
|
||||
socket.on("disconnect", () => {
|
||||
const user = removeUser(socket.id);
|
||||
|
||||
if (user) {
|
||||
io.to(user.room).emit(
|
||||
"message",
|
||||
generateMessage("Admin", `${user.username} has left`)
|
||||
);
|
||||
io.to(user.room).emit("roomData", {
|
||||
room: user.room,
|
||||
users: getUsersInRoom(user.room),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("sendLocation", (coords, callback) => {
|
||||
const user = getUser(socket.id);
|
||||
|
||||
if (!user) {
|
||||
return callback("You are not authenticated");
|
||||
}
|
||||
|
||||
io.to(user.room).emit(
|
||||
"locationMessage",
|
||||
generateLocationMessage(
|
||||
user.username,
|
||||
`https://google.com/maps?q=${coords.Latitude},${coords.Longitude}`
|
||||
)
|
||||
);
|
||||
callback();
|
||||
});
|
||||
});
|
||||
|
||||
// start the server
|
||||
server.listen(port, () => {
|
||||
console.log(`Server is up on port ${port}!`);
|
||||
});
|
||||
24
src/utils/messages.js
Normal file
24
src/utils/messages.js
Normal file
@@ -0,0 +1,24 @@
|
||||
// Date time :: getDate, getTime, setDate and so on...
|
||||
// getTime gives a positive number starting from 1 which depicts the time after 1970
|
||||
// if we try to access before 1970 in JS than it would gives a negative number
|
||||
|
||||
const generateMessage = (username, text) => {
|
||||
return {
|
||||
username: username,
|
||||
text: text,
|
||||
createdAt: new Date().getTime(),
|
||||
};
|
||||
};
|
||||
|
||||
const generateLocationMessage = (username, url) => {
|
||||
return {
|
||||
username: username,
|
||||
url: url,
|
||||
createdAt: new Date().getTime(),
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
generateMessage,
|
||||
generateLocationMessage,
|
||||
};
|
||||
57
src/utils/user.js
Normal file
57
src/utils/user.js
Normal file
@@ -0,0 +1,57 @@
|
||||
const users = [];
|
||||
|
||||
// addUser, removeUser, getUser, getUserInRoom
|
||||
|
||||
const addUser = ({ id, username, room }) => {
|
||||
// Clean the data
|
||||
username = username.trim().toLowerCase();
|
||||
room = room.trim().toLowerCase();
|
||||
|
||||
// validate the data
|
||||
if (!username || !room) {
|
||||
return {
|
||||
error: "Username and room are required!",
|
||||
};
|
||||
}
|
||||
|
||||
// Check for existing user
|
||||
const existingUser = users.find((user) => {
|
||||
return user.room === room && user.username === username;
|
||||
});
|
||||
|
||||
// validate username
|
||||
if (existingUser) {
|
||||
return {
|
||||
error: "Username is in use!",
|
||||
};
|
||||
}
|
||||
|
||||
//Store user
|
||||
const user = { id, username, room };
|
||||
users.push(user);
|
||||
return { user };
|
||||
};
|
||||
|
||||
const removeUser = (id) => {
|
||||
const index = users.findIndex((user) => user.id === id);
|
||||
|
||||
if (index !== -1) {
|
||||
return users.splice(index, 1)[0];
|
||||
}
|
||||
};
|
||||
|
||||
const getUser = (id) => {
|
||||
return users.find((user) => user.id === id);
|
||||
};
|
||||
|
||||
const getUsersInRoom = (room) => {
|
||||
room = room.trim().toLowerCase();
|
||||
return users.filter((user) => user.room === room);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
addUser,
|
||||
removeUser,
|
||||
getUser,
|
||||
getUsersInRoom,
|
||||
};
|
||||
Reference in New Issue
Block a user