Skip to content
Open
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
34 changes: 34 additions & 0 deletions packages/app-core/src/store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1719,6 +1719,40 @@ describe('renameNote heading sync (#455)', () => {
expect(writeNote).toHaveBeenCalledWith('inbox/Groceries.md', '# Groceries\n\nbody\n')
})

it('keeps the focused editor open when its rename unlink arrives first (#713)', async () => {
const pendingRename = deferred<typeof renamedMeta>()
const renameNote = vi.fn().mockReturnValue(pendingRename.promise)
installRename({ renameNote })
const { useStore } = await loadStore()
await useStore.getState().selectNote('inbox/Untitled.md')

const renaming = useStore.getState().renameNote('inbox/Untitled.md', 'Groceries')
await vi.waitFor(() => expect(renameNote).toHaveBeenCalledWith('inbox/Untitled.md', 'Groceries'))
await useStore.getState().applyChange({
kind: 'unlink',
path: 'inbox/Untitled.md',
folder: 'inbox'
})
pendingRename.resolve(renamedMeta)
await renaming

expect(useStore.getState().selectedPath).toBe('inbox/Groceries.md')
})

it('still closes the focused editor for an ordinary unlink', async () => {
installRename()
const { useStore } = await loadStore()
await useStore.getState().selectNote('inbox/Untitled.md')

await useStore.getState().applyChange({
kind: 'unlink',
path: 'inbox/Untitled.md',
folder: 'inbox'
})

expect(useStore.getState().selectedPath).toBeNull()
})

it('leaves the body alone when the setting is off', async () => {
const { writeNote, readNote } = installRename()
const { useStore } = await loadStore()
Expand Down
26 changes: 17 additions & 9 deletions packages/app-core/src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,7 @@ let coalescedNotesRefreshPending = false
* IPC listener behind for the rest of the session.
*/
let vaultChangeUnsubscribe: (() => void) | null = null
const renamingNotePaths = new Set<string>()

function refreshNotesCoalesced(): Promise<void> {
if (coalescedNotesRefreshInFlight) {
Expand Down Expand Up @@ -6175,6 +6176,8 @@ export const useStore = create<Store>((set, get) => {
},

applyChange: async (ev) => {
if (ev.kind === 'unlink' && renamingNotePaths.has(ev.path)) return

// The live feed's unlink handling, shared with the resync path below:
// a deleted note's tab closes wherever it is open.
const closeUnlinkedNote = (notePath: string): void => {
Expand Down Expand Up @@ -6620,15 +6623,20 @@ export const useStore = create<Store>((set, get) => {
if (Object.values(get().noteDirty).some(Boolean)) {
throw new Error('Could not rename while notes still have unsaved changes')
}
const meta = await window.zen.renameNote(oldPath, nextTitle)
set((s) => renameNoteState(s, oldPath, meta))
await get().applyFavorites(
rewriteFavoriteNotePath(get().vaultSettings.favorites, oldPath, meta.path)
)
// Before the refresh so one listing picks up both the rename and the
// rewritten heading (excerpt, size).
await syncHeadingAfterRename(meta, get)
await get().refreshNotes()
renamingNotePaths.add(oldPath)
try {
const meta = await window.zen.renameNote(oldPath, nextTitle)
set((s) => renameNoteState(s, oldPath, meta))
await get().applyFavorites(
rewriteFavoriteNotePath(get().vaultSettings.favorites, oldPath, meta.path)
)
// Before the refresh so one listing picks up both the rename and the
// rewritten heading (excerpt, size).
await syncHeadingAfterRename(meta, get)
await get().refreshNotes()
} finally {
renamingNotePaths.delete(oldPath)
}
} catch (err) {
console.error('renameNote failed', err)
}
Expand Down