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
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,16 @@ function V2WorkspaceLayout() {
.where(({ v2Workspaces }) => eq(v2Workspaces.id, workspaceId ?? "")),
[collections, workspaceId],
);
const workspace = workspaces?.[0] ?? null;
const syncedWorkspace = workspaces?.[0] ?? null;
const inFlight = useWorkspaceCreatesStore((store) =>
workspaceId
? store.entries.find((entry) => entry.snapshot.id === workspaceId)
: undefined,
);
// Fall back to the cloud row cached on the in-flight entry while
// Electric hasn't yet delivered the synced row. The cloud has already
// confirmed the workspace at this point — no need to block on sync.
const workspace = syncedWorkspace ?? inFlight?.cloudRow ?? null;

const lastEnsuredWorkspaceIdRef = useRef<string | null>(null);
useEffect(() => {
Expand Down
15 changes: 15 additions & 0 deletions apps/desktop/src/renderer/stores/workspace-creates/store.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { SelectV2Workspace } from "@superset/db/schema";
import type { AppRouter } from "@superset/host-service";
import type { inferRouterInputs } from "@trpc/server";
import { create } from "zustand";
Expand All @@ -11,13 +12,21 @@ export interface InFlightEntry {
state: "creating" | "error";
error?: string;
startedAt: number;
/**
* Cloud row returned by the host-service mutation. Set once
* `workspaces.create` resolves successfully — the workspace detail
* layout falls back to this row while Electric hasn't yet delivered
* the synced row into `collections.v2Workspaces`.
*/
cloudRow?: SelectV2Workspace;
}

interface WorkspaceCreatesState {
entries: InFlightEntry[];
add: (entry: Omit<InFlightEntry, "startedAt">) => void;
markError: (workspaceId: string, error: string) => void;
markCreating: (workspaceId: string) => void;
markCloudRow: (workspaceId: string, cloudRow: SelectV2Workspace) => void;
remove: (workspaceId: string) => void;
}

Expand All @@ -44,6 +53,12 @@ export const useWorkspaceCreatesStore = create<WorkspaceCreatesState>(
: entry,
),
})),
markCloudRow: (workspaceId, cloudRow) =>
set((state) => ({
entries: state.entries.map((entry) =>
entry.snapshot.id === workspaceId ? { ...entry, cloudRow } : entry,
),
})),
remove: (workspaceId) =>
set((state) => ({
entries: state.entries.filter(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ export function useWorkspaceCreates(): UseWorkspaceCreatesApi {
const client = getHostServiceClientByUrl(hostUrl);
const result = await client.workspaces.create.mutate(args.snapshot);

// Cache the cloud row on the in-flight entry so the workspace
// detail layout can render the workspace immediately, without
// waiting for Electric to deliver the synced row. Manager.tsx
// removes the entry once the row appears in collections.
useWorkspaceCreatesStore
.getState()
.markCloudRow(result.workspace.id, result.workspace);

const existing = collections.v2WorkspaceLocalState.get(
result.workspace.id,
);
Expand Down
44 changes: 11 additions & 33 deletions packages/host-service/src/trpc/router/workspaces/workspaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,18 +74,17 @@ type AgentLaunchResult =
| ({ ok: true } & AgentRunResult)
| { ok: false; error: string };

interface ResolvedWorkspace {
id: string;
projectId: string;
name: string;
branch: string;
}
type CloudWorkspace = NonNullable<
Awaited<
ReturnType<HostServiceContext["api"]["v2Workspace"]["getFromHost"]["query"]>
>
>;

async function findExistingWorkspaceByBranch(
ctx: HostServiceContext,
projectId: string,
branch: string,
): Promise<ResolvedWorkspace | null> {
): Promise<CloudWorkspace | null> {
const local = ctx.db.query.workspaces
.findFirst({
where: and(
Expand All @@ -100,13 +99,7 @@ async function findExistingWorkspaceByBranch(
organizationId: ctx.organizationId,
id: local.id,
});
if (!cloud) return null;
return {
id: cloud.id,
projectId: cloud.projectId,
name: cloud.name,
branch: cloud.branch,
};
return cloud ?? null;
}

interface PrMetadata {
Expand Down Expand Up @@ -381,7 +374,7 @@ async function registerCloudAndLocal(args: {
taskId: string | undefined;
rollbackWorktree: () => Promise<void>;
hostPromise: Promise<{ machineId: string }>;
}): Promise<{ id: string; projectId: string; name: string; branch: string }> {
}): Promise<CloudWorkspace> {
const { ctx } = args;
let host: { machineId: string };
try {
Expand Down Expand Up @@ -443,12 +436,7 @@ async function registerCloudAndLocal(args: {
});
}

return {
id: cloudRow.id,
projectId: cloudRow.projectId,
name: cloudRow.name,
branch: cloudRow.branch,
};
return cloudRow;
}

async function dispatchSugarAgents(
Expand Down Expand Up @@ -526,12 +514,7 @@ export const workspacesRouter = router({
let resolvedBranch: string;
let worktreePath: string;
let alreadyExists = false;
let workspaceRow: {
id: string;
projectId: string;
name: string;
branch: string;
};
let workspaceRow: CloudWorkspace;
let prMetadata: PrMetadata | null = null;

if (input.pr !== undefined) {
Expand Down Expand Up @@ -872,12 +855,7 @@ export const workspacesRouter = router({
);

return {
workspace: {
id: workspaceRow.id,
projectId: workspaceRow.projectId,
name: workspaceRow.name,
branch: workspaceRow.branch,
},
workspace: workspaceRow,
terminals: terminalsResult,
agents: agentsResult,
alreadyExists,
Expand Down
7 changes: 7 additions & 0 deletions packages/sdk/src/resources/workspaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,16 @@ export type WorkspaceCreateAgentResult =
export interface WorkspaceCreateResult {
workspace: {
id: string;
organizationId: string;
projectId: string;
hostId: string;
name: string;
branch: string;
type: "main" | "worktree";
createdByUserId: string | null;
taskId: string | null;
createdAt: Date;
updatedAt: Date;
};
terminals: Array<{ terminalId: string; label?: string }>;
agents: WorkspaceCreateAgentResult[];
Expand Down
Loading