| 1 | import { CommandCancelled } from "../lib/commandOutcome"; |
| 2 | import type { RemoteSessionView } from "../lib/remoteTypes"; |
| 3 | import type { SessionOperationAuthority } from "./useResourceOperations"; |
| 4 | import type { SessionSelector } from "../generated/desktopContract.generated"; |
| 5 | |
| 6 | export type TopicRenameTarget = |
| 7 | | { kind: "local"; topicId: string; selector: SessionSelector } |
| 8 | | { kind: "remote"; hostId: string; workspace: string; sessionPath: string; sessionId?: string }; |
| 9 | export type ProjectTopicPorts = { |
| 10 | renameLocal: (selector: SessionSelector, title: string) => Promise<unknown>; |
| 11 | listRemote: (host: string, workspace: string) => Promise<RemoteSessionView[]>; |
| 12 | renameRemote: (host: string, workspace: string, name: string, title: string) => Promise<void>; |
| 13 | markChanged: (update: (value: number) => number) => void; |
| 14 | refreshTabs: (apply?: () => boolean, options?: { afterMutation?: boolean }) => Promise<readonly { id: string }[]>; |
| 15 | syncActive: (rebuild: boolean) => Promise<unknown>; |
| 16 | }; |
| 17 | export type ProjectRefreshInput = { activeTabId?: string; ports: ProjectTopicPorts }; |
| 18 | |
| 19 | export async function refreshProjectTopics(input: ProjectRefreshInput, authority: SessionOperationAuthority) { |
| 20 | authority.checkpoint(); |
| 21 | input.ports.markChanged(value => value + 1); |
| 22 | const tabs = await input.ports.refreshTabs(() => { |
| 23 | try { authority.checkpoint(); return true; } catch { return false; } |
| 24 | }, { afterMutation: true }); |
| 25 | authority.checkpoint(); |
| 26 | if (authority.ownsUI() && input.activeTabId && !tabs.some(tab => tab.id === input.activeTabId)) await input.ports.syncActive(false); |
| 27 | } |
| 28 | |
| 29 | export async function renameProjectTopic(input: ProjectRefreshInput & { target: TopicRenameTarget; title: string }, authority: SessionOperationAuthority) { |
| 30 | const { target, title, ports } = input; |
| 31 | authority.checkpoint(); |
| 32 | if (target.kind === "local") await ports.renameLocal(target.selector, title); |
| 33 | else { |
| 34 | const sessions = await ports.listRemote(target.hostId, target.workspace); |
| 35 | authority.checkpoint(); |
| 36 | // `current` is a navigation snapshot, not the identity of the rename target. |
| 37 | const matches = sessions.filter(session => target.sessionId |
| 38 | ? session.sessionId === target.sessionId |
| 39 | : Boolean(target.sessionPath) && session.path === target.sessionPath); |
| 40 | if (matches.length === 0) throw new CommandCancelled("superseded"); |
| 41 | const source = matches[0]; |
| 42 | if (matches.length !== 1 || !source.name || sessions.filter(session => session.name === source.name).length !== 1) { |
| 43 | throw new Error("The remote service cannot uniquely address this session. Upgrade the remote service before renaming it."); |
| 44 | } |
| 45 | await ports.renameRemote(target.hostId, target.workspace, source.name, title); |
| 46 | } |
| 47 | authority.checkpoint(); |
| 48 | await refreshProjectTopics(input, authority); |
| 49 | } |
| 50 |