183 lines · 5.8 KB · TypeScript
RawDownload
import { useCallback, useEffect, useMemo } from "react"; import type { EnvironmentId, OrchestrationCheckpointSummary, ThreadId } from "@t3tools/contracts"; import { useCheckpointDiff } from "../../state/queries";import { useEnvironmentQuery } from "../../state/query";import { reviewEnvironment } from "../../state/review";import { useSelectedThreadDetail } from "../../state/use-thread-detail";import { useSelectedThreadWorktree } from "../../state/use-selected-thread-worktree";import {  buildReviewSectionItems,  getDefaultReviewSectionId,  getReadyReviewCheckpoints,  getReviewSectionIdForCheckpoint,} from "./reviewModel";import {  setReviewAsyncError,  setReviewGitSections,  setReviewSelectedSectionId,  setReviewTurnDiff,  setReviewTurnDiffLoading,  type ReviewCacheForThread,} from "./reviewState"; export function useReviewSections(input: {  readonly enabled?: boolean;  readonly environmentId?: EnvironmentId;  readonly threadId?: ThreadId;  readonly reviewCache: ReviewCacheForThread;}) {  const { environmentId, reviewCache, threadId } = input;  const enabled = input.enabled ?? true;  const selectedThread = useSelectedThreadDetail();  const { selectedThreadCwd } = useSelectedThreadWorktree();  const diffPreview = useEnvironmentQuery(    enabled && environmentId !== undefined && selectedThreadCwd !== null      ? reviewEnvironment.diffPreview({          environmentId,          input: { cwd: selectedThreadCwd },        })      : null,  );  const { loadingTurnIds } = reviewCache.asyncState;   useEffect(() => {    if (reviewCache.threadKey && diffPreview.data) {      setReviewGitSections(reviewCache.threadKey, diffPreview.data.sources);    }  }, [diffPreview.data, reviewCache.threadKey]);   const readyCheckpoints = useMemo(    () => getReadyReviewCheckpoints(selectedThread?.checkpoints ?? []),    [selectedThread?.checkpoints],  );  const checkpointBySectionId = useMemo(    () =>      Object.fromEntries(        readyCheckpoints.map((checkpoint) => [          getReviewSectionIdForCheckpoint(checkpoint),          checkpoint,        ]),      ) as Record<string, OrchestrationCheckpointSummary>,    [readyCheckpoints],  );  const reviewSections = useMemo(    () =>      buildReviewSectionItems({        checkpoints: readyCheckpoints,        gitSections: reviewCache.gitSections,        turnDiffById: reviewCache.turnDiffById,        loadingTurnIds,        loadingGitSections: diffPreview.isPending,      }),    [      diffPreview.isPending,      loadingTurnIds,      readyCheckpoints,      reviewCache.gitSections,      reviewCache.turnDiffById,    ],  );  const selectedSection = useMemo(    () =>      reviewSections.find((section) => section.id === reviewCache.selectedSectionId) ??      reviewSections[0] ??      null,    [reviewCache.selectedSectionId, reviewSections],  );  const fallbackSectionId = useMemo(    () => getDefaultReviewSectionId(reviewSections),    [reviewSections],  );  const selectedSectionIdExists = useMemo(    () =>      reviewCache.selectedSectionId        ? reviewSections.some((section) => section.id === reviewCache.selectedSectionId)        : false,    [reviewCache.selectedSectionId, reviewSections],  );   useEffect(() => {    if (      reviewSections.length > 0 &&      reviewCache.threadKey &&      (!reviewCache.selectedSectionId || !selectedSectionIdExists)    ) {      setReviewSelectedSectionId(reviewCache.threadKey, fallbackSectionId);    }  }, [    fallbackSectionId,    reviewCache.selectedSectionId,    reviewCache.threadKey,    reviewSections.length,    selectedSectionIdExists,  ]);   let activeCheckpoint = readyCheckpoints[0] ?? null;  if (selectedSection?.kind === "turn") {    activeCheckpoint = checkpointBySectionId[selectedSection.id] ?? activeCheckpoint;  }  const activeSectionId = activeCheckpoint    ? getReviewSectionIdForCheckpoint(activeCheckpoint)    : null;  const activeTurnDiff = useCheckpointDiff({    environmentId: enabled ? (environmentId ?? null) : null,    threadId: enabled ? (threadId ?? null) : null,    fromTurnCount:      enabled && activeCheckpoint ? Math.max(0, activeCheckpoint.checkpointTurnCount - 1) : null,    toTurnCount: enabled ? (activeCheckpoint?.checkpointTurnCount ?? null) : null,    ignoreWhitespace: false,  });   useEffect(() => {    if (!reviewCache.threadKey || !activeSectionId) {      return;    }    setReviewTurnDiffLoading(reviewCache.threadKey, activeSectionId, activeTurnDiff.isPending);  }, [activeSectionId, activeTurnDiff.isPending, reviewCache.threadKey]);   useEffect(() => {    if (!reviewCache.threadKey || !activeSectionId || !activeTurnDiff.data) {      return;    }    setReviewTurnDiff(reviewCache.threadKey, activeSectionId, activeTurnDiff.data.diff);    setReviewAsyncError(reviewCache.threadKey, null);  }, [activeSectionId, activeTurnDiff.data, reviewCache.threadKey]);   useEffect(() => {    if (reviewCache.threadKey && activeTurnDiff.error) {      setReviewAsyncError(reviewCache.threadKey, activeTurnDiff.error);    }  }, [activeTurnDiff.error, reviewCache.threadKey]);   const refreshSelectedSection = useCallback(async () => {    if (!enabled) {      return;    }    if (selectedSection?.kind === "turn") {      activeTurnDiff.refresh();      return;    }    diffPreview.refresh();  }, [activeTurnDiff, diffPreview, enabled, selectedSection?.kind]);   const selectSection = useCallback(    (sectionId: string) => {      if (reviewCache.threadKey) {        setReviewSelectedSectionId(reviewCache.threadKey, sectionId);      }    },    [reviewCache.threadKey],  );   return {    error: diffPreview.error ?? activeTurnDiff.error ?? reviewCache.asyncState.error,    loadingGitDiffs: diffPreview.isPending,    loadingTurnIds,    reviewSections,    selectedSection,    refreshSelectedSection,    selectSection,  };}