From 243da6dc34450bdbde35b4dde47ab4de845b1cbc Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Mon, 1 Dec 2025 16:00:23 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=A4=96=20feat:=20show=20deletion=20feedba?= =?UTF-8?q?ck=20in=20workspace=20sidebar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a workspace is being deleted: - Track deletingWorkspaceIds state in ProjectSidebar - Pass isDeleting prop to WorkspaceListItem - Dim the row (opacity-50) and disable interaction (pointer-events-none) - Show '🗑️ Deleting...' status message in place of normal status Works for both regular deletion and force deletion flows. --- src/browser/components/ProjectSidebar.tsx | 70 ++++++++++++++------ src/browser/components/WorkspaceListItem.tsx | 14 +++- 2 files changed, 60 insertions(+), 24 deletions(-) diff --git a/src/browser/components/ProjectSidebar.tsx b/src/browser/components/ProjectSidebar.tsx index 938ff073f8..11bd0eddce 100644 --- a/src/browser/components/ProjectSidebar.tsx +++ b/src/browser/components/ProjectSidebar.tsx @@ -211,6 +211,7 @@ const ProjectSidebarInner: React.FC = ({ const [expandedOldWorkspaces, setExpandedOldWorkspaces] = usePersistedState< Record >("expandedOldWorkspaces", {}); + const [deletingWorkspaceIds, setDeletingWorkspaceIds] = useState>(new Set()); const [removeError, setRemoveError] = useState<{ workspaceId: string; error: string; @@ -289,22 +290,34 @@ const ProjectSidebarInner: React.FC = ({ const handleRemoveWorkspace = useCallback( async (workspaceId: string, buttonElement: HTMLElement) => { - const result = await onRemoveWorkspace(workspaceId); - if (!result.success) { - const error = result.error ?? "Failed to remove workspace"; - const rect = buttonElement.getBoundingClientRect(); - const anchor = { - top: rect.top + window.scrollY, - left: rect.right + 10, // 10px to the right of button - }; - - // Show force delete modal on any error to handle all cases - // (uncommitted changes, submodules, etc.) - setForceDeleteModal({ - isOpen: true, - workspaceId, - error, - anchor, + // Mark workspace as being deleted for UI feedback + setDeletingWorkspaceIds((prev) => new Set(prev).add(workspaceId)); + + try { + const result = await onRemoveWorkspace(workspaceId); + if (!result.success) { + const error = result.error ?? "Failed to remove workspace"; + const rect = buttonElement.getBoundingClientRect(); + const anchor = { + top: rect.top + window.scrollY, + left: rect.right + 10, // 10px to the right of button + }; + + // Show force delete modal on any error to handle all cases + // (uncommitted changes, submodules, etc.) + setForceDeleteModal({ + isOpen: true, + workspaceId, + error, + anchor, + }); + } + } finally { + // Clear deleting state (workspace removed or error shown) + setDeletingWorkspaceIds((prev) => { + const next = new Set(prev); + next.delete(workspaceId); + return next; }); } }, @@ -326,13 +339,25 @@ const ProjectSidebarInner: React.FC = ({ // Close modal immediately to show that action is in progress setForceDeleteModal(null); - // Use the same state update logic as regular removal - const result = await onRemoveWorkspace(workspaceId, { force: true }); - if (!result.success) { - const errorMessage = result.error ?? "Failed to remove workspace"; - console.error("Force delete failed:", result.error); + // Mark workspace as being deleted for UI feedback + setDeletingWorkspaceIds((prev) => new Set(prev).add(workspaceId)); + + try { + // Use the same state update logic as regular removal + const result = await onRemoveWorkspace(workspaceId, { force: true }); + if (!result.success) { + const errorMessage = result.error ?? "Failed to remove workspace"; + console.error("Force delete failed:", result.error); - showRemoveError(workspaceId, errorMessage, modalState?.anchor ?? undefined); + showRemoveError(workspaceId, errorMessage, modalState?.anchor ?? undefined); + } + } finally { + // Clear deleting state + setDeletingWorkspaceIds((prev) => { + const next = new Set(prev); + next.delete(workspaceId); + return next; + }); } }; @@ -572,6 +597,7 @@ const ProjectSidebarInner: React.FC = ({ projectPath={projectPath} projectName={projectName} isSelected={selectedWorkspace?.workspaceId === metadata.id} + isDeleting={deletingWorkspaceIds.has(metadata.id)} lastReadTimestamp={lastReadTimestamps[metadata.id] ?? 0} onSelectWorkspace={onSelectWorkspace} onRemoveWorkspace={handleRemoveWorkspace} diff --git a/src/browser/components/WorkspaceListItem.tsx b/src/browser/components/WorkspaceListItem.tsx index bb8e17db41..8a702e00bd 100644 --- a/src/browser/components/WorkspaceListItem.tsx +++ b/src/browser/components/WorkspaceListItem.tsx @@ -23,6 +23,7 @@ export interface WorkspaceListItemProps { projectPath: string; projectName: string; isSelected: boolean; + isDeleting?: boolean; lastReadTimestamp: number; // Event handlers onSelectWorkspace: (selection: WorkspaceSelection) => void; @@ -35,6 +36,7 @@ const WorkspaceListItemInner: React.FC = ({ projectPath, projectName, isSelected, + isDeleting, lastReadTimestamp, onSelectWorkspace, onRemoveWorkspace, @@ -104,7 +106,8 @@ const WorkspaceListItemInner: React.FC = ({
onSelectWorkspace({ @@ -199,7 +202,14 @@ const WorkspaceListItemInner: React.FC = ({
- + {isDeleting ? ( +
+ 🗑️ + Deleting... +
+ ) : ( + + )}