Commit Graph

4 Commits

Author SHA1 Message Date
Bohan Jiang
71eb938a67 fix: preserve inbox comment anchors for MUL-3294 (#4139)
Co-authored-by: J <j@multica.ai>
Co-authored-by: multica-agent <github@multica.ai>
2026-06-15 15:52:18 +08:00
Bohan Jiang
bbe73ade8b feat(desktop): dock unread badge + focus-gated inbox notifications (#1445)
* feat(desktop): dock unread badge + focus-gated inbox notifications

Wire two OS-level integrations for inbox activity. Both degrade cleanly on
web and unsupported platforms.

- Unread badge on the macOS dock / Linux Unity launcher. Derived from the
  same inbox list the UI renders, deduplicated per issue, capped as "99+"
  on macOS via `app.dock.setBadge` (setBadgeCount truncates at 99). New
  `useInboxUnreadCount` hook (core/inbox) + `useDesktopUnreadBadge`
  (views/platform) keep renderer and main in sync via a `badge:set` IPC.
- Native OS notification on `inbox:new`, fired from the renderer only when
  `document.hasFocus()` is false — in-focus feedback is the existing inbox
  sidebar's unread styling, so we don't fight macOS's deliberate foreground
  suppression. Clicking the banner focuses the main window and navigates
  to `/inbox?issue=<key>` via the shared `multica:navigate` bus.

Refactors `inbox-page.tsx` to read the unread count through the new hook
(was a per-render inline filter).

* fix(desktop): pin notification routing to source workspace + mark read on URL select

Two bugs GPT-Boy caught on PR #1445:

1. A notification from workspace A used `getCurrentSlug()` at click time,
   so if the user switched to workspace B before clicking the banner (macOS
   Notification Center persists banners), routing landed on `/B/inbox?issue=<A key>`
   and 404'd. Fix: round-trip the emit-time `slug` through the IPC payload
   and use it in the click handler.
2. Notification-click navigation set the URL param but never fired the
   mark-read mutation (only InboxPage's click-handler did). The row stayed
   unread and the dock badge didn't decrement. Fix: move the mark-read
   logic from handleSelect into a useEffect keyed on the selected item —
   it now covers both click-to-select and URL-param-select.

IPC payload gains `slug` and `itemId`; preload types + main handler + the
desktop bridge are updated to match.
2026-04-28 17:33:48 +08:00
Bohan Jiang
6366e2f4ba fix(inbox): don't archive after deleting an issue (#1444)
* fix(inbox): don't archive after deleting an issue

Deleting an issue from the Inbox page was calling the archive API on the
inbox item right after deleteIssue succeeded. Because the inbox_item row
has ON DELETE CASCADE on issue_id, it was already gone by then and the
archive call 404'd with "inbox item not found", surfacing a "Failed to
archive" toast.

Drop the redundant archive call and invalidate the inbox cache through
the issue:deleted WS handler so every tab stays in sync without an extra
round trip.

* fix(inbox): keep stale selection on /inbox instead of the deleted issue

When another tab deletes the selected inbox issue, onInboxIssueDeleted
prunes the cache and `selected` becomes null. The existing fallback then
redirected to the issue detail page — which is also gone, so the user
landed on a "This issue does not exist..." screen instead of back in the
inbox list.

Track the last key that actually resolved against the inbox list. If it
used to be in the list and just disappeared, clear the selection and
stay on /inbox. Only shared links that were never in the user's inbox
continue to fall back to the issue detail page.

Also add ws-updaters tests covering onInboxIssueDeleted and
onInboxIssueStatusChanged.
2026-04-21 17:12:53 +08:00
Naiyuan Qing
e1e7f68330 feat: extract packages/core — Turborepo infrastructure + headless business logic
Phase 1: Monorepo infrastructure
- Add Turborepo with turbo.json pipeline (build, dev, typecheck, test)
- Update pnpm-workspace.yaml to include packages/*
- Create shared TypeScript config (packages/tsconfig)

Phase 2: Extract packages/core (zero react-dom, all-platform reuse)
- Move domain types, API client, logger, utils → packages/core/
- Move TanStack Query modules (issues, inbox, workspace, runtimes)
- Move Zustand stores (auth, workspace, issues, navigation, modals)
- Move realtime sync (WSProvider, hooks, ws-updaters)
- Refactor auth/workspace stores to factory pattern for DI
- Refactor ApiClient with onUnauthorized callback
- Refactor useWorkspaceId to React Context (WorkspaceIdProvider)
- Refactor WSProvider to accept wsUrl + store props
- Create apps/web/platform/ bridge layer (api singleton, store instances)
- Update 91 import paths across apps/web/
- Fix 3 test files for new import paths

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 10:20:00 +08:00