import type { QueryClient } from "@tanstack/react-query"; import type { CheckoutStatusResponse, CheckoutStatusUpdate } from "@getpaseo/protocol/messages"; import equal from "fast-deep-equal/es6"; import { checkoutPrStatusQueryKey, checkoutStatusQueryKey, invalidatePrPaneTimelineForCheckout, } from "@/git/query-keys"; import { expireStaleDiffModeOverrides } from "@/review/store"; export type CheckoutStatusPayload = CheckoutStatusResponse["payload"]; export type CheckoutPrStatusPayload = NonNullable; export interface CheckoutStatusClient { getCheckoutStatus: (cwd: string) => Promise; } // Checkout status enters the app through exactly two doors: daemon pushes // (applyCheckoutStatusUpdateFromEvent) and query fetches (fetchCheckoutStatus). Both run // the dirty-state reactions, so they hold regardless of which screens are mounted. export async function fetchCheckoutStatus({ client, serverId, cwd, }: { client: CheckoutStatusClient; serverId: string; cwd: string; }): Promise { const payload = await client.getCheckoutStatus(cwd); expireStaleDiffModeOverrides({ serverId, cwd, isDirty: payload.isGit && payload.isDirty }); return payload; } export function applyCheckoutStatusUpdateFromEvent({ queryClient, serverId, message, }: { queryClient: QueryClient; serverId: string; message: CheckoutStatusUpdate; }): void { const { payload } = message; queryClient.setQueryData(checkoutStatusQueryKey(serverId, payload.cwd), payload); expireStaleDiffModeOverrides({ serverId, cwd: payload.cwd, isDirty: payload.isGit && payload.isDirty, }); const prStatus = payload.prStatus; if (!prStatus) { return; } const previous = queryClient.getQueryData( checkoutPrStatusQueryKey(serverId, prStatus.cwd), ); queryClient.setQueryData(checkoutPrStatusQueryKey(serverId, prStatus.cwd), prStatus); // The PR activity timeline has no push channel; mark it stale when the pushed PR status // meaningfully changed. Active panes refetch immediately, evicted ones on next mount. if (hasPrStatusChanged(previous, prStatus)) { void invalidatePrPaneTimelineForCheckout(queryClient, { serverId, cwd: prStatus.cwd }); } } // requestId changes on every emission and carries no PR state. function prStatusWithoutVolatileFields( prStatus: CheckoutPrStatusPayload, ): Omit { const { requestId: _requestId, ...rest } = prStatus; return rest; } function hasPrStatusChanged( previous: CheckoutPrStatusPayload | undefined, next: CheckoutPrStatusPayload, ): boolean { if (!previous) { return true; } return !equal(prStatusWithoutVolatileFields(previous), prStatusWithoutVolatileFields(next)); }