History

fix grouped project menu actions

Julius Marminge committed 3 months ago

Commit ebb4fa01 parent 2e484a21 file · +68 −48Browse files

Unified · Split

apps/web/src/components/SidebarV2.tsx+68−48
@@ -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"; · 51 unmodified lines
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() { · 825 unmodified lines
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() { · 690 unmodified lines
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) => {