From 58549975e0d407ed0e158ed169bbab8405bd2235 Mon Sep 17 00:00:00 2001 From: Jiayuan Date: Sat, 4 Apr 2026 00:18:14 +0800 Subject: [PATCH] fix(inbox): archive all items for the same issue instead of just one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The inbox UI deduplicates items by issue_id (showing only the latest notification per issue). Previously, clicking archive only archived the single visible item, so older items for the same issue would reappear. Now archiving operates at the issue level — both the backend and frontend archive all inbox items sharing the same issue_id. --- apps/web/features/inbox/store.ts | 14 +++++++++++--- server/internal/handler/inbox.go | 11 +++++++++++ server/pkg/db/generated/inbox.sql.go | 25 +++++++++++++++++++++++++ server/pkg/db/queries/inbox.sql | 4 ++++ 4 files changed, 51 insertions(+), 3 deletions(-) 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;