Skip to content
Open
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
56 changes: 40 additions & 16 deletions src/application/services/useNote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,13 +201,28 @@ export default function (options: UseNoteComposableOptions): UseNoteComposableSt
try {
const response = await noteService.getNoteById(id);

/**
* If route already points to another note,
* ignore this outdated response
*/
if (currentId.value !== id) {
return;
}

note.value = response.note;
canEdit.value = response.accessRights.canEdit;
noteTools.value = response.tools;
parentNote.value = response.parentNote;
noteParents.value = response.parents;
void getNoteHierarchy(id);
} catch (error) {
/**
* If user already switched to another note,
* do not handle errors from the previous request
*/
if (currentId.value !== id) {
return;
}
Comment on lines +223 to +225
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can't understand why we need to check this condition one more time here (you've checked it right above, what is the difference here?)

deleteOpenedPageByUrl(route.path);
if (error instanceof DomainError) {
void router.push(`/error/${error.statusCode}`);
Expand Down Expand Up @@ -255,9 +270,14 @@ export default function (options: UseNoteComposableOptions): UseNoteComposableSt
*/
const specifiedNoteTools = resolveToolsByContent(content);

/**
* Remember current note id to detect route changes during save
*/
const savedNoteId = currentId.value;

isNoteSaving.value = true;

if (currentId.value === null) {
if (savedNoteId === null) {
/**
* @todo try-catch domain errors
*/
Expand All @@ -273,25 +293,26 @@ export default function (options: UseNoteComposableOptions): UseNoteComposableSt
},
});

patchOpenedPageByUrl(
route.path,
{
title: noteTitle.value,
url: route.path,
});
patchOpenedPageByUrl(route.path, {
title: noteTitle.value,
url: route.path,
});

/**
* Get note Hierarchy when new Note is created
*/
void getNoteHierarchy(noteCreated.id);
} else {
await noteService.updateNoteContentAndTools(currentId.value, content, specifiedNoteTools);
await noteService.updateNoteContentAndTools(savedNoteId, content, specifiedNoteTools);
}

/**
* Store just saved content in memory
* If id changed, do not store content
*/
lastUpdateContent.value = content;
if (currentId.value === savedNoteId) {
lastUpdateContent.value = content;
}

isNoteSaving.value = false;
}
Expand Down Expand Up @@ -391,13 +412,16 @@ export default function (options: UseNoteComposableOptions): UseNoteComposableSt
});

watch(noteTitle, (currentNoteTitle) => {
if (route.name == 'note') {
patchOpenedPageByUrl(
route.path,
{
title: currentNoteTitle,
url: route.path,
});
if (route.name == 'note' && currentId.value !== null) {
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use strict equality (===) instead of loose equality (==) for consistency with the rest of the codebase and to avoid potential type coercion issues.

Suggested change
if (route.name == 'note' && currentId.value !== null) {
if (route.name === 'note' && currentId.value !== null) {

Copilot uses AI. Check for mistakes.
/**
* Build tab URL from current note id to avoid using outdated route.path
*/
const noteUrl = `/note/${currentId.value}`;

patchOpenedPageByUrl(noteUrl, {
title: currentNoteTitle,
url: noteUrl,
});
}
Comment on lines 414 to 425
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The noteTitle watcher has a potential race condition. While currentId.value is checked, it's not captured at the time the watcher is triggered. If currentId changes between when noteTitle is recalculated and when this watcher callback runs, the title from one note could be used to update the opened page for a different note. Consider capturing currentId.value at the start of the watcher callback, similar to how savedNoteId is captured in the save function.

Copilot uses AI. Check for mistakes.
updateNoteHierarchyContent(noteHierarchy.value, currentNoteTitle);
});
Expand Down
Loading