Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/blog/tsconfig.tsbuildinfo

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion apps/desktop/src/renderer/screens/main/MainScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export function MainScreen() {
// Worktree operations
const {
handleWorktreeCreated,
handleWorktreeCreatedWithResult,
handleUpdateWorktree,
handleCreatePR,
handleMergePR,
Expand Down Expand Up @@ -107,7 +108,7 @@ export function MainScreen() {
currentWorkspace,
setSelectedWorktreeId,
handleTabSelect,
handleWorktreeCreated,
handleWorktreeCreated: handleWorktreeCreatedWithResult,
});

return (
Expand Down
66 changes: 39 additions & 27 deletions apps/desktop/src/renderer/screens/main/hooks/useTabs.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import type React from "react";
import type { Tab, Workspace } from "shared/types";
import { findTabRecursive } from "../utils";

interface UseTabsProps {
currentWorkspace: Workspace | null;
setCurrentWorkspace: (workspace: Workspace) => void;
setCurrentWorkspace: React.Dispatch<React.SetStateAction<Workspace | null>>;
selectedWorktreeId: string | null;
setSelectedWorktreeId: (id: string | null) => void;
selectedTabId: string | null;
Expand Down Expand Up @@ -36,25 +37,28 @@ export function useTabs({
const handleTabCreated = (worktreeId: string, tab: Tab) => {
if (!currentWorkspace) return;

// Find the worktree and add the tab
const updatedWorktrees = currentWorkspace.worktrees.map((wt) => {
if (wt.id === worktreeId) {
return {
...wt,
tabs: [...wt.tabs, tab],
};
}
return wt;
});

const updatedWorkspace = {
...currentWorkspace,
worktrees: updatedWorktrees,
activeWorktreeId: worktreeId,
activeTabId: tab.id,
};
// Use functional setState to avoid stale closures
setCurrentWorkspace((prev) => {
if (!prev) return prev;

// Find the worktree and add the tab
const updatedWorktrees = prev.worktrees.map((wt) => {
if (wt.id === worktreeId) {
return {
...wt,
tabs: [...wt.tabs, tab],
};
}
return wt;
});

setCurrentWorkspace(updatedWorkspace);
return {
...prev,
worktrees: updatedWorktrees,
activeWorktreeId: worktreeId,
activeTabId: tab.id,
};
});
};

// Handle tab selection
Expand All @@ -69,10 +73,14 @@ export function useTabs({
tabId,
});

setCurrentWorkspace({
...currentWorkspace,
activeWorktreeId: worktreeId,
activeTabId: tabId,
// Use functional setState to avoid overwriting concurrent updates
setCurrentWorkspace((prev) => {
if (!prev) return prev;
return {
...prev,
activeWorktreeId: worktreeId,
activeTabId: tabId,
};
});
}
};
Expand All @@ -89,10 +97,14 @@ export function useTabs({
tabId,
});

setCurrentWorkspace({
...currentWorkspace,
activeWorktreeId: selectedWorktreeId,
activeTabId: tabId,
// Use functional setState to avoid overwriting concurrent updates
setCurrentWorkspace((prev) => {
if (!prev) return prev;
return {
...prev,
activeWorktreeId: selectedWorktreeId,
activeTabId: tabId,
};
});
};

Expand Down
19 changes: 8 additions & 11 deletions apps/desktop/src/renderer/screens/main/hooks/useTasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ interface UseTasksProps {
} | null;
setSelectedWorktreeId: (id: string | null) => void;
handleTabSelect: (worktreeId: string, tabId: string) => void;
handleWorktreeCreated: () => Promise<void>;
handleWorktreeCreated: () => Promise<{ id: string; worktrees?: Worktree[] } | null>;
}

export function useTasks({
Expand Down Expand Up @@ -171,28 +171,25 @@ export function useTasks({
setIsCreatingWorktree(false);

// Reload workspace to get the new worktree
await handleWorktreeCreated();
// handleWorktreeCreated returns the refreshed workspace directly
const refreshedWorkspace = await handleWorktreeCreated();

// Small delay to ensure state has propagated
// Wait for React to process the state update from handleWorktreeCreated
// This ensures handleTabSelect sees the updated workspace in its functional setState
await new Promise((resolve) => setTimeout(resolve, 50));

// Only close modal and select worktree if modal is still open
if (isAddTaskModalOpen) {
// Fetch the workspace again to get the latest state with correct IDs
const latestWorkspace = await window.ipcRenderer.invoke(
"workspace-get",
currentWorkspace.id,
);

// Close modal and reset state
setIsAddTaskModalOpen(false);
setSetupStatus(undefined);
setSetupOutput(undefined);

// Switch to the new worktree if available
if (result.worktree && latestWorkspace) {
// Use the refreshed workspace returned by handleWorktreeCreated
if (result.worktree && refreshedWorkspace) {
// Find the worktree by branch name to get the correct ID
const newWorktree = latestWorkspace.worktrees?.find(
const newWorktree = refreshedWorkspace.worktrees?.find(
(wt) => wt.branch === result.worktree?.branch,
);

Expand Down
20 changes: 14 additions & 6 deletions apps/desktop/src/renderer/screens/main/hooks/useWorktrees.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { Workspace, Worktree } from "shared/types";

interface UseWorktreesProps {
currentWorkspace: Workspace | null;
setCurrentWorkspace: (workspace: Workspace) => void;
setCurrentWorkspace: React.Dispatch<React.SetStateAction<Workspace | null>>;
setWorkspaces: React.Dispatch<React.SetStateAction<Workspace[] | null>>;
loadAllWorkspaces: () => Promise<void>;
selectedWorktreeId: string | null;
Expand All @@ -22,7 +22,7 @@ export function useWorktrees({
}: UseWorktreesProps) {
// Handle worktree created
const handleWorktreeCreated = async () => {
if (!currentWorkspace) return;
if (!currentWorkspace) return null;

try {
// Small delay to ensure backend has saved the workspace config
Expand All @@ -48,12 +48,14 @@ export function useWorktrees({
worktrees: refreshedWorkspace.worktrees ? [...refreshedWorkspace.worktrees] : [],
};
});
// Don't call loadAllWorkspaces here - it can cause race conditions
// Update workspaces list asynchronously without blocking
void loadAllWorkspaces();

// Return the refreshed workspace so callers can use it immediately
return refreshedWorkspace;
}
return null;
} catch (error) {
console.error("Failed to refresh workspace:", error);
return null;
}
};

Expand Down Expand Up @@ -205,8 +207,14 @@ export function useWorktrees({
}
};

// Wrapper for components that expect Promise<void>
const handleWorktreeCreatedVoid = async () => {
await handleWorktreeCreated();
};

return {
handleWorktreeCreated,
handleWorktreeCreated: handleWorktreeCreatedVoid,
handleWorktreeCreatedWithResult: handleWorktreeCreated,
handleUpdateWorktree,
handleCreatePR,
handleMergePR,
Expand Down
Loading