From dd830812716660c7c08196fcb35a59192af57f41 Mon Sep 17 00:00:00 2001 From: H3ALY Date: Wed, 2 Apr 2025 21:29:54 +0100 Subject: [PATCH] Added full message handling system with archive view, pagination, and send support - Implemented message inbox and archived messages view - Added pagination logic to both inbox and archive handlers - Integrated message sending functionality with CSRF protection - Updated schema to include `archived_at` timestamp - Included archive button and logic with feedback flash messaging - Fixed message dropdown routing and rendering in topbar - Cleaned up template load paths and error handling --- handlers/messages.go | 64 +++++++++++++++++++++++++ main.go | 2 + models/user.go | 1 + storage/messages.go | 39 ++++++++++++++- storage/schema.go | 5 +- templates/account/messages/achived.html | 39 +++++++++++++++ templates/account/messages/index.html | 61 ++++++++++++++++------- templates/account/messages/send.html | 27 +++++++++++ templates/topbar.html | 4 +- 9 files changed, 221 insertions(+), 21 deletions(-) create mode 100644 templates/account/messages/achived.html create mode 100644 templates/account/messages/send.html diff --git a/handlers/messages.go b/handlers/messages.go index 3726239..5e5ecd2 100644 --- a/handlers/messages.go +++ b/handlers/messages.go @@ -72,3 +72,67 @@ func ArchiveMessageHandler(db *sql.DB) http.HandlerFunc { http.Redirect(w, r, "/account/messages", http.StatusSeeOther) } } + +func ArchivedMessagesHandler(db *sql.DB) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + userID, ok := helpers.GetCurrentUserID(r) + if !ok { + helpers.RenderError(w, r, 403) + return + } + + page := helpers.Atoi(r.URL.Query().Get("page")) + if page < 1 { + page = 1 + } + perPage := 10 + + messages := storage.GetArchivedMessages(db, userID, page, perPage) + hasMore := len(messages) == perPage + + data := BuildTemplateData(db, w, r) + context := helpers.TemplateContext(w, r, data) + context["Messages"] = messages + context["Page"] = page + context["HasMore"] = hasMore + + tmpl := helpers.LoadTemplateFiles("archived.html", "templates/account/messages/archived.html") + tmpl.ExecuteTemplate(w, "layout", context) + } +} + +func SendMessageHandler(db *sql.DB) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodGet: + // Display the form + data := BuildTemplateData(db, w, r) + context := helpers.TemplateContext(w, r, data) + tmpl := helpers.LoadTemplateFiles("send-message.html", "templates/account/messages/send.html") + + if err := tmpl.ExecuteTemplate(w, "layout", context); err != nil { + helpers.RenderError(w, r, 500) + } + case http.MethodPost: + // Handle form submission + senderID, ok := helpers.GetCurrentUserID(r) + if !ok { + helpers.RenderError(w, r, 403) + return + } + + recipientID := helpers.Atoi(r.FormValue("recipient_id")) + subject := r.FormValue("subject") + body := r.FormValue("message") + + if err := storage.SendMessage(db, senderID, recipientID, subject, body); err != nil { + helpers.SetFlash(w, r, "Failed to send message.") + } else { + helpers.SetFlash(w, r, "Message sent.") + } + http.Redirect(w, r, "/account/messages", http.StatusSeeOther) + default: + helpers.RenderError(w, r, 405) + } + } +} diff --git a/main.go b/main.go index 978253d..39c35ea 100644 --- a/main.go +++ b/main.go @@ -71,6 +71,8 @@ func setupAccountRoutes(mux *http.ServeMux, db *sql.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/messages/archived", middleware.Auth(true)(handlers.ArchivedMessagesHandler(db))) + mux.HandleFunc("/account/messages/send", middleware.Auth(true)(handlers.SendMessageHandler(db))) mux.HandleFunc("/account/notifications", middleware.Auth(true)(handlers.NotificationsHandler(db))) mux.HandleFunc("/account/notifications/read", middleware.Auth(true)(handlers.MarkNotificationReadHandler(db))) } diff --git a/models/user.go b/models/user.go index 2ffaf9d..b05faab 100644 --- a/models/user.go +++ b/models/user.go @@ -30,6 +30,7 @@ type Message struct { Message string IsRead bool CreatedAt time.Time + ArchivedAt *time.Time } var db *sql.DB diff --git a/storage/messages.go b/storage/messages.go index 9e68b96..4bebf36 100644 --- a/storage/messages.go +++ b/storage/messages.go @@ -85,8 +85,45 @@ func MarkMessageAsRead(db *sql.DB, messageID, userID int) error { func ArchiveMessage(db *sql.DB, userID, messageID int) error { _, err := db.Exec(` UPDATE users_messages - SET is_archived = TRUE + SET is_archived = TRUE, archived_at = CURRENT_TIMESTAMP WHERE id = ? AND recipientId = ? `, messageID, userID) return err } + +func SendMessage(db *sql.DB, senderID, recipientID int, subject, message string) error { + _, err := db.Exec(` + INSERT INTO users_messages (senderId, recipientId, subject, message) + VALUES (?, ?, ?, ?) + `, senderID, recipientID, subject, message) + return err +} + +func GetArchivedMessages(db *sql.DB, userID int, page, perPage int) []models.Message { + offset := (page - 1) * perPage + rows, err := db.Query(` + SELECT id, senderId, recipientId, subject, message, is_read, created_at, archived_at + FROM users_messages + WHERE recipientId = ? AND is_archived = TRUE + ORDER BY archived_at DESC + LIMIT ? OFFSET ? + `, userID, perPage, offset) + if err != nil { + return nil + } + defer rows.Close() + + var messages []models.Message + for rows.Next() { + var m models.Message + err := rows.Scan( + &m.ID, &m.SenderId, &m.RecipientId, + &m.Subject, &m.Message, &m.IsRead, + &m.CreatedAt, &m.ArchivedAt, + ) + if err == nil { + messages = append(messages, m) + } + } + return messages +} diff --git a/storage/schema.go b/storage/schema.go index 77523a9..a5177c0 100644 --- a/storage/schema.go +++ b/storage/schema.go @@ -115,12 +115,13 @@ const SchemaUsersMessages = ` CREATE TABLE IF NOT EXISTS users_messages ( id INTEGER PRIMARY KEY AUTOINCREMENT, senderId INTEGER NOT NULL REFERENCES users(id), - recipientId int, + recipientId INTEGER NOT NULL REFERENCES users(id), subject TEXT NOT NULL, message TEXT, is_read BOOLEAN DEFAULT FALSE, is_archived BOOLEAN DEFAULT FALSE, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + archived_at TIMESTAMP );` const SchemaUsersNotifications = ` diff --git a/templates/account/messages/achived.html b/templates/account/messages/achived.html new file mode 100644 index 0000000..16b8456 --- /dev/null +++ b/templates/account/messages/achived.html @@ -0,0 +1,39 @@ +{{ define "content" }} +
+

Archived Messages

+ + {{ if .Messages }} + {{ range .Messages }} +
+
+
{{ .Subject }}
+

{{ .Message }}

+

+ Archived: {{ .ArchivedAt.Format "02 Jan 2006 15:04" }} +

+
+
+ {{ end }} + + + + {{ else }} +
No archived messages.
+ {{ end }} + + Back to Inbox +
+{{ end }} \ No newline at end of file diff --git a/templates/account/messages/index.html b/templates/account/messages/index.html index ff21f25..ed4b662 100644 --- a/templates/account/messages/index.html +++ b/templates/account/messages/index.html @@ -1,22 +1,51 @@ {{ define "content" }} -
-

Your Messages

-