File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -90,6 +90,7 @@ import {
9090 useProjects ,
9191 useThreadShells ,
9292 useThreadShellsForProjectRefs ,
93+ waitForProjectRemoval ,
9394} from "../state/entities" ;
9495import { selectThreadTerminalUiState , useTerminalUiStateStore } from "../terminalUiStateStore" ;
9596import { useThreadRunningTerminalIds } from "../state/terminalSessions" ;
@@ -1529,6 +1530,7 @@ const SidebarProjectItem = memo(function SidebarProjectItem(props: SidebarProjec
15291530 if ( result . _tag === "Failure" ) {
15301531 return result ;
15311532 }
1533+ await waitForProjectRemoval ( memberProjectRef ) ;
15321534 const draftStore = useComposerDraftStore . getState ( ) ;
15331535 releaseProjectDraftUploads (
15341536 memberProjectRef ,
Original file line number Diff line number Diff line change @@ -21,7 +21,7 @@ import {
2121 type SidebarProjectSnapshot ,
2222} from "../../sidebarProjectGrouping" ;
2323import { useEnvironments , usePrimaryEnvironmentId } from "../../state/environments" ;
24- import { useThreadShells } from "../../state/entities" ;
24+ import { useThreadShells , waitForProjectRemoval } from "../../state/entities" ;
2525import { projectEnvironment } from "../../state/projects" ;
2626import { useAtomCommand } from "../../state/use-atom-command" ;
2727import { ProjectFavicon } from "../ProjectFavicon" ;
@@ -358,6 +358,7 @@ function ProjectDetail({
358358 return ;
359359 }
360360 const projectRef = scopeProjectRef ( member . environmentId , member . id ) ;
361+ await waitForProjectRemoval ( projectRef ) ;
361362 releaseProjectDraftUploads (
362363 projectRef ,
363364 memberThreads . map ( ( thread ) => scopeThreadRef ( thread . environmentId , thread . id ) ) ,
Original file line number Diff line number Diff line change @@ -6,6 +6,7 @@ import { useCallback } from "react";
66import { useComposerDraftStore } from "../composerDraftStore" ;
77import { resolveThreadRouteTarget } from "../threadRoutes" ;
88import { releaseProjectDraftUploads } from "../lib/composerDraftUploads" ;
9+ import { waitForProjectRemoval } from "../state/entities" ;
910import { projectEnvironment } from "../state/projects" ;
1011import { useAtomCommand } from "../state/use-atom-command" ;
1112import { stackedThreadToast , toastManager } from "../components/ui/toast" ;
@@ -41,6 +42,7 @@ export function useRemoveClonedProject() {
4142 ) ;
4243 return false ;
4344 }
45+ await waitForProjectRemoval ( projectRef ) ;
4446 // Read the route after the await: the user may have moved on while the
4547 // delete was in flight, and only a draft of this project needs to go.
4648 const routeParams = router . state . matches [ router . state . matches . length - 1 ] ?. params ?? { } ;
Original file line number Diff line number Diff line change @@ -181,6 +181,20 @@ export function waitForProject(
181181 } ) ;
182182}
183183
184+ /**
185+ * Resolves once a deleted project leaves the live client store. The delete
186+ * command returns before the shell stream does, so anything that picks a
187+ * project in between (the index route's draft landing) still sees it.
188+ */
189+ export function waitForProjectRemoval ( ref : ScopedProjectRef , timeoutMs = 5_000 ) : Promise < boolean > {
190+ return waitForAtomValue ( {
191+ registry : appAtomRegistry ,
192+ atom : environmentProjects . projectAtom ( ref ) ,
193+ predicate : ( project ) => project === null ,
194+ timeoutMs,
195+ } ) ;
196+ }
197+
184198export function readThreadShell ( ref : ScopedThreadRef ) : EnvironmentThreadShell | null {
185199 return appAtomRegistry . get ( environmentThreadShells . threadShellAtom ( ref ) ) ;
186200}
You can’t perform that action at this time.
0 commit comments