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