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