feat: use workspace ids instead of paths for workspace operations

This commit is contained in:
Haileyesus
2026-04-07 17:03:35 +03:00
parent 6589867d78
commit 8e6fc15a1d
12 changed files with 204 additions and 55 deletions

View File

@@ -57,16 +57,16 @@ export const getWorkspaceSessions = async (): Promise<WorkspaceRecord[]> => {
};
export const updateWorkspaceStar = async (
workspacePath: string,
): Promise<{ workspacePath: string; isStarred: boolean }> => {
workspaceId: string,
): Promise<{ workspaceId: string; isStarred: boolean }> => {
const response = await authenticatedFetch(SIDEBAR_ENDPOINTS.updateWorkspaceStar, {
method: 'PATCH',
body: JSON.stringify({ workspacePath }),
body: JSON.stringify({ workspaceId }),
});
const payload = await parseJsonSafely<{
success?: boolean;
data?: {
workspacePath?: string;
workspaceId?: string;
isStarred?: boolean;
};
error?: { message?: string };
@@ -80,18 +80,18 @@ export const updateWorkspaceStar = async (
}
return {
workspacePath: payload?.data?.workspacePath || workspacePath,
workspaceId: payload?.data?.workspaceId || workspaceId,
isStarred: Boolean(payload?.data?.isStarred),
};
};
export const updateWorkspaceCustomName = async (
workspacePath: string,
workspaceId: string,
workspaceCustomName: string | null,
): Promise<void> => {
const response = await authenticatedFetch(SIDEBAR_ENDPOINTS.updateWorkspaceCustomName, {
method: 'PATCH',
body: JSON.stringify({ workspacePath, workspaceCustomName }),
body: JSON.stringify({ workspaceId, workspaceCustomName }),
});
const payload = await parseJsonSafely<{ error?: { message?: string } }>(response);
@@ -103,10 +103,10 @@ export const updateWorkspaceCustomName = async (
}
};
export const deleteWorkspaceByPath = async (workspacePath: string): Promise<void> => {
export const deleteWorkspaceById = async (workspaceId: string): Promise<void> => {
const response = await authenticatedFetch(SIDEBAR_ENDPOINTS.deleteWorkspace, {
method: 'DELETE',
body: JSON.stringify({ workspacePath }),
body: JSON.stringify({ workspaceId }),
});
const payload = await parseJsonSafely<{ error?: { message?: string } }>(response);

View File

@@ -3,7 +3,7 @@ import { useLocation, useNavigate } from 'react-router-dom';
import {
deleteSessionById,
deleteWorkspaceByPath,
deleteWorkspaceById,
getWorkspaceSessions,
updateSessionCustomName,
updateWorkspaceCustomName,
@@ -95,7 +95,7 @@ export const useWorkspaces = () => {
[filteredWorkspaces],
);
const toggleWorkspace = useCallback((workspacePath: string) => {
const toggleWorkspace = useCallback((workspaceId: string, workspacePath: string) => {
setExpandedWorkspaces((previousSet) => {
const nextSet = new Set(previousSet);
@@ -107,7 +107,8 @@ export const useWorkspaces = () => {
return nextSet;
});
}, []);
navigate(`/workspace/${encodeURIComponent(workspaceId)}`);
}, [navigate]);
const openSession = useCallback(
(workspacePath: string, sessionId: string) => {
@@ -125,9 +126,9 @@ export const useWorkspaces = () => {
navigate('/');
}, [navigate]);
const toggleWorkspaceStar = useCallback(async (workspacePath: string) => {
const toggleWorkspaceStar = useCallback(async (workspaceId: string) => {
try {
await updateWorkspaceStar(workspacePath);
await updateWorkspaceStar(workspaceId);
await refreshWorkspaces();
} catch (error) {
console.error('Failed to update workspace star:', error);
@@ -149,10 +150,17 @@ export const useWorkspaces = () => {
return;
}
const editingWorkspace = workspaces.find(
(workspace) => workspace.workspaceOriginalPath === editingWorkspacePath,
);
if (!editingWorkspace) {
return;
}
setIsSavingWorkspaceName(true);
try {
const trimmedName = editingWorkspaceName.trim();
await updateWorkspaceCustomName(editingWorkspacePath, trimmedName || null);
await updateWorkspaceCustomName(editingWorkspace.workspaceId, trimmedName || null);
await refreshWorkspaces();
cancelWorkspaceRename();
} catch (error) {
@@ -165,10 +173,12 @@ export const useWorkspaces = () => {
editingWorkspaceName,
editingWorkspacePath,
refreshWorkspaces,
workspaces,
]);
const requestWorkspaceDelete = useCallback((workspace: WorkspaceRecord) => {
setWorkspaceDeleteTarget({
workspaceId: workspace.workspaceId,
workspacePath: workspace.workspaceOriginalPath,
workspaceName: getWorkspaceDisplayName(workspace),
sessionCount: workspace.sessions.length,
@@ -184,10 +194,11 @@ export const useWorkspaces = () => {
return;
}
const deletingWorkspaceId = workspaceDeleteTarget.workspaceId;
const deletingWorkspacePath = workspaceDeleteTarget.workspacePath;
setWorkspaceDeleteTarget(null);
try {
await deleteWorkspaceByPath(deletingWorkspacePath);
await deleteWorkspaceById(deletingWorkspaceId);
// If the current session belonged to the deleted workspace, reset to root.
const hadSelectedSession = workspaces.some(

View File

@@ -15,6 +15,7 @@ export type WorkspaceSession = {
};
export type WorkspaceRecord = {
workspaceId: string;
workspaceOriginalPath: string;
workspaceCustomName: string | null;
workspaceDisplayName: string;
@@ -24,6 +25,7 @@ export type WorkspaceRecord = {
};
export type WorkspaceDeleteTarget = {
workspaceId: string;
workspacePath: string;
workspaceName: string;
sessionCount: number;

View File

@@ -29,8 +29,8 @@ type SidebarWorkspaceItemProps = {
isSavingSessionName: boolean;
onEditingWorkspaceNameChange: (name: string) => void;
onEditingSessionNameChange: (name: string) => void;
onToggleWorkspace: (workspacePath: string) => void;
onToggleWorkspaceStar: (workspacePath: string) => void;
onToggleWorkspace: (workspaceId: string, workspacePath: string) => void;
onToggleWorkspaceStar: (workspaceId: string) => void;
onStartWorkspaceRename: (workspace: WorkspaceRecord) => void;
onCancelWorkspaceRename: () => void;
onSaveWorkspaceRename: () => void;
@@ -95,7 +95,7 @@ export function SidebarWorkspaceItem({
!hasSelectedSession &&
'border-yellow-200/30 bg-yellow-50/50 dark:border-yellow-800/30 dark:bg-yellow-900/5',
)}
onClick={() => onToggleWorkspace(workspace.workspaceOriginalPath)}
onClick={() => onToggleWorkspace(workspace.workspaceId, workspace.workspaceOriginalPath)}
>
<div className="flex items-center justify-between">
<div className="flex min-w-0 flex-1 items-center gap-3">
@@ -179,7 +179,7 @@ export function SidebarWorkspaceItem({
)}
onClick={(event) => {
event.stopPropagation();
onToggleWorkspaceStar(workspace.workspaceOriginalPath);
onToggleWorkspaceStar(workspace.workspaceId);
}}
title={workspace.isStarred ? 'Remove from Starred' : 'Add to Starred'}
>
@@ -236,7 +236,7 @@ export function SidebarWorkspaceItem({
!hasSelectedSession &&
'bg-yellow-50/50 hover:bg-yellow-100/50 dark:bg-yellow-900/10 dark:hover:bg-yellow-900/20',
)}
onClick={() => onToggleWorkspace(workspace.workspaceOriginalPath)}
onClick={() => onToggleWorkspace(workspace.workspaceId, workspace.workspaceOriginalPath)}
>
<div className="flex min-w-0 flex-1 items-center gap-3">
{isExpanded ? (
@@ -318,7 +318,7 @@ export function SidebarWorkspaceItem({
)}
onClick={(event) => {
event.stopPropagation();
onToggleWorkspaceStar(workspace.workspaceOriginalPath);
onToggleWorkspaceStar(workspace.workspaceId);
}}
title={workspace.isStarred ? 'Remove from Starred' : 'Add to Starred'}
>

View File

@@ -16,8 +16,8 @@ type SidebarWorkspaceListProps = {
isSavingSessionName: boolean;
onEditingWorkspaceNameChange: (name: string) => void;
onEditingSessionNameChange: (name: string) => void;
onToggleWorkspace: (workspacePath: string) => void;
onToggleWorkspaceStar: (workspacePath: string) => void;
onToggleWorkspace: (workspaceId: string, workspacePath: string) => void;
onToggleWorkspaceStar: (workspaceId: string) => void;
onStartWorkspaceRename: (workspace: WorkspaceRecord) => void;
onCancelWorkspaceRename: () => void;
onSaveWorkspaceRename: () => void;