1package notifications
2
3import (
4 "fmt"
5 "log"
6 "net/http"
7 "strconv"
8
9 "github.com/go-chi/chi/v5"
10 "tangled.org/core/appview/db"
11 "tangled.org/core/appview/middleware"
12 "tangled.org/core/appview/oauth"
13 "tangled.org/core/appview/pages"
14 "tangled.org/core/appview/pagination"
15)
16
17type Notifications struct {
18 db *db.DB
19 oauth *oauth.OAuth
20 pages *pages.Pages
21}
22
23func New(database *db.DB, oauthHandler *oauth.OAuth, pagesHandler *pages.Pages) *Notifications {
24 return &Notifications{
25 db: database,
26 oauth: oauthHandler,
27 pages: pagesHandler,
28 }
29}
30
31func (n *Notifications) Router(mw *middleware.Middleware) http.Handler {
32 r := chi.NewRouter()
33
34 r.Use(middleware.AuthMiddleware(n.oauth))
35
36 r.With(middleware.Paginate).Get("/", n.notificationsPage)
37
38 r.Get("/count", n.getUnreadCount)
39 r.Post("/{id}/read", n.markRead)
40 r.Post("/read-all", n.markAllRead)
41 r.Delete("/{id}", n.deleteNotification)
42
43 return r
44}
45
46func (n *Notifications) notificationsPage(w http.ResponseWriter, r *http.Request) {
47 userDid := n.oauth.GetDid(r)
48
49 page, ok := r.Context().Value("page").(pagination.Page)
50 if !ok {
51 log.Println("failed to get page")
52 page = pagination.FirstPage()
53 }
54
55 total, err := db.CountNotifications(
56 n.db,
57 db.FilterEq("recipient_did", userDid),
58 )
59 if err != nil {
60 log.Println("failed to get total notifications:", err)
61 n.pages.Error500(w)
62 return
63 }
64
65 notifications, err := db.GetNotificationsWithEntities(
66 n.db,
67 page,
68 db.FilterEq("recipient_did", userDid),
69 )
70 if err != nil {
71 log.Println("failed to get notifications:", err)
72 n.pages.Error500(w)
73 return
74 }
75
76 err = n.db.MarkAllNotificationsRead(r.Context(), userDid)
77 if err != nil {
78 log.Println("failed to mark notifications as read:", err)
79 }
80
81 unreadCount := 0
82
83 user := n.oauth.GetUser(r)
84 if user == nil {
85 http.Error(w, "Failed to get user", http.StatusInternalServerError)
86 return
87 }
88
89 fmt.Println(n.pages.Notifications(w, pages.NotificationsParams{
90 LoggedInUser: user,
91 Notifications: notifications,
92 UnreadCount: unreadCount,
93 Page: page,
94 Total: total,
95 }))
96}
97
98func (n *Notifications) getUnreadCount(w http.ResponseWriter, r *http.Request) {
99 user := n.oauth.GetUser(r)
100 count, err := db.CountNotifications(
101 n.db,
102 db.FilterEq("recipient_did", user.Did),
103 db.FilterEq("read", 0),
104 )
105 if err != nil {
106 http.Error(w, "Failed to get unread count", http.StatusInternalServerError)
107 return
108 }
109
110 params := pages.NotificationCountParams{
111 Count: count,
112 }
113 err = n.pages.NotificationCount(w, params)
114 if err != nil {
115 http.Error(w, "Failed to render count", http.StatusInternalServerError)
116 return
117 }
118}
119
120func (n *Notifications) markRead(w http.ResponseWriter, r *http.Request) {
121 userDid := n.oauth.GetDid(r)
122
123 idStr := chi.URLParam(r, "id")
124 notificationID, err := strconv.ParseInt(idStr, 10, 64)
125 if err != nil {
126 http.Error(w, "Invalid notification ID", http.StatusBadRequest)
127 return
128 }
129
130 err = n.db.MarkNotificationRead(r.Context(), notificationID, userDid)
131 if err != nil {
132 http.Error(w, "Failed to mark notification as read", http.StatusInternalServerError)
133 return
134 }
135
136 w.WriteHeader(http.StatusNoContent)
137}
138
139func (n *Notifications) markAllRead(w http.ResponseWriter, r *http.Request) {
140 userDid := n.oauth.GetDid(r)
141
142 err := n.db.MarkAllNotificationsRead(r.Context(), userDid)
143 if err != nil {
144 http.Error(w, "Failed to mark all notifications as read", http.StatusInternalServerError)
145 return
146 }
147
148 http.Redirect(w, r, "/notifications", http.StatusSeeOther)
149}
150
151func (n *Notifications) deleteNotification(w http.ResponseWriter, r *http.Request) {
152 userDid := n.oauth.GetDid(r)
153
154 idStr := chi.URLParam(r, "id")
155 notificationID, err := strconv.ParseInt(idStr, 10, 64)
156 if err != nil {
157 http.Error(w, "Invalid notification ID", http.StatusBadRequest)
158 return
159 }
160
161 err = n.db.DeleteNotification(r.Context(), notificationID, userDid)
162 if err != nil {
163 http.Error(w, "Failed to delete notification", http.StatusInternalServerError)
164 return
165 }
166
167 w.WriteHeader(http.StatusOK)
168}