This commit is contained in:
Henry Hiles 2022-01-31 10:57:31 -05:00
commit 75840914cf
5 changed files with 322 additions and 117 deletions

View file

@ -2,10 +2,13 @@ import moment from "https://jspm.dev/moment"
import { io } from "https://cdn.socket.io/4.3.2/socket.io.esm.min.js" import { io } from "https://cdn.socket.io/4.3.2/socket.io.esm.min.js"
const socket = io(":3000") const socket = io(":3000")
const messageButton = document.querySelector("#send") const messageButton = document.querySelector("#send")
const menuButton = document.querySelector("#menu")
const nameButton = document.querySelector("#name") const nameButton = document.querySelector("#name")
const roomContainer = document.querySelector("#room-container")
const nameInput = document.querySelector("input") const nameInput = document.querySelector("input")
const roomContainer = document.querySelector("#room-container")
const messageInput = document.querySelector("textarea") const messageInput = document.querySelector("textarea")
const login = document.querySelector("#login")
const nameDisplay = document.querySelector("#name-display")
setInterval( setInterval(
() => () =>
@ -25,7 +28,7 @@ const addMessage = (messageObject) => {
.querySelector(".message") .querySelector(".message")
if (isYours) messageDiv.classList.add("message-right") if (isYours) messageDiv.classList.add("message-right")
else if (isSystem) messageDiv.classList.add("system") if (isSystem) messageDiv.classList.add("system")
messageDiv.querySelector(".message-text").innerText = message messageDiv.querySelector(".message-text").innerText = message
const time = Date.now() const time = Date.now()
messageDiv.dataset.time = time messageDiv.dataset.time = time
@ -35,6 +38,26 @@ const addMessage = (messageObject) => {
document.querySelector("#messages").prepend(messageDiv) document.querySelector("#messages").prepend(messageDiv)
} }
if (roomName) {
socket.emit("new-user", roomName, localStorage.getItem("name"))
addMessage({
message: "You joined",
isYours: true,
isSystem: true,
})
}
menuButton.addEventListener("click", () =>
document.querySelector("#rooms").classList.toggle("opened")
)
if (!localStorage.getItem("name")) login.classList.remove("done")
else nameDisplay.innerHTML = localStorage.getItem("name")
document
.querySelector("#change-name")
.addEventListener("click", () => login.classList.remove("done"))
socket.on("room-created", (room) => { socket.on("room-created", (room) => {
const roomItem = document.createElement("li") const roomItem = document.createElement("li")
const roomLink = document.createElement("a") const roomLink = document.createElement("a")
@ -42,18 +65,23 @@ socket.on("room-created", (room) => {
roomLink.innerText = room roomLink.innerText = room
roomItem.append(roomLink) roomItem.append(roomLink)
roomContainer.append(roomItem) roomContainer.append(roomItem)
if (document.querySelector("#no-rooms"))
document.querySelector("#no-rooms").classList.add("changed")
}) })
if (nameButton) if (nameButton)
nameButton.addEventListener("click", () => { nameButton.addEventListener("click", () => {
if (!nameInput.value) return (nameInput.required = true) if (!nameInput.value) return (nameInput.required = true)
document.querySelector("#login").classList.add("done") document.querySelector("#login").classList.add("done")
socket.emit("new-user", roomName, nameInput.value) localStorage.setItem("name", nameInput.value)
addMessage({ nameDisplay.innerHTML = localStorage.getItem("name")
message: "You joined", if (roomName) {
isYours: true, socket.emit("name-change", roomName, nameInput.value)
isSystem: true, addMessage({
}) message: `You renamed yourself to ${nameInput.value}`,
isSystem: true,
})
}
}) })
if (messageButton) if (messageButton)
@ -69,8 +97,14 @@ socket.on("chat-message", (message, user) => {
addMessage({ message, user }) addMessage({ message, user })
}) })
socket.on("name-changed", (oldName, newName) => {
addMessage({
message: `${oldName} was renamed to ${newName}.`,
isSystem: true,
})
})
socket.on("user-connected", (name) => { socket.on("user-connected", (name) => {
socket.emit("join-room", ROOM_ID)
addMessage({ message: `${name} joined`, isSystem: true }) addMessage({ message: `${name} joined`, isSystem: true })
}) })

View file

@ -16,11 +16,92 @@ body {
"Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
font-size: 1.2em; font-size: 1.2em;
scroll-behavior: smooth; scroll-behavior: smooth;
display: flex;
}
#menu {
position: absolute;
top: 25px;
z-index: 2;
left: 0;
}
#content {
width: 100%;
height: 100vh; height: 100vh;
display: grid; display: grid;
grid-template-rows: 100px 1fr 175px; grid-template-rows: 100px 1fr 175px;
} }
#rooms {
background-color: var(--secondary);
width: 0;
overflow: hidden;
align-items: center;
transition: width 2s;
display: flex;
z-index: 1;
color: white;
flex-direction: column;
}
#rooms h1 {
margin-bottom: 30px;
}
#rooms a:hover {
background-color: #485b70 !important;
}
#rooms a {
background-color: #374a5e;
}
#rooms a.active {
background-color: var(--primary);
font-weight: bold;
}
#rooms ul {
list-style: none;
padding: 0;
margin: 0;
gap: 10px;
font-size: 1.5em;
}
#rooms :is(li, ul, a) {
width: 95%;
display: flex;
flex-direction: column;
align-items: center;
}
#rooms.opened {
width: 18vw;
}
#rooms:not(.opened) + div nav {
padding-left: 70px;
}
#rooms input {
width: 80%;
}
#rooms input::placeholder {
text-align: center;
}
#rooms form {
text-align: center;
margin: auto 0 20px 0;
}
#rooms form button {
margin-top: 10px;
}
nav, nav,
footer { footer {
background-color: var(--secondary); background-color: var(--secondary);
@ -28,6 +109,8 @@ footer {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
max-width: 100vw;
transition: padding 0.6s;
} }
a { a {
@ -38,10 +121,6 @@ a {
grid-template-columns: 1fr 500px; grid-template-columns: 1fr 500px;
} }
a:hover {
text-decoration: underline;
}
nav:first-child a { nav:first-child a {
font-size: 1.5em !important; font-size: 1.5em !important;
} }
@ -61,11 +140,11 @@ input[type="text"] {
font-family: inherit; font-family: inherit;
font-size: 2rem; font-size: 2rem;
max-width: 100%; max-width: 100%;
max-height: 90%; max-height: 10vh;
} }
:is(textarea, input[type="text"])::placeholder { :is(textarea, input[type="text"])::placeholder {
color: white; color: #dbcece;
} }
button { button {
@ -74,7 +153,6 @@ button {
color: #f5f0f0; color: #f5f0f0;
background-color: transparent; background-color: transparent;
font-size: 1em; font-size: 1em;
cursor: pointer;
padding: 0.7rem 1.6rem; padding: 0.7rem 1.6rem;
text-decoration: none; text-decoration: none;
} }
@ -96,7 +174,11 @@ svg {
width: 1.5em; width: 1.5em;
} }
button:hover { button:not(:disabled) {
cursor: pointer;
}
button:is(:disabled, :hover) {
filter: brightness(0.9); filter: brightness(0.9);
} }
@ -106,6 +188,14 @@ button:hover {
text-transform: capitalize; text-transform: capitalize;
} }
#change-name {
margin: 0 10px 0 auto;
}
#name-display {
font-size: 1.5em;
}
.message-text { .message-text {
padding: 10px; padding: 10px;
font-size: 17px; font-size: 17px;
@ -119,6 +209,7 @@ button:hover {
color: white; color: white;
border-radius: 20px; border-radius: 20px;
} }
#main-content { #main-content {
background-color: var(--primary); background-color: var(--primary);
color: white; color: white;
@ -128,6 +219,7 @@ button:hover {
} }
#messages { #messages {
max-width: 100vw !important;
background-color: var(--primary); background-color: var(--primary);
padding: 30px; padding: 30px;
overflow-y: auto; overflow-y: auto;
@ -168,10 +260,6 @@ button:hover {
justify-content: right; justify-content: right;
} }
.message-middle {
justify-content: center;
}
.message-right .message-text { .message-right .message-text {
background-color: #e9e932; background-color: #e9e932;
color: #424242; color: #424242;
@ -190,21 +278,21 @@ button:hover {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 100vh; height: 100vh;
width: 100vw;
} }
#login.done { #login.done {
display: none; display: none;
} }
#login:not(.done) + nav, #login:not(.done) + div,
#login:not(.done) + nav + div, #login:not(.done) + div + div {
#login:not(.done) + nav + div + footer {
display: none !important; display: none !important;
} }
.medium-header { .medium-header {
font-size: 3rem; font-size: 2.5rem;
font-weight: 250; font-weight: 200;
line-height: 1.2; line-height: 1.2;
margin-bottom: 20px; margin-bottom: 20px;
} }
@ -226,6 +314,59 @@ button:hover {
color: #f20000; color: #f20000;
} }
#no-rooms.changed {
display: none;
}
@media (max-width: 1520px) {
#rooms-header {
visibility: hidden;
}
#rooms.opened {
width: 30vw;
}
}
@media (max-width: 1000px) {
#rooms.opened {
width: 100vw;
}
#rooms {
position: absolute;
height: 100vh;
}
#rooms-header {
visibility: visible;
}
}
@media (max-width: 500px) {
#login input {
max-width: 60vw;
}
}
@media (max-width: 725px) {
#name-display {
display: none;
}
body {
font-size: 1.1em;
}
footer {
flex-direction: column;
}
button:not(#menu, .button-circle) {
display: block;
width: 100%;
}
}
@keyframes shake { @keyframes shake {
0% { 0% {
margin-left: 0rem; margin-left: 0rem;

View file

@ -4,10 +4,9 @@ const server = require("http").Server(app)
const io = require("socket.io")(server, { const io = require("socket.io")(server, {
cors: { cors: {
origin: [ origin: [
"http://localhost:5500", "http://127.0.0.1:3000",
"http://127.0.0.1:5500", "http://192.168.1.226:3000",
"http//192.168.1.226:5500", "http://192.168.1.226:3000",
"http//192.168.1.226:5500",
], ],
}, },
}) })
@ -26,10 +25,10 @@ const getUserRooms = (socket) =>
const rooms = {} const rooms = {}
app.get("/", (_, res) => { app.get("/", (_, res) => {
res.render("index", { rooms: rooms }) res.render("index", { rooms: rooms, roomName: null })
}) })
app.post("/room", (req, res) => { app.post("/", (req, res) => {
if (rooms[req.body.room] != null) { if (rooms[req.body.room] != null) {
return res.redirect("/") return res.redirect("/")
} }
@ -42,7 +41,7 @@ app.get("/:room", (req, res) => {
if (rooms[req.params.room] == null) { if (rooms[req.params.room] == null) {
return res.redirect("/") return res.redirect("/")
} }
res.render("room", { roomName: req.params.room }) res.render("index", { rooms: rooms, roomName: req.params.room })
}) })
server.listen(3000) server.listen(3000)
@ -60,6 +59,13 @@ io.on("connection", (socket) => {
.emit("chat-message", message, rooms[room].users[socket.id]) .emit("chat-message", message, rooms[room].users[socket.id])
}) })
socket.on("name-change", (room, newName) => {
socket.broadcast
.to(room)
.emit("name-changed", rooms[room].users[socket.id], newName)
rooms[room].users[socket.id] = newName
})
socket.on("disconnect", () => socket.on("disconnect", () =>
getUserRooms(socket).forEach((room) => { getUserRooms(socket).forEach((room) => {
socket.broadcast socket.broadcast

View file

@ -6,38 +6,126 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title> <title>Document</title>
<link rel="stylesheet" href="style.css" /> <link rel="stylesheet" href="style.css" />
<script>
const roomName = "<%= roomName %>"
</script>
<script type="module" src="script.js"></script> <script type="module" src="script.js"></script>
</head> </head>
<body> <body>
<nav><a href="#">Home</a><a href="#">Also home</a></nav> <div id="login" class="done">
<!-- <% if("<%= roomName %>") {} else --> <h1 class="large-header">Login</h1>
<div id="main-content"> <div>
<div id="room-container"> <input type="text" placeholder="Username" />
<span>Joinable rooms:</span> <button class="button-circle" id="name">
<ul> <svg
<% Object.keys(rooms).forEach(room => { %> xmlns="http://www.w3.org/2000/svg"
<li><a href="/<%= room %>"><%= room %></a></li> width="16"
<% }) %> height="16"
</ul> fill="currentColor"
class="bi bi-pencil"
viewBox="0 0 16 16"
>
<path
d="M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168l10-10zM11.207 2.5 13.5 4.793 14.793 3.5 12.5 1.207 11.207 2.5zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293l6.5-6.5zm-9.761 5.175-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325z"
/>
</svg>
</button>
</div> </div>
<form action="/room" method="POST"> </div>
<label for="room"></label> <div id="rooms" class="opened">
<h1 class="medium-header" id="rooms-header">Rooms</h1>
<ul id="room-container">
<% if(Object.keys(rooms).length)
{Object.keys(rooms).forEach(room => { %>
<li>
<a
href="/<%= room %>"
class="<%= room == roomName ? 'active' : '' %>"
><%= room %></a
>
</li>
<% })} else {%>
<h1 class="medium-header" id="no-rooms">No rooms</h1>
<%} %>
</ul>
<form method="POST" action="/">
<input <input
id="room" id="room"
name="room" name="room"
autocomplete="off"
type="text" type="text"
placeholder="Room Name" placeholder="Room Name"
/> />
<button class="button-primary" type="submit">New Room</button> <button class="button-primary" type="submit">New Room</button>
</form> </form>
</div> </div>
<footer> <div id="content">
<a <nav>
target="_blank" <button id="menu">
rel="noreferrer" <svg
href="https://github.com/Henry-Hiles" xmlns="http://www.w3.org/2000/svg"
>My Github</a width="16"
> height="16"
</footer> fill="currentColor"
class="bi bi-list"
viewBox="0 0 16 16"
>
<path
fill-rule="evenodd"
d="M2.5 12a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5z"
/>
</svg>
</button>
<a href="#">Home</a><a href="#">Also home</a>
<div id="change-name">
<span id="name-display"></span>
<button class="button-circle">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
fill="currentColor"
class="bi bi-pencil"
viewBox="0 0 16 16"
>
<path
d="M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168l10-10zM11.207 2.5 13.5 4.793 14.793 3.5 12.5 1.207 11.207 2.5zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293l6.5-6.5zm-9.761 5.175-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325z"
/>
</svg>
</button>
</div>
</nav>
<div id="messages">
</div>
<footer>
<textarea
id="message"
cols="30"
rows="3"
<%- !roomName ? "disabled placeholder='Join a room to send messages'" : 'placeholder="Enter your message"' %>
></textarea>
<button
id="send"
class="button-primary"
<%= !roomName ? "disabled" : "" %>
>
Send
</button>
</footer>
</div>
<template>
<div class="message">
<div class="message-content">
<span class="message-text"></span>
<span class="message-info"
><span class="message-user"></span>
<span class="message-timestamp"></span
></span>
</div>
</div>
</template>
</body> </body>
</html> </html>

View file

@ -1,64 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Messaging - Chatting</title>
<link rel="stylesheet" href="style.css" />
<script>
const roomName = "<%= roomName %>"
</script>
<script type="module" src="script.js"></script>
</head>
<body>
<div id="login">
<h1 class="large-header">Login</h1>
<div>
<input type="text" placeholder="Username" />
<button class="button-circle" id="name">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
fill="currentColor"
class="bi bi-pencil"
viewBox="0 0 16 16"
>
<path
d="M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168l10-10zM11.207 2.5 13.5 4.793 14.793 3.5 12.5 1.207 11.207 2.5zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293l6.5-6.5zm-9.761 5.175-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325z"
/>
</svg>
</button>
</div>
</div>
<nav><a href="#">Home</a><a href="#">Also home</a></nav>
<div id="messages">
<div class="message message-right"></div>
</div>
<footer>
<textarea
id="message"
placeholder="Enter your message here"
cols="30"
rows="3"
></textarea>
<button id="send" class="button-primary">Send</button>
</footer>
<template>
<div class="message">
<div class="message-content">
<span class="message-text"></span>
<span class="message-info"
><span class="message-user"></span>
<span class="message-timestamp"></span
></span>
</div>
</div>
</template>
</body>
</html>