changes
This commit is contained in:
parent
feab14a5be
commit
890fa31ed4
BIN
chatserver
Normal file
BIN
chatserver
Normal file
Binary file not shown.
@ -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;
|
||||
|
@ -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
59
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<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
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user