diff --git a/apps/code/snapshots.yml b/apps/code/snapshots.yml index cfbdd290b1..26cb8c4587 100644 --- a/apps/code/snapshots.yml +++ b/apps/code/snapshots.yml @@ -27,7 +27,7 @@ snapshots: archive-archivedtasksview--many-tasks--dark: hash: v1.k4693efd2.40d24f07a24c6400c32ae68b39fd908a056beb5dc6df8bcb237ec2ab2b494f4a.l2vjfSQDalCPRtDNV7pbfTKGlMsQoI81jI-UdJfLHWE archive-archivedtasksview--many-tasks--light: - hash: v1.k4693efd2.52ec9963bfe6f248ffcdaa4c26945d2b7e303115825b65d978aa5aefd4af1007.ZLtOfTZUrznhYeB-N2SIHzQWRzF6TDSaipNkfd8uzjM + hash: v1.k4693efd2.74c25b303262f2d4c0f22890d351e76f482827548548f7c023bc309327c927b8.DP9VVrvBz1naIJMwHORkfqm69BlO785ulOt4o6zFs94 archive-archivedtasksview--mixed-modes--dark: hash: v1.k4693efd2.d94039b8cc17a4ad1b720364f41fee58a4843aa9a901443997ba62602f698794.441LWZhT-2WQZJHni4LoOgQsOSr2O103NZOk1pF8ME4 archive-archivedtasksview--mixed-modes--light: diff --git a/packages/core/src/code-review/reviewShellGeometry.test.ts b/packages/core/src/code-review/reviewShellGeometry.test.ts index 75838ac479..a671fa7fbd 100644 --- a/packages/core/src/code-review/reviewShellGeometry.test.ts +++ b/packages/core/src/code-review/reviewShellGeometry.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it } from "vitest"; import { buildItemIndex, + findNextScrollKey, getDeferredMessage, splitFilePath, sumHunkStats, @@ -45,6 +46,18 @@ describe("buildItemIndex", () => { }); }); +describe("findNextScrollKey", () => { + it.each([ + ["a", "b"], + ["b", null], + ["missing", null], + ])("finds the next file after %s", (currentKey, expected) => { + const items = [{ scrollKey: "a" }, {}, { scrollKey: "b" }, {}]; + + expect(findNextScrollKey(items, currentKey)).toBe(expected); + }); +}); + describe("getDeferredMessage", () => { it("returns the line-limit message", () => { expect(getDeferredMessage("line-limit")).toContain("5,000-line"); diff --git a/packages/core/src/code-review/reviewShellGeometry.ts b/packages/core/src/code-review/reviewShellGeometry.ts index 02bcae15bb..77df74087a 100644 --- a/packages/core/src/code-review/reviewShellGeometry.ts +++ b/packages/core/src/code-review/reviewShellGeometry.ts @@ -37,6 +37,20 @@ export function buildItemIndex( return index; } +export function findNextScrollKey( + items: { scrollKey?: string }[], + currentKey: string, +): string | null { + const currentIndex = items.findIndex((item) => item.scrollKey === currentKey); + if (currentIndex === -1) return null; + + for (let i = currentIndex + 1; i < items.length; i++) { + const key = items[i].scrollKey; + if (key) return key; + } + return null; +} + export function getDeferredMessage(reason: DeferredReason): string { switch (reason) { case "line-limit": diff --git a/packages/ui/src/features/code-review/components/ReviewShell.tsx b/packages/ui/src/features/code-review/components/ReviewShell.tsx index 3e03271bfc..45e32b71ef 100644 --- a/packages/ui/src/features/code-review/components/ReviewShell.tsx +++ b/packages/ui/src/features/code-review/components/ReviewShell.tsx @@ -1,4 +1,5 @@ import { WorkerPoolContextProvider } from "@pierre/diffs/react"; +import { findNextScrollKey } from "@posthog/core/code-review/reviewShellGeometry"; import { useService } from "@posthog/di/react"; import type { Task } from "@posthog/shared/domain-types"; import { useArchivedTaskIds } from "@posthog/ui/features/archive/useArchivedTaskIds"; @@ -187,26 +188,40 @@ export function ReviewShell({ if (prState === "merged") clearTasks([taskId]); }, [prState, taskId, clearTasks]); - const viewedContextValue = useMemo( - () => ({ - viewedRecord, - currentSignatures, - toggleViewed: onToggleViewed, - }), - [viewedRecord, currentSignatures, onToggleViewed], - ); - const scrollRequest = useReviewNavigationStore( (s) => s.scrollRequests[taskId] ?? null, ); const clearScrollRequest = useReviewNavigationStore( (s) => s.clearScrollRequest, ); + const requestScrollToFile = useReviewNavigationStore( + (s) => s.requestScrollToFile, + ); const setActiveFilePath = useReviewNavigationStore( (s) => s.setActiveFilePath, ); const clearTask = useReviewNavigationStore((s) => s.clearTask); + const toggleViewed = useCallback( + (key: string, signature: string | null) => { + onToggleViewed(key, signature); + if (signature === null) return; + + const nextKey = findNextScrollKey(items, key); + if (nextKey) requestScrollToFile(taskId, nextKey); + }, + [items, onToggleViewed, requestScrollToFile, taskId], + ); + + const viewedContextValue = useMemo( + () => ({ + viewedRecord, + currentSignatures, + toggleViewed, + }), + [viewedRecord, currentSignatures, toggleViewed], + ); + useEffect(() => { return () => { if (navigationFrameRef.current !== null) {