Fleshing out some routes from notifications and messages
This commit is contained in:
128
internal/handlers/account/messages/list.go
Normal file
128
internal/handlers/account/messages/list.go
Normal file
@@ -0,0 +1,128 @@
|
||||
package accountMessageHandler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// GET /account/messages
|
||||
// Renders: web/templates/account/messages/index.html
|
||||
func (h *AccountMessageHandlers) List(c *gin.Context) {
|
||||
userID := mustUserID(c) // replace with your auth/user extraction
|
||||
msgs, err := h.Svc.ListInbox(userID)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "failed to load messages"})
|
||||
return
|
||||
}
|
||||
c.HTML(http.StatusOK, "account/messages/index.html", gin.H{
|
||||
"title": "Messages",
|
||||
"messages": msgs,
|
||||
})
|
||||
}
|
||||
|
||||
// GET /account/messages/add
|
||||
// Renders: web/templates/account/messages/send.html
|
||||
func (h *AccountMessageHandlers) AddGet(c *gin.Context) {
|
||||
c.HTML(http.StatusOK, "account/messages/send.html", gin.H{
|
||||
"title": "Send Message",
|
||||
})
|
||||
}
|
||||
|
||||
// POST /account/messages/add
|
||||
func (h *AccountMessageHandlers) AddPost(c *gin.Context) {
|
||||
userID := mustUserID(c)
|
||||
var in CreateMessageInput
|
||||
if err := c.ShouldBind(&in); err != nil {
|
||||
// Re-render form with validation errors
|
||||
c.HTML(http.StatusBadRequest, "account/messages/send.html", gin.H{
|
||||
"title": "Send Message",
|
||||
"error": "Please correct the errors below.",
|
||||
"form": in,
|
||||
})
|
||||
return
|
||||
}
|
||||
if _, err := h.Svc.Create(userID, in); err != nil {
|
||||
c.HTML(http.StatusInternalServerError, "account/messages/send.html", gin.H{
|
||||
"title": "Send Message",
|
||||
"error": "Could not send message.",
|
||||
"form": in,
|
||||
})
|
||||
return
|
||||
}
|
||||
// Redirect back to inbox
|
||||
c.Redirect(http.StatusSeeOther, "/account/messages")
|
||||
}
|
||||
|
||||
// --- Optional extras since you have read.html and archived.html ---
|
||||
|
||||
// GET /account/messages/:id
|
||||
// Renders: web/templates/account/messages/read.html
|
||||
func (h *AccountMessageHandlers) ReadGet(c *gin.Context) {
|
||||
userID := mustUserID(c)
|
||||
id, err := parseIDParam(c, "id")
|
||||
if err != nil {
|
||||
c.AbortWithStatus(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
msg, err := h.Svc.GetByID(userID, id)
|
||||
if err != nil || msg == nil {
|
||||
c.AbortWithStatus(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
c.HTML(http.StatusOK, "account/messages/read.html", gin.H{
|
||||
"title": msg.Subject,
|
||||
"message": msg,
|
||||
})
|
||||
}
|
||||
|
||||
// GET /account/messages/archived
|
||||
// Renders: web/templates/account/messages/archived.html
|
||||
func (h *AccountMessageHandlers) ArchivedList(c *gin.Context) {
|
||||
userID := mustUserID(c)
|
||||
msgs, err := h.Svc.ListArchived(userID)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "failed to load archived messages"})
|
||||
return
|
||||
}
|
||||
c.HTML(http.StatusOK, "account/messages/archived.html", gin.H{
|
||||
"title": "Archived Messages",
|
||||
"messages": msgs,
|
||||
})
|
||||
}
|
||||
|
||||
// --- helpers ---
|
||||
|
||||
func mustUserID(c *gin.Context) int64 {
|
||||
// Pull from your auth middleware/session. Panic-unsafe alternative:
|
||||
if v, ok := c.Get("userID"); ok {
|
||||
if id, ok2 := v.(int64); ok2 {
|
||||
return id
|
||||
}
|
||||
}
|
||||
// Fallback for stubs:
|
||||
return 1
|
||||
}
|
||||
|
||||
func parseIDParam(c *gin.Context, name string) (int64, error) {
|
||||
// typical atoi wrapper
|
||||
// (implement: strconv.ParseInt(c.Param(name), 10, 64))
|
||||
return atoi64(c.Param(name))
|
||||
}
|
||||
|
||||
func atoi64(s string) (int64, error) {
|
||||
// small helper to keep imports focused
|
||||
// replace with strconv.ParseInt in real code
|
||||
var n int64
|
||||
for _, ch := range []byte(s) {
|
||||
if ch < '0' || ch > '9' {
|
||||
return 0, &strconvNumErr{}
|
||||
}
|
||||
n = n*10 + int64(ch-'0')
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
type strconvNumErr struct{}
|
||||
|
||||
func (e *strconvNumErr) Error() string { return "invalid number" }
|
||||
32
internal/handlers/account/messages/types.go
Normal file
32
internal/handlers/account/messages/types.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package accountMessageHandlers
|
||||
|
||||
import "time"
|
||||
|
||||
type AccountMessageHandlers struct {
|
||||
Svc MessageService
|
||||
}
|
||||
|
||||
type CreateMessageInput struct {
|
||||
To string `form:"to" binding:"required,email"`
|
||||
Subject string `form:"subject" binding:"required,max=200"`
|
||||
Body string `form:"body" binding:"required"`
|
||||
}
|
||||
|
||||
type Message struct {
|
||||
ID int64
|
||||
From string
|
||||
To string
|
||||
Subject string
|
||||
Body string
|
||||
IsRead bool
|
||||
IsArchived bool
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
// ToDo: Should interfaces be else where?
|
||||
type MessageService interface {
|
||||
ListInbox(userID int64) ([]Message, error)
|
||||
ListArchived(userID int64) ([]Message, error)
|
||||
GetByID(userID, id int64) (*Message, error)
|
||||
Create(userID int64, in CreateMessageInput) (int64, error)
|
||||
}
|
||||
78
internal/handlers/account/notifications/list.go
Normal file
78
internal/handlers/account/notifications/list.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package accountNotificationHandler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func NewAccountNotificationHandlers(svc NotificationService) *AccountNotificationHandlers {
|
||||
return &AccountNotificationHandlers{Svc: svc}
|
||||
}
|
||||
|
||||
// GET /account/notifications
|
||||
// Renders: web/templates/account/notifications/index.html
|
||||
func (h *AccountNotificationHandlers) List(c *gin.Context) {
|
||||
userID := mustUserID(c)
|
||||
items, err := h.Svc.List(userID)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "failed to load notifications"})
|
||||
return
|
||||
}
|
||||
c.HTML(http.StatusOK, "account/notifications/index.html", gin.H{
|
||||
"title": "Notifications",
|
||||
"notifications": items,
|
||||
})
|
||||
}
|
||||
|
||||
// --- Optional since you have read.html ---
|
||||
|
||||
// GET /account/notifications/:id
|
||||
// Renders: web/templates/account/notifications/read.html
|
||||
func (h *AccountNotificationHandlers) ReadGet(c *gin.Context) {
|
||||
userID := mustUserID(c)
|
||||
id, err := parseIDParam(c, "id")
|
||||
if err != nil {
|
||||
c.AbortWithStatus(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
nt, err := h.Svc.GetByID(userID, id)
|
||||
if err != nil || nt == nil {
|
||||
c.AbortWithStatus(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
c.HTML(http.StatusOK, "account/notifications/read.html", gin.H{
|
||||
"title": nt.Title,
|
||||
"notification": nt,
|
||||
})
|
||||
}
|
||||
|
||||
// --- helpers (same as in messages; consider sharing in a pkg) ---
|
||||
|
||||
func mustUserID(c *gin.Context) int64 {
|
||||
if v, ok := c.Get("userID"); ok {
|
||||
if id, ok2 := v.(int64); ok2 {
|
||||
return id
|
||||
}
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func parseIDParam(c *gin.Context, name string) (int64, error) {
|
||||
return atoi64(c.Param(name))
|
||||
}
|
||||
|
||||
func atoi64(s string) (int64, error) {
|
||||
var n int64
|
||||
for _, ch := range []byte(s) {
|
||||
if ch < '0' || ch > '9' {
|
||||
return 0, &strconvNumErr{}
|
||||
}
|
||||
n = n*10 + int64(ch-'0')
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
type strconvNumErr struct{}
|
||||
|
||||
func (e *strconvNumErr) Error() string { return "invalid number" }
|
||||
21
internal/handlers/account/notifications/types.go
Normal file
21
internal/handlers/account/notifications/types.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package accountNotificationHandler
|
||||
|
||||
import "time"
|
||||
|
||||
type Notification struct {
|
||||
ID int64
|
||||
Title string
|
||||
Body string
|
||||
IsRead bool
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
type AccountNotificationHandlers struct {
|
||||
Svc NotificationService
|
||||
}
|
||||
|
||||
// ToDo: Should interfaces be else where?
|
||||
type NotificationService interface {
|
||||
List(userID int64) ([]Notification, error)
|
||||
GetByID(userID, id int64) (*Notification, error)
|
||||
}
|
||||
Reference in New Issue
Block a user