Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b88f430

Browse files
Claude AnthropicClaude
Claude Anthropic
and
Claude
committed
feat: add Mark All as Read feature for inbox notifications
Implements feature request from issue #15264 to add a "Mark all as read" feature for notifications. This PR adds: 1. A new SQL query to mark all unread notifications as read for a user 2. A new REST API endpoint at /api/v2/notifications/inbox/mark-all-read 3. A new SDK method MarkAllInboxNotificationsRead 4. A comprehensive test to verify the functionality 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 3ae55bb commit b88f430

File tree

5 files changed

+139
-0
lines changed

5 files changed

+139
-0
lines changed

coderd/coderd.go

+1
Original file line numberDiff line numberDiff line change
@@ -1391,6 +1391,7 @@ func New(options *Options) *API {
13911391
r.Get("/", api.listInboxNotifications)
13921392
r.Get("/watch", api.watchInboxNotifications)
13931393
r.Put("/{id}/read-status", api.updateInboxNotificationReadStatus)
1394+
r.Put("/mark-all-read", api.updateAllInboxNotificationsReadStatus)
13941395
})
13951396
r.Get("/settings", api.notificationsSettings)
13961397
r.Put("/settings", api.putNotificationsSettings)

coderd/database/queries/notificationsinbox.sql

+10
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,13 @@ SET
5757
read_at = $1
5858
WHERE
5959
id = $2;
60+
61+
-- name: UpdateAllInboxNotificationsReadStatusByUserID :exec
62+
-- Marks all unread notifications as read for a user
63+
UPDATE
64+
inbox_notifications
65+
SET
66+
read_at = $1
67+
WHERE
68+
user_id = $2 AND
69+
read_at IS NULL;

coderd/inboxnotifications.go

+44
Original file line numberDiff line numberDiff line change
@@ -345,3 +345,47 @@ func (api *API) updateInboxNotificationReadStatus(rw http.ResponseWriter, r *htt
345345
UnreadCount: int(unreadCount),
346346
})
347347
}
348+
349+
// updateAllInboxNotificationsReadStatus marks all notifications as read for the user.
350+
// @Summary Mark all notifications as read
351+
// @ID mark-all-notifications-as-read
352+
// @Security CoderSessionToken
353+
// @Produce json
354+
// @Tags Notifications
355+
// @Success 200 {object} codersdk.Response
356+
// @Router /notifications/inbox/mark-all-read [put]
357+
func (api *API) updateAllInboxNotificationsReadStatus(rw http.ResponseWriter, r *http.Request) {
358+
var (
359+
ctx = r.Context()
360+
apikey = httpmw.APIKey(r)
361+
)
362+
363+
err := api.Database.UpdateAllInboxNotificationsReadStatusByUserID(ctx, database.UpdateAllInboxNotificationsReadStatusByUserIDParams{
364+
ReadAt: sql.NullTime{
365+
Time: dbtime.Now(),
366+
Valid: true,
367+
},
368+
UserID: apikey.UserID,
369+
})
370+
if err != nil {
371+
api.Logger.Error(ctx, "failed to update all inbox notifications read status", slog.Error(err))
372+
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
373+
Message: "Failed to update all inbox notifications read status.",
374+
})
375+
return
376+
}
377+
378+
// Get the updated unread count
379+
unreadCount, err := api.Database.CountUnreadInboxNotificationsByUserID(ctx, apikey.UserID)
380+
if err != nil {
381+
api.Logger.Error(ctx, "failed to call count unread inbox notifications", slog.Error(err))
382+
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
383+
Message: "Failed to call count unread inbox notifications.",
384+
})
385+
return
386+
}
387+
388+
httpapi.Write(ctx, rw, http.StatusOK, codersdk.MarkAllInboxNotificationsReadResponse{
389+
UnreadCount: int(unreadCount),
390+
})
391+
}

coderd/inboxnotifications_test.go

+61
Original file line numberDiff line numberDiff line change
@@ -723,3 +723,64 @@ func TestInboxNotifications_ReadStatus(t *testing.T) {
723723
require.Empty(t, updatedNotif.Notification)
724724
})
725725
}
726+
727+
func TestInboxNotifications_MarkAllRead(t *testing.T) {
728+
t.Parallel()
729+
730+
// I skip these tests specifically on windows as for now they are flaky - only on Windows.
731+
// For now the idea is that the runner takes too long to insert the entries, could be worth
732+
// investigating a manual Tx.
733+
if runtime.GOOS == "windows" {
734+
t.Skip("our runners are randomly taking too long to insert entries")
735+
}
736+
737+
t.Run("ok", func(t *testing.T) {
738+
t.Parallel()
739+
client, _, api := coderdtest.NewWithAPI(t, &coderdtest.Options{})
740+
firstUser := coderdtest.CreateFirstUser(t, client)
741+
client, member := coderdtest.CreateAnotherUser(t, client, firstUser.OrganizationID)
742+
743+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
744+
defer cancel()
745+
746+
notifs, err := client.ListInboxNotifications(ctx, codersdk.ListInboxNotificationsRequest{})
747+
require.NoError(t, err)
748+
require.NotNil(t, notifs)
749+
require.Equal(t, 0, notifs.UnreadCount)
750+
require.Empty(t, notifs.Notifications)
751+
752+
for i := range 20 {
753+
dbgen.NotificationInbox(t, api.Database, database.InsertInboxNotificationParams{
754+
ID: uuid.New(),
755+
UserID: member.ID,
756+
TemplateID: notifications.TemplateWorkspaceOutOfMemory,
757+
Title: fmt.Sprintf("Notification %d", i),
758+
Actions: json.RawMessage("[]"),
759+
Content: fmt.Sprintf("Content of the notif %d", i),
760+
CreatedAt: dbtime.Now(),
761+
})
762+
}
763+
764+
notifs, err = client.ListInboxNotifications(ctx, codersdk.ListInboxNotificationsRequest{})
765+
require.NoError(t, err)
766+
require.NotNil(t, notifs)
767+
require.Equal(t, 20, notifs.UnreadCount)
768+
require.Len(t, notifs.Notifications, 20)
769+
770+
// Mark all as read
771+
response, err := client.MarkAllInboxNotificationsRead(ctx)
772+
require.NoError(t, err)
773+
require.NotNil(t, response)
774+
require.Equal(t, 0, response.UnreadCount)
775+
776+
// Check that all notifications are marked as read
777+
notifs, err = client.ListInboxNotifications(ctx, codersdk.ListInboxNotificationsRequest{})
778+
require.NoError(t, err)
779+
require.Equal(t, 0, notifs.UnreadCount)
780+
781+
// All notifications should be marked as read
782+
for _, notif := range notifs.Notifications {
783+
require.NotNil(t, notif.ReadAt)
784+
}
785+
})
786+
}

codersdk/inboxnotification.go

+23
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,26 @@ func (c *Client) UpdateInboxNotificationReadStatus(ctx context.Context, notifID
109109
var resp UpdateInboxNotificationReadStatusResponse
110110
return resp, json.NewDecoder(res.Body).Decode(&resp)
111111
}
112+
113+
type MarkAllInboxNotificationsReadResponse struct {
114+
UnreadCount int `json:"unread_count"`
115+
}
116+
117+
func (c *Client) MarkAllInboxNotificationsRead(ctx context.Context) (MarkAllInboxNotificationsReadResponse, error) {
118+
res, err := c.Request(
119+
ctx, http.MethodPut,
120+
"/api/v2/notifications/inbox/mark-all-read",
121+
nil,
122+
)
123+
if err != nil {
124+
return MarkAllInboxNotificationsReadResponse{}, err
125+
}
126+
defer res.Body.Close()
127+
128+
if res.StatusCode != http.StatusOK {
129+
return MarkAllInboxNotificationsReadResponse{}, ReadBodyAsError(res)
130+
}
131+
132+
var resp MarkAllInboxNotificationsReadResponse
133+
return resp, json.NewDecoder(res.Body).Decode(&resp)
134+
}

0 commit comments

Comments
 (0)