Use useMutation for worktree creation with loading spinner on mobile
Move worktree creation into a useMutation in ProjectHeaderRow so each row owns its own pending state. Show ActivityIndicator on the + button while the request is in flight, and always show the + button on mobile breakpoints where hover isn't available.
This commit is contained in:
@@ -77,7 +77,6 @@ interface SidebarSharedProps {
|
|||||||
collapsedProjectKeys: SidebarShortcutModel["collapsedProjectKeys"];
|
collapsedProjectKeys: SidebarShortcutModel["collapsedProjectKeys"];
|
||||||
shortcutIndexByWorkspaceKey: SidebarShortcutModel["shortcutIndexByWorkspaceKey"];
|
shortcutIndexByWorkspaceKey: SidebarShortcutModel["shortcutIndexByWorkspaceKey"];
|
||||||
toggleProjectCollapsed: SidebarShortcutModel["toggleProjectCollapsed"];
|
toggleProjectCollapsed: SidebarShortcutModel["toggleProjectCollapsed"];
|
||||||
setProjectCollapsed: SidebarShortcutModel["setProjectCollapsed"];
|
|
||||||
handleRefresh: () => void;
|
handleRefresh: () => void;
|
||||||
handleHostSelect: (nextServerId: string) => void;
|
handleHostSelect: (nextServerId: string) => void;
|
||||||
handleOpenProject: () => void;
|
handleOpenProject: () => void;
|
||||||
@@ -184,7 +183,6 @@ export const LeftSidebar = memo(function LeftSidebar({
|
|||||||
collapsedProjectKeys,
|
collapsedProjectKeys,
|
||||||
shortcutIndexByWorkspaceKey,
|
shortcutIndexByWorkspaceKey,
|
||||||
toggleProjectCollapsed,
|
toggleProjectCollapsed,
|
||||||
setProjectCollapsed,
|
|
||||||
} = useSidebarShortcutModel(projects);
|
} = useSidebarShortcutModel(projects);
|
||||||
|
|
||||||
const [isManualRefresh, setIsManualRefresh] = useState(false);
|
const [isManualRefresh, setIsManualRefresh] = useState(false);
|
||||||
@@ -261,7 +259,6 @@ export const LeftSidebar = memo(function LeftSidebar({
|
|||||||
collapsedProjectKeys,
|
collapsedProjectKeys,
|
||||||
shortcutIndexByWorkspaceKey,
|
shortcutIndexByWorkspaceKey,
|
||||||
toggleProjectCollapsed,
|
toggleProjectCollapsed,
|
||||||
setProjectCollapsed,
|
|
||||||
handleRefresh,
|
handleRefresh,
|
||||||
handleHostSelect,
|
handleHostSelect,
|
||||||
};
|
};
|
||||||
@@ -346,7 +343,6 @@ function MobileSidebar({
|
|||||||
collapsedProjectKeys,
|
collapsedProjectKeys,
|
||||||
shortcutIndexByWorkspaceKey,
|
shortcutIndexByWorkspaceKey,
|
||||||
toggleProjectCollapsed,
|
toggleProjectCollapsed,
|
||||||
setProjectCollapsed,
|
|
||||||
handleRefresh,
|
handleRefresh,
|
||||||
handleHostSelect,
|
handleHostSelect,
|
||||||
handleOpenProject,
|
handleOpenProject,
|
||||||
@@ -515,7 +511,7 @@ function MobileSidebar({
|
|||||||
serverId={activeServerId}
|
serverId={activeServerId}
|
||||||
collapsedProjectKeys={collapsedProjectKeys}
|
collapsedProjectKeys={collapsedProjectKeys}
|
||||||
onToggleProjectCollapsed={toggleProjectCollapsed}
|
onToggleProjectCollapsed={toggleProjectCollapsed}
|
||||||
onSetProjectCollapsed={setProjectCollapsed}
|
|
||||||
shortcutIndexByWorkspaceKey={shortcutIndexByWorkspaceKey}
|
shortcutIndexByWorkspaceKey={shortcutIndexByWorkspaceKey}
|
||||||
projects={projects}
|
projects={projects}
|
||||||
isRefreshing={isManualRefresh && isRevalidating}
|
isRefreshing={isManualRefresh && isRevalidating}
|
||||||
@@ -623,7 +619,6 @@ function DesktopSidebar({
|
|||||||
collapsedProjectKeys,
|
collapsedProjectKeys,
|
||||||
shortcutIndexByWorkspaceKey,
|
shortcutIndexByWorkspaceKey,
|
||||||
toggleProjectCollapsed,
|
toggleProjectCollapsed,
|
||||||
setProjectCollapsed,
|
|
||||||
handleRefresh,
|
handleRefresh,
|
||||||
handleHostSelect,
|
handleHostSelect,
|
||||||
handleOpenProject,
|
handleOpenProject,
|
||||||
@@ -660,7 +655,6 @@ function DesktopSidebar({
|
|||||||
serverId={activeServerId}
|
serverId={activeServerId}
|
||||||
collapsedProjectKeys={collapsedProjectKeys}
|
collapsedProjectKeys={collapsedProjectKeys}
|
||||||
onToggleProjectCollapsed={toggleProjectCollapsed}
|
onToggleProjectCollapsed={toggleProjectCollapsed}
|
||||||
onSetProjectCollapsed={setProjectCollapsed}
|
|
||||||
shortcutIndexByWorkspaceKey={shortcutIndexByWorkspaceKey}
|
shortcutIndexByWorkspaceKey={shortcutIndexByWorkspaceKey}
|
||||||
projects={projects}
|
projects={projects}
|
||||||
isRefreshing={isManualRefresh && isRevalidating}
|
isRefreshing={isManualRefresh && isRevalidating}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
type GestureResponderEvent,
|
type GestureResponderEvent,
|
||||||
} from "react-native";
|
} from "react-native";
|
||||||
import * as Haptics from "expo-haptics";
|
import * as Haptics from "expo-haptics";
|
||||||
import { useQueries } from "@tanstack/react-query";
|
import { useMutation, useQueries } from "@tanstack/react-query";
|
||||||
import {
|
import {
|
||||||
useCallback,
|
useCallback,
|
||||||
useMemo,
|
useMemo,
|
||||||
@@ -97,7 +97,6 @@ interface SidebarWorkspaceListProps {
|
|||||||
serverId: string | null;
|
serverId: string | null;
|
||||||
collapsedProjectKeys: ReadonlySet<string>;
|
collapsedProjectKeys: ReadonlySet<string>;
|
||||||
onToggleProjectCollapsed: (projectKey: string) => void;
|
onToggleProjectCollapsed: (projectKey: string) => void;
|
||||||
onSetProjectCollapsed: (projectKey: string, collapsed: boolean) => void;
|
|
||||||
shortcutIndexByWorkspaceKey: Map<string, number>;
|
shortcutIndexByWorkspaceKey: Map<string, number>;
|
||||||
isRefreshing?: boolean;
|
isRefreshing?: boolean;
|
||||||
onRefresh?: () => void;
|
onRefresh?: () => void;
|
||||||
@@ -115,7 +114,10 @@ interface ProjectHeaderRowProps {
|
|||||||
selected?: boolean;
|
selected?: boolean;
|
||||||
chevron: "expand" | "collapse" | null;
|
chevron: "expand" | "collapse" | null;
|
||||||
onPress: () => void;
|
onPress: () => void;
|
||||||
onCreateWorktree?: () => void;
|
serverId: string | null;
|
||||||
|
canCreateWorktree: boolean;
|
||||||
|
onWorkspacePress?: () => void;
|
||||||
|
onWorktreeCreated?: (workspaceId: string) => void;
|
||||||
shortcutNumber?: number | null;
|
shortcutNumber?: number | null;
|
||||||
showShortcutBadge?: boolean;
|
showShortcutBadge?: boolean;
|
||||||
drag: () => void;
|
drag: () => void;
|
||||||
@@ -263,11 +265,13 @@ function NewWorktreeButton({
|
|||||||
displayName,
|
displayName,
|
||||||
onPress,
|
onPress,
|
||||||
visible,
|
visible,
|
||||||
|
loading = false,
|
||||||
testID,
|
testID,
|
||||||
}: {
|
}: {
|
||||||
displayName: string;
|
displayName: string;
|
||||||
onPress: () => void;
|
onPress: () => void;
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
|
loading?: boolean;
|
||||||
testID: string;
|
testID: string;
|
||||||
}) {
|
}) {
|
||||||
const { theme } = useUnistyles();
|
const { theme } = useUnistyles();
|
||||||
@@ -280,22 +284,29 @@ function NewWorktreeButton({
|
|||||||
style={({ hovered, pressed }) => [
|
style={({ hovered, pressed }) => [
|
||||||
styles.projectIconActionButton,
|
styles.projectIconActionButton,
|
||||||
!visible && styles.projectIconActionButtonHidden,
|
!visible && styles.projectIconActionButtonHidden,
|
||||||
(hovered || pressed) && styles.projectIconActionButtonHovered,
|
(hovered || pressed) && !loading && styles.projectIconActionButtonHovered,
|
||||||
]}
|
]}
|
||||||
onPress={(event) => {
|
onPress={(event) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
onPress();
|
onPress();
|
||||||
}}
|
}}
|
||||||
|
disabled={loading}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel={`Create a new worktree for ${displayName}`}
|
accessibilityLabel={`Create a new worktree for ${displayName}`}
|
||||||
testID={testID}
|
testID={testID}
|
||||||
>
|
>
|
||||||
{({ hovered, pressed }) => (
|
{({ hovered, pressed }) =>
|
||||||
<Plus
|
loading ? (
|
||||||
size={14}
|
<ActivityIndicator size={14} color={theme.colors.foregroundMuted} />
|
||||||
color={hovered || pressed ? theme.colors.foreground : theme.colors.foregroundMuted}
|
) : (
|
||||||
/>
|
<Plus
|
||||||
)}
|
size={14}
|
||||||
|
color={
|
||||||
|
hovered || pressed ? theme.colors.foreground : theme.colors.foregroundMuted
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
</Pressable>
|
</Pressable>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent side="bottom" align="end" offset={8}>
|
<TooltipContent side="bottom" align="end" offset={8}>
|
||||||
@@ -531,7 +542,10 @@ function ProjectHeaderRow({
|
|||||||
selected = false,
|
selected = false,
|
||||||
chevron,
|
chevron,
|
||||||
onPress,
|
onPress,
|
||||||
onCreateWorktree,
|
serverId,
|
||||||
|
canCreateWorktree,
|
||||||
|
onWorkspacePress,
|
||||||
|
onWorktreeCreated,
|
||||||
shortcutNumber = null,
|
shortcutNumber = null,
|
||||||
showShortcutBadge = false,
|
showShortcutBadge = false,
|
||||||
drag,
|
drag,
|
||||||
@@ -541,6 +555,45 @@ function ProjectHeaderRow({
|
|||||||
dragHandleProps,
|
dragHandleProps,
|
||||||
}: ProjectHeaderRowProps) {
|
}: ProjectHeaderRowProps) {
|
||||||
const [isHovered, setIsHovered] = useState(false);
|
const [isHovered, setIsHovered] = useState(false);
|
||||||
|
const isMobileBreakpoint =
|
||||||
|
UnistylesRuntime.breakpoint === "xs" || UnistylesRuntime.breakpoint === "sm";
|
||||||
|
const mergeWorkspaces = useSessionStore((state) => state.mergeWorkspaces);
|
||||||
|
const toast = useToast();
|
||||||
|
|
||||||
|
const createWorktreeMutation = useMutation({
|
||||||
|
mutationFn: async () => {
|
||||||
|
if (!serverId) {
|
||||||
|
throw new Error("No server");
|
||||||
|
}
|
||||||
|
const client = getHostRuntimeStore().getClient(serverId);
|
||||||
|
if (!client || !isHostRuntimeConnected(getHostRuntimeStore().getSnapshot(serverId))) {
|
||||||
|
throw new Error("Host is not connected");
|
||||||
|
}
|
||||||
|
const payload = await client.createPaseoWorktree({
|
||||||
|
cwd: project.iconWorkingDir,
|
||||||
|
worktreeSlug: createNameId(),
|
||||||
|
});
|
||||||
|
if (payload.error || !payload.workspace) {
|
||||||
|
throw new Error(payload.error ?? "Failed to create worktree");
|
||||||
|
}
|
||||||
|
return payload.workspace;
|
||||||
|
},
|
||||||
|
onSuccess: (workspace) => {
|
||||||
|
mergeWorkspaces(serverId!, [normalizeWorkspaceDescriptor(workspace)]);
|
||||||
|
onWorktreeCreated?.(workspace.id);
|
||||||
|
onWorkspacePress?.();
|
||||||
|
router.navigate(
|
||||||
|
prepareWorkspaceTab({
|
||||||
|
serverId: serverId!,
|
||||||
|
workspaceId: workspace.id,
|
||||||
|
target: { kind: "draft", draftId: "new" },
|
||||||
|
}) as any,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
toast.error(error instanceof Error ? error.message : String(error));
|
||||||
|
},
|
||||||
|
});
|
||||||
const interaction = useLongPressDragInteraction({
|
const interaction = useLongPressDragInteraction({
|
||||||
drag,
|
drag,
|
||||||
menuController,
|
menuController,
|
||||||
@@ -577,11 +630,12 @@ function ProjectHeaderRow({
|
|||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
{onCreateWorktree ? (
|
{canCreateWorktree ? (
|
||||||
<NewWorktreeButton
|
<NewWorktreeButton
|
||||||
displayName={displayName}
|
displayName={displayName}
|
||||||
onPress={onCreateWorktree}
|
onPress={() => createWorktreeMutation.mutate()}
|
||||||
visible={isHovered}
|
visible={isHovered || isMobileBreakpoint}
|
||||||
|
loading={createWorktreeMutation.isPending}
|
||||||
testID={`sidebar-project-new-worktree-${project.projectKey}`}
|
testID={`sidebar-project-new-worktree-${project.projectKey}`}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
@@ -1064,6 +1118,8 @@ function NonGitProjectRowWithMenuContent({
|
|||||||
selected={selected}
|
selected={selected}
|
||||||
chevron={null}
|
chevron={null}
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
|
serverId={null}
|
||||||
|
canCreateWorktree={false}
|
||||||
shortcutNumber={shortcutNumber}
|
shortcutNumber={shortcutNumber}
|
||||||
showShortcutBadge={showShortcutBadge}
|
showShortcutBadge={showShortcutBadge}
|
||||||
drag={drag}
|
drag={drag}
|
||||||
@@ -1118,7 +1174,9 @@ function FlattenedProjectRow({
|
|||||||
iconDataUri,
|
iconDataUri,
|
||||||
rowModel,
|
rowModel,
|
||||||
onPress,
|
onPress,
|
||||||
onCreateWorktree,
|
serverId,
|
||||||
|
onWorkspacePress,
|
||||||
|
onWorktreeCreated,
|
||||||
shortcutNumber,
|
shortcutNumber,
|
||||||
showShortcutBadge,
|
showShortcutBadge,
|
||||||
drag,
|
drag,
|
||||||
@@ -1130,7 +1188,9 @@ function FlattenedProjectRow({
|
|||||||
iconDataUri: string | null;
|
iconDataUri: string | null;
|
||||||
rowModel: Extract<ReturnType<typeof buildSidebarProjectRowModel>, { kind: "workspace_link" }>;
|
rowModel: Extract<ReturnType<typeof buildSidebarProjectRowModel>, { kind: "workspace_link" }>;
|
||||||
onPress: () => void;
|
onPress: () => void;
|
||||||
onCreateWorktree?: () => void;
|
serverId: string | null;
|
||||||
|
onWorkspacePress?: () => void;
|
||||||
|
onWorktreeCreated?: (workspaceId: string) => void;
|
||||||
shortcutNumber: number | null;
|
shortcutNumber: number | null;
|
||||||
showShortcutBadge: boolean;
|
showShortcutBadge: boolean;
|
||||||
drag: () => void;
|
drag: () => void;
|
||||||
@@ -1164,7 +1224,10 @@ function FlattenedProjectRow({
|
|||||||
selected={rowModel.selected}
|
selected={rowModel.selected}
|
||||||
chevron={rowModel.chevron}
|
chevron={rowModel.chevron}
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
onCreateWorktree={rowModel.trailingAction === "new_worktree" ? onCreateWorktree : undefined}
|
serverId={serverId}
|
||||||
|
canCreateWorktree={rowModel.trailingAction === "new_worktree"}
|
||||||
|
onWorkspacePress={onWorkspacePress}
|
||||||
|
onWorktreeCreated={onWorktreeCreated}
|
||||||
shortcutNumber={shortcutNumber}
|
shortcutNumber={shortcutNumber}
|
||||||
showShortcutBadge={showShortcutBadge}
|
showShortcutBadge={showShortcutBadge}
|
||||||
drag={drag}
|
drag={drag}
|
||||||
@@ -1227,7 +1290,7 @@ function ProjectBlock({
|
|||||||
onToggleCollapsed,
|
onToggleCollapsed,
|
||||||
onWorkspacePress,
|
onWorkspacePress,
|
||||||
onWorkspaceReorder,
|
onWorkspaceReorder,
|
||||||
onCreateWorktree,
|
onWorktreeCreated,
|
||||||
drag,
|
drag,
|
||||||
isDragging,
|
isDragging,
|
||||||
dragHandleProps,
|
dragHandleProps,
|
||||||
@@ -1246,7 +1309,7 @@ function ProjectBlock({
|
|||||||
onToggleCollapsed: () => void;
|
onToggleCollapsed: () => void;
|
||||||
onWorkspacePress?: () => void;
|
onWorkspacePress?: () => void;
|
||||||
onWorkspaceReorder: (projectKey: string, workspaces: SidebarWorkspaceEntry[]) => void;
|
onWorkspaceReorder: (projectKey: string, workspaces: SidebarWorkspaceEntry[]) => void;
|
||||||
onCreateWorktree?: (project: SidebarProjectEntry) => void;
|
onWorktreeCreated?: (workspaceId: string) => void;
|
||||||
drag: () => void;
|
drag: () => void;
|
||||||
isDragging: boolean;
|
isDragging: boolean;
|
||||||
dragHandleProps?: DraggableListDragHandleProps;
|
dragHandleProps?: DraggableListDragHandleProps;
|
||||||
@@ -1348,11 +1411,9 @@ function ProjectBlock({
|
|||||||
onWorkspacePress?.();
|
onWorkspacePress?.();
|
||||||
navigateToWorkspace(serverId, rowModel.workspace.workspaceId);
|
navigateToWorkspace(serverId, rowModel.workspace.workspaceId);
|
||||||
}}
|
}}
|
||||||
onCreateWorktree={
|
serverId={serverId}
|
||||||
rowModel.trailingAction === "new_worktree" && onCreateWorktree
|
onWorkspacePress={onWorkspacePress}
|
||||||
? () => onCreateWorktree(project)
|
onWorktreeCreated={onWorktreeCreated}
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
shortcutNumber={shortcutIndexByWorkspaceKey.get(rowModel.workspace.workspaceKey) ?? null}
|
shortcutNumber={shortcutIndexByWorkspaceKey.get(rowModel.workspace.workspaceKey) ?? null}
|
||||||
showShortcutBadge={showShortcutBadges}
|
showShortcutBadge={showShortcutBadges}
|
||||||
drag={drag}
|
drag={drag}
|
||||||
@@ -1369,11 +1430,10 @@ function ProjectBlock({
|
|||||||
selected={false}
|
selected={false}
|
||||||
chevron={rowModel.chevron}
|
chevron={rowModel.chevron}
|
||||||
onPress={onToggleCollapsed}
|
onPress={onToggleCollapsed}
|
||||||
onCreateWorktree={
|
serverId={serverId}
|
||||||
rowModel.trailingAction === "new_worktree" && onCreateWorktree
|
canCreateWorktree={rowModel.trailingAction === "new_worktree"}
|
||||||
? () => onCreateWorktree(project)
|
onWorkspacePress={onWorkspacePress}
|
||||||
: undefined
|
onWorktreeCreated={onWorktreeCreated}
|
||||||
}
|
|
||||||
drag={drag}
|
drag={drag}
|
||||||
isDragging={isDragging}
|
isDragging={isDragging}
|
||||||
menuController={null}
|
menuController={null}
|
||||||
@@ -1405,7 +1465,6 @@ export function SidebarWorkspaceList({
|
|||||||
serverId,
|
serverId,
|
||||||
collapsedProjectKeys,
|
collapsedProjectKeys,
|
||||||
onToggleProjectCollapsed,
|
onToggleProjectCollapsed,
|
||||||
onSetProjectCollapsed,
|
|
||||||
shortcutIndexByWorkspaceKey,
|
shortcutIndexByWorkspaceKey,
|
||||||
isRefreshing = false,
|
isRefreshing = false,
|
||||||
onRefresh,
|
onRefresh,
|
||||||
@@ -1417,9 +1476,6 @@ export function SidebarWorkspaceList({
|
|||||||
const isNative = Platform.OS !== "web";
|
const isNative = Platform.OS !== "web";
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const activeWorkspaceSelection = useNavigationActiveWorkspaceSelection();
|
const activeWorkspaceSelection = useNavigationActiveWorkspaceSelection();
|
||||||
const toast = useToast();
|
|
||||||
const mergeWorkspaces = useSessionStore((state) => state.mergeWorkspaces);
|
|
||||||
const [creatingProjectKey, setCreatingProjectKey] = useState<string | null>(null);
|
|
||||||
const [creatingWorkspaceIds, setCreatingWorkspaceIds] = useState<Set<string>>(() => new Set());
|
const [creatingWorkspaceIds, setCreatingWorkspaceIds] = useState<Set<string>>(() => new Set());
|
||||||
const creatingWorkspaceTimeoutsRef = useRef<Map<string, ReturnType<typeof setTimeout>>>(
|
const creatingWorkspaceTimeoutsRef = useRef<Map<string, ReturnType<typeof setTimeout>>>(
|
||||||
new Map(),
|
new Map(),
|
||||||
@@ -1608,68 +1664,33 @@ export function SidebarWorkspaceList({
|
|||||||
[getWorkspaceOrder, serverId, setWorkspaceOrder],
|
[getWorkspaceOrder, serverId, setWorkspaceOrder],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleCreateWorktree = useCallback(
|
const handleWorktreeCreated = useCallback(
|
||||||
async (project: SidebarProjectEntry) => {
|
(workspaceId: string) => {
|
||||||
if (!serverId || project.projectKind !== "git") {
|
setCreatingWorkspaceIds((current) => {
|
||||||
return;
|
const next = new Set(current);
|
||||||
}
|
next.add(workspaceId);
|
||||||
if (creatingProjectKey) {
|
return next;
|
||||||
return;
|
});
|
||||||
}
|
const existingTimeout = creatingWorkspaceTimeoutsRef.current.get(workspaceId);
|
||||||
onSetProjectCollapsed(project.projectKey, false);
|
if (existingTimeout) {
|
||||||
setCreatingProjectKey(project.projectKey);
|
clearTimeout(existingTimeout);
|
||||||
try {
|
|
||||||
const client = getHostRuntimeStore().getClient(serverId);
|
|
||||||
if (!client || !isHostRuntimeConnected(getHostRuntimeStore().getSnapshot(serverId))) {
|
|
||||||
throw new Error("Host is not connected");
|
|
||||||
}
|
|
||||||
const payload = await client.createPaseoWorktree({
|
|
||||||
cwd: project.iconWorkingDir,
|
|
||||||
worktreeSlug: createNameId(),
|
|
||||||
});
|
|
||||||
if (payload.error || !payload.workspace) {
|
|
||||||
throw new Error(payload.error ?? "Failed to create worktree");
|
|
||||||
}
|
|
||||||
const workspace = payload.workspace;
|
|
||||||
mergeWorkspaces(serverId, [normalizeWorkspaceDescriptor(workspace)]);
|
|
||||||
setCreatingWorkspaceIds((current) => {
|
|
||||||
const next = new Set(current);
|
|
||||||
next.add(workspace.id);
|
|
||||||
return next;
|
|
||||||
});
|
|
||||||
const existingTimeout = creatingWorkspaceTimeoutsRef.current.get(workspace.id);
|
|
||||||
if (existingTimeout) {
|
|
||||||
clearTimeout(existingTimeout);
|
|
||||||
}
|
|
||||||
creatingWorkspaceTimeoutsRef.current.set(
|
|
||||||
workspace.id,
|
|
||||||
setTimeout(() => {
|
|
||||||
creatingWorkspaceTimeoutsRef.current.delete(workspace.id);
|
|
||||||
setCreatingWorkspaceIds((current) => {
|
|
||||||
if (!current.has(workspace.id)) {
|
|
||||||
return current;
|
|
||||||
}
|
|
||||||
const next = new Set(current);
|
|
||||||
next.delete(workspace.id);
|
|
||||||
return next;
|
|
||||||
});
|
|
||||||
}, 3000),
|
|
||||||
);
|
|
||||||
onWorkspacePress?.();
|
|
||||||
router.navigate(
|
|
||||||
prepareWorkspaceTab({
|
|
||||||
serverId,
|
|
||||||
workspaceId: workspace.id,
|
|
||||||
target: { kind: "draft", draftId: "new" },
|
|
||||||
}) as any,
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
toast.error(error instanceof Error ? error.message : String(error));
|
|
||||||
} finally {
|
|
||||||
setCreatingProjectKey((current) => (current === project.projectKey ? null : current));
|
|
||||||
}
|
}
|
||||||
|
creatingWorkspaceTimeoutsRef.current.set(
|
||||||
|
workspaceId,
|
||||||
|
setTimeout(() => {
|
||||||
|
creatingWorkspaceTimeoutsRef.current.delete(workspaceId);
|
||||||
|
setCreatingWorkspaceIds((current) => {
|
||||||
|
if (!current.has(workspaceId)) {
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
const next = new Set(current);
|
||||||
|
next.delete(workspaceId);
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
}, 3000),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
[creatingProjectKey, mergeWorkspaces, onSetProjectCollapsed, onWorkspacePress, serverId, toast],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderProject = useCallback(
|
const renderProject = useCallback(
|
||||||
@@ -1688,7 +1709,7 @@ export function SidebarWorkspaceList({
|
|||||||
onToggleCollapsed={() => onToggleProjectCollapsed(item.projectKey)}
|
onToggleCollapsed={() => onToggleProjectCollapsed(item.projectKey)}
|
||||||
onWorkspacePress={onWorkspacePress}
|
onWorkspacePress={onWorkspacePress}
|
||||||
onWorkspaceReorder={handleWorkspaceReorder}
|
onWorkspaceReorder={handleWorkspaceReorder}
|
||||||
onCreateWorktree={handleCreateWorktree}
|
onWorktreeCreated={handleWorktreeCreated}
|
||||||
drag={drag}
|
drag={drag}
|
||||||
isDragging={isActive}
|
isDragging={isActive}
|
||||||
dragHandleProps={dragHandleProps}
|
dragHandleProps={dragHandleProps}
|
||||||
@@ -1700,7 +1721,7 @@ export function SidebarWorkspaceList({
|
|||||||
[
|
[
|
||||||
collapsedProjectKeys,
|
collapsedProjectKeys,
|
||||||
effectiveActiveWorkspaceSelection,
|
effectiveActiveWorkspaceSelection,
|
||||||
handleCreateWorktree,
|
handleWorktreeCreated,
|
||||||
handleWorkspaceReorder,
|
handleWorkspaceReorder,
|
||||||
onWorkspacePress,
|
onWorkspacePress,
|
||||||
onToggleProjectCollapsed,
|
onToggleProjectCollapsed,
|
||||||
|
|||||||
Reference in New Issue
Block a user