mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-08 06:45:55 +02:00
`ListPins` used to join `issues` / `projects` so each pin row carried a `title`, `status`, `identifier`, and `icon`. Convenient for the sidebar but architecturally wrong: those fields live on a different cache key than the pin query, so an `issue:updated` WS event invalidates `issueKeys` and never touches `pinKeys`. The sidebar therefore showed stale issue status / titles on pinned rows until a hard refresh — and the same shape would silently re-emerge for any new enriched field added later. This refactor moves the join to the client so display data flows from its real source of truth: Server (`server/internal/handler/pin.go`): - `PinnedItemResponse` keeps only pin-owned columns (id, workspace_id, user_id, item_type, item_id, position, created_at). - `ListPins` no longer fetches issues / projects in the loop and no longer hides orphaned pins; the client decides how to render a pin whose target was deleted. - `formatIdentifier` helper deleted (was only used by the enrichment branch); `strconv` import dropped along with it. Types (`packages/core/types/pin.ts`): - `PinnedItem` interface now mirrors the bare server shape. The four enriched fields are removed. Sidebar (`packages/views/layout/app-sidebar.tsx`): - New smart wrapper `PinRow` resolves each pin's display data via `useQuery(issueDetailOptions(...))` or `useQuery(projectDetailOptions(...))` with `enabled` gates on `pin.item_type` so the hook order stays stable. Loading renders a flat skeleton; error / 404 renders null (orphan pins hide themselves). - `SortablePinItem` becomes purely presentational: it now takes `label` and `iconNode` as props instead of reading them off the pin object. dnd-kit / navigation wiring untouched. - Same pattern as `packages/views/search/search-command.tsx:151`, which already uses per-row detail queries for Recent issues. WS sync layer is unchanged: `onIssueUpdated` already patches `issueKeys.detail`, so changing an issue's status now flows directly into the sidebar without any cross-entity invalidate. The `pin:*` prefix handler still invalidates `pinKeys` for create / delete / reorder — that's still the correct signal for the pin LIST itself. Verified: views typecheck + core typecheck + web typecheck + desktop typecheck + go test ./internal/handler/... + vitest (views: 165 tests, core: 83 tests) all pass. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>