diff --git a/apps/web/features/inbox/store.ts b/apps/web/features/inbox/store.ts index 49f49ef2..0489a30b 100644 --- a/apps/web/features/inbox/store.ts +++ b/apps/web/features/inbox/store.ts @@ -90,9 +90,17 @@ export const useInboxStore = create((set, get) => ({ items: s.items.map((i) => (i.id === id ? { ...i, read: true } : i)), })), archive: (id) => - set((s) => ({ - items: s.items.map((i) => (i.id === id ? { ...i, archived: true } : i)), - })), + set((s) => { + const target = s.items.find((i) => i.id === id); + const issueId = target?.issue_id; + return { + items: s.items.map((i) => + i.id === id || (issueId && i.issue_id === issueId) + ? { ...i, archived: true } + : i, + ), + }; + }), markAllRead: () => set((s) => ({ items: s.items.map((i) => (!i.archived ? { ...i, read: true } : i)), diff --git a/server/internal/handler/inbox.go b/server/internal/handler/inbox.go index a1d5f0ef..3dc0d20a 100644 --- a/server/internal/handler/inbox.go +++ b/server/internal/handler/inbox.go @@ -143,10 +143,21 @@ func (h *Handler) ArchiveInboxItem(w http.ResponseWriter, r *http.Request) { return } + // Archive all sibling inbox items for the same issue (issue-level archive) + if item.IssueID.Valid { + h.Queries.ArchiveInboxByIssue(r.Context(), db.ArchiveInboxByIssueParams{ + WorkspaceID: item.WorkspaceID, + RecipientType: item.RecipientType, + RecipientID: item.RecipientID, + IssueID: item.IssueID, + }) + } + userID := requestUserID(r) workspaceID := uuidToString(item.WorkspaceID) h.publish(protocol.EventInboxArchived, workspaceID, "member", userID, map[string]any{ "item_id": uuidToString(item.ID), + "issue_id": uuidToPtr(item.IssueID), "recipient_id": uuidToString(item.RecipientID), }) diff --git a/server/pkg/db/generated/inbox.sql.go b/server/pkg/db/generated/inbox.sql.go index b0c46a0d..542c8158 100644 --- a/server/pkg/db/generated/inbox.sql.go +++ b/server/pkg/db/generated/inbox.sql.go @@ -66,6 +66,31 @@ func (q *Queries) ArchiveCompletedInbox(ctx context.Context, arg ArchiveComplete return result.RowsAffected(), nil } +const archiveInboxByIssue = `-- name: ArchiveInboxByIssue :execrows +UPDATE inbox_item SET archived = true +WHERE workspace_id = $1 AND recipient_type = $2 AND recipient_id = $3 AND issue_id = $4 AND archived = false +` + +type ArchiveInboxByIssueParams struct { + WorkspaceID pgtype.UUID `json:"workspace_id"` + RecipientType string `json:"recipient_type"` + RecipientID pgtype.UUID `json:"recipient_id"` + IssueID pgtype.UUID `json:"issue_id"` +} + +func (q *Queries) ArchiveInboxByIssue(ctx context.Context, arg ArchiveInboxByIssueParams) (int64, error) { + result, err := q.db.Exec(ctx, archiveInboxByIssue, + arg.WorkspaceID, + arg.RecipientType, + arg.RecipientID, + arg.IssueID, + ) + if err != nil { + return 0, err + } + return result.RowsAffected(), nil +} + const archiveInboxItem = `-- name: ArchiveInboxItem :one UPDATE inbox_item SET archived = true WHERE id = $1 diff --git a/server/pkg/db/queries/inbox.sql b/server/pkg/db/queries/inbox.sql index 301d93e3..98a21b33 100644 --- a/server/pkg/db/queries/inbox.sql +++ b/server/pkg/db/queries/inbox.sql @@ -32,6 +32,10 @@ UPDATE inbox_item SET archived = true WHERE id = $1 RETURNING *; +-- name: ArchiveInboxByIssue :execrows +UPDATE inbox_item SET archived = true +WHERE workspace_id = $1 AND recipient_type = $2 AND recipient_id = $3 AND issue_id = $4 AND archived = false; + -- name: CountUnreadInbox :one SELECT count(*) FROM inbox_item WHERE workspace_id = $1 AND recipient_type = $2 AND recipient_id = $3 AND read = false AND archived = false;