Changes to pagination and fixing archive messages in progress
This commit is contained in:
@@ -18,4 +18,9 @@ type MessageService interface {
|
|||||||
ListArchived(userID int64) ([]Message, error)
|
ListArchived(userID int64) ([]Message, error)
|
||||||
GetByID(userID, id int64) (*Message, error)
|
GetByID(userID, id int64) (*Message, error)
|
||||||
Create(userID int64, in CreateMessageInput) (int64, error)
|
Create(userID int64, in CreateMessageInput) (int64, error)
|
||||||
|
Archive(userID, id int64) error
|
||||||
|
//Restore()
|
||||||
|
//ToDo: implement
|
||||||
|
//Unarchive(userID, id int64) error
|
||||||
|
//MarkRead(userID, id int64) error
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,13 @@
|
|||||||
package accountMessageHandler
|
package accountMessageHandler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"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"
|
||||||
|
|
||||||
"synlotto-website/internal/logging"
|
"synlotto-website/internal/logging"
|
||||||
"synlotto-website/internal/platform/bootstrap"
|
"synlotto-website/internal/platform/bootstrap"
|
||||||
@@ -22,30 +25,118 @@ import (
|
|||||||
func (h *AccountMessageHandlers) ArchivedList(c *gin.Context) {
|
func (h *AccountMessageHandlers) ArchivedList(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)
|
||||||
msgs, err := h.Svc.ListArchived(userID)
|
|
||||||
|
// pagination
|
||||||
|
page := 1
|
||||||
|
if ps := c.Query("page"); ps != "" {
|
||||||
|
if n, err := strconv.Atoi(ps); err == nil && n > 0 {
|
||||||
|
page = n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pageSize := 20
|
||||||
|
|
||||||
|
totalPages, totalCount, err := templateHelpers.GetTotalPages(
|
||||||
|
c.Request.Context(),
|
||||||
|
app.DB,
|
||||||
|
"user_messages",
|
||||||
|
"recipientId = ? AND is_archived = TRUE",
|
||||||
|
[]any{userID},
|
||||||
|
pageSize,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
logging.Info("❌ count archived error: %v", err)
|
||||||
|
c.String(http.StatusInternalServerError, "Failed to load archived messages")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if page > totalPages {
|
||||||
|
page = totalPages
|
||||||
|
}
|
||||||
|
|
||||||
|
msgsAll, err := h.Svc.ListArchived(userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logging.Info("❌ list archived error: %v", err)
|
logging.Info("❌ list archived error: %v", err)
|
||||||
c.String(http.StatusInternalServerError, "Failed to load archived messages")
|
c.String(http.StatusInternalServerError, "Failed to load archived messages")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// slice in-memory for now
|
||||||
|
start := (page - 1) * pageSize
|
||||||
|
if start > len(msgsAll) {
|
||||||
|
start = len(msgsAll)
|
||||||
|
}
|
||||||
|
end := start + pageSize
|
||||||
|
if end > len(msgsAll) {
|
||||||
|
end = len(msgsAll)
|
||||||
|
}
|
||||||
|
msgs := msgsAll[start:end]
|
||||||
|
|
||||||
data := templateHandlers.BuildTemplateData(app, c.Writer, c.Request)
|
data := templateHandlers.BuildTemplateData(app, c.Writer, c.Request)
|
||||||
ctx := templateHelpers.TemplateContext(c.Writer, c.Request, data)
|
ctx := templateHelpers.TemplateContext(c.Writer, c.Request, data)
|
||||||
|
|
||||||
if f := sm.PopString(c.Request.Context(), "flash"); f != "" {
|
if f := sm.PopString(c.Request.Context(), "flash"); f != "" {
|
||||||
ctx["Flash"] = f
|
ctx["Flash"] = f
|
||||||
}
|
}
|
||||||
ctx["CSRFToken"] = nosurf.Token(c.Request)
|
ctx["CSRFToken"] = nosurf.Token(c.Request)
|
||||||
ctx["Title"] = "Archived Messages"
|
ctx["Title"] = "Archived Messages"
|
||||||
ctx["Messages"] = msgs
|
ctx["Messages"] = msgs
|
||||||
|
ctx["CurrentPage"] = page
|
||||||
|
ctx["TotalPages"] = totalPages
|
||||||
|
ctx["TotalCount"] = totalCount
|
||||||
|
ctx["PageRange"] = templateHelpers.MakePageRange(1, totalPages)
|
||||||
|
|
||||||
tmpl := templateHelpers.LoadTemplateFiles("layout.html", "web/templates/account/messages/archived.html")
|
tmpl := templateHelpers.LoadTemplateFiles("layout.html", "web/templates/account/messages/archived.html")
|
||||||
|
|
||||||
c.Status(http.StatusOK)
|
var buf bytes.Buffer
|
||||||
if err := tmpl.ExecuteTemplate(c.Writer, "layout", ctx); err != nil {
|
if err := tmpl.ExecuteTemplate(&buf, "layout", ctx); err != nil {
|
||||||
logging.Info("❌ Template render error: %v", err)
|
logging.Info("❌ Template render error: %v", err)
|
||||||
c.String(http.StatusInternalServerError, "Error rendering archived messages")
|
c.String(http.StatusInternalServerError, "Error rendering archived messages")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
c.Data(http.StatusOK, "text/html; charset=utf-8", buf.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST /account/messages/archive
|
||||||
|
func (h *AccountMessageHandlers) ArchivePost(c *gin.Context) {
|
||||||
|
app := c.MustGet("app").(*bootstrap.App)
|
||||||
|
sm := app.SessionManager
|
||||||
|
userID := mustUserID(c)
|
||||||
|
|
||||||
|
idStr := c.PostForm("id")
|
||||||
|
id, err := strconv.ParseInt(idStr, 10, 64)
|
||||||
|
if err != nil || id <= 0 {
|
||||||
|
errors.RenderStatus(c, sm, http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := h.Svc.Archive(userID, id); err != nil {
|
||||||
|
logging.Info("❌ Archive error: %v", err)
|
||||||
|
sm.Put(c.Request.Context(), "flash", "Could not archive message.")
|
||||||
|
c.Redirect(http.StatusSeeOther, "/account/messages")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sm.Put(c.Request.Context(), "flash", "Message archived.")
|
||||||
|
c.Redirect(http.StatusSeeOther, "/account/messages")
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST /account/messages/restore
|
||||||
|
func (h *AccountMessageHandlers) RestorePost(c *gin.Context) {
|
||||||
|
app := c.MustGet("app").(*bootstrap.App)
|
||||||
|
sm := app.SessionManager
|
||||||
|
//userID := mustUserID(c)
|
||||||
|
|
||||||
|
idStr := c.PostForm("id")
|
||||||
|
id, err := strconv.ParseInt(idStr, 10, 64)
|
||||||
|
if err != nil || id <= 0 {
|
||||||
|
errors.RenderStatus(c, sm, http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// if err := h.Svc.Unarchive(userID, id); err != nil {
|
||||||
|
// logging.Info("❌ Restore error: %v", err)
|
||||||
|
// sm.Put(c.Request.Context(), "flash", "Could not restore message.")
|
||||||
|
// } else {
|
||||||
|
// sm.Put(c.Request.Context(), "flash", "Message restored.")
|
||||||
|
// }
|
||||||
|
c.Redirect(http.StatusSeeOther, "/account/messages/archived")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
// Path: /internal/handlers/account/messages
|
// Path: /internal/handlers/account/messages
|
||||||
// File: read.go
|
// File: read.go
|
||||||
// ToDo: Remove SQL
|
// ToDo: Remove SQL
|
||||||
// add LIMIT/OFFSET in service
|
|
||||||
|
|
||||||
package accountMessageHandler
|
package accountMessageHandler
|
||||||
|
|
||||||
@@ -13,8 +12,8 @@ import (
|
|||||||
|
|
||||||
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"
|
errors "synlotto-website/internal/http/error"
|
||||||
|
|
||||||
"synlotto-website/internal/logging"
|
"synlotto-website/internal/logging"
|
||||||
"synlotto-website/internal/platform/bootstrap"
|
"synlotto-website/internal/platform/bootstrap"
|
||||||
|
|
||||||
@@ -27,31 +26,36 @@ import (
|
|||||||
func (h *AccountMessageHandlers) List(c *gin.Context) {
|
func (h *AccountMessageHandlers) List(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)
|
||||||
|
|
||||||
// 1) Parse page param (default 1)
|
// --- Pagination ---
|
||||||
page := 1
|
page := 1
|
||||||
if ps := c.Query("page"); ps != "" {
|
if ps := c.Query("page"); ps != "" {
|
||||||
if n, err := strconv.Atoi(ps); err == nil && n > 0 {
|
if n, err := strconv.Atoi(ps); err == nil && n > 0 {
|
||||||
page = n
|
page = n
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pageSize := 20
|
pageSize := 20
|
||||||
|
|
||||||
// 2) Count total for this user (so TotalPages is accurate)
|
totalPages, totalCount, err := templateHelpers.GetTotalPages(
|
||||||
totalPages, totalCount := templateHelpers.GetTotalPages(
|
c.Request.Context(),
|
||||||
app.DB,
|
app.DB,
|
||||||
"user_messages",
|
"user_messages",
|
||||||
"WHERE recipientId = ? AND is_archived = FALSE",
|
"recipientId = ? AND is_archived = FALSE",
|
||||||
[]interface{}{userID},
|
[]any{userID},
|
||||||
pageSize,
|
pageSize,
|
||||||
)
|
)
|
||||||
|
if err != nil {
|
||||||
|
logging.Info("❌ count inbox error: %v", err)
|
||||||
|
c.String(http.StatusInternalServerError, "Failed to load messages")
|
||||||
|
return
|
||||||
|
}
|
||||||
if page > totalPages {
|
if page > totalPages {
|
||||||
page = totalPages
|
page = totalPages
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3) Fetch (existing service returns all inbox items)
|
// --- Data ---
|
||||||
msgsAll, err := h.Svc.ListInbox(userID)
|
msgsAll, err := h.Svc.ListInbox(userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logging.Info("❌ list inbox error: %v", err)
|
logging.Info("❌ list inbox error: %v", err)
|
||||||
@@ -59,7 +63,7 @@ func (h *AccountMessageHandlers) List(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4) Slice in-memory for now (until you add LIMIT/OFFSET in service)
|
// Temporary in-memory slice (until LIMIT/OFFSET is added)
|
||||||
start := (page - 1) * pageSize
|
start := (page - 1) * pageSize
|
||||||
if start > len(msgsAll) {
|
if start > len(msgsAll) {
|
||||||
start = len(msgsAll)
|
start = len(msgsAll)
|
||||||
@@ -70,7 +74,7 @@ func (h *AccountMessageHandlers) List(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
msgs := msgsAll[start:end]
|
msgs := msgsAll[start:end]
|
||||||
|
|
||||||
// 5) Build context with paging + CSRF + session-driven user meta
|
// --- Template context ---
|
||||||
data := templateHandlers.BuildTemplateData(app, c.Writer, c.Request)
|
data := templateHandlers.BuildTemplateData(app, c.Writer, c.Request)
|
||||||
ctx := templateHelpers.TemplateContext(c.Writer, c.Request, data)
|
ctx := templateHelpers.TemplateContext(c.Writer, c.Request, data)
|
||||||
|
|
||||||
@@ -85,7 +89,7 @@ func (h *AccountMessageHandlers) List(c *gin.Context) {
|
|||||||
ctx["TotalCount"] = totalCount
|
ctx["TotalCount"] = totalCount
|
||||||
ctx["PageRange"] = templateHelpers.MakePageRange(1, totalPages)
|
ctx["PageRange"] = templateHelpers.MakePageRange(1, totalPages)
|
||||||
|
|
||||||
// 6) Render (Buffer to avoid “headers already written” on error
|
// --- Render ---
|
||||||
tmpl := templateHelpers.LoadTemplateFiles("layout.html", "web/templates/account/messages/index.html")
|
tmpl := templateHelpers.LoadTemplateFiles("layout.html", "web/templates/account/messages/index.html")
|
||||||
|
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
@@ -97,6 +101,7 @@ func (h *AccountMessageHandlers) List(c *gin.Context) {
|
|||||||
c.Data(http.StatusOK, "text/html; charset=utf-8", buf.Bytes())
|
c.Data(http.StatusOK, "text/html; charset=utf-8", buf.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GET /account/messages/read?id=123
|
||||||
// Renders: web/templates/account/messages/read.html
|
// Renders: web/templates/account/messages/read.html
|
||||||
func (h *AccountMessageHandlers) ReadGet(c *gin.Context) {
|
func (h *AccountMessageHandlers) ReadGet(c *gin.Context) {
|
||||||
app := c.MustGet("app").(*bootstrap.App)
|
app := c.MustGet("app").(*bootstrap.App)
|
||||||
|
|||||||
@@ -9,9 +9,8 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
templateHelpers "synlotto-website/internal/helpers/template"
|
|
||||||
|
|
||||||
"synlotto-website/internal/helpers"
|
"synlotto-website/internal/helpers"
|
||||||
|
templateHelpers "synlotto-website/internal/helpers/template"
|
||||||
"synlotto-website/internal/http/middleware"
|
"synlotto-website/internal/http/middleware"
|
||||||
"synlotto-website/internal/models"
|
"synlotto-website/internal/models"
|
||||||
)
|
)
|
||||||
@@ -20,7 +19,6 @@ func ResultsThunderball(db *sql.DB) http.HandlerFunc {
|
|||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
ip, _, _ := net.SplitHostPort(r.RemoteAddr)
|
ip, _, _ := net.SplitHostPort(r.RemoteAddr)
|
||||||
limiter := middleware.GetVisitorLimiter(ip)
|
limiter := middleware.GetVisitorLimiter(ip)
|
||||||
|
|
||||||
if !limiter.Allow() {
|
if !limiter.Allow() {
|
||||||
http.Error(w, "Rate limit exceeded", http.StatusTooManyRequests)
|
http.Error(w, "Rate limit exceeded", http.StatusTooManyRequests)
|
||||||
return
|
return
|
||||||
@@ -46,7 +44,7 @@ func ResultsThunderball(db *sql.DB) http.HandlerFunc {
|
|||||||
doSearch := isValidDate(query) || isValidNumber(query)
|
doSearch := isValidDate(query) || isValidNumber(query)
|
||||||
|
|
||||||
whereClause := "WHERE 1=1"
|
whereClause := "WHERE 1=1"
|
||||||
args := []interface{}{}
|
args := []any{}
|
||||||
|
|
||||||
if doSearch {
|
if doSearch {
|
||||||
whereClause += " AND (draw_date = ? OR id = ?)"
|
whereClause += " AND (draw_date = ? OR id = ?)"
|
||||||
@@ -65,7 +63,21 @@ func ResultsThunderball(db *sql.DB) http.HandlerFunc {
|
|||||||
args = append(args, ballSetFilter)
|
args = append(args, ballSetFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
totalPages, totalResults := templateHelpers.GetTotalPages(db, "results_thunderball", whereClause, args, pageSize)
|
// ✅ FIX: Proper GetTotalPages call with context + correct table name
|
||||||
|
totalPages, totalResults, err := templateHelpers.GetTotalPages(
|
||||||
|
r.Context(),
|
||||||
|
db,
|
||||||
|
"results_thunderball",
|
||||||
|
whereClause,
|
||||||
|
args,
|
||||||
|
pageSize,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("❌ Pagination count error:", err)
|
||||||
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if page < 1 || page > totalPages {
|
if page < 1 || page > totalPages {
|
||||||
http.NotFound(w, r)
|
http.NotFound(w, r)
|
||||||
return
|
return
|
||||||
@@ -79,7 +91,7 @@ func ResultsThunderball(db *sql.DB) http.HandlerFunc {
|
|||||||
LIMIT ? OFFSET ?`
|
LIMIT ? OFFSET ?`
|
||||||
argsWithLimit := append(args, pageSize, offset)
|
argsWithLimit := append(args, pageSize, offset)
|
||||||
|
|
||||||
rows, err := db.Query(querySQL, argsWithLimit...)
|
rows, err := db.QueryContext(r.Context(), querySQL, argsWithLimit...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "Database error", http.StatusInternalServerError)
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
||||||
log.Println("❌ DB error:", err)
|
log.Println("❌ DB error:", err)
|
||||||
@@ -113,7 +125,7 @@ func ResultsThunderball(db *sql.DB) http.HandlerFunc {
|
|||||||
noResultsMsg = "No results found for \"" + query + "\""
|
noResultsMsg = "No results found for \"" + query + "\""
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpl := templateHelpers.LoadTemplateFiles("thunderball.html", "web/templates/results/thunderball.html")
|
tmpl := templateHelpers.LoadTemplateFiles("layout.html", "web/templates/results/thunderball.html")
|
||||||
|
|
||||||
err = tmpl.ExecuteTemplate(w, "layout", map[string]interface{}{
|
err = tmpl.ExecuteTemplate(w, "layout", map[string]interface{}{
|
||||||
"Results": results,
|
"Results": results,
|
||||||
|
|||||||
@@ -1,27 +1,72 @@
|
|||||||
|
// internal/helpers/pagination/pagination.go (move out of template/*)
|
||||||
package templateHelper
|
package templateHelper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ToDo: Sql shouldnt be here.
|
// Whitelist
|
||||||
func GetTotalPages(db *sql.DB, tableName, whereClause string, args []interface{}, pageSize int) (totalPages, totalCount int) {
|
var allowedTables = map[string]struct{}{
|
||||||
query := "SELECT COUNT(*) FROM " + tableName + " " + whereClause
|
"user_messages": {},
|
||||||
row := db.QueryRow(query, args...)
|
"user_notifications": {},
|
||||||
if err := row.Scan(&totalCount); err != nil {
|
"results_thunderball": {},
|
||||||
return 1, 0
|
}
|
||||||
|
|
||||||
|
// GetTotalPages counts rows and returns (totalPages, totalCount).
|
||||||
|
func GetTotalPages(ctx context.Context, db *sql.DB, table, whereClause string, args []any, pageSize int) (int, int64, error) {
|
||||||
|
if pageSize <= 0 {
|
||||||
|
pageSize = 20
|
||||||
}
|
}
|
||||||
totalPages = (totalCount + pageSize - 1) / pageSize
|
if _, ok := allowedTables[table]; !ok {
|
||||||
|
return 1, 0, fmt.Errorf("table not allowed: %s", table)
|
||||||
|
}
|
||||||
|
|
||||||
|
q := fmt.Sprintf("SELECT COUNT(*) FROM %s", table)
|
||||||
|
if whereClause != "" {
|
||||||
|
q += " WHERE " + whereClause
|
||||||
|
}
|
||||||
|
|
||||||
|
var totalCount int64
|
||||||
|
cctx, cancel := context.WithTimeout(ctx, 2*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := db.QueryRowContext(cctx, q, args...).Scan(&totalCount); err != nil {
|
||||||
|
return 1, 0, fmt.Errorf("count %s: %w", table, err)
|
||||||
|
}
|
||||||
|
totalPages := int((totalCount + int64(pageSize) - 1) / int64(pageSize))
|
||||||
if totalPages < 1 {
|
if totalPages < 1 {
|
||||||
totalPages = 1
|
totalPages = 1
|
||||||
}
|
}
|
||||||
return totalPages, totalCount
|
return totalPages, totalCount, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func MakePageRange(current, total int) []int {
|
func MakePageRange(current, total int) []int {
|
||||||
var pages []int
|
if total < 1 {
|
||||||
|
return []int{1}
|
||||||
|
}
|
||||||
|
|
||||||
|
pages := make([]int, 0, total)
|
||||||
for i := 1; i <= total; i++ {
|
for i := 1; i <= total; i++ {
|
||||||
pages = append(pages, i)
|
pages = append(pages, i)
|
||||||
}
|
}
|
||||||
return pages
|
return pages
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ClampPage(p, total int) int {
|
||||||
|
if p < 1 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if p > total {
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
func OffsetLimit(page, pageSize int) (int, int) {
|
||||||
|
if page < 1 {
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
|
return (page - 1) * pageSize, pageSize
|
||||||
|
}
|
||||||
|
|||||||
@@ -66,11 +66,12 @@ func RegisterAccountRoutes(app *bootstrap.App) {
|
|||||||
messages.Use(middleware.AuthMiddleware(), middleware.RequireAuth())
|
messages.Use(middleware.AuthMiddleware(), middleware.RequireAuth())
|
||||||
{
|
{
|
||||||
messages.GET("/", msgH.List)
|
messages.GET("/", msgH.List)
|
||||||
|
messages.GET("/read", msgH.ReadGet)
|
||||||
messages.GET("/send", msgH.SendGet)
|
messages.GET("/send", msgH.SendGet)
|
||||||
messages.POST("/send", msgH.SendPost)
|
messages.POST("/send", msgH.SendPost)
|
||||||
messages.GET("/archived", msgH.ArchivedList) // renders archived.html
|
messages.GET("/archive", msgH.ArchivedList) // view archived messages
|
||||||
messages.GET("/read", msgH.ReadGet)
|
messages.POST("/archive", msgH.ArchivePost) // archive a message
|
||||||
|
messages.POST("/restore", msgH.RestorePost)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notifications (auth-required)
|
// Notifications (auth-required)
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ func New(db *sql.DB, opts ...func(*Service)) *Service {
|
|||||||
// Ensure *Service satisfies the domain interface.
|
// Ensure *Service satisfies the domain interface.
|
||||||
var _ domain.MessageService = (*Service)(nil)
|
var _ domain.MessageService = (*Service)(nil)
|
||||||
|
|
||||||
// ToDo: Needs a userId on table or rename the recipiant id.. but then again dont want to expose userids to users for sending.
|
|
||||||
func (s *Service) ListInbox(userID int64) ([]domain.Message, error) {
|
func (s *Service) ListInbox(userID int64) ([]domain.Message, error) {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), s.Timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), s.Timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@@ -164,7 +163,6 @@ func (s *Service) Create(senderID int64, in domain.CreateMessageInput) (int64, e
|
|||||||
return id, nil
|
return id, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// compactSQL removes newlines/extra spaces for cleaner logs
|
|
||||||
func compactSQL(s string) string {
|
func compactSQL(s string) string {
|
||||||
out := make([]rune, 0, len(s))
|
out := make([]rune, 0, len(s))
|
||||||
space := false
|
space := false
|
||||||
@@ -200,7 +198,28 @@ func (s *Service) bind(q string) string {
|
|||||||
return string(out)
|
return string(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToDo: helper dont think it should be here.
|
func (s *Service) Archive(userID, id int64) error {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), s.Timeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
q := `
|
||||||
|
UPDATE user_messages
|
||||||
|
SET is_archived = 1, archived_at = CURRENT_TIMESTAMP
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
func intToStr(n int) string {
|
func intToStr(n int) string {
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
return "0"
|
return "0"
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<small class="text-muted">
|
<small class="text-muted">
|
||||||
Archived:
|
Archived:
|
||||||
{{ if .ArchivedAt.Valid }}
|
{{ if .ArchivedAt.Valid }}
|
||||||
{{ .ArchivedAt.Time.Format "02 Jan 2006 15:04" }}
|
{{ .Format "02 Jan 2006 15:04" }}
|
||||||
{{ else }}
|
{{ else }}
|
||||||
—
|
—
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@
|
|||||||
|
|
||||||
<div class="mt-3">
|
<div class="mt-3">
|
||||||
<a href="/account/messages/send" class="btn btn-primary">Compose Message</a>
|
<a href="/account/messages/send" class="btn btn-primary">Compose Message</a>
|
||||||
<a href="/account/messages/archived" class="btn btn-outline-secondary ms-2">View Archived</a>
|
<a href="/account/messages/archive" class="btn btn-outline-secondary ms-2">View Archived</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|||||||
Reference in New Issue
Block a user