Fix archiving and unarchiving functionality.

This commit is contained in:
2025-11-01 22:37:47 +00:00
parent 9dc01f925a
commit 61ad033520
8 changed files with 88 additions and 32 deletions

View File

@@ -21,6 +21,6 @@ type MessageService interface {
Archive(userID, id int64) error Archive(userID, id int64) error
//Restore() //Restore()
//ToDo: implement //ToDo: implement
//Unarchive(userID, id int64) error Unarchive(userID, id int64) error
//MarkRead(userID, id int64) error //MarkRead(userID, id int64) error
} }

View File

@@ -6,12 +6,14 @@ package accountMessageHandler
import ( import (
"bytes" "bytes"
"database/sql"
"errors"
"net/http" "net/http"
"strconv" "strconv"
templateHandlers "synlotto-website/internal/handlers/template" templateHandlers "synlotto-website/internal/handlers/template"
templateHelpers "synlotto-website/internal/helpers/template" templateHelpers "synlotto-website/internal/helpers/template"
errors "synlotto-website/internal/http/error" httpErrors "synlotto-website/internal/http/error"
"synlotto-website/internal/logging" "synlotto-website/internal/logging"
"synlotto-website/internal/platform/bootstrap" "synlotto-website/internal/platform/bootstrap"
@@ -104,7 +106,7 @@ func (h *AccountMessageHandlers) ArchivePost(c *gin.Context) {
idStr := c.PostForm("id") idStr := c.PostForm("id")
id, err := strconv.ParseInt(idStr, 10, 64) id, err := strconv.ParseInt(idStr, 10, 64)
if err != nil || id <= 0 { if err != nil || id <= 0 {
errors.RenderStatus(c, sm, http.StatusBadRequest) httpErrors.RenderStatus(c, sm, http.StatusBadRequest)
return return
} }
@@ -119,24 +121,32 @@ func (h *AccountMessageHandlers) ArchivePost(c *gin.Context) {
c.Redirect(http.StatusSeeOther, "/account/messages") c.Redirect(http.StatusSeeOther, "/account/messages")
} }
// POST /account/messages/restore // POST /account/messages/archived
func (h *AccountMessageHandlers) RestorePost(c *gin.Context) { func (h *AccountMessageHandlers) RestoreArchived(c *gin.Context) {
app := c.MustGet("app").(*bootstrap.App) app := c.MustGet("app").(*bootstrap.App)
sm := app.SessionManager sm := app.SessionManager
//userID := mustUserID(c) userID := mustUserID(c)
idStr := c.PostForm("id") idStr := c.PostForm("id")
id, err := strconv.ParseInt(idStr, 10, 64) id, err := strconv.ParseInt(idStr, 10, 64)
if err != nil || id <= 0 { if err != nil || id <= 0 {
errors.RenderStatus(c, sm, http.StatusBadRequest) sm.Put(c.Request.Context(), "flash", "Invalid message id.")
c.Redirect(http.StatusSeeOther, "/account/messages/archive")
return return
} }
//
// if err := h.Svc.Unarchive(userID, id); err != nil { if err := h.Svc.Unarchive(userID, id); err != nil {
// logging.Info("❌ Restore error: %v", err) logging.Info("❌ restore/unarchive error: %v", err)
// sm.Put(c.Request.Context(), "flash", "Could not restore message.") // If no rows affected, show friendly flash; otherwise generic message.
// } else { if errors.Is(err, sql.ErrNoRows) {
// sm.Put(c.Request.Context(), "flash", "Message restored.") sm.Put(c.Request.Context(), "flash", "Message not found or not permitted.")
// } } else {
c.Redirect(http.StatusSeeOther, "/account/messages/archived") sm.Put(c.Request.Context(), "flash", "Could not restore message.")
}
c.Redirect(http.StatusSeeOther, "/account/messages/archive")
return
}
sm.Put(c.Request.Context(), "flash", "Message restored.")
c.Redirect(http.StatusSeeOther, "/account/messages/archive")
} }

View File

@@ -177,6 +177,6 @@ func RestoreMessageHandler(app *bootstrap.App) http.HandlerFunc {
templateHelpers.SetFlash(r, "Message restored.") templateHelpers.SetFlash(r, "Message restored.")
} }
http.Redirect(w, r, "/account/messages/archived", http.StatusSeeOther) http.Redirect(w, r, "/account/messages/archive", http.StatusSeeOther)
} }
} }

View File

@@ -71,7 +71,7 @@ func RegisterAccountRoutes(app *bootstrap.App) {
messages.POST("/send", msgH.SendPost) messages.POST("/send", msgH.SendPost)
messages.GET("/archive", msgH.ArchivedList) // view archived messages messages.GET("/archive", msgH.ArchivedList) // view archived messages
messages.POST("/archive", msgH.ArchivePost) // archive a message messages.POST("/archive", msgH.ArchivePost) // archive a message
messages.POST("/restore", msgH.RestorePost) messages.POST("/restore", msgH.RestoreArchived)
} }
// Notifications (auth-required) // Notifications (auth-required)

View File

@@ -1,6 +1,8 @@
package models package models
import "time" import (
"time"
)
type Message struct { type Message struct {
ID int ID int

View File

@@ -76,7 +76,8 @@ func (s *Service) ListArchived(userID int64) ([]domain.Message, error) {
defer cancel() defer cancel()
q := ` q := `
SELECT id, senderId, recipientId, subject, body, is_read, is_archived, created_at SELECT id, senderId, recipientId, subject, body,
is_read, is_archived, created_at, archived_at
FROM user_messages FROM user_messages
WHERE recipientId = ? AND is_archived = TRUE WHERE recipientId = ? AND is_archived = TRUE
ORDER BY created_at DESC` ORDER BY created_at DESC`
@@ -91,11 +92,32 @@ func (s *Service) ListArchived(userID int64) ([]domain.Message, error) {
var out []domain.Message var out []domain.Message
for rows.Next() { for rows.Next() {
var m domain.Message var m domain.Message
if err := rows.Scan(&m.ID, &m.SenderId, &m.RecipientId, &m.Subject, &m.Body, &m.IsRead, &m.IsArchived, &m.CreatedAt); err != nil { var archived sql.NullTime
if err := rows.Scan(
&m.ID,
&m.SenderId,
&m.RecipientId,
&m.Subject,
&m.Body,
&m.IsRead,
&m.IsArchived,
&m.CreatedAt,
&archived,
); err != nil {
return nil, err return nil, err
} }
if archived.Valid {
t := archived.Time
m.ArchivedAt = &t
} else {
m.ArchivedAt = nil
}
out = append(out, m) out = append(out, m)
} }
return out, rows.Err() return out, rows.Err()
} }
@@ -233,3 +255,25 @@ func intToStr(n int) string {
} }
return string(b[i:]) return string(b[i:])
} }
func (s *Service) Unarchive(userID, id int64) error {
ctx, cancel := context.WithTimeout(context.Background(), s.Timeout)
defer cancel()
q := `
UPDATE user_messages
SET is_archived = 0, archived_at = NULL
WHERE id = ? AND recipientId = ?
`
q = s.bind(q)
res, err := s.DB.ExecContext(ctx, q, id, userID)
if err != nil {
return err
}
n, _ := res.RowsAffected()
if n == 0 {
return sql.ErrNoRows
}
return nil
}

View File

@@ -11,7 +11,7 @@
<p class="card-text"> <p class="card-text">
<small class="text-muted"> <small class="text-muted">
Archived: Archived:
{{ if .ArchivedAt.Valid }} {{ with .ArchivedAt }}
{{ .Format "02 Jan 2006 15:04" }} {{ .Format "02 Jan 2006 15:04" }}
{{ else }} {{ else }}
@@ -28,20 +28,20 @@
{{ end }} {{ end }}
<!-- Pagination Controls (keep if your funcs exist) --> <!-- Pagination Controls (keep if your funcs exist) -->
<nav> <nav>
<ul class="pagination"> <ul class="pagination">
{{ if gt .Page 1 }} {{ if gt .CurrentPage 1 }}
<li class="page-item"> <li class="page-item">
<a class="page-link" href="?page={{ minus1 .Page }}">Previous</a> <a class="page-link" href="?page={{ sub .CurrentPage 1 }}">Previous</a>
</li> </li>
{{ end }} {{ end }}
{{ if .HasMore }} {{ if lt .CurrentPage .TotalPages }}
<li class="page-item"> <li class="page-item">
<a class="page-link" href="?page={{ plus1 .Page }}">Next</a> <a class="page-link" href="?page={{ add .CurrentPage 1 }}">Next</a>
</li> </li>
{{ end }} {{ end }}
</ul> </ul>
</nav> </nav>
{{ else }} {{ else }}
<div class="alert alert-info text-center">No archived messages.</div> <div class="alert alert-info text-center">No archived messages.</div>
{{ end }} {{ end }}

View File

@@ -7,7 +7,7 @@
{{ range .Messages }} {{ range .Messages }}
<li class="list-group-item d-flex justify-content-between align-items-center"> <li class="list-group-item d-flex justify-content-between align-items-center">
<div> <div>
<a href="/account/messages/read?={{ .ID }}" class="fw-bold text-dark">{{ .Subject }}</a><br> <a href="/account/messages/read?id={{ .ID }}" class="fw-bold text-dark">{{ .Subject }}</a><br>
<small class="text-muted">{{ .CreatedAt.Format "02 Jan 2006 15:04" }}</small> <small class="text-muted">{{ .CreatedAt.Format "02 Jan 2006 15:04" }}</small>
</div> </div>
<form method="POST" action="/account/messages/archive" class="m-0"> <form method="POST" action="/account/messages/archive" class="m-0">