First Commit
This commit is contained in:
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");
|
||||
// });
|
||||
Reference in New Issue
Block a user