This commit is contained in:
Radon 2025-01-20 19:16:35 -06:00
parent feab14a5be
commit 890fa31ed4
5 changed files with 128 additions and 35 deletions

BIN
chatserver Normal file

Binary file not shown.

View File

@ -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;

View File

@ -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(
"<br>",
);
const [
messageId,
username,
timestamp,
content,
] = msg
.innerHTML.split(
"<br>",
);
const linkedContent = content.replace(
/(?![^<]*>)(https?:\/\/[^\s<]+)/g,
function (url) {
@ -176,11 +211,30 @@ async function loadMessages() {
return `<a href="${url}" target="_blank" rel="noopener noreferrer">${url}</a>`;
},
);
messageDiv.innerHTML =
'<div class="username">' + username +
"</div>" +
'<div class="content">' +
linkedContent + "</div>";
let deleteHtml = "";
const usernameDiv = document.createElement(
"div",
);
usernameDiv.innerHTML = username;
compareUsername = usernameDiv.textContent;
if (
compareUsername ===
document.getElementById(
"current-user",
).textContent
) {
deleteHtml =
`<button class="delete-button" onclick="deleteMessage('${messageId}')" style="display: inline;">🗑️</button>`;
}
messageDiv.innerHTML = `
<div class="message-header">
<div class="username">${username} ${timestamp} ${deleteHtml}</div>
</div>
<div class="content">${linkedContent}</div>`;
messagesDiv.appendChild(messageDiv);
});
scrollToBottom();

59
main.go
View File

@ -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<p><span class="username">%s </span><span class="timestamp">%s</span><br><span class="message">%s</span></p>`,
body += fmt.Sprintf(`<p>%s<br><span class="username">%s</span><br><span class="timestamp">%s</span><br><span class="message">%s</span></p>`,
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

View File

@ -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)