Monorepo for Tangled
at c2049613392b034666f88e5c2b55d991f0c51040 167 lines 4.1 kB view raw
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.GetMultiAccountUser(r) 52 53 page := pagination.FromContext(r.Context()) 54 55 total, err := db.CountNotifications( 56 n.db, 57 orm.FilterEq("recipient_did", user.Active.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.Active.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.Active.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.GetMultiAccountUser(r) 94 if user == nil { 95 http.Error(w, "Forbidden", http.StatusUnauthorized) 96 return 97 } 98 99 count, err := db.CountNotifications( 100 n.db, 101 orm.FilterEq("recipient_did", user.Active.Did), 102 orm.FilterEq("read", 0), 103 ) 104 if err != nil { 105 http.Error(w, "Failed to get unread count", http.StatusInternalServerError) 106 return 107 } 108 109 params := pages.NotificationCountParams{ 110 Count: count, 111 } 112 err = n.pages.NotificationCount(w, params) 113 if err != nil { 114 http.Error(w, "Failed to render count", http.StatusInternalServerError) 115 return 116 } 117} 118 119func (n *Notifications) markRead(w http.ResponseWriter, r *http.Request) { 120 userDid := n.oauth.GetDid(r) 121 122 idStr := chi.URLParam(r, "id") 123 notificationID, err := strconv.ParseInt(idStr, 10, 64) 124 if err != nil { 125 http.Error(w, "Invalid notification ID", http.StatusBadRequest) 126 return 127 } 128 129 err = db.MarkNotificationRead(n.db, notificationID, userDid) 130 if err != nil { 131 http.Error(w, "Failed to mark notification as read", http.StatusInternalServerError) 132 return 133 } 134 135 w.WriteHeader(http.StatusNoContent) 136} 137 138func (n *Notifications) markAllRead(w http.ResponseWriter, r *http.Request) { 139 userDid := n.oauth.GetDid(r) 140 141 err := db.MarkAllNotificationsRead(n.db, userDid) 142 if err != nil { 143 http.Error(w, "Failed to mark all notifications as read", http.StatusInternalServerError) 144 return 145 } 146 147 http.Redirect(w, r, "/notifications", http.StatusSeeOther) 148} 149 150func (n *Notifications) deleteNotification(w http.ResponseWriter, r *http.Request) { 151 userDid := n.oauth.GetDid(r) 152 153 idStr := chi.URLParam(r, "id") 154 notificationID, err := strconv.ParseInt(idStr, 10, 64) 155 if err != nil { 156 http.Error(w, "Invalid notification ID", http.StatusBadRequest) 157 return 158 } 159 160 err = db.DeleteNotification(n.db, notificationID, userDid) 161 if err != nil { 162 http.Error(w, "Failed to delete notification", http.StatusInternalServerError) 163 return 164 } 165 166 w.WriteHeader(http.StatusOK) 167}