From f056d2b6169c6ff5cc8f162a780b5199618743dd Mon Sep 17 00:00:00 2001 From: Josh Snyder Date: Sat, 16 May 2026 15:56:17 +0000 Subject: [PATCH 01/10] fix(inbox): polish signals inbox card and sidebar styling - Drop yellowish row background for suggested-reviewer cards; the eye badge alone is enough to indicate suggestion and the amber tint read as "highlighted" - Normalize the suggested-reviewer badge height to match P2/Actionable by removing its leading-none override - Increase the gap between title and badges (and badges and summary) in compact list rows - Clamp the list-row summary to 3 lines (was 4) - Round the sidebar Inbox "Alpha" badge to match the rest of the sidebar's rounded pills Generated-By: PostHog Code Task-Id: 9fcb5fbc-ec76-4b37-a0bc-dc4556c0d8a6 --- .../features/inbox/components/list/ReportListRow.tsx | 12 ++---------- .../inbox/components/utils/ReportCardContent.tsx | 4 ++-- .../components/utils/SignalReportSummaryMarkdown.tsx | 2 +- .../features/sidebar/components/items/HomeItem.tsx | 6 +++++- 4 files changed, 10 insertions(+), 14 deletions(-) diff --git a/apps/code/src/renderer/features/inbox/components/list/ReportListRow.tsx b/apps/code/src/renderer/features/inbox/components/list/ReportListRow.tsx index 003406683..21a046282 100644 --- a/apps/code/src/renderer/features/inbox/components/list/ReportListRow.tsx +++ b/apps/code/src/renderer/features/inbox/components/list/ReportListRow.tsx @@ -75,18 +75,10 @@ export function ReportListRow({ onClick({ metaKey: e.metaKey, shiftKey: e.shiftKey }); }; - const rowBgClass = isSelected - ? report.is_suggested_reviewer - ? "bg-amber-3" - : "bg-gray-3" - : report.is_suggested_reviewer - ? "bg-amber-2" - : ""; + const rowBgClass = isSelected ? "bg-gray-3" : ""; const hoverOverlayClass = - isSelected && report.is_suggested_reviewer - ? "before:bg-amber-12 before:opacity-0 hover:before:opacity-[0.07]" - : "before:bg-gray-12 before:opacity-0 hover:before:opacity-[0.07]"; + "before:bg-gray-12 before:opacity-0 hover:before:opacity-[0.07]"; return ( @@ -71,7 +71,7 @@ export function ReportCardContent({ diff --git a/apps/code/src/renderer/features/inbox/components/utils/SignalReportSummaryMarkdown.tsx b/apps/code/src/renderer/features/inbox/components/utils/SignalReportSummaryMarkdown.tsx index e5f199de3..d2c3d81d3 100644 --- a/apps/code/src/renderer/features/inbox/components/utils/SignalReportSummaryMarkdown.tsx +++ b/apps/code/src/renderer/features/inbox/components/utils/SignalReportSummaryMarkdown.tsx @@ -33,7 +33,7 @@ export function SignalReportSummaryMarkdown({ if (variant === "list") { return ( diff --git a/apps/code/src/renderer/features/sidebar/components/items/HomeItem.tsx b/apps/code/src/renderer/features/sidebar/components/items/HomeItem.tsx index 5fcef26b0..72574ce1a 100644 --- a/apps/code/src/renderer/features/sidebar/components/items/HomeItem.tsx +++ b/apps/code/src/renderer/features/sidebar/components/items/HomeItem.tsx @@ -72,7 +72,11 @@ export function InboxItem({ isActive, onClick, signalCount }: InboxItemProps) { } isActive={isActive} onClick={onClick} - endContent={Alpha} + endContent={ + + Alpha + + } /> From 5d676d34c89754b4e80c278467ade73404a2dbb5 Mon Sep 17 00:00:00 2001 From: Josh Snyder Date: Sat, 16 May 2026 16:00:13 +0000 Subject: [PATCH 02/10] fix(inbox): drop redundant changes already shipped on main MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revert the eye-badge leading override, the compact column gap bump, and the sidebar Alpha badge radius — those were already in place; the earlier commit was made against a stale local copy. Keeps only the two genuinely new fixes: removing the suggested-reviewer row tint and clamping the list summary to 3 lines. Generated-By: PostHog Code Task-Id: 9fcb5fbc-ec76-4b37-a0bc-dc4556c0d8a6 --- .../features/inbox/components/utils/ReportCardContent.tsx | 4 ++-- .../renderer/features/sidebar/components/items/HomeItem.tsx | 6 +----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/apps/code/src/renderer/features/inbox/components/utils/ReportCardContent.tsx b/apps/code/src/renderer/features/inbox/components/utils/ReportCardContent.tsx index e790440b7..83db11fb1 100644 --- a/apps/code/src/renderer/features/inbox/components/utils/ReportCardContent.tsx +++ b/apps/code/src/renderer/features/inbox/components/utils/ReportCardContent.tsx @@ -36,7 +36,7 @@ export function ReportCardContent({ @@ -71,7 +71,7 @@ export function ReportCardContent({ diff --git a/apps/code/src/renderer/features/sidebar/components/items/HomeItem.tsx b/apps/code/src/renderer/features/sidebar/components/items/HomeItem.tsx index 72574ce1a..5fcef26b0 100644 --- a/apps/code/src/renderer/features/sidebar/components/items/HomeItem.tsx +++ b/apps/code/src/renderer/features/sidebar/components/items/HomeItem.tsx @@ -72,11 +72,7 @@ export function InboxItem({ isActive, onClick, signalCount }: InboxItemProps) { } isActive={isActive} onClick={onClick} - endContent={ - - Alpha - - } + endContent={Alpha} /> From 03d39602e141cfaac8e34fd3bd7cc328b4a89610 Mon Sep 17 00:00:00 2001 From: Josh Snyder Date: Sat, 16 May 2026 16:09:12 +0000 Subject: [PATCH 03/10] fix(inbox): shorten task-bar labels to "Research" and "Implementation" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop the trailing "task" in the relationship labels shown on the report detail task bars and in the matching pending-state tooltips. The "task" suffix was redundant context — the bar itself already denotes a task. Generated-By: PostHog Code Task-Id: 9fcb5fbc-ec76-4b37-a0bc-dc4556c0d8a6 --- .../features/inbox/components/detail/ReportTaskLogs.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/code/src/renderer/features/inbox/components/detail/ReportTaskLogs.tsx b/apps/code/src/renderer/features/inbox/components/detail/ReportTaskLogs.tsx index 2717b95ad..ef72d67d6 100644 --- a/apps/code/src/renderer/features/inbox/components/detail/ReportTaskLogs.tsx +++ b/apps/code/src/renderer/features/inbox/components/detail/ReportTaskLogs.tsx @@ -19,8 +19,8 @@ type Relationship = SignalReportTask["relationship"]; const RELATIONSHIP_LABELS: Record = { repo_selection: "Repository selection", - research: "Research task", - implementation: "Implementation task", + research: "Research", + implementation: "Implementation", }; interface BarSummary { @@ -79,7 +79,7 @@ function getResearchPendingSummary( color: "var(--gray-9)", icon: , }, - tooltip: "Checking if a research task exists for this report.", + tooltip: "Checking if research exists for this report.", }; } if (reportStatus === "candidate") { @@ -111,7 +111,7 @@ function getResearchPendingSummary( icon: , }, tooltip: - "No research task is recorded for this report. It may have been created before research tracking was in place.", + "No research is recorded for this report. It may have been created before research tracking was in place.", }; } From a5abb65f53bb05496b340e5d474c967c713c4f42 Mon Sep 17 00:00:00 2001 From: Josh Snyder Date: Sat, 16 May 2026 16:12:07 +0000 Subject: [PATCH 04/10] fix(inbox): keep report selected when clicked again MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plain-clicking the already-selected report was calling clearSelection, which dropped the user back to the "Select a report" empty state — not expected. Dropping the toggle-off branch so the second click is a no-op (the row stays selected); cmd-click still toggles selection as before. Generated-By: PostHog Code Task-Id: 9fcb5fbc-ec76-4b37-a0bc-dc4556c0d8a6 --- .../features/inbox/components/InboxSignalsTab.tsx | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx b/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx index a39d37418..3c5e5343a 100644 --- a/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx +++ b/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx @@ -283,18 +283,12 @@ export function InboxSignalsTab() { ); } else if (event.metaKey) { toggleReportSelection(reportId); - } else if ( - selectedReportIdsRef.current.length === 1 && - selectedReportIdsRef.current[0] === reportId - ) { - // Plain click on the only selected report — deselect it - clearSelection(); } else { - // Plain click — select only this report + // Plain click — select only this report (no-op if already the sole selection) setSelectedReportIds([reportId]); } }, - [selectRange, toggleReportSelection, setSelectedReportIds, clearSelection], + [selectRange, toggleReportSelection, setSelectedReportIds], ); // Select-all checkbox From 37b5ca17068e62209deb936378c8d3d4ed2c7003 Mon Sep 17 00:00:00 2001 From: Josh Snyder Date: Sat, 16 May 2026 16:15:31 +0000 Subject: [PATCH 05/10] fix(inbox): always label implementation run button "Create PR" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The button was switching to "Implement as new task" whenever the report was awaiting human input, even though clicking it does the same thing either way — kick off onCreateImplementationTask. Always show the user-meaningful "Create PR" label. Generated-By: PostHog Code Task-Id: 9fcb5fbc-ec76-4b37-a0bc-dc4556c0d8a6 --- .../features/inbox/components/detail/ReportTaskLogs.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/apps/code/src/renderer/features/inbox/components/detail/ReportTaskLogs.tsx b/apps/code/src/renderer/features/inbox/components/detail/ReportTaskLogs.tsx index ef72d67d6..7e0d037a6 100644 --- a/apps/code/src/renderer/features/inbox/components/detail/ReportTaskLogs.tsx +++ b/apps/code/src/renderer/features/inbox/components/detail/ReportTaskLogs.tsx @@ -191,11 +191,7 @@ export function ReportTaskLogs({ const isPendingInput = reportStatus === "pending_input"; const awaitingInput = isPendingInput || isAwaitingInput; - const runActionLabel = onCreateImplementationTask - ? awaitingInput - ? "Implement as new task" - : "Create PR" - : undefined; + const runActionLabel = onCreateImplementationTask ? "Create PR" : undefined; if (implementationTask) { bars.push({ From 3232bf373efe12dd62345a275d087fdc9163e74c Mon Sep 17 00:00:00 2001 From: Josh Snyder Date: Sat, 16 May 2026 16:24:31 +0000 Subject: [PATCH 06/10] feat(inbox): drop weight badge, link out to error tracking issue - Remove the "Weight: x.x" badge from signal card headers. It was noisy internal detail with no actionable meaning for the reviewer. - For error_tracking signals, render a "View issue" link in the card using signal.source_id (the issue UUID). Mirrors the existing "View on GitHub" / Zendesk "Open" links on other source-specific cards. Adds errorTrackingIssueUrl() to the shared posthogLinks helpers so it picks up the user's cloud region and project id. Generated-By: PostHog Code Task-Id: 9fcb5fbc-ec76-4b37-a0bc-dc4556c0d8a6 --- .../inbox/components/detail/SignalCard.tsx | 33 ++++++++++++++----- apps/code/src/renderer/utils/posthogLinks.ts | 10 ++++++ 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/apps/code/src/renderer/features/inbox/components/detail/SignalCard.tsx b/apps/code/src/renderer/features/inbox/components/detail/SignalCard.tsx index ea254b4fa..0b0e7b3da 100644 --- a/apps/code/src/renderer/features/inbox/components/detail/SignalCard.tsx +++ b/apps/code/src/renderer/features/inbox/components/detail/SignalCard.tsx @@ -12,6 +12,7 @@ import { } from "@phosphor-icons/react"; import { Badge, Box, Flex, Text } from "@radix-ui/themes"; import type { Signal, SignalFindingContent } from "@shared/types"; +import { errorTrackingIssueUrl } from "@utils/posthogLinks"; import { useRef, useState } from "react"; const COLLAPSE_THRESHOLD = 300; @@ -267,14 +268,6 @@ function SignalCardHeader({ {verified !== undefined && } - - Weight: {signal.weight.toFixed(1)} - ); } @@ -669,10 +662,34 @@ function ErrorTrackingSignalCard({ codePaths?: string[]; dataQueried?: string; }) { + const projectId = useAuthStateValue((s) => s.projectId); + const cloudRegion = useAuthStateValue((s) => s.cloudRegion); + const issueUrl = signal.source_id + ? errorTrackingIssueUrl(signal.source_id, { projectId, cloudRegion }) + : null; + return ( + {issueUrl && ( + + + View issue + + + + )} diff --git a/apps/code/src/renderer/utils/posthogLinks.ts b/apps/code/src/renderer/utils/posthogLinks.ts index 8a74b5120..5512b0ea4 100644 --- a/apps/code/src/renderer/utils/posthogLinks.ts +++ b/apps/code/src/renderer/utils/posthogLinks.ts @@ -65,3 +65,13 @@ export function experimentUrl( export function featureFlagsIndexUrl(overrides?: LinkOverrides): string | null { return withProjectId((pid) => `/project/${pid}/feature_flags`, overrides); } + +export function errorTrackingIssueUrl( + issueId: string, + overrides?: LinkOverrides, +): string | null { + return withProjectId( + (pid) => `/project/${pid}/error_tracking/${encodeURIComponent(issueId)}`, + overrides, + ); +} From f6359c86ee34828045799b085c4539bb96e9f4d2 Mon Sep 17 00:00:00 2001 From: Josh Snyder Date: Sat, 16 May 2026 16:27:26 +0000 Subject: [PATCH 07/10] fix(inbox): only show verified badge when actually verified MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop the "Unverified" rendering of VerificationBadge — showing a gray "Unverified" chip on every unverified signal added noise without adding signal. The badge now only renders when finding.verified is true. Generated-By: PostHog Code Task-Id: 9fcb5fbc-ec76-4b37-a0bc-dc4556c0d8a6 --- .../inbox/components/detail/SignalCard.tsx | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/apps/code/src/renderer/features/inbox/components/detail/SignalCard.tsx b/apps/code/src/renderer/features/inbox/components/detail/SignalCard.tsx index 0b0e7b3da..df23ffcd1 100644 --- a/apps/code/src/renderer/features/inbox/components/detail/SignalCard.tsx +++ b/apps/code/src/renderer/features/inbox/components/detail/SignalCard.tsx @@ -7,7 +7,6 @@ import { CaretDownIcon, CaretRightIcon, CheckCircleIcon, - QuestionIcon, TagIcon, } from "@phosphor-icons/react"; import { Badge, Box, Flex, Text } from "@radix-ui/themes"; @@ -221,23 +220,16 @@ function isErrorTrackingExtra( // ── Shared components ──────────────────────────────────────────────────────── -function VerificationBadge({ verified }: { verified: boolean }) { +function VerificationBadge() { return ( - {verified ? ( - - ) : ( - - )} - {verified ? "Verified" : "Unverified"} + + Verified ); } @@ -267,7 +259,7 @@ function SignalCardHeader({ {signalCardSourceLine(signal)} - {verified !== undefined && } + {verified === true && } ); } From 8d720b3e93c6b03f9e199787816212c480fb6784 Mon Sep 17 00:00:00 2001 From: Josh Snyder Date: Sat, 16 May 2026 16:37:35 +0000 Subject: [PATCH 08/10] fix(inbox): move Create/View PR action into the report header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Create PR button sat on the bottom implementation task bar, which felt like an out-of-the-way place for the primary action on a report. Move it to the header next to Dismiss so all top-level controls live in one corner. Header right-side order is now: copy-link, Dismiss, (Create PR | View PR pill), close. The Create PR button and View PR pill are mutually exclusive — same slot, same role. Drops the now-unused onCreateImplementationTask / isAwaitingInput plumbing from ReportTaskLogs along with the run-action button JSX and the role="button" workaround that only existed so the nested run button could coexist with the expand toggle. Generated-By: PostHog Code Task-Id: 9fcb5fbc-ec76-4b37-a0bc-dc4556c0d8a6 --- .../components/detail/ReportDetailPane.tsx | 63 ++++---- .../components/detail/ReportTaskLogs.tsx | 153 +++++------------- 2 files changed, 76 insertions(+), 140 deletions(-) diff --git a/apps/code/src/renderer/features/inbox/components/detail/ReportDetailPane.tsx b/apps/code/src/renderer/features/inbox/components/detail/ReportDetailPane.tsx index 4dd14652d..c8680a9ca 100644 --- a/apps/code/src/renderer/features/inbox/components/detail/ReportDetailPane.tsx +++ b/apps/code/src/renderer/features/inbox/components/detail/ReportDetailPane.tsx @@ -17,6 +17,7 @@ import { CaretRightIcon, EyeIcon, LinkSimpleIcon, + Plus, ThumbsDownIcon, WarningIcon, XIcon, @@ -298,12 +299,25 @@ export function ReportDetailPane({ - {headerImplementationPrUrl ? ( - - ) : null} + + + - + + Create PR + + ) : null} - )} + + {taskStartedAt ? ( + + + {bar.prUrl + ? summary.label + : relationship === "implementation" && + (task?.latest_run?.status === "queued" || + task?.latest_run?.status === "in_progress") + ? "Working on a PR…" + : summary.label} + + + ) : bar.prUrl ? ( + summary.label + ) : relationship === "implementation" && + (task?.latest_run?.status === "queued" || + task?.latest_run?.status === "in_progress") ? ( + "Working on a PR…" + ) : ( + summary.label + )} + {isInteractive && ( can't contain the nested run-action - ) + ) : (
Date: Mon, 18 May 2026 20:15:03 +0000 Subject: [PATCH 09/10] feat(inbox): seed reviewer filter with current user on first visit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Default the "Suggested reviewer" filter to just the current user the first time the inbox loads, so the list opens scoped to "signals assigned to me" instead of every signal in the project. Users can still clear or expand the filter — we just no longer start with everything visible. Tracks a persisted `hasInitializedSuggestedReviewerFilter` flag so the seed runs once per install and never overrides a filter the user has actively set. Generated-By: PostHog Code Task-Id: 9fcb5fbc-ec76-4b37-a0bc-dc4556c0d8a6 --- .../inbox/components/InboxSignalsTab.tsx | 33 +++++++++++++++++++ .../inbox/stores/inboxSignalsFilterStore.ts | 8 +++++ 2 files changed, 41 insertions(+) diff --git a/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx b/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx index 3c5e5343a..bc2b33945 100644 --- a/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx +++ b/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx @@ -1,3 +1,5 @@ +import { useOptionalAuthenticatedClient } from "@features/auth/hooks/authClient"; +import { useCurrentUser } from "@features/auth/hooks/authQueries"; import { SelectReportPane, SkeletonBackdrop, @@ -66,6 +68,37 @@ export function InboxSignalsTab() { const suggestedReviewerFilter = useInboxSignalsFilterStore( (s) => s.suggestedReviewerFilter, ); + const hasInitializedSuggestedReviewerFilter = useInboxSignalsFilterStore( + (s) => s.hasInitializedSuggestedReviewerFilter, + ); + const setSuggestedReviewerFilter = useInboxSignalsFilterStore( + (s) => s.setSuggestedReviewerFilter, + ); + const markSuggestedReviewerFilterInitialized = useInboxSignalsFilterStore( + (s) => s.markSuggestedReviewerFilterInitialized, + ); + + // ── Current user (seeds reviewer filter on first inbox visit) ─────────── + const authClient = useOptionalAuthenticatedClient(); + const { data: currentUser } = useCurrentUser({ + client: authClient, + enabled: !!authClient, + }); + + useEffect(() => { + if (hasInitializedSuggestedReviewerFilter) return; + if (!currentUser?.uuid) return; + if (suggestedReviewerFilter.length === 0) { + setSuggestedReviewerFilter([currentUser.uuid]); + } + markSuggestedReviewerFilterInitialized(); + }, [ + hasInitializedSuggestedReviewerFilter, + currentUser?.uuid, + suggestedReviewerFilter.length, + setSuggestedReviewerFilter, + markSuggestedReviewerFilterInitialized, + ]); // ── GitHub integration ─────────────────────────────────────────────── const { hasGithubIntegration } = useRepositoryIntegration(); diff --git a/apps/code/src/renderer/features/inbox/stores/inboxSignalsFilterStore.ts b/apps/code/src/renderer/features/inbox/stores/inboxSignalsFilterStore.ts index c53c4c9e5..0fa0c3c38 100644 --- a/apps/code/src/renderer/features/inbox/stores/inboxSignalsFilterStore.ts +++ b/apps/code/src/renderer/features/inbox/stores/inboxSignalsFilterStore.ts @@ -39,6 +39,8 @@ interface InboxSignalsFilterState { sourceProductFilter: SourceProduct[]; /** Empty array means "all suggested reviewers" (no filter). Stored as PostHog user UUID strings. */ suggestedReviewerFilter: string[]; + /** Tracks whether we've seeded the reviewer filter with the current user once. Persisted so the seed only runs on first inbox visit. */ + hasInitializedSuggestedReviewerFilter: boolean; } interface InboxSignalsFilterActions { @@ -49,6 +51,7 @@ interface InboxSignalsFilterActions { toggleSourceProduct: (source: SourceProduct) => void; toggleSuggestedReviewer: (reviewerUuid: string) => void; setSuggestedReviewerFilter: (reviewerUuids: string[]) => void; + markSuggestedReviewerFilterInitialized: () => void; /** Reset all filters when a deep link arrives so the linked report isn't hidden. */ resetFilters: () => void; } @@ -65,6 +68,7 @@ export const useInboxSignalsFilterStore = create()( statusFilter: DEFAULT_STATUS_FILTER, sourceProductFilter: [], suggestedReviewerFilter: [], + hasInitializedSuggestedReviewerFilter: false, setSort: (sortField, sortDirection) => set({ sortField, sortDirection }), setSearchQuery: (searchQuery) => set({ searchQuery }), setStatusFilter: (statusFilter) => set({ statusFilter }), @@ -96,6 +100,8 @@ export const useInboxSignalsFilterStore = create()( set({ suggestedReviewerFilter: Array.from(new Set(reviewerUuids)), }), + markSuggestedReviewerFilterInitialized: () => + set({ hasInitializedSuggestedReviewerFilter: true }), resetFilters: () => set({ searchQuery: "", @@ -112,6 +118,8 @@ export const useInboxSignalsFilterStore = create()( statusFilter: state.statusFilter, sourceProductFilter: state.sourceProductFilter, suggestedReviewerFilter: state.suggestedReviewerFilter, + hasInitializedSuggestedReviewerFilter: + state.hasInitializedSuggestedReviewerFilter, }), }, ), From f0dd45ca40d0f1f3a5b93e5b764ce5f31663ea97 Mon Sep 17 00:00:00 2001 From: Josh Snyder Date: Mon, 18 May 2026 20:22:31 +0000 Subject: [PATCH 10/10] refactor(inbox): move reviewer-seed policy into the store Collapse the two-step "set filter, then mark initialized" dance into a single store action that owns the idempotency check. The component effect becomes a trivial bridge from `useCurrentUser` to the store. Adds tests covering the seed, the no-op-after-init, and the preserve-existing-choice cases. Generated-By: PostHog Code Task-Id: 9fcb5fbc-ec76-4b37-a0bc-dc4556c0d8a6 --- .../inbox/components/InboxSignalsTab.tsx | 24 ++--------- .../stores/inboxSignalsFilterStore.test.ts | 40 +++++++++++++++++++ .../inbox/stores/inboxSignalsFilterStore.ts | 20 ++++++++-- 3 files changed, 61 insertions(+), 23 deletions(-) diff --git a/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx b/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx index bc2b33945..a937fb8cb 100644 --- a/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx +++ b/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx @@ -68,14 +68,8 @@ export function InboxSignalsTab() { const suggestedReviewerFilter = useInboxSignalsFilterStore( (s) => s.suggestedReviewerFilter, ); - const hasInitializedSuggestedReviewerFilter = useInboxSignalsFilterStore( - (s) => s.hasInitializedSuggestedReviewerFilter, - ); - const setSuggestedReviewerFilter = useInboxSignalsFilterStore( - (s) => s.setSuggestedReviewerFilter, - ); - const markSuggestedReviewerFilterInitialized = useInboxSignalsFilterStore( - (s) => s.markSuggestedReviewerFilterInitialized, + const seedSuggestedReviewerFilterWithCurrentUser = useInboxSignalsFilterStore( + (s) => s.seedSuggestedReviewerFilterWithCurrentUser, ); // ── Current user (seeds reviewer filter on first inbox visit) ─────────── @@ -86,19 +80,9 @@ export function InboxSignalsTab() { }); useEffect(() => { - if (hasInitializedSuggestedReviewerFilter) return; if (!currentUser?.uuid) return; - if (suggestedReviewerFilter.length === 0) { - setSuggestedReviewerFilter([currentUser.uuid]); - } - markSuggestedReviewerFilterInitialized(); - }, [ - hasInitializedSuggestedReviewerFilter, - currentUser?.uuid, - suggestedReviewerFilter.length, - setSuggestedReviewerFilter, - markSuggestedReviewerFilterInitialized, - ]); + seedSuggestedReviewerFilterWithCurrentUser(currentUser.uuid); + }, [currentUser?.uuid, seedSuggestedReviewerFilterWithCurrentUser]); // ── GitHub integration ─────────────────────────────────────────────── const { hasGithubIntegration } = useRepositoryIntegration(); diff --git a/apps/code/src/renderer/features/inbox/stores/inboxSignalsFilterStore.test.ts b/apps/code/src/renderer/features/inbox/stores/inboxSignalsFilterStore.test.ts index 00693aa44..ff698fa17 100644 --- a/apps/code/src/renderer/features/inbox/stores/inboxSignalsFilterStore.test.ts +++ b/apps/code/src/renderer/features/inbox/stores/inboxSignalsFilterStore.test.ts @@ -18,6 +18,7 @@ describe("inboxSignalsFilterStore", () => { ], sourceProductFilter: [], suggestedReviewerFilter: [], + hasInitializedSuggestedReviewerFilter: false, }); }); @@ -129,6 +130,45 @@ describe("inboxSignalsFilterStore", () => { expect(state.suggestedReviewerFilter).toEqual([]); }); + it("seedSuggestedReviewerFilterWithCurrentUser seeds when empty and uninitialized", () => { + useInboxSignalsFilterStore + .getState() + .seedSuggestedReviewerFilterWithCurrentUser("me-uuid"); + + const state = useInboxSignalsFilterStore.getState(); + expect(state.suggestedReviewerFilter).toEqual(["me-uuid"]); + expect(state.hasInitializedSuggestedReviewerFilter).toBe(true); + }); + + it("seedSuggestedReviewerFilterWithCurrentUser is a no-op once initialized", () => { + useInboxSignalsFilterStore + .getState() + .seedSuggestedReviewerFilterWithCurrentUser("me-uuid"); + useInboxSignalsFilterStore.getState().setSuggestedReviewerFilter([]); + + useInboxSignalsFilterStore + .getState() + .seedSuggestedReviewerFilterWithCurrentUser("me-uuid"); + + expect( + useInboxSignalsFilterStore.getState().suggestedReviewerFilter, + ).toEqual([]); + }); + + it("seedSuggestedReviewerFilterWithCurrentUser preserves an existing non-empty filter", () => { + useInboxSignalsFilterStore + .getState() + .setSuggestedReviewerFilter(["someone-else"]); + + useInboxSignalsFilterStore + .getState() + .seedSuggestedReviewerFilterWithCurrentUser("me-uuid"); + + const state = useInboxSignalsFilterStore.getState(); + expect(state.suggestedReviewerFilter).toEqual(["someone-else"]); + expect(state.hasInitializedSuggestedReviewerFilter).toBe(true); + }); + it("resetFilters preserves sort preferences", () => { useInboxSignalsFilterStore.getState().setSort("created_at", "asc"); diff --git a/apps/code/src/renderer/features/inbox/stores/inboxSignalsFilterStore.ts b/apps/code/src/renderer/features/inbox/stores/inboxSignalsFilterStore.ts index 0fa0c3c38..31da51321 100644 --- a/apps/code/src/renderer/features/inbox/stores/inboxSignalsFilterStore.ts +++ b/apps/code/src/renderer/features/inbox/stores/inboxSignalsFilterStore.ts @@ -51,7 +51,12 @@ interface InboxSignalsFilterActions { toggleSourceProduct: (source: SourceProduct) => void; toggleSuggestedReviewer: (reviewerUuid: string) => void; setSuggestedReviewerFilter: (reviewerUuids: string[]) => void; - markSuggestedReviewerFilterInitialized: () => void; + /** + * Seed the reviewer filter with the current user on first inbox visit. + * No-op if already initialized, or if the user has actively chosen reviewers. + * Always flips the initialized flag so we don't override later user choices. + */ + seedSuggestedReviewerFilterWithCurrentUser: (currentUserUuid: string) => void; /** Reset all filters when a deep link arrives so the linked report isn't hidden. */ resetFilters: () => void; } @@ -100,8 +105,17 @@ export const useInboxSignalsFilterStore = create()( set({ suggestedReviewerFilter: Array.from(new Set(reviewerUuids)), }), - markSuggestedReviewerFilterInitialized: () => - set({ hasInitializedSuggestedReviewerFilter: true }), + seedSuggestedReviewerFilterWithCurrentUser: (currentUserUuid) => + set((state) => { + if (state.hasInitializedSuggestedReviewerFilter) return {}; + return { + hasInitializedSuggestedReviewerFilter: true, + suggestedReviewerFilter: + state.suggestedReviewerFilter.length === 0 + ? [currentUserUuid] + : state.suggestedReviewerFilter, + }; + }), resetFilters: () => set({ searchQuery: "",