mirror of
https://github.com/getpaseo/paseo.git
synced 2026-08-15 04:42:45 +00:00
Preserve workspace diff stats across rehydration
This commit is contained in:
@@ -33,6 +33,7 @@ import {
|
|||||||
type Agent,
|
type Agent,
|
||||||
type SessionState,
|
type SessionState,
|
||||||
type WorkspaceDescriptor,
|
type WorkspaceDescriptor,
|
||||||
|
mergeWorkspaceSnapshotWithExisting,
|
||||||
normalizeWorkspaceDescriptor,
|
normalizeWorkspaceDescriptor,
|
||||||
} from "@/stores/session-store";
|
} from "@/stores/session-store";
|
||||||
import { useDraftStore } from "@/stores/draft-store";
|
import { useDraftStore } from "@/stores/draft-store";
|
||||||
@@ -326,6 +327,7 @@ function SessionProviderInternal({ children, serverId, client }: SessionProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
const workspaces = new Map<string, WorkspaceDescriptor>();
|
const workspaces = new Map<string, WorkspaceDescriptor>();
|
||||||
|
const existingWorkspaces = useSessionStore.getState().sessions[serverId]?.workspaces;
|
||||||
let cursor: string | null = null;
|
let cursor: string | null = null;
|
||||||
let includeSubscribe = options?.subscribe ?? false;
|
let includeSubscribe = options?.subscribe ?? false;
|
||||||
|
|
||||||
@@ -341,7 +343,13 @@ function SessionProviderInternal({ children, serverId, client }: SessionProvider
|
|||||||
|
|
||||||
for (const entry of payload.entries) {
|
for (const entry of payload.entries) {
|
||||||
const workspace = normalizeWorkspaceDescriptor(entry);
|
const workspace = normalizeWorkspaceDescriptor(entry);
|
||||||
workspaces.set(workspace.id, workspace);
|
workspaces.set(
|
||||||
|
workspace.id,
|
||||||
|
mergeWorkspaceSnapshotWithExisting({
|
||||||
|
incoming: workspace,
|
||||||
|
existing: existingWorkspaces?.get(workspace.id),
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!payload.pageInfo.hasMore || !payload.pageInfo.nextCursor) {
|
if (!payload.pageInfo.hasMore || !payload.pageInfo.nextCursor) {
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
import { useCallback, useEffect, useMemo, useSyncExternalStore } from "react";
|
import { useCallback, useEffect, useMemo, useSyncExternalStore } from "react";
|
||||||
import { normalizeWorkspaceDescriptor, useSessionStore } from "@/stores/session-store";
|
import {
|
||||||
|
mergeWorkspaceSnapshotWithExisting,
|
||||||
|
normalizeWorkspaceDescriptor,
|
||||||
|
useSessionStore,
|
||||||
|
} from "@/stores/session-store";
|
||||||
import { getHostRuntimeStore } from "@/runtime/host-runtime";
|
import { getHostRuntimeStore } from "@/runtime/host-runtime";
|
||||||
import { useSidebarOrderStore } from "@/stores/sidebar-order-store";
|
import { useSidebarOrderStore } from "@/stores/sidebar-order-store";
|
||||||
import type { WorkspaceDescriptor } from "@/stores/session-store";
|
import type { WorkspaceDescriptor } from "@/stores/session-store";
|
||||||
@@ -356,6 +360,7 @@ export function useSidebarWorkspacesList(options?: {
|
|||||||
}
|
}
|
||||||
void (async () => {
|
void (async () => {
|
||||||
const next = new Map<string, WorkspaceDescriptor>();
|
const next = new Map<string, WorkspaceDescriptor>();
|
||||||
|
const existingWorkspaces = useSessionStore.getState().sessions[serverId]?.workspaces;
|
||||||
let cursor: string | null = null;
|
let cursor: string | null = null;
|
||||||
try {
|
try {
|
||||||
while (true) {
|
while (true) {
|
||||||
@@ -365,7 +370,13 @@ export function useSidebarWorkspacesList(options?: {
|
|||||||
});
|
});
|
||||||
for (const entry of payload.entries) {
|
for (const entry of payload.entries) {
|
||||||
const workspace = toWorkspaceDescriptor(entry);
|
const workspace = toWorkspaceDescriptor(entry);
|
||||||
next.set(workspace.id, workspace);
|
next.set(
|
||||||
|
workspace.id,
|
||||||
|
mergeWorkspaceSnapshotWithExisting({
|
||||||
|
incoming: workspace,
|
||||||
|
existing: existingWorkspaces?.get(workspace.id),
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (!payload.pageInfo.hasMore || !payload.pageInfo.nextCursor) {
|
if (!payload.pageInfo.hasMore || !payload.pageInfo.nextCursor) {
|
||||||
break;
|
break;
|
||||||
|
|||||||
53
packages/app/src/stores/session-store.test.ts
Normal file
53
packages/app/src/stores/session-store.test.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import {
|
||||||
|
mergeWorkspaceSnapshotWithExisting,
|
||||||
|
type WorkspaceDescriptor,
|
||||||
|
} from "./session-store";
|
||||||
|
|
||||||
|
function createWorkspace(
|
||||||
|
input: Partial<WorkspaceDescriptor> & Pick<WorkspaceDescriptor, "id">,
|
||||||
|
): WorkspaceDescriptor {
|
||||||
|
return {
|
||||||
|
id: input.id,
|
||||||
|
projectId: input.projectId ?? "remote:github.com/getpaseo/paseo",
|
||||||
|
projectDisplayName: input.projectDisplayName ?? "getpaseo/paseo",
|
||||||
|
projectRootPath: input.projectRootPath ?? "/tmp/repo",
|
||||||
|
projectKind: input.projectKind ?? "git",
|
||||||
|
workspaceKind: input.workspaceKind ?? "local_checkout",
|
||||||
|
name: input.name ?? "main",
|
||||||
|
status: input.status ?? "done",
|
||||||
|
activityAt: input.activityAt ?? null,
|
||||||
|
diffStat: input.diffStat ?? null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("mergeWorkspaceSnapshotWithExisting", () => {
|
||||||
|
it("preserves the last known diff stat when a snapshot only has baseline null data", () => {
|
||||||
|
const existing = createWorkspace({
|
||||||
|
id: "/tmp/repo",
|
||||||
|
diffStat: { additions: 4, deletions: 2 },
|
||||||
|
});
|
||||||
|
const incoming = createWorkspace({
|
||||||
|
id: "/tmp/repo",
|
||||||
|
diffStat: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mergeWorkspaceSnapshotWithExisting({ incoming, existing })).toEqual({
|
||||||
|
...incoming,
|
||||||
|
diffStat: { additions: 4, deletions: 2 },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uses the incoming diff stat when the server provides a known value", () => {
|
||||||
|
const existing = createWorkspace({
|
||||||
|
id: "/tmp/repo",
|
||||||
|
diffStat: { additions: 4, deletions: 2 },
|
||||||
|
});
|
||||||
|
const incoming = createWorkspace({
|
||||||
|
id: "/tmp/repo",
|
||||||
|
diffStat: { additions: 0, deletions: 0 },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mergeWorkspaceSnapshotWithExisting({ incoming, existing })).toEqual(incoming);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -142,6 +142,21 @@ export function normalizeWorkspaceDescriptor(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function mergeWorkspaceSnapshotWithExisting(input: {
|
||||||
|
incoming: WorkspaceDescriptor;
|
||||||
|
existing?: WorkspaceDescriptor | null;
|
||||||
|
}): WorkspaceDescriptor {
|
||||||
|
const { incoming, existing } = input;
|
||||||
|
if (!existing || existing.id !== incoming.id) {
|
||||||
|
return incoming;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...incoming,
|
||||||
|
diffStat: incoming.diffStat ?? existing.diffStat,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export type ExplorerEntryKind = "file" | "directory";
|
export type ExplorerEntryKind = "file" | "directory";
|
||||||
export type ExplorerFileKind = "text" | "image" | "binary";
|
export type ExplorerFileKind = "text" | "image" | "binary";
|
||||||
export type ExplorerEncoding = "utf-8" | "base64" | "none";
|
export type ExplorerEncoding = "utf-8" | "base64" | "none";
|
||||||
|
|||||||
Reference in New Issue
Block a user