Gate workspace_update emissions to prevent unnecessary sidebar re-renders

Server: remove activityAt computation from workspace descriptors (always
sends null for backwards compat) and deduplicate emissions in
emitWorkspaceUpdatesForWorkspaceIds using fast-deep-equal — workspace
updates are now only sent when the descriptor actually changed.

Client: remove activityAt from WorkspaceDescriptor, sidebar entry types,
and baseline sort comparators. Workspace/project ordering is fully
handled by the persisted sidebar order store.
This commit is contained in:
Mohamed Boudra
2026-04-12 12:10:14 +07:00
parent b9269df3e4
commit 0d340959d7
12 changed files with 29 additions and 99 deletions
@@ -15,7 +15,7 @@ function item(key: string): OrderedItem {
}
function workspace(
input: Pick<WorkspaceDescriptor, "id" | "projectId" | "name" | "status" | "activityAt"> &
input: Pick<WorkspaceDescriptor, "id" | "projectId" | "name" | "status"> &
Partial<
Pick<
WorkspaceDescriptor,
@@ -32,7 +32,6 @@ function workspace(
workspaceKind: input.workspaceKind ?? "local_checkout",
name: input.name,
status: input.status,
activityAt: input.activityAt,
diffStat: null,
};
}
@@ -100,7 +99,6 @@ describe("buildSidebarProjectsFromWorkspaces", () => {
projectId: "project-1",
name: "feat/hard-cut",
status: "failed",
activityAt: new Date("2026-01-01T00:00:00.000Z"),
}),
];
@@ -117,21 +115,19 @@ describe("buildSidebarProjectsFromWorkspaces", () => {
expect(projects[0]?.workspaces[0]?.statusBucket).toBe("failed");
});
it("preserves stored project order even when activity changes", () => {
it("preserves stored project order even when input order differs", () => {
const initialWorkspaces: WorkspaceDescriptor[] = [
workspace({
id: "/repo/b",
projectId: "project-b",
name: "feat/b",
status: "running",
activityAt: new Date("2026-01-02T00:00:00.000Z"),
}),
workspace({
id: "/repo/a",
projectId: "project-a",
name: "feat/a",
status: "running",
activityAt: new Date("2026-01-01T00:00:00.000Z"),
}),
];
@@ -148,19 +144,17 @@ describe("buildSidebarProjectsFromWorkspaces", () => {
const updatedProjects = buildSidebarProjectsFromWorkspaces({
serverId: "srv",
workspaces: [
workspace({
id: "/repo/b",
projectId: "project-b",
name: "feat/b",
status: "running",
activityAt: new Date("2026-01-02T00:00:00.000Z"),
}),
workspace({
id: "/repo/a",
projectId: "project-a",
name: "feat/a",
status: "running",
activityAt: new Date("2026-01-03T00:00:00.000Z"),
}),
workspace({
id: "/repo/b",
projectId: "project-b",
name: "feat/b",
status: "running",
}),
],
projectOrder: seededOrder,
@@ -168,8 +162,8 @@ describe("buildSidebarProjectsFromWorkspaces", () => {
});
expect(updatedProjects.map((project) => project.projectKey)).toEqual([
"project-b",
"project-a",
"project-b",
]);
});
@@ -182,21 +176,18 @@ describe("buildSidebarProjectsFromWorkspaces", () => {
projectId: "project-c",
name: "feat/c",
status: "running",
activityAt: new Date("2026-01-04T00:00:00.000Z"),
}),
workspace({
id: "/repo/b",
projectId: "project-b",
name: "feat/b",
status: "running",
activityAt: new Date("2026-01-02T00:00:00.000Z"),
}),
workspace({
id: "/repo/a",
projectId: "project-a",
name: "feat/a",
status: "running",
activityAt: new Date("2026-01-01T00:00:00.000Z"),
}),
],
projectOrder: ["project-b", "project-a", "project-c"],
@@ -219,14 +210,12 @@ describe("buildSidebarProjectsFromWorkspaces", () => {
projectId: "project-1",
name: "main",
status: "running",
activityAt: new Date("2026-01-02T00:00:00.000Z"),
}),
workspace({
id: "/repo/feature",
projectId: "project-1",
name: "feature",
status: "running",
activityAt: new Date("2026-01-01T00:00:00.000Z"),
}),
],
projectOrder: ["project-1"],
@@ -246,14 +235,12 @@ describe("buildSidebarProjectsFromWorkspaces", () => {
projectId: "project-1",
name: "main",
status: "running",
activityAt: new Date("2026-01-02T00:00:00.000Z"),
}),
workspace({
id: "/repo/feature",
projectId: "project-1",
name: "feature",
status: "running",
activityAt: new Date("2026-01-03T00:00:00.000Z"),
}),
],
projectOrder: ["project-1"],
@@ -263,8 +250,8 @@ describe("buildSidebarProjectsFromWorkspaces", () => {
});
expect(projects[0]?.workspaces.map((workspace) => workspace.workspaceId)).toEqual([
"/repo/main",
"/repo/feature",
"/repo/main",
]);
});
});