Skip to content

feat(team-inbox): unified team inbox - #578

Merged
Neonforge98 merged 5 commits into
developfrom
feat/team-inbox-unified
Jul 30, 2026
Merged

feat(team-inbox): unified team inbox#578
Neonforge98 merged 5 commits into
developfrom
feat/team-inbox-unified

Conversation

@Neonforge98

Copy link
Copy Markdown
Collaborator

Summary

This started as a task to extract the team-inbox feature from #530 onto a clean develop. That premise turned out to be stale: develop already carries its own, more advanced team-inbox lineage (#521 merged, reverted, re-added via 3fd19ba4f, then extended three more times through 9238b2cb0 — session handoff, collaborative work item threads — none of which #530 has). Porting #530's snapshot over that would have been a regression, not an extraction.

So this PR does not port #530. It closes four real gaps found while diffing develop's current team-inbox implementation against #530 and against itself:

  • Orphaned IPC registration (the actual blocker). team_inbox_list_page / unread_count / mark_read / mark_all_read / mark_unread have existed in the project-management crate and been called by the frontend since the post-feat(team-inbox): add unified team inbox #521-revert re-add, but handler_list.inc never got the registration back. Every Team Inbox call has been rejecting at the Tauri boundary as an unknown command — the feature has been non-functional on develop.
  • Read-state idempotency lived only in the view. TeamInboxView's readAt guard protected its own handlers but not the coordinator itself, so any other caller could double-fire markRead/markUnread and trigger a redundant local/cloud round trip. Moved the guard into teamInboxCoordinator.setReadState, checked against live cache state rather than a caller's snapshot.
  • No forced resync after a failed optimistic mutation. The coordinator already reverts an item's own optimistic readAt on failure, but nothing resynced the list/badge against server truth afterward, which risked drift if the revert raced another concurrent update. Extracted the success/failure/resync orchestration into a pure, framework-free helper (teamInboxReadTransitions.ts — repo policy is no .tsx unit tests) and wired handleSelect/handleMarkRead/handleMarkUnread through it.
  • i18n gaps. The unread sidebar badge's aria-label was a hardcoded English template literal (now uses the teamInbox.unreadCount key that already existed in common.json but was unused); the teamInbox namespace itself only existed in en/zh — added to the other 11 shipped locales (de, es, fr, ja, ko, pl, pt, ru, tr, vi, zh-Hant), mirroring en's key structure.

Checked and found already correct, no change needed: markAllRead's unread-count bookkeeping already derives from the DB-wide bulk result and the server's mention-read response (not a loaded-page subtraction), and en/zh common.json have no duplicate teamInbox key or duplicate loadMore key — those specific defects were unique to #530's history and aren't present on develop.

Supersedes the team-inbox portion of #530.

Commits

  • fix(team-inbox): register orphaned IPC commands
  • fix(team-inbox): guard read-state idempotency in the coordinator
  • fix(team-inbox): revert and resync optimistic read state on failure
  • fix(team-inbox): translate the unread sidebar badge aria-label
  • feat(team-inbox): add teamInbox namespace to remaining locales

Validation

  • tsc --noEmit --pretty false: clean
  • vitest run (TeamInbox + sidebar menu suites): 16 files, 89 tests passed, including 5 new tests for the read-transition helper and 2 new tests for the aria-label wiring
  • eslint (all changed files): clean
  • cargo check -p org2: passed
  • cargo test -p project_management team_inbox --no-fail-fast: 14 passed, 0 failed

team_inbox_list_page/unread_count/mark_read/mark_all_read/mark_unread
have existed in the project-management crate and been invoked by the
frontend since the feature was re-added after the #521 revert, but the
handler_list.inc registration never came back with it. Every Team
Inbox IPC call has been rejecting at the Tauri boundary as an unknown
command.
The readAt check before calling markRead/markUnread lived only in
TeamInboxView, so any other caller could double-fire a mutation on an
item that's already in the target state. The coordinator's own
optimistic patch already recomputes unread deltas from live cache
state on each call, so client-side counts stayed safe, but a
redundant call still meant an unnecessary local/cloud round trip on
every double click. Move the guard into setReadState, checked against
the live cache rather than the caller's (possibly stale) item
snapshot, so it holds regardless of caller.
The coordinator already reverts an item's own optimistic readAt on a
failed mutation, but nothing forced a resync against server truth
afterwards, so a failure racing with another concurrent update could
leave the list or badge quietly drifted. Extract the
success/failure/resync orchestration out of TeamInboxView into a
pure, framework-free helper (repo policy is no .tsx unit tests) and
wire handleSelect/handleMarkRead/handleMarkUnread through it. Covers
the success path, failure with revert already handled by the
coordinator plus resync trigger, and resync failure not masking the
original mutation error.
The unread-count badge's aria-label was a hardcoded English template
literal, so screen reader users on every non-English locale heard
English. Thread the existing teamInbox.unreadCount translation (it
was already in common.json, just unused here) down through
usePinnedMenuItems into buildPinnedMenuItems, with the old string
kept only as a fallback for callers that don't supply it.
teamInbox only existed in en and zh common.json; every other shipped
locale (de, es, fr, ja, ko, pl, pt, ru, tr, vi, zh-Hant) fell back to
raw English for the whole feature. Add the namespace to all 11,
mirroring en's key structure exactly (including the _one/_other
plural key pairs, per this file's existing convention across other
namespaces).
@Neonforge98
Neonforge98 merged commit ed86cc6 into develop Jul 30, 2026
3 checks passed
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