* perf(app): retain inactive workspace screens App-wide routes cleared the active workspace selection, which removed the retained workspace deck from the React tree and made returning rebuild every panel. Keep mounted deck entries inactive across those routes and suspend inactive panel subscriptions so state is preserved without background work. * perf(app): suspend remaining inactive queries Retained composers and commit diff panels still subscribed through query hooks that did not receive panel activity. Propagate the existing activity gate so app-wide routes leave those hidden surfaces dormant. * refactor(app): separate panel activity from connectivity A hidden retained panel is still connected to its host. Keep connectivity truthful, pause work inside query and stream owners, and preserve terminal emulator identity while its stream is detached. * test(app): target active retained controls * fix(app): retain compact and terminal route content * fix(app): hide inactive workspace overlays
121 lines
4.2 KiB
TypeScript
121 lines
4.2 KiB
TypeScript
import { useMemo } from "react";
|
|
import type { CheckoutCommitFile, ParsedDiffFile } from "@getpaseo/protocol/messages";
|
|
import { useRetainedPanelActive } from "@/components/retained-panel";
|
|
import { useFetchQueries } from "@/data/query";
|
|
import { checkoutCommitFileDiffQueryKey, COMMIT_FILE_DIFF_STALE_TIME } from "@/git/query-keys";
|
|
import { useCheckoutCommitsQuery } from "@/git/use-commits-query";
|
|
import { useHostRuntimeClient, useHostRuntimeIsConnected } from "@/runtime/host-runtime";
|
|
|
|
/**
|
|
* Context needed to resolve a commit diff against a host: which daemon
|
|
* (`serverId`), which checkout (`cwd`), and which commit (`sha`). `enabled`
|
|
* lets the consumer pause all fetching (e.g. an inactive tab).
|
|
*/
|
|
export interface CommitDiffFilesContext {
|
|
serverId: string;
|
|
cwd: string;
|
|
sha: string;
|
|
enabled?: boolean;
|
|
}
|
|
|
|
export interface CommitDiffFilesResult {
|
|
files: ParsedDiffFile[];
|
|
isLoading: boolean;
|
|
error: Error | null;
|
|
capabilityMissing: boolean;
|
|
}
|
|
|
|
export function resolveCommitDiffFile(
|
|
file: CheckoutCommitFile,
|
|
resolved: ParsedDiffFile | null | undefined,
|
|
): ParsedDiffFile | null {
|
|
if (resolved !== undefined && resolved !== null) {
|
|
return resolved;
|
|
}
|
|
if (resolved === undefined) {
|
|
return null;
|
|
}
|
|
return {
|
|
path: file.path,
|
|
isNew: file.status === "added",
|
|
isDeleted: file.status === "deleted",
|
|
additions: file.additions,
|
|
deletions: file.deletions,
|
|
hunks: [],
|
|
status: "binary",
|
|
};
|
|
}
|
|
|
|
export function resolveCommitDiffFiles(
|
|
files: CheckoutCommitFile[],
|
|
resolvedByPath: ReadonlyMap<string, ParsedDiffFile | null | undefined>,
|
|
): ParsedDiffFile[] {
|
|
return files.flatMap((file) => {
|
|
const resolved = resolveCommitDiffFile(file, resolvedByPath.get(file.path));
|
|
return resolved ? [resolved] : [];
|
|
});
|
|
}
|
|
|
|
export function useCommitDiffFiles(ctx: CommitDiffFilesContext): CommitDiffFilesResult {
|
|
const { serverId, cwd, sha, enabled = true } = ctx;
|
|
const retainedPanelActive = useRetainedPanelActive();
|
|
const queryEnabled = enabled && retainedPanelActive;
|
|
const client = useHostRuntimeClient(serverId);
|
|
const isConnected = useHostRuntimeIsConnected(serverId);
|
|
const commitsQuery = useCheckoutCommitsQuery({ serverId, cwd, enabled: queryEnabled });
|
|
const commitsData = commitsQuery.status === "loaded" ? commitsQuery.data : null;
|
|
const commitFiles = useMemo(() => {
|
|
if (!sha || !commitsData) {
|
|
return [];
|
|
}
|
|
return commitsData.commits.find((commit) => commit.sha === sha)?.files ?? [];
|
|
}, [commitsData, sha]);
|
|
|
|
const fileDiffsEnabled =
|
|
queryEnabled &&
|
|
commitsQuery.status === "loaded" &&
|
|
Boolean(cwd) &&
|
|
Boolean(sha) &&
|
|
Boolean(client) &&
|
|
isConnected;
|
|
const fileDiffResults = useFetchQueries(
|
|
commitFiles.map((file) => ({
|
|
queryKey: checkoutCommitFileDiffQueryKey(serverId, cwd, sha, file.path),
|
|
queryFn: async (): Promise<{ file: ParsedDiffFile | null }> => {
|
|
if (!client) {
|
|
throw new Error("Host disconnected");
|
|
}
|
|
return client.getCommitFileDiff(cwd, sha, file.path);
|
|
},
|
|
enabled: fileDiffsEnabled,
|
|
staleTimeMs: COMMIT_FILE_DIFF_STALE_TIME,
|
|
dataShape: "value" as const,
|
|
})),
|
|
);
|
|
const commitsLoading = commitsQuery.status === "connecting" || commitsQuery.status === "loading";
|
|
const commitsError = commitsQuery.status === "error" ? commitsQuery.error : null;
|
|
const capabilityMissing = commitsQuery.status === "unsupported";
|
|
|
|
return useMemo<CommitDiffFilesResult>(() => {
|
|
const resolvedByPath = new Map<string, ParsedDiffFile | null | undefined>();
|
|
commitFiles.forEach((file, index) => {
|
|
const fileResult = fileDiffResults[index];
|
|
resolvedByPath.set(file.path, fileResult?.data ? fileResult.data.file : undefined);
|
|
});
|
|
const files = resolveCommitDiffFiles(commitFiles, resolvedByPath);
|
|
let firstFileError: Error | null = null;
|
|
for (const fileResult of fileDiffResults) {
|
|
if (fileResult.error) {
|
|
firstFileError = fileResult.error;
|
|
break;
|
|
}
|
|
}
|
|
return {
|
|
files,
|
|
isLoading: commitsLoading || fileDiffResults.some((r) => r.isLoading),
|
|
error: commitsError ?? firstFileError,
|
|
capabilityMissing,
|
|
};
|
|
}, [capabilityMissing, commitFiles, commitsError, commitsLoading, fileDiffResults]);
|
|
}
|