Working
This commit is contained in:
parent
d8146c3723
commit
abdffe2e7a
7 changed files with 1184 additions and 51 deletions
79
public/script.js
Normal file
79
public/script.js
Normal file
|
@ -0,0 +1,79 @@
|
|||
import moment from "https://jspm.dev/moment"
|
||||
import { io } from "https://cdn.socket.io/4.3.2/socket.io.esm.min.js"
|
||||
const socket = io(":3000")
|
||||
const messageButton = document.querySelector("#send")
|
||||
const nameButton = document.querySelector("#name")
|
||||
const roomContainer = document.querySelector("#room-container")
|
||||
const nameInput = document.querySelector("input")
|
||||
const messageInput = document.querySelector("textarea")
|
||||
|
||||
setInterval(
|
||||
() =>
|
||||
document.querySelectorAll(".message-timestamp").forEach((element) => {
|
||||
element.innerText = moment(
|
||||
new Date(Number(element.closest(".message").dataset.time))
|
||||
).fromNow()
|
||||
}),
|
||||
45000
|
||||
)
|
||||
|
||||
const addMessage = (messageObject) => {
|
||||
const { message, isYours, isSystem, user } = messageObject
|
||||
const messageDiv = document
|
||||
.querySelector("template")
|
||||
.content.cloneNode(true)
|
||||
.querySelector(".message")
|
||||
|
||||
if (isYours) messageDiv.classList.add("message-right")
|
||||
else if (isSystem) messageDiv.classList.add("system")
|
||||
messageDiv.querySelector(".message-text").innerText = message
|
||||
const time = Date.now()
|
||||
messageDiv.dataset.time = time
|
||||
messageDiv.querySelector(".message-timestamp").innerText =
|
||||
moment(time).fromNow()
|
||||
if (user) messageDiv.querySelector(".message-user").innerText = `${user} • `
|
||||
document.querySelector("#messages").prepend(messageDiv)
|
||||
}
|
||||
|
||||
socket.on("room-created", (room) => {
|
||||
const roomItem = document.createElement("li")
|
||||
const roomLink = document.createElement("a")
|
||||
roomLink.href = `/${room}`
|
||||
roomLink.innerText = room
|
||||
roomItem.append(roomLink)
|
||||
roomContainer.append(roomItem)
|
||||
})
|
||||
|
||||
if (nameButton)
|
||||
nameButton.addEventListener("click", () => {
|
||||
if (!nameInput.value) return (nameInput.required = true)
|
||||
document.querySelector("#login").classList.add("done")
|
||||
socket.emit("new-user", roomName, nameInput.value)
|
||||
addMessage({
|
||||
message: "You joined",
|
||||
isYours: true,
|
||||
isSystem: true,
|
||||
})
|
||||
})
|
||||
|
||||
if (messageButton)
|
||||
messageButton.addEventListener("click", () => {
|
||||
if (!messageInput.value) return (messageInput.required = true)
|
||||
messageInput.required = false
|
||||
socket.emit("send-chat-message", roomName, messageInput.value)
|
||||
addMessage({ user: "you", message: messageInput.value, isYours: true })
|
||||
messageInput.value = ""
|
||||
})
|
||||
|
||||
socket.on("chat-message", (message, user) => {
|
||||
addMessage({ message, user })
|
||||
})
|
||||
|
||||
socket.on("user-connected", (name) => {
|
||||
socket.emit("join-room", ROOM_ID)
|
||||
addMessage({ message: `${name} joined`, isSystem: true })
|
||||
})
|
||||
|
||||
socket.on("user-disconnected", (name) => {
|
||||
addMessage({ message: `${name} disconnected`, isSystem: true })
|
||||
})
|
242
public/style.css
Normal file
242
public/style.css
Normal file
|
@ -0,0 +1,242 @@
|
|||
:root {
|
||||
--primary: #14bbaa;
|
||||
--secondary: #2c3c4c;
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: "Lato", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
||||
"Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji",
|
||||
"Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
font-size: 1.2em;
|
||||
scroll-behavior: smooth;
|
||||
height: 100vh;
|
||||
display: grid;
|
||||
grid-template-rows: 100px 1fr 175px;
|
||||
}
|
||||
|
||||
nav,
|
||||
footer {
|
||||
background-color: var(--secondary);
|
||||
color: white;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
a {
|
||||
margin: 0 0.5em;
|
||||
text-decoration: none;
|
||||
width: fit-content;
|
||||
color: white;
|
||||
grid-template-columns: 1fr 500px;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
nav:first-child a {
|
||||
font-size: 1.5em !important;
|
||||
}
|
||||
|
||||
footer {
|
||||
justify-content: center;
|
||||
gap: 30px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
textarea,
|
||||
input[type="text"] {
|
||||
border: none;
|
||||
color: white;
|
||||
background-color: transparent;
|
||||
border-bottom: 0.05em solid white;
|
||||
font-family: inherit;
|
||||
font-size: 2rem;
|
||||
max-width: 100%;
|
||||
max-height: 90%;
|
||||
}
|
||||
|
||||
:is(textarea, input[type="text"])::placeholder {
|
||||
color: white;
|
||||
}
|
||||
|
||||
button {
|
||||
border: none;
|
||||
border-radius: 2em;
|
||||
color: #f5f0f0;
|
||||
background-color: transparent;
|
||||
font-size: 1em;
|
||||
cursor: pointer;
|
||||
padding: 0.7rem 1.6rem;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.button-primary {
|
||||
background-color: var(--primary);
|
||||
}
|
||||
|
||||
.button-secondary {
|
||||
background-color: var(--secondary);
|
||||
}
|
||||
|
||||
.button-circle {
|
||||
padding: 0.85em 1em;
|
||||
}
|
||||
|
||||
svg {
|
||||
height: 1.5em;
|
||||
width: 1.5em;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
filter: brightness(0.9);
|
||||
}
|
||||
|
||||
.message-info {
|
||||
font-size: 12px;
|
||||
color: white;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.message-text {
|
||||
padding: 10px;
|
||||
font-size: 17px;
|
||||
white-space: initial;
|
||||
line-height: 23px;
|
||||
font-weight: normal;
|
||||
word-wrap: break-word;
|
||||
display: inline-block;
|
||||
background-color: #e4366a;
|
||||
text-align: center;
|
||||
color: white;
|
||||
border-radius: 20px;
|
||||
}
|
||||
#main-content {
|
||||
background-color: var(--primary);
|
||||
color: white;
|
||||
display: flex;
|
||||
padding: 30px;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#messages {
|
||||
background-color: var(--primary);
|
||||
padding: 30px;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
#messages::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
#messages::-webkit-scrollbar-track {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#messages::-webkit-scrollbar-thumb {
|
||||
background: var(--secondary);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
#messages::-webkit-scrollbar-thumb:hover {
|
||||
filter: brightness(0.9);
|
||||
}
|
||||
|
||||
.message-content {
|
||||
max-width: 75%;
|
||||
}
|
||||
|
||||
.message {
|
||||
padding: 5px;
|
||||
margin-top: 5px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.message-right {
|
||||
justify-content: right;
|
||||
}
|
||||
|
||||
.message-middle {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.message-right .message-text {
|
||||
background-color: #e9e932;
|
||||
color: #424242;
|
||||
}
|
||||
|
||||
.system .message-text {
|
||||
background-color: #05d0eb;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#login {
|
||||
background-color: var(--primary);
|
||||
color: white;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
#login.done {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#login:not(.done) + nav,
|
||||
#login:not(.done) + nav + div,
|
||||
#login:not(.done) + nav + div + footer {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.medium-header {
|
||||
font-size: 3rem;
|
||||
font-weight: 250;
|
||||
line-height: 1.2;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.large-header {
|
||||
font-size: 5rem;
|
||||
font-weight: 300;
|
||||
line-height: 1.2;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
:is(input[type="text"], textarea):required {
|
||||
animation: shake 0.2s ease-in-out 0s 2;
|
||||
color: #ff0000;
|
||||
border-color: red !important;
|
||||
}
|
||||
|
||||
:is(input[type="text"], textarea):required::placeholder {
|
||||
color: #f20000;
|
||||
}
|
||||
|
||||
@keyframes shake {
|
||||
0% {
|
||||
margin-left: 0rem;
|
||||
}
|
||||
25% {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
75% {
|
||||
margin-left: -0.5rem;
|
||||
}
|
||||
100% {
|
||||
margin-left: 0rem;
|
||||
}
|
||||
}
|
Reference in a new issue