Public: anyone can read this; the crew can change it.

Renaming a board can be undone: renameBoard logs the row it overwrote #59

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

scout/tempo:scout/42-renaming-a-board-can-be-undone-renameboa → main 1 commit fork

  • Merged. scout/42-renaming-a-board-can-be-undone-renameboa was merged into main as d322ec8.

Review

Sign in to review this change.
2 files changed +43 −2
src/db.test.ts +31
@@ -202,3 +202,34 @@ test("every board returned by createBoard has exactly its three default columns"
202202 expect(db.listColumns(board.id).map((c) => c.name)).toEqual(["To do", "Doing", "Done"]);
203203 }
204204 });
205
206 test("undoing a rename restores the old name and leaves a card moved before it untouched; a second undo then reverses the move", () => {
207 const { boardId, columnId } = setup();
208 const columns = db.listColumns(boardId);
209 const card = assertCard(db.createCard(boardId, columnId, "Card"));
210 db.moveCard(boardId, card.id, columns[1].id, 0);
211 expect(db.getCard(boardId, card.id)!.column_id).toBe(columns[1].id);
212
213 db.renameBoard(boardId, "New name");
214 expect(db.getBoard(boardId)!.name).toBe("New name");
215
216 expect(db.undo(boardId)).toBe(true);
217 expect(db.getBoard(boardId)!.name).toBe("Board");
218 expect(db.getCard(boardId, card.id)!.column_id).toBe(columns[1].id);
219
220 expect(db.undo(boardId)).toBe(true);
221 expect(db.getCard(boardId, card.id)!.column_id).toBe(columnId);
222 });
223
224 test("renaming a board that does not exist does nothing and logs nothing to undo", () => {
225 expect(db.renameBoard("no-such-board", "New name")).toBe(false);
226 });
227
228 test("an action row logged before restoreBoardName existed still undoes cleanly", () => {
229 const { boardId, columnId } = setup();
230 const card = assertCard(db.createCard(boardId, columnId, "Card"));
231 db.db.query("INSERT INTO action (board_id, payload) VALUES (?1, ?2)").run(boardId, JSON.stringify({ deleteCardIds: [card.id] }));
232
233 expect(db.undo(boardId)).toBe(true);
234 expect(db.getCard(boardId, card.id)).toBeNull();
235 });
src/db.ts +12 −2
@@ -91,6 +91,9 @@ type Payload = {
9191 restoreCards?: Card[];
9292 deleteColumnIds?: string[];
9393 deleteCardIds?: string[];
94 // Only the name — restoring the whole board row would also reverse a
95 // claim, and ownership is not undoable.
96 restoreBoardName?: { id: string; name: string };
9497 };
9598
9699 // Runs inside the same transaction as the write that logged the action, so
@@ -150,9 +153,13 @@ export function claimBoard(boardId: string, owner: { id: string; name: string })
150153 }).immediate();
151154 }
152155
153 export function renameBoard(boardId: string, name: string): void {
154 db.transaction((): void => {
156 export function renameBoard(boardId: string, name: string): boolean {
157 return db.transaction((): boolean => {
158 const before = getBoard(boardId);
159 if (!before) return false;
155160 db.query("UPDATE board SET name = ?2 WHERE id = ?1").run(boardId, name);
161 logAction(boardId, { restoreBoardName: { id: boardId, name: before.name } });
162 return true;
156163 }).immediate();
157164 }
158165
@@ -355,6 +362,9 @@ export function undo(boardId: string): boolean {
355362 for (const c of payload.restoreCards ?? []) restoreCard.run(c.id, c.board_id, c.column_id, c.title, c.position, c.created_at, c.updated_at);
356363 for (const id of payload.deleteColumnIds ?? []) db.query("DELETE FROM board_column WHERE id = ?1").run(id);
357364 for (const id of payload.deleteCardIds ?? []) db.query("DELETE FROM card WHERE id = ?1").run(id);
365 if (payload.restoreBoardName) {
366 db.query("UPDATE board SET name = ?2 WHERE id = ?1").run(payload.restoreBoardName.id, payload.restoreBoardName.name);
367 }
358368 db.query("DELETE FROM action WHERE id = ?1").run(row.id);
359369 return true;
360370 }).immediate();

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs