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, ok := r.Context().Value("page").(pagination.Page) 53 if !ok { 54 l.Error("failed to get page") 55 page = pagination.FirstPage() 56 } 57 58 total, err := db.CountNotifications( 59 n.db, 60 db.FilterEq("recipient_did", user.Did), 61 ) 62 if err != nil { 63 l.Error("failed to get total notifications", "err", err) 64 n.pages.Error500(w) 65 return 66 } 67 68 notifications, err := db.GetNotificationsWithEntities( 69 n.db, 70 page, 71 db.FilterEq("recipient_did", user.Did), 72 ) 73 if err != nil { 74 l.Error("failed to get notifications", "err", err) 75 n.pages.Error500(w) 76 return 77 } 78 79 err = n.db.MarkAllNotificationsRead(r.Context(), user.Did) 80 if err != nil { 81 l.Error("failed to mark notifications as read", "err", err) 82 } 83 84 unreadCount := 0 85 86 n.pages.Notifications(w, pages.NotificationsParams{ 87 LoggedInUser: user, 88 Notifications: notifications, 89 UnreadCount: unreadCount, 90 Page: page, 91 Total: total, 92 }) 93} 94 95func (n *Notifications) getUnreadCount(w http.ResponseWriter, r *http.Request) { 96 user := n.oauth.GetUser(r) 97 if user == nil { 98 return 99 } 100 101 count, err := db.CountNotifications( 102 n.db, 103 db.FilterEq("recipient_did", user.Did), 104 db.FilterEq("read", 0), 105 ) 106 if err != nil { 107 http.Error(w, "Failed to get unread count", http.StatusInternalServerError) 108 return 109 } 110 111 params := pages.NotificationCountParams{ 112 Count: count, 113 } 114 err = n.pages.NotificationCount(w, params) 115 if err != nil { 116 http.Error(w, "Failed to render count", http.StatusInternalServerError) 117 return 118 } 119} 120 121func (n *Notifications) markRead(w http.ResponseWriter, r *http.Request) { 122 userDid := n.oauth.GetDid(r) 123 124 idStr := chi.URLParam(r, "id") 125 notificationID, err := strconv.ParseInt(idStr, 10, 64) 126 if err != nil { 127 http.Error(w, "Invalid notification ID", http.StatusBadRequest) 128 return 129 } 130 131 err = n.db.MarkNotificationRead(r.Context(), notificationID, userDid) 132 if err != nil { 133 http.Error(w, "Failed to mark notification as read", http.StatusInternalServerError) 134 return 135 } 136 137 w.WriteHeader(http.StatusNoContent) 138} 139 140func (n *Notifications) markAllRead(w http.ResponseWriter, r *http.Request) { 141 userDid := n.oauth.GetDid(r) 142 143 err := n.db.MarkAllNotificationsRead(r.Context(), userDid) 144 if err != nil { 145 http.Error(w, "Failed to mark all notifications as read", http.StatusInternalServerError) 146 return 147 } 148 149 http.Redirect(w, r, "/notifications", http.StatusSeeOther) 150} 151 152func (n *Notifications) deleteNotification(w http.ResponseWriter, r *http.Request) { 153 userDid := n.oauth.GetDid(r) 154 155 idStr := chi.URLParam(r, "id") 156 notificationID, err := strconv.ParseInt(idStr, 10, 64) 157 if err != nil { 158 http.Error(w, "Invalid notification ID", http.StatusBadRequest) 159 return 160 } 161 162 err = n.db.DeleteNotification(r.Context(), notificationID, userDid) 163 if err != nil { 164 http.Error(w, "Failed to delete notification", http.StatusInternalServerError) 165 return 166 } 167 168 w.WriteHeader(http.StatusOK) 169}