Keep selected projects when switching hosts (#2700)

* fix(app): preserve project selection across hosts

Rendered project identity and persisted equivalence were conflated after duplicate-project handling. Keep them separate so host changes resolve selectable equivalents deterministically without resetting legacy sidebar identity.

* fix(app): restore selected clone after host round trip

Keep manual origin and current destination placements separately so switching back restores the chosen checkout while archive gaps preserve the active host selection.

* fix(app): key status icons by project view

* fix(app): preserve origin during archive gaps
This commit is contained in:
Mohamed Boudra
2026-07-31 15:42:08 +02:00
committed by GitHub
parent 87ef631ac4
commit 99eca19a76
56 changed files with 1234 additions and 428 deletions
+12 -12
View File
@@ -27,7 +27,7 @@ export interface ProjectHostEntry {
}
export interface ProjectSummary {
projectKey: string;
viewKey: string;
projectName: string;
projectCustomName?: string | null;
hosts: ProjectHostEntry[];
@@ -91,7 +91,7 @@ interface HostGroup {
}
interface ProjectGroup {
projectKey: string;
viewKey: string;
projectName: string;
projectCustomName: string | null;
hostsByServerId: Map<string, HostGroup>;
@@ -99,10 +99,10 @@ interface ProjectGroup {
function findProjectMetadata(
host: ProjectHost,
projectKey: string,
projectId: string,
): { customName: string | null; displayName: string } | null {
for (const project of host.projects) {
if (project.projectId === projectKey) {
if (project.projectId === projectId) {
return {
customName: project.projectCustomName ?? null,
displayName: project.projectDisplayName,
@@ -172,7 +172,7 @@ function toProjectSummary(draft: ProjectGroup): ProjectSummary {
const totalWorkspaceCount = hosts.reduce((sum, host) => sum + host.workspaceCount, 0);
const onlineHostCount = hosts.filter((host) => host.isOnline).length;
return {
projectKey: draft.projectKey,
viewKey: draft.viewKey,
projectName: draft.projectName,
projectCustomName: draft.projectCustomName,
hosts,
@@ -196,15 +196,15 @@ function addHostProjects(
if (!placement) continue;
const projectId = placement.projectId;
const customName = findProjectMetadata(host, projectId);
let group = groups.get(hostProject.projectKey);
let group = groups.get(hostProject.viewKey);
if (!group) {
group = {
projectKey: hostProject.projectKey,
viewKey: hostProject.viewKey,
projectName: customName?.displayName ?? hostProject.projectName,
projectCustomName: customName?.customName ?? null,
hostsByServerId: new Map(),
};
groups.set(hostProject.projectKey, group);
groups.set(hostProject.viewKey, group);
} else if (customName?.customName && !group.projectCustomName) {
group.projectCustomName = customName.customName;
group.projectName = customName.displayName;
@@ -230,16 +230,16 @@ function attachHostWorkspaces(
host: ProjectHost,
hostProjects: HostProjectListItem[],
): void {
const projectKeyByProjectId = new Map(
const projectViewKeyByProjectId = new Map(
hostProjects.flatMap((project) =>
project.hosts
.filter((placement) => placement.serverId === host.serverId && placement.projectId)
.map((placement) => [placement.projectId!, project.projectKey] as const),
.map((placement) => [placement.projectId!, project.viewKey] as const),
),
);
for (const workspace of host.workspaces) {
const key = projectKeyByProjectId.get(workspace.projectId);
const key = projectViewKeyByProjectId.get(workspace.projectId);
if (key) groups.get(key)?.hostsByServerId.get(host.serverId)?.workspaces.push(workspace);
}
}
@@ -262,7 +262,7 @@ export function buildProjects(input: BuildProjectsInput): BuildProjectsResult {
if (name !== 0) {
return name;
}
return left.projectKey.localeCompare(right.projectKey);
return left.viewKey.localeCompare(right.viewKey);
});
return { projects };