`;
+
+ setTimeout(() => {
+ feedbackEl.innerHTML = "";
+ }, 3000);
+
+ // userEl.value = ""; //Clear user for same browser, but diff browsers for users ? leave the username foreach user's browser
+ messageEl.value = "";
+ } catch (error) {
+ console.error(error.message);
+ feedbackEl.innerHTML = `
Something went wrong.
`;
+ }
+}
+
+formEl.addEventListener("submit", handleSubmit);
+
+keepFetchingMessages();
From 1518e8bb9983952fd1726a4efb629c5e895ce82e Mon Sep 17 00:00:00 2001
From: JanefrancessC
Date: Fri, 22 May 2026 13:03:29 +0100
Subject: [PATCH 2/8] feat: Implement like and dislike buttons logic
---
chat-app/.gitignore | 1 +
chat-app/README.md | 17 ++++++------
chat-app/backend/index.js | 43 ++++++++++++++++++++++++++++--
chat-app/frontend/script.js | 52 ++++++++++++++++++++++++++++++++++---
4 files changed, 99 insertions(+), 14 deletions(-)
diff --git a/chat-app/.gitignore b/chat-app/.gitignore
index c2658d7d..2752eb92 100644
--- a/chat-app/.gitignore
+++ b/chat-app/.gitignore
@@ -1 +1,2 @@
node_modules/
+.DS_Store
diff --git a/chat-app/README.md b/chat-app/README.md
index d26b1a13..ee46cd43 100644
--- a/chat-app/README.md
+++ b/chat-app/README.md
@@ -7,9 +7,10 @@ https://sdc.codeyourfuture.io/decomposition/sprints/2/prep/
You must complete and deploy a chat application. You have two weeks to complete this.
It must support at least the following requirements:
-* As a user, I can send add a message to the chat.
-* As a user, when I open the chat I see the messages that have been sent by any user.
-* As a user, when someone sends a message, it gets added to what I see.
+
+- As a user, I can send add a message to the chat.
+- As a user, when I open the chat I see the messages that have been sent by any user.
+- As a user, when someone sends a message, it gets added to what I see.
It must also support at least one additional feature.
@@ -27,8 +28,8 @@ Exploring and understanding different ways of sending information between a clie
### How to submit
-* Fork the Module-Decomposition repository
-* Develop and deploy your chat app
-* Create a pull request back into the original Module-Decomposition repo, including:
- * A link to the deployed frontend on the CYF hosting environment
- * A link to the deployed backend on the CYF hosting environment
+- Fork the Module-Decomposition repository
+- Develop and deploy your chat app
+- Create a pull request back into the original Module-Decomposition repo, including:
+ - A link to the deployed frontend on the CYF hosting environment
+ - A link to the deployed backend on the CYF hosting environment
diff --git a/chat-app/backend/index.js b/chat-app/backend/index.js
index d7e57753..6c22c1c5 100644
--- a/chat-app/backend/index.js
+++ b/chat-app/backend/index.js
@@ -6,21 +6,31 @@ app.use(cors());
app.use(express.json());
const port = 3000;
+let nextMessageId = 1;
const messages = [
{
+ id: nextMessageId++,
message: "Hello",
user: "Jane",
time: Date.now() - 6000,
+ likes: 1,
+ dislikes: 2,
},
{
+ id: nextMessageId++,
message: "Hey",
user: "John",
time: Date.now() - 3000,
+ likes: 1,
+ dislikes: 2,
},
{
+ id: nextMessageId++,
message: "Hi",
user: "Bob",
time: Date.now(),
+ likes: 1,
+ dislikes: 2,
},
];
const callbacksForNewMessages = [];
@@ -57,11 +67,16 @@ app.post("/messages", (req, res) => {
return;
}
- messages.push({
+ const newMessage = {
+ id: nextMessageId++,
message,
user,
time: Date.now(),
- });
+ likes: 0,
+ dislikes: 0,
+ };
+
+ messages.push(newMessage);
while (callbacksForNewMessages.length > 0) {
const callback = callbacksForNewMessages.pop();
callback([messages[messages.length - 1]]);
@@ -69,6 +84,30 @@ app.post("/messages", (req, res) => {
res.status(201).json({ success: true });
});
+app.post("/messages/:id/like", (req, res) => {
+ const id = Number(req.params.id);
+ const message = messages.find((msg) => msg.id === id);
+
+ if (!message) {
+ res.status(404).json({ error: "Message not found" });
+ return;
+ }
+ message.likes++;
+ res.json(message);
+});
+
+app.post("/messages/:id/dislike", (req, res) => {
+ const id = Number(req.params.id);
+ const message = messages.find((msg) => msg.id === id);
+
+ if (!message) {
+ res.status(404).json({ error: "Message not found" });
+ return;
+ }
+ message.dislikes++;
+ res.json(message);
+});
+
app.listen(port, () => {
console.log(`Chat server listening on port ${port}`);
});
diff --git a/chat-app/frontend/script.js b/chat-app/frontend/script.js
index bf0a211f..ba844adc 100644
--- a/chat-app/frontend/script.js
+++ b/chat-app/frontend/script.js
@@ -5,8 +5,8 @@ let messageEl = document.getElementById("message");
let displayBox = document.getElementById("display-message");
let feedbackEl = document.getElementById("feedback");
-const serverURL = `https://janefrancessc-chat-application-backend.hosting.codeyourfuture.io/messages`;
-// const serverURL = "http://127.0.0.1:3000/messages";
+// const serverURL = `https://janefrancessc-chat-application-backend.hosting.codeyourfuture.io/messages`;
+const serverURL = "http://127.0.0.1:3000/messages";
const state = { messages: [] };
let pollingMode = "regular";
let longPoll = false;
@@ -19,7 +19,7 @@ pollingForm.addEventListener("change", (e) => {
longPoll = pollingMode === "long";
feedbackEl.innerHTML = `
-
${longPoll ? "Using long polling" : "Using regular polling"}
+
${longPoll ? "Using long polling!" : "Using regular polling!"}
+
+
`,
)
From c310b9c7c8f6a51da33d1d1e352bf53d619a349d Mon Sep 17 00:00:00 2001
From: JanefrancessC
Date: Tue, 26 May 2026 11:26:57 +0100
Subject: [PATCH 3/8] chore: cleanup code
---
chat-app/README.md | 22 ++++++++++++++++++++++
chat-app/backend/index.js | 4 +++-
chat-app/backend/package-lock.json | 13 +++++++++++++
chat-app/backend/package.json | 4 +++-
chat-app/frontend/script.js | 4 ++--
5 files changed, 43 insertions(+), 4 deletions(-)
diff --git a/chat-app/README.md b/chat-app/README.md
index ee46cd43..9980eea1 100644
--- a/chat-app/README.md
+++ b/chat-app/README.md
@@ -1,5 +1,27 @@
# Write and Deploy Chat Application Frontend and Backend
+### To start the program
+
+First install dependencies:
+
+```bash
+npm install
+```
+
+From `/backend` directory:
+
+Option 1. Production mode
+
+```bash
+npm start
+```
+
+Option 2. Development mode
+
+```bash
+npm run dev
+```
+
### Link to the coursework
https://sdc.codeyourfuture.io/decomposition/sprints/2/prep/
diff --git a/chat-app/backend/index.js b/chat-app/backend/index.js
index 6c22c1c5..6bbe52a0 100644
--- a/chat-app/backend/index.js
+++ b/chat-app/backend/index.js
@@ -1,11 +1,13 @@
import express from "express";
import cors from "cors";
+import dotenv from "dotenv";
const app = express();
app.use(cors());
app.use(express.json());
+app.use(dotenv())
-const port = 3000;
+const port = process.env.PORT || 3000;
let nextMessageId = 1;
const messages = [
{
diff --git a/chat-app/backend/package-lock.json b/chat-app/backend/package-lock.json
index f055b3ca..07ae784a 100644
--- a/chat-app/backend/package-lock.json
+++ b/chat-app/backend/package-lock.json
@@ -10,6 +10,7 @@
"license": "ISC",
"dependencies": {
"cors": "^2.8.6",
+ "dotenv": "^17.4.2",
"express": "^5.2.1"
},
"devDependencies": {
@@ -262,6 +263,18 @@
"node": ">= 0.8"
}
},
+ "node_modules/dotenv": {
+ "version": "17.4.2",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.2.tgz",
+ "integrity": "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
diff --git a/chat-app/backend/package.json b/chat-app/backend/package.json
index 342edcde..d8008f23 100644
--- a/chat-app/backend/package.json
+++ b/chat-app/backend/package.json
@@ -8,10 +8,12 @@
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
- "start": "nodemon ./index.js"
+ "start": "node index.js",
+ "dev": "nodemon index.js"
},
"dependencies": {
"cors": "^2.8.6",
+ "dotenv": "^17.4.2",
"express": "^5.2.1"
},
"devDependencies": {
diff --git a/chat-app/frontend/script.js b/chat-app/frontend/script.js
index ba844adc..bae02b34 100644
--- a/chat-app/frontend/script.js
+++ b/chat-app/frontend/script.js
@@ -5,8 +5,8 @@ let messageEl = document.getElementById("message");
let displayBox = document.getElementById("display-message");
let feedbackEl = document.getElementById("feedback");
-// const serverURL = `https://janefrancessc-chat-application-backend.hosting.codeyourfuture.io/messages`;
-const serverURL = "http://127.0.0.1:3000/messages";
+const serverURL = `https://janefrancessc-chat-application-backend.hosting.codeyourfuture.io/messages`;
+// const serverURL = "http://127.0.0.1:3000/messages";
const state = { messages: [] };
let pollingMode = "regular";
let longPoll = false;
From 9b80f19ca29d176ace702a9e524a8e3236ac530e Mon Sep 17 00:00:00 2001
From: JanefrancessC
Date: Tue, 26 May 2026 12:49:10 +0100
Subject: [PATCH 4/8] fix: Remove dotenv since not used
---
chat-app/backend/index.js | 2 --
chat-app/backend/package.json | 1 -
2 files changed, 3 deletions(-)
diff --git a/chat-app/backend/index.js b/chat-app/backend/index.js
index 6bbe52a0..3d9d935b 100644
--- a/chat-app/backend/index.js
+++ b/chat-app/backend/index.js
@@ -1,11 +1,9 @@
import express from "express";
import cors from "cors";
-import dotenv from "dotenv";
const app = express();
app.use(cors());
app.use(express.json());
-app.use(dotenv())
const port = process.env.PORT || 3000;
let nextMessageId = 1;
diff --git a/chat-app/backend/package.json b/chat-app/backend/package.json
index d8008f23..f7eb3994 100644
--- a/chat-app/backend/package.json
+++ b/chat-app/backend/package.json
@@ -13,7 +13,6 @@
},
"dependencies": {
"cors": "^2.8.6",
- "dotenv": "^17.4.2",
"express": "^5.2.1"
},
"devDependencies": {
From 85ad07e0aa43250495c8557319e74b4463595f3a Mon Sep 17 00:00:00 2001
From: JanefrancessC
Date: Tue, 26 May 2026 13:33:55 +0100
Subject: [PATCH 5/8] fix: Modify endpoints to accomodate like feature
---
chat-app/backend/index.js | 10 +++-------
chat-app/frontend/script.js | 4 ++--
2 files changed, 5 insertions(+), 9 deletions(-)
diff --git a/chat-app/backend/index.js b/chat-app/backend/index.js
index 3d9d935b..d4294818 100644
--- a/chat-app/backend/index.js
+++ b/chat-app/backend/index.js
@@ -36,10 +36,6 @@ const messages = [
const callbacksForNewMessages = [];
app.get("/", (req, res) => {
- res.json({ message: `Welcome to my Chat Application!` });
-});
-
-app.get("/messages", (req, res) => {
let since = Number(req.query.since);
let longPoll = req.query.longPoll === "true";
@@ -54,7 +50,7 @@ app.get("/messages", (req, res) => {
res.json(messages);
});
-app.post("/messages", (req, res) => {
+app.post("/", (req, res) => {
const { message, user } = req.body;
if (
@@ -84,7 +80,7 @@ app.post("/messages", (req, res) => {
res.status(201).json({ success: true });
});
-app.post("/messages/:id/like", (req, res) => {
+app.post("/:id/like", (req, res) => {
const id = Number(req.params.id);
const message = messages.find((msg) => msg.id === id);
@@ -96,7 +92,7 @@ app.post("/messages/:id/like", (req, res) => {
res.json(message);
});
-app.post("/messages/:id/dislike", (req, res) => {
+app.post("/:id/dislike", (req, res) => {
const id = Number(req.params.id);
const message = messages.find((msg) => msg.id === id);
diff --git a/chat-app/frontend/script.js b/chat-app/frontend/script.js
index bae02b34..b16bca9e 100644
--- a/chat-app/frontend/script.js
+++ b/chat-app/frontend/script.js
@@ -5,8 +5,8 @@ let messageEl = document.getElementById("message");
let displayBox = document.getElementById("display-message");
let feedbackEl = document.getElementById("feedback");
-const serverURL = `https://janefrancessc-chat-application-backend.hosting.codeyourfuture.io/messages`;
-// const serverURL = "http://127.0.0.1:3000/messages";
+const serverURL = `https://janefrancessc-chat-application-backend.hosting.codeyourfuture.io`;
+// const serverURL = "http://127.0.0.1:3000";
const state = { messages: [] };
let pollingMode = "regular";
let longPoll = false;
From b39395951706345c71b04c9c31d2ca0858a77aff Mon Sep 17 00:00:00 2001
From: JanefrancessC
Date: Tue, 26 May 2026 13:36:25 +0100
Subject: [PATCH 6/8] fix: edit URL
---
chat-app/frontend/script.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/chat-app/frontend/script.js b/chat-app/frontend/script.js
index b16bca9e..77853749 100644
--- a/chat-app/frontend/script.js
+++ b/chat-app/frontend/script.js
@@ -5,7 +5,7 @@ let messageEl = document.getElementById("message");
let displayBox = document.getElementById("display-message");
let feedbackEl = document.getElementById("feedback");
-const serverURL = `https://janefrancessc-chat-application-backend.hosting.codeyourfuture.io`;
+const serverURL = `https://janefrancessc-chat-app-backend.hosting.codeyourfuture.io`;
// const serverURL = "http://127.0.0.1:3000";
const state = { messages: [] };
let pollingMode = "regular";
From 3627170df730d265d2a4247e929220cb1b6b2572 Mon Sep 17 00:00:00 2001
From: JanefrancessC
Date: Thu, 4 Jun 2026 23:48:52 +0100
Subject: [PATCH 7/8] fix: Refactor code to adjust review suggestions
---
chat-app/backend/index.js | 93 +++++++++--------
chat-app/frontend/script.js | 195 +++++++++++++++++++++++++-----------
2 files changed, 185 insertions(+), 103 deletions(-)
diff --git a/chat-app/backend/index.js b/chat-app/backend/index.js
index d4294818..6439542f 100644
--- a/chat-app/backend/index.js
+++ b/chat-app/backend/index.js
@@ -1,55 +1,55 @@
import express from "express";
import cors from "cors";
-const app = express();
-app.use(cors());
-app.use(express.json());
-
const port = process.env.PORT || 3000;
+// Auto-increment IDS
let nextMessageId = 1;
-const messages = [
- {
- id: nextMessageId++,
- message: "Hello",
- user: "Jane",
- time: Date.now() - 6000,
- likes: 1,
- dislikes: 2,
- },
- {
- id: nextMessageId++,
- message: "Hey",
- user: "John",
- time: Date.now() - 3000,
- likes: 1,
- dislikes: 2,
- },
- {
- id: nextMessageId++,
- message: "Hi",
- user: "Bob",
- time: Date.now(),
- likes: 1,
- dislikes: 2,
- },
-];
+// In-memory message store, shared by all clients
+const messages = [];
+// Stores pending longPoll requests waiting for new messages
const callbacksForNewMessages = [];
+// App setup
+const app = express();
+app.use(cors());
+app.use(express.json());
+
+// Routes
+/**
+ * GET /
+ * Returns messages to client
+ *
+ * Query params:
+ * since - timestamps (ms). If provided, only messages newer than this are returned
+ * longPoll - If true and there are no new messages, holds the connection open
+ * until there are new messages instead of returning empty array.
+ */
app.get("/", (req, res) => {
let since = Number(req.query.since);
let longPoll = req.query.longPoll === "true";
if (since) {
const messagesToSend = messages.filter((msg) => msg.time > since);
+
+ // callback until a new message is posted
+ // No new messages and client wants long-polling - park the response
if (messagesToSend.length === 0 && longPoll) {
callbacksForNewMessages.push((val) => res.json(val));
return;
- } else res.json(messagesToSend);
+ }
+ res.json(messagesToSend);
return;
}
+ // No "since" param, return the full message history
res.json(messages);
});
+/**
+ * POST /
+ * Accepts a new message from a client.
+ * Trims whitespace from user and message before saving so that
+ * values like " hello " are stored as "hello".
+ */
app.post("/", (req, res) => {
const { message, user } = req.body;
@@ -73,37 +73,42 @@ app.post("/", (req, res) => {
};
messages.push(newMessage);
+
+ // Resolve all parked long-poll clients with new messages
while (callbacksForNewMessages.length > 0) {
const callback = callbacksForNewMessages.pop();
- callback([messages[messages.length - 1]]);
+ callback([newMessage]);
}
res.status(201).json({ success: true });
});
-app.post("/:id/like", (req, res) => {
+/**
+ * POST /:id/:reaction
+ * Increments the like or dislike count on a message.
+ * Returns the updated message object.
+ */
+app.post("/:id/:reaction", (req, res) => {
const id = Number(req.params.id);
- const message = messages.find((msg) => msg.id === id);
+ const reaction = req.params.reaction;
+ const message = messages.find((msg) => msg.id === id);
if (!message) {
res.status(404).json({ error: "Message not found" });
return;
}
- message.likes++;
- res.json(message);
-});
-app.post("/:id/dislike", (req, res) => {
- const id = Number(req.params.id);
- const message = messages.find((msg) => msg.id === id);
-
- if (!message) {
- res.status(404).json({ error: "Message not found" });
+ if (reaction === "like") {
+ message.likes++;
+ } else if (reaction === "dislike") {
+ message.dislikes++;
+ } else {
+ res.status(400).json({ error: "Invalid reaction!" });
return;
}
- message.dislikes++;
res.json(message);
});
+// start the server
app.listen(port, () => {
console.log(`Chat server listening on port ${port}`);
});
diff --git a/chat-app/frontend/script.js b/chat-app/frontend/script.js
index 77853749..fc9c2986 100644
--- a/chat-app/frontend/script.js
+++ b/chat-app/frontend/script.js
@@ -1,33 +1,73 @@
+// Accessing DOM elements
let pollingForm = document.getElementById("polling-form");
let formEl = document.getElementById("send-message");
let userEl = document.getElementById("user");
let messageEl = document.getElementById("message");
-let displayBox = document.getElementById("display-message");
let feedbackEl = document.getElementById("feedback");
+let displayBox = document.getElementById("display-message");
+let displayText = document.createElement("p");
+
+displayBox.appendChild(displayText);
const serverURL = `https://janefrancessc-chat-app-backend.hosting.codeyourfuture.io`;
// const serverURL = "http://127.0.0.1:3000";
+const feedbackDuration = 3000; //ms before feedback clears
+const pollingDuration = 2000; //ms between regular poll requests
+
+// client-side cache of messages received so far
const state = { messages: [] };
+
let pollingMode = "regular";
let longPoll = false;
+
+// prevents overlapping fetch requests from stacking up
let isFetching = false;
+// Tracks the pending setTimeout so it can be cancelled on mode switch
+let pollingTimeoutId = null;
+
+/**
+ * Displays temporarily feedback message to the user e.g. ("Message sent")
+ * then clears the message after "duration" ms.
+ */
+function showFeedback(message, duration = feedbackDuration) {
+ feedbackEl.textContent = message;
+
+ setTimeout(() => {
+ feedbackEl.textContent = "";
+ }, duration);
+}
+
+/**
+ * Switches between regular polling and long polling.
+ * Cancels any pending timeout and restarts the polling loop immediately.
+ */
pollingForm.addEventListener("change", (e) => {
e.preventDefault();
pollingMode = e.target.value;
longPoll = pollingMode === "long";
- feedbackEl.innerHTML = `
-
${longPoll ? "Using long polling!" : "Using regular polling!"}
- `;
- setTimeout(() => {
- feedbackEl.innerHTML = "";
- }, 3000);
+ clearTimeout(pollingTimeoutId);
+ isFetching = false;
+ keepFetchingMessages();
+
+ showFeedback(longPoll ? "Using long polling!" : "Using regular polling!");
});
+/**
+ * Continuously fetches new messages from the server.
+ *
+ * Regular polling: waits `pollingDuration` ms between each request.
+ * Long polling: sends a request that the server holds open until a new
+ * message arrives, then immediately sends the next request.
+ *
+ * The `since` query param tells the server to only return messages
+ * newer than the last one we already have, avoiding duplicates.
+ */
async function keepFetchingMessages() {
if (isFetching) return;
+
isFetching = true;
const lastMsgTime =
@@ -38,99 +78,140 @@ async function keepFetchingMessages() {
const queryString = lastMsgTime
? `?since=${lastMsgTime}${longPoll ? "&longPoll=true" : ""}`
: "";
+
const url = `${serverURL}${queryString}`;
try {
const response = await fetch(url);
+
if (!response.ok) {
throw new Error(`Status: ${response.status}`);
}
+
const updatedMessages = await response.json();
if (updatedMessages.length > 0) {
state.messages.push(...updatedMessages);
- displayMessages();
+ displayMessages(state.messages);
}
} catch (error) {
console.error(error.message);
- displayBox.innerHTML = `
Error: Server is down!
`;
+ displayText.textContent = `Error: Server is down!`;
} finally {
isFetching = false;
}
+
+ // Schedule the next fetch. Long poll retries sooner since the server
+ // already held the connection open; a 500ms gap just avoids tight loops
+ // if the server is down.
if (longPoll) {
- keepFetchingMessages();
+ pollingTimeoutId = setTimeout(keepFetchingMessages, 500);
} else {
- setTimeout(keepFetchingMessages, 2000);
+ pollingTimeoutId = setTimeout(keepFetchingMessages, pollingDuration);
}
}
-async function likeMessage(msgId) {
+/**
+ * Sends a like or dislike reaction to the server for a given message,
+ * then updates only that message in local state and re-renders.
+ */
+async function reactToMessage(msgId, reaction) {
try {
- const response = await fetch(`${serverURL}/${msgId}/like`, {
+ const response = await fetch(`${serverURL}/${msgId}/${reaction}`, {
method: "POST",
});
if (!response.ok) {
- feedbackEl.innerHTML = `
${await response.text()}
`;
+ showFeedback(await response.text());
return;
}
const updatedMessage = await response.json();
- state.messages = state.messages.map((msg) =>
- msg.id === updatedMessage.id ? updatedMessage : msg,
+ // clear any error messages
+ displayText.remove();
+
+ // Find and update only the affected message rather than replacing the whole array
+ const existingMessage = state.messages.find(
+ (msg) => msg.id === updatedMessage.id,
);
- displayMessages();
+
+ if (existingMessage) {
+ existingMessage.likes = updatedMessage.likes;
+ existingMessage.dislikes = updatedMessage.dislikes;
+ }
+
+ displayMessages(state.messages);
} catch (error) {
console.error(error.message);
}
}
-async function dislikeMessage(msgId) {
- try {
- const response = await fetch(`${serverURL}/${msgId}/dislike`, {
- method: "POST",
- });
+/**
+ * Builds a single message card as a DOM element.
+ * Using textContent so strings are never interpreted as HTML
+ */
+function buildMessageEl(msg) {
+ const wrapper = document.createElement("div");
- if (!response.ok) {
- feedbackEl.innerHTML = `
- `,
- )
- .join("");
+/**
+ * Replaces the entire message list in the DOM with the latest state.
+ * replaceChildren avoids stale nodes.
+ */
+function displayMessages(messages) {
+ displayBox.replaceChildren(...messages.map(buildMessageEl));
}
+// Add event listener to the displayBox to listen for likes/dislikes
+displayBox.addEventListener("click", (e) => {
+ const btn = e.target.closest("button[data-reaction]");
+ if (!btn) return;
+
+ const msgId = Number(btn.dataset.id);
+ const reaction = btn.dataset.reaction;
+ reactToMessage(msgId, reaction);
+});
+
+/**
+ * Handles the send-message form submission.
+ * Trims input on the client side for UX (empty-check), but the server
+ * also validates and trims before saving.
+ */
async function handleSubmit(event) {
event.preventDefault();
- let message = messageEl.value;
- let user = userEl.value;
+ let message = messageEl.value.trim();
+ let user = userEl.value.trim();
if (!message || !user) {
- feedbackEl.innerHTML = `
Field cannot be empty!
`;
+ showFeedback(`Field cannot be empty!`);
return;
}
@@ -144,21 +225,17 @@ async function handleSubmit(event) {
});
if (!response.ok) {
- feedbackEl.innerHTML = `