mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-10 10:25:03 +00:00
fix(app): hold a saved inline title against the title it replaced
The field closed once the row's title moved off the one editing started from. When another client renamed the workspace while the field was open, that title had already moved, so a save closed the field on the daemon's answer and showed the other client's title until the saved one arrived. The session now records the title at submit and waits for it to change.
This commit is contained in:
@@ -41,6 +41,22 @@ async function waitForTwoFrames(page: Page): Promise<void> {
|
||||
});
|
||||
}
|
||||
|
||||
type DaemonWebSocketGate = Awaited<ReturnType<typeof installDaemonWebSocketGate>>;
|
||||
|
||||
/** Sets the title from a second daemon client and waits until this page has rendered it. */
|
||||
async function renameFromAnotherClient(
|
||||
page: Page,
|
||||
gate: DaemonWebSocketGate,
|
||||
workspace: Awaited<ReturnType<typeof seedWorkspace>>,
|
||||
title: string,
|
||||
): Promise<void> {
|
||||
gate.holdNextServerMessage("workspace_update");
|
||||
await workspace.client.setWorkspaceTitle(workspace.workspaceId, title);
|
||||
await gate.waitForHeldServerMessage("workspace_update");
|
||||
gate.releaseHeldServerMessage("workspace_update");
|
||||
await waitForTwoFrames(page);
|
||||
}
|
||||
|
||||
interface Box {
|
||||
x: number;
|
||||
y: number;
|
||||
@@ -190,12 +206,7 @@ test.describe("Sidebar workspace rename", () => {
|
||||
await expect(input).toBeFocused();
|
||||
await expect(input).toHaveValue("main");
|
||||
|
||||
// Another client renames the workspace while this field is open and untouched.
|
||||
gate.holdNextServerMessage("workspace_update");
|
||||
await workspace.client.setWorkspaceTitle(workspace.workspaceId, "External title");
|
||||
await gate.waitForHeldServerMessage("workspace_update");
|
||||
gate.releaseHeldServerMessage("workspace_update");
|
||||
await waitForTwoFrames(page);
|
||||
await renameFromAnotherClient(page, gate, workspace, "External title");
|
||||
|
||||
await page.mouse.click(900, 700);
|
||||
await expect(input).toHaveCount(0);
|
||||
@@ -206,6 +217,38 @@ test.describe("Sidebar workspace rename", () => {
|
||||
}
|
||||
});
|
||||
|
||||
test("saving after a rename elsewhere holds the submitted title until it arrives", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspace = await seedWorkspace({ repoPrefix: "sidebar-rename-external-save-" });
|
||||
|
||||
try {
|
||||
const gate = await installDaemonWebSocketGate(page);
|
||||
await page.setViewportSize({ width: 1280, height: 800 });
|
||||
await gotoAppShell(page);
|
||||
const row = page.getByTestId(workspaceRowTestId(workspace.workspaceId));
|
||||
const input = page.getByTestId(workspaceRenameInputTestId(workspace.workspaceId));
|
||||
|
||||
await chooseRenameFromKebab(page, workspace.workspaceId);
|
||||
await expect(input).toBeFocused();
|
||||
await renameFromAnotherClient(page, gate, workspace, "External title");
|
||||
|
||||
await input.fill("Submitted title");
|
||||
gate.holdNextServerMessage("workspace_update");
|
||||
await page.keyboard.press("Enter");
|
||||
await gate.waitForHeldServerMessage("workspace_update");
|
||||
|
||||
await expect(input).toHaveValue("Submitted title");
|
||||
await expect(row).not.toContainText("External title");
|
||||
|
||||
gate.releaseHeldServerMessage("workspace_update");
|
||||
await expect(input).toHaveCount(0);
|
||||
await expect(row).toContainText("Submitted title");
|
||||
} finally {
|
||||
await workspace.cleanup();
|
||||
}
|
||||
});
|
||||
|
||||
test("below the lg breakpoint renaming opens the modal", async ({ page }) => {
|
||||
const workspace = await seedWorkspace({ repoPrefix: "sidebar-rename-modal-" });
|
||||
|
||||
|
||||
@@ -19,14 +19,13 @@ export interface SidebarRenamableWorkspace extends RenamableWorkspace {
|
||||
*
|
||||
* `saving` and `saved` keep the field on screen holding the submitted text. The daemon answers
|
||||
* the request before it pushes the updated workspace, so closing on the answer would show the
|
||||
* old title for a beat. `saved` ends when the row's title moves off the one editing started from.
|
||||
* old title for a beat. `saved` ends when the row's title moves off the one it had at submit,
|
||||
* which is not the one editing started from when another client renamed it in between.
|
||||
*/
|
||||
interface InlineRenameSession {
|
||||
kind: "inline";
|
||||
phase: "editing" | "saving" | "saved";
|
||||
initialValue: string;
|
||||
titleAtStart: string | null;
|
||||
}
|
||||
type InlineRenameSession = { kind: "inline"; initialValue: string } & (
|
||||
| { phase: "editing" }
|
||||
| { phase: "saving" | "saved"; titleBeforeSave: string | null }
|
||||
);
|
||||
|
||||
type RenameSession = { kind: "closed" } | { kind: "modal" } | InlineRenameSession;
|
||||
|
||||
@@ -52,7 +51,7 @@ export function useSidebarWorkspaceRename(
|
||||
if (
|
||||
session.kind === "inline" &&
|
||||
session.phase === "saved" &&
|
||||
currentTitle !== session.titleAtStart
|
||||
currentTitle !== session.titleBeforeSave
|
||||
) {
|
||||
setSession(CLOSED);
|
||||
}
|
||||
@@ -61,10 +60,10 @@ export function useSidebarWorkspaceRename(
|
||||
const begin = useCallback(() => {
|
||||
setSession(
|
||||
resolveWorkspaceRenameSurface() === "inline"
|
||||
? { kind: "inline", phase: "editing", initialValue, titleAtStart: currentTitle }
|
||||
? { kind: "inline", phase: "editing", initialValue }
|
||||
: { kind: "modal" },
|
||||
);
|
||||
}, [currentTitle, initialValue]);
|
||||
}, [initialValue]);
|
||||
|
||||
const close = useCallback(() => setSession(CLOSED), []);
|
||||
|
||||
@@ -76,7 +75,12 @@ export function useSidebarWorkspaceRename(
|
||||
setSession(CLOSED);
|
||||
return;
|
||||
}
|
||||
setSession({ ...session, phase: "saving" });
|
||||
setSession({
|
||||
kind: "inline",
|
||||
initialValue: session.initialValue,
|
||||
phase: "saving",
|
||||
titleBeforeSave: currentTitle,
|
||||
});
|
||||
setWorkspaceTitle(title).then(
|
||||
() =>
|
||||
setSession((previous) =>
|
||||
@@ -90,7 +94,7 @@ export function useSidebarWorkspaceRename(
|
||||
},
|
||||
);
|
||||
},
|
||||
[session, setWorkspaceTitle, toast],
|
||||
[currentTitle, session, setWorkspaceTitle, toast],
|
||||
);
|
||||
|
||||
const titleEditor =
|
||||
|
||||
Reference in New Issue
Block a user