Messages: Add archive (soft-delete) support + dropdown UI polish
- Implemented `/account/messages/archive` route for soft-archiving messages - Added `is_archived` flag to `users_messages` schema and model - Topbar dropdown now reflects accurate unread message count - Fixed missing route registration for archive handler - Improved message visibility checks to prevent access violations - Placeholder for rate-limit (429) error page rendering identified
This commit is contained in:
@@ -52,3 +52,23 @@ func ReadMessageHandler(db *sql.DB) http.HandlerFunc {
|
||||
tmpl.ExecuteTemplate(w, "layout", context)
|
||||
}
|
||||
}
|
||||
|
||||
func ArchiveMessageHandler(db *sql.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
id := helpers.Atoi(r.URL.Query().Get("id"))
|
||||
userID, ok := helpers.GetCurrentUserID(r)
|
||||
if !ok {
|
||||
helpers.RenderError(w, r, 403)
|
||||
return
|
||||
}
|
||||
|
||||
err := storage.ArchiveMessage(db, userID, id)
|
||||
if err != nil {
|
||||
helpers.SetFlash(w, r, "Failed to archive message.")
|
||||
} else {
|
||||
helpers.SetFlash(w, r, "Message archived.")
|
||||
}
|
||||
|
||||
http.Redirect(w, r, "/account/messages", http.StatusSeeOther)
|
||||
}
|
||||
}
|
||||
|
||||
1
main.go
1
main.go
@@ -70,6 +70,7 @@ func setupAccountRoutes(mux *http.ServeMux, db *sql.DB) {
|
||||
mux.HandleFunc("/account/tickets/my_tickets", handlers.GetMyTickets(db))
|
||||
mux.HandleFunc("/account/messages", middleware.Auth(true)(handlers.MessagesInboxHandler(db)))
|
||||
mux.HandleFunc("/account/messages/read", middleware.Auth(true)(handlers.ReadMessageHandler(db)))
|
||||
mux.HandleFunc("/account/messages/archive", middleware.Auth(true)(handlers.ArchiveMessageHandler(db)))
|
||||
mux.HandleFunc("/account/notifications", middleware.Auth(true)(handlers.NotificationsHandler(db)))
|
||||
mux.HandleFunc("/account/notifications/read", middleware.Auth(true)(handlers.MarkNotificationReadHandler(db)))
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ func GetMessageCount(db *sql.DB, userID int) (int, error) {
|
||||
var count int
|
||||
err := db.QueryRow(`
|
||||
SELECT COUNT(*) FROM users_messages
|
||||
WHERE recipientId = ? AND is_read = FALSE
|
||||
WHERE recipientId = ? AND is_read = FALSE AND is_archived = FALSE
|
||||
`, userID).Scan(&count)
|
||||
return count, err
|
||||
}
|
||||
@@ -19,7 +19,7 @@ func GetRecentMessages(db *sql.DB, userID int, limit int) []models.Message {
|
||||
rows, err := db.Query(`
|
||||
SELECT id, senderId, recipientId, subject, message, is_read, created_at
|
||||
FROM users_messages
|
||||
WHERE recipientId = ?
|
||||
WHERE recipientId = ? AND is_archived = FALSE
|
||||
ORDER BY created_at DESC
|
||||
LIMIT ?
|
||||
`, userID, limit)
|
||||
@@ -81,3 +81,12 @@ func MarkMessageAsRead(db *sql.DB, messageID, userID int) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ArchiveMessage(db *sql.DB, userID, messageID int) error {
|
||||
_, err := db.Exec(`
|
||||
UPDATE users_messages
|
||||
SET is_archived = TRUE
|
||||
WHERE id = ? AND recipientId = ?
|
||||
`, messageID, userID)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -119,6 +119,7 @@ CREATE TABLE IF NOT EXISTS users_messages (
|
||||
subject TEXT NOT NULL,
|
||||
message TEXT,
|
||||
is_read BOOLEAN DEFAULT FALSE,
|
||||
is_archived BOOLEAN DEFAULT FALSE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);`
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<p class="text-muted">Received: {{ .Message.CreatedAt.Format "02 Jan 2006 15:04" }}</p>
|
||||
<hr>
|
||||
<p>{{ .Message.Message }}</p>
|
||||
<a href="/account/messages" class="btn btn-secondary mt-4">Back to Inbox</a>
|
||||
<a href="/account/messages" class="btn btn-secondary mt-4">Back to Inbox</a> <a href="/account/messages/archive?id={{ .Message.ID }}" class="btn btn-outline-danger mt-3">Archive</a>
|
||||
{{ else }}
|
||||
<div class="alert alert-danger text-center">
|
||||
Message not found or access denied.
|
||||
|
||||
Reference in New Issue
Block a user