Skip to content

Commit ebb4fa0

Browse files
juliusmarmingecodex
andcommitted
fix grouped project menu actions
Co-authored-by: codex <[email protected]>
1 parent 2e484a2 commit ebb4fa0

1 file changed

Lines changed: 68 additions & 48 deletions

File tree

‎apps/web/src/components/SidebarV2.tsx‎

Lines changed: 68 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
import { autoAnimate } from "@formkit/auto-animate";
22
import { useAtomValue } from "@effect/atom-react";
33
import { effectiveSettled } from "@t3tools/client-runtime/state/thread-settled";
4-
import type {
5-
EnvironmentProject,
6-
EnvironmentThreadShell,
7-
} from "@t3tools/client-runtime/state/models";
4+
import type { EnvironmentThreadShell } from "@t3tools/client-runtime/state/models";
85
import {
96
scopeProjectRef,
107
scopeThreadRef,
@@ -62,7 +59,10 @@ import { isMacPlatform } from "~/lib/utils";
6259
import { useOpenPrLink } from "../lib/openPullRequestLink";
6360
import { readLocalApi } from "../localApi";
6461
import { getProjectOrderKey, selectProjectGroupingSettings } from "../logicalProject";
65-
import { buildSidebarProjectSnapshots } from "../sidebarProjectGrouping";
62+
import {
63+
buildSidebarProjectSnapshots,
64+
type SidebarProjectSnapshot,
65+
} from "../sidebarProjectGrouping";
6666
import { legacyProjectCwdPreferenceKey, useUiStateStore } from "../uiStateStore";
6767
import { useThreadSelectionStore } from "../threadSelectionStore";
6868
import { useThreadActions } from "../hooks/useThreadActions";
@@ -894,66 +894,86 @@ export default function SidebarV2() {
894894
}, [clearSelection, projectScopeKey]);
895895

896896
const handleRemoveProject = useCallback(
897-
async (project: EnvironmentProject) => {
897+
async (projectGroup: SidebarProjectSnapshot) => {
898898
const api = readLocalApi();
899899
if (!api) return;
900900

901-
const projectThreads = threads.filter(
902-
(thread) =>
903-
thread.environmentId === project.environmentId && thread.projectId === project.id,
901+
const memberKeys = new Set(
902+
projectGroup.memberProjectRefs.map(
903+
(projectRef) => `${projectRef.environmentId}:${projectRef.projectId}`,
904+
),
905+
);
906+
const projectThreads = threads.filter((thread) =>
907+
memberKeys.has(`${thread.environmentId}:${thread.projectId}`),
904908
);
905909
const confirmed = await settlePromise(() =>
906910
api.dialogs.confirm(
907911
projectThreads.length > 0
908912
? [
909-
`Remove project "${project.title}" and delete its ${projectThreads.length} thread${projectThreads.length === 1 ? "" : "s"}?`,
910-
`Path: ${project.workspaceRoot}`,
913+
`Remove project "${projectGroup.displayName}" and delete its ${projectThreads.length} thread${projectThreads.length === 1 ? "" : "s"}?`,
914+
...(projectGroup.memberProjects.length === 1
915+
? [`Path: ${projectGroup.workspaceRoot}`]
916+
: [
917+
`This removes ${projectGroup.memberProjects.length} grouped project entries.`,
918+
]),
911919
"This permanently clears conversation history for those threads.",
912-
"This removes only the project entry, not the files on disk.",
920+
"This removes only the project entries, not the files on disk.",
913921
"This action cannot be undone.",
914922
].join("\n")
915923
: [
916-
`Remove project "${project.title}"?`,
917-
`Path: ${project.workspaceRoot}`,
918-
"This removes only the project entry, not the files on disk.",
924+
`Remove project "${projectGroup.displayName}"?`,
925+
...(projectGroup.memberProjects.length === 1
926+
? [`Path: ${projectGroup.workspaceRoot}`]
927+
: [
928+
`This removes ${projectGroup.memberProjects.length} grouped project entries.`,
929+
]),
930+
"This removes only the project entries, not the files on disk.",
919931
].join("\n"),
920932
),
921933
);
922934
if (confirmed._tag === "Failure" || !confirmed.value) return;
923935

924-
const projectRef = scopeProjectRef(project.environmentId, project.id);
925-
const result = await deleteProject({
926-
environmentId: project.environmentId,
927-
input: {
928-
projectId: project.id,
929-
...(projectThreads.length > 0 ? { force: true } : {}),
930-
},
931-
});
932-
if (result._tag === "Failure") {
933-
if (!isAtomCommandInterrupted(result)) {
934-
const error = squashAtomCommandFailure(result);
935-
toastManager.add(
936-
stackedThreadToast({
937-
type: "error",
938-
title: `Failed to remove "${project.title}"`,
939-
description: error instanceof Error ? error.message : "An error occurred.",
940-
}),
941-
);
936+
const draftStore = useComposerDraftStore.getState();
937+
let shouldNavigate = false;
938+
for (const project of projectGroup.memberProjects) {
939+
const memberThreads = projectThreads.filter(
940+
(thread) =>
941+
thread.environmentId === project.environmentId && thread.projectId === project.id,
942+
);
943+
const projectRef = scopeProjectRef(project.environmentId, project.id);
944+
const projectDraftThread = draftStore.getDraftThreadByProjectRef(projectRef);
945+
shouldNavigate ||= shouldNavigateAfterProjectRemoval({
946+
routeTarget: routeTargetRef.current,
947+
projectThreads: memberThreads,
948+
projectDraftId: projectDraftThread?.draftId ?? null,
949+
});
950+
951+
const result = await deleteProject({
952+
environmentId: project.environmentId,
953+
input: {
954+
projectId: project.id,
955+
...(memberThreads.length > 0 ? { force: true } : {}),
956+
},
957+
});
958+
if (result._tag === "Failure") {
959+
if (!isAtomCommandInterrupted(result)) {
960+
const error = squashAtomCommandFailure(result);
961+
toastManager.add(
962+
stackedThreadToast({
963+
type: "error",
964+
title: `Failed to remove "${project.title}"`,
965+
description: error instanceof Error ? error.message : "An error occurred.",
966+
}),
967+
);
968+
}
969+
return;
942970
}
943-
return;
944-
}
945971

946-
const draftStore = useComposerDraftStore.getState();
947-
const projectDraftThread = draftStore.getDraftThreadByProjectRef(projectRef);
948-
const shouldNavigate = shouldNavigateAfterProjectRemoval({
949-
routeTarget: routeTargetRef.current,
950-
projectThreads,
951-
projectDraftId: projectDraftThread?.draftId ?? null,
952-
});
953-
if (projectDraftThread) {
954-
draftStore.clearDraftThread(projectDraftThread.draftId);
972+
if (projectDraftThread) {
973+
draftStore.clearDraftThread(projectDraftThread.draftId);
974+
}
975+
draftStore.clearProjectDraftThreadId(projectRef);
955976
}
956-
draftStore.clearProjectDraftThreadId(projectRef);
957977

958978
if (shouldNavigate) {
959979
void router.navigate({ to: "/" });
@@ -1650,11 +1670,11 @@ export default function SidebarV2() {
16501670
cwd={project.workspaceRoot}
16511671
className="size-4 shrink-0"
16521672
/>
1653-
<span className="min-w-0 truncate text-sm">{project.title}</span>
1673+
<span className="min-w-0 truncate text-sm">{project.displayName}</span>
16541674
<button
16551675
type="button"
1656-
aria-label={`Remove project ${project.title}`}
1657-
title={`Remove ${project.title}`}
1676+
aria-label={`Remove project ${project.displayName}`}
1677+
title={`Remove ${project.displayName}`}
16581678
className="ml-auto inline-flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-md text-muted-foreground/55 outline-none transition-colors hover:bg-destructive/12 hover:text-destructive focus-visible:bg-destructive/12 focus-visible:text-destructive focus-visible:ring-2 focus-visible:ring-destructive/40"
16591679
onPointerDown={(event) => event.stopPropagation()}
16601680
onClick={(event) => {

0 commit comments

Comments
 (0)