Skip to content

feat: split header notifications into Unread and Archive views#155

Merged
parth0025 merged 2 commits into
stagingfrom
feature/notification-unread-archive-view
May 15, 2026
Merged

feat: split header notifications into Unread and Archive views#155
parth0025 merged 2 commits into
stagingfrom
feature/notification-unread-archive-view

Conversation

@parth0025
Copy link
Copy Markdown
Collaborator

Previously the notification bell fetched every notification the user had ever received (read or unread) in a single combined list, making it hard to focus on actionable items. This change splits the dropdown into two views — Unread (default on every open) and Archive — gated by a single toggle button in the dropdown head.

Changes:

  • Backend: GET /api/v1/app-notification/notification accepts a new optional filter query param ('unread' | 'archived'). 'unread' adds notSeen: { $in: [userId] } to the aggregation match; 'archived' adds notSeen: { $nin: [userId] }. Default is 'unread' to keep the bell focused on actionable items. Refactored the match clauses into a baseMatch array so the filter is appended cleanly; query shape and pagination are otherwise unchanged.
  • Header.vue: added a notificationFilter ref ('unread' by default), a "View Archive" / "View Unread" toggle in the dropdown head, a switchNotificationFilter() handler that resets paging state and refetches, and an openNotificationsDropdown() helper that the bell click handlers now use so each fresh dropdown open lands in Unread. markRead() and markAllRead() clear the read items from the local list while the Unread filter is active so the dropdown reflects the filter without an extra refetch. The "Mark all as read" button visibility now keys off notifications.length (the list is already filtered to unread on this view) rather than the server-side totalNotification counter, which can lag and falsely hide the button.
  • i18n: added Header.View_Archive / Header.View_Unread to the English locale; other locales fall back to English via vue-i18n until translators backfill (flagged as a follow-up).

Out of scope (deferred per user instruction): goals 3 and 4 from the original spec — creator-prefs fire policy verification and email preference parity audit.

Pull Request Template Chooser

Please click the link that matches your contribution type to load the correct format.

Note: Clicking a link will reload this page and clear any text you've already typed here.

  • Bug Fix
    Use this for fixing broken logic or UI glitches.

  • New Feature
    Use this for adding new functionality or components.

  • Refactor
    Use this for code cleanup, performance tweaks, or technical debt.


General Summary

If you don't want to use a specific template, please provide a brief summary of your changes below.

parth0025 and others added 2 commits May 15, 2026 11:52
Previously the notification bell fetched every notification the user
had ever received (read or unread) in a single combined list, making
it hard to focus on actionable items. This change splits the dropdown
into two views — Unread (default on every open) and Archive — gated
by a single toggle button in the dropdown head.

Changes:
- Backend: GET /api/v1/app-notification/notification accepts a new
  optional `filter` query param ('unread' | 'archived'). 'unread' adds
  `notSeen: { $in: [userId] }` to the aggregation match; 'archived'
  adds `notSeen: { $nin: [userId] }`. Default is 'unread' to keep the
  bell focused on actionable items. Refactored the match clauses into
  a `baseMatch` array so the filter is appended cleanly; query shape
  and pagination are otherwise unchanged.
- Header.vue: added a `notificationFilter` ref ('unread' by default),
  a "View Archive" / "View Unread" toggle in the dropdown head, a
  `switchNotificationFilter()` handler that resets paging state and
  refetches, and an `openNotificationsDropdown()` helper that the
  bell click handlers now use so each fresh dropdown open lands in
  Unread. `markRead()` and `markAllRead()` clear the read items from
  the local list while the Unread filter is active so the dropdown
  reflects the filter without an extra refetch. The "Mark all as
  read" button visibility now keys off `notifications.length` (the
  list is already filtered to unread on this view) rather than the
  server-side `totalNotification` counter, which can lag and falsely
  hide the button.
- i18n: added Header.View_Archive / Header.View_Unread to the English
  locale; other locales fall back to English via vue-i18n until
  translators backfill (flagged as a follow-up).

Out of scope (deferred per user instruction): goals 3 and 4 from the
original spec — creator-prefs fire policy verification and email
preference parity audit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes goals 3 & 4 of the notification refactor. Two parallel bypasses
in handleNotification.HandleBothNotification let task creators and
company owners through the project-level watcher filter (the Ignore /
All Activity / Participating setting in the List of Watcher panel):

- Task branch (taskId path): Task_Leader was Set-union'd into the
  recipient list AFTER `projectData.watchers` had already filtered out
  any user set to "ignore". So a creator who had set the project to
  Ignore still ended up in `assigneeUsers`, and the downstream
  per-event preference check (NOTIFICATIONS_SETTINGS — a separate
  preference layer that defaults email=true) emitted both an in-app
  and an email notification. Now Task_Leader is included only if their
  project-watcher setting is not "ignore".

- Project branch (type === 'project'): same pattern — companyOwnerId
  was union'd in unconditionally, bypassing the watcher filter.
  Honours "ignore" too.

This explains the goal-4 symptom of "I'm getting emails for events I
disabled": the disablement was set at the project-watcher layer, but
the bypass routed the recipient straight to the per-event layer where
email was still on.

Other recipient paths are untouched. Default behaviour for users who
have not chosen "ignore" is unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@parth0025 parth0025 self-assigned this May 15, 2026
@parth0025 parth0025 merged commit 430485f into staging May 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant