diff --git a/chatserver b/chatserver
new file mode 100644
index 0000000..30fdc90
Binary files /dev/null and b/chatserver differ
diff --git a/content/root.css b/content/root.css
index aaef1bd..5bae233 100644
--- a/content/root.css
+++ b/content/root.css
@@ -114,6 +114,32 @@ body {
right: calc(30%);
}
+.message-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding-right: 8px;
+}
+
+.delete-button {
+ background: none;
+ border: none;
+ cursor: pointer;
+ color: var(--timestamp-color);
+ padding: 2px 6px;
+ font-size: 12px;
+ opacity: 0;
+ transition: opacity 0.3s;
+}
+
+.message:hover .delete-button {
+ opacity: 1;
+}
+
+.delete-button:hover {
+ background: var(--pum-button-inactive-fg);
+}
+
.settings-icon, .users-icon, .theme-icon{
width: 24px;
height: 24px;
diff --git a/content/root.js b/content/root.js
index 1b6773d..fb265b8 100644
--- a/content/root.js
+++ b/content/root.js
@@ -1,6 +1,8 @@
function toggleSettings() {
const panel = document.getElementById("settings-panel");
- panel.style.display = panel.style.display === "none" ? "block" : "none";
+ panel.style.display = panel.style.display === "block"
+ ? "none"
+ : "block";
if (panel.style.display === "block") {
const username = document.getElementById("username");
username.focus();
@@ -11,7 +13,9 @@ function toggleSettings() {
function toggleUsers() {
const panel = document.getElementById("users-panel");
- panel.style.display = panel.style.display === "none" ? "block" : "none";
+ panel.style.display = panel.style.display === "block"
+ ? "none"
+ : "block";
}
function toggleTheme() {
@@ -23,6 +27,30 @@ function toggleTheme() {
localStorage.setItem("theme", newTheme);
}
+async function deleteMessage(messageId) {
+ if (confirm("Delete this message?")) {
+ try {
+ const response = await fetch("/messages", {
+ method: "DELETE",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({ messageId: messageId }),
+ });
+
+ if (response.ok) {
+ // Refresh messages
+ console.log("Message deleted successfully");
+ location.reload();
+ } else {
+ console.error("Failed to delete message");
+ }
+ } catch (error) {
+ console.error("Error deleting message:", error);
+ }
+ }
+}
+
document.addEventListener("click", function (event) {
const settingsPanel = document.getElementById("settings-panel");
const settingsButton = document.querySelector(".settings-button");
@@ -134,9 +162,16 @@ async function loadMessages() {
"div",
);
messageDiv.className = "message";
- const [username, content] = msg.innerHTML.split(
- "
",
- );
+ const [
+ messageId,
+ username,
+ timestamp,
+ content,
+ ] = msg
+ .innerHTML.split(
+ "
",
+ );
+
const linkedContent = content.replace(
/(?![^<]*>)(https?:\/\/[^\s<]+)/g,
function (url) {
@@ -176,11 +211,30 @@ async function loadMessages() {
return `${url}`;
},
);
- messageDiv.innerHTML =
- '
' + username +
- "
" +
- '' +
- linkedContent + "
";
+
+ let deleteHtml = "";
+
+ const usernameDiv = document.createElement(
+ "div",
+ );
+ usernameDiv.innerHTML = username;
+ compareUsername = usernameDiv.textContent;
+
+ if (
+ compareUsername ===
+ document.getElementById(
+ "current-user",
+ ).textContent
+ ) {
+ deleteHtml =
+ ``;
+ }
+ messageDiv.innerHTML = `
+
+ ${linkedContent}
`;
+
messagesDiv.appendChild(messageDiv);
});
scrollToBottom();
diff --git a/main.go b/main.go
index bcc1133..8d9c534 100644
--- a/main.go
+++ b/main.go
@@ -182,26 +182,6 @@ func (db *Database) MessagesGet() []Message {
var username string
rows.Scan(&id, &ip_address, &content, &created_at, &username)
- // TODO: Implement better message parsing for admin commands
- // TESTING:
- // if msgId, ok := strings.CutPrefix(content, "@delete message id"); ok {
- // msgId = strings.TrimSpace(msgId)
- // go func() {
- // db.MessageDeleteId(msgId)
- // // db.MessageDeleteId(id)
- // }()
- // continue
- // }
- // TESTING:
- // if msgId, ok := strings.CutPrefix(content, "@delete user messages"); ok {
- // msgId = strings.TrimSpace(msgId)
- // go func() {
- // db.UserMessagesDelete(ip_address)
- // // db.MessageDeleteId(id)
- // }()
- // continue
- // }
-
message := Message{
Id: id,
Content: content,
@@ -288,6 +268,18 @@ func (db *Database) MessageDeleteId(id string) {
fmt.Println(err)
}
}
+func (db *Database) MessageDeleteIfOwner(id string, ip string) (int, error) {
+ res, err := db.db.Exec("DELETE FROM messages WHERE id = ? AND ip_address = ?", id, ip)
+ if err != nil {
+ return 0, err
+ }
+ affected, err := res.RowsAffected()
+ if err != nil {
+ return 0, err
+ }
+ return int(affected), nil
+
+}
func (db *Database) DeleteOldMessages(ageMinutes int) {
if ageMinutes <= 0 {
@@ -491,12 +483,11 @@ func (s *Server) handleMessages(w http.ResponseWriter, r *http.Request) {
clientIP := getClientIP(r)
timeZone := s.Database.UserGetTimezone(clientIP)
timeLocal := TimeStringToTimeInLocation(msg.Timestamp, timeZone)
- body += fmt.Sprintf(`%s%s %s
%s
`,
+ body += fmt.Sprintf(`%s
%s
%s
%s
`,
msg.Id, msg.SenderUsername, timeLocal, msg.Content)
}
w.Write([]byte(getMessageTemplate(s.Config.Paths.MessagesHtmlPath, body)))
-
case http.MethodPut:
w.Header().Set("Content-Type", "application/json")
@@ -529,6 +520,30 @@ func (s *Server) handleMessages(w http.ResponseWriter, r *http.Request) {
"status": "Message received",
"from": username,
})
+ case http.MethodDelete:
+ w.Header().Set("Content-Type", "application/json")
+
+ var req struct {
+ MessageId string `json:"messageId"`
+ }
+
+ if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
+ http.Error(w, `{"error": "Invalid JSON"}`, http.StatusBadRequest)
+ return
+ }
+
+ clientIP := getClientIP(r)
+ if affected, err := s.Database.MessageDeleteIfOwner(req.MessageId, clientIP); err != nil {
+ http.Error(w, `{"error": "Unauthorized"}`, http.StatusNotFound)
+ return
+ } else if affected == 0 {
+ http.Error(w, `{"error": "Message not found"}`, http.StatusNotFound)
+ return
+ }
+
+ json.NewEncoder(w).Encode(map[string]string{
+ "status": "Message deleted",
+ })
default:
http.Error(w, `{"error": "Method not allowed"}`, http.StatusMethodNotAllowed)
return
diff --git a/readme.md b/readme.md
index 32c11e4..8f015df 100644
--- a/readme.md
+++ b/readme.md
@@ -6,12 +6,10 @@
- Nothing yet
### Low Priority
- Mobile formatting @media
-- First click of user list does not register
## Backend
### High Priority
-- Updating messages should lazily load prior messages (pagination?)
+- Nothing yet
### Mid Priority
- Nothing yet
### Low Priority
- Search functionality?
-- Delete messages? (in progress, capability exists, flesh this out a bit)