fix: race condition when editor values read could belong to newly selected note as opposed to note for which save was triggered

This commit is contained in:
Mo Bitar
2021-04-17 19:27:16 -05:00
parent 729a1a8dfd
commit 64945abbcc

View File

@@ -88,11 +88,15 @@ type EditorState = {
}; };
type EditorValues = { type EditorValues = {
title?: string; title: string;
text?: string; text: string;
tagsInputValue?: string; tagsInputValue?: string;
}; };
function copyEditorValues(values: EditorValues) {
return Object.assign({}, values);
}
function sortAlphabetically(array: SNComponent[]): SNComponent[] { function sortAlphabetically(array: SNComponent[]): SNComponent[] {
return array.sort((a, b) => return array.sort((a, b) =>
a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1 a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1
@@ -110,7 +114,7 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
private saveTimeout?: ng.IPromise<void>; private saveTimeout?: ng.IPromise<void>;
private statusTimeout?: ng.IPromise<void>; private statusTimeout?: ng.IPromise<void>;
private lastEditorFocusEventSource?: EventSource; private lastEditorFocusEventSource?: EventSource;
public editorValues: EditorValues = {}; public editorValues: EditorValues = { title: '', text: '' };
onEditorLoad?: () => void; onEditorLoad?: () => void;
private tags: SNTag[] = []; private tags: SNTag[] = [];
@@ -464,26 +468,30 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
* close the editor (if we closed the editor before sync began, we'd get an exception, * close the editor (if we closed the editor before sync began, we'd get an exception,
* since the debouncer will be triggered on a non-existent editor) * since the debouncer will be triggered on a non-existent editor)
*/ */
async saveNote( async save(
note: SNNote,
editorValues: EditorValues,
bypassDebouncer = false, bypassDebouncer = false,
isUserModified = false, isUserModified = false,
dontUpdatePreviews = false, dontUpdatePreviews = false,
customMutate?: (mutator: NoteMutator) => void, customMutate?: (mutator: NoteMutator) => void,
closeAfterSync = false closeAfterSync = false
) { ) {
const title = editorValues.title;
const text = editorValues.text;
const isTemplate = this.editor.isTemplateNote;
const selectedTag = this.appState.selectedTag;
if (document.hidden) { if (document.hidden) {
this.application.alertService!.alert(STRING_SAVING_WHILE_DOCUMENT_HIDDEN); this.application.alertService.alert(STRING_SAVING_WHILE_DOCUMENT_HIDDEN);
return; return;
} }
const note = this.note;
if (note.deleted) { if (note.deleted) {
this.application.alertService!.alert(STRING_DELETED_NOTE); this.application.alertService.alert(STRING_DELETED_NOTE);
return; return;
} }
if (this.editor.isTemplateNote) { if (isTemplate) {
await this.editor.insertTemplatedNote(); await this.editor.insertTemplatedNote();
} }
const selectedTag = this.appState.selectedTag;
if ( if (
!selectedTag?.isSmartTag && !selectedTag?.isSmartTag &&
!selectedTag?.hasRelationshipWithItem(note) !selectedTag?.hasRelationshipWithItem(note)
@@ -493,7 +501,7 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
}); });
} }
if (!this.application.findItem(note.uuid)) { if (!this.application.findItem(note.uuid)) {
this.application.alertService!.alert(STRING_INVALID_NOTE); this.application.alertService.alert(STRING_INVALID_NOTE);
return; return;
} }
await this.application.changeItem( await this.application.changeItem(
@@ -503,12 +511,12 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
if (customMutate) { if (customMutate) {
customMutate(noteMutator); customMutate(noteMutator);
} }
noteMutator.title = this.editorValues.title!; noteMutator.title = title;
noteMutator.text = this.editorValues.text!; noteMutator.text = text;
if (!dontUpdatePreviews) { if (!dontUpdatePreviews) {
const text = this.editorValues.text || ''; const noteText = text || '';
const truncate = text.length > NOTE_PREVIEW_CHAR_LIMIT; const truncate = noteText.length > NOTE_PREVIEW_CHAR_LIMIT;
const substring = text.substring(0, NOTE_PREVIEW_CHAR_LIMIT); const substring = noteText.substring(0, NOTE_PREVIEW_CHAR_LIMIT);
const previewPlain = substring + (truncate ? STRING_ELLIPSES : ''); const previewPlain = substring + (truncate ? STRING_ELLIPSES : '');
noteMutator.preview_plain = previewPlain; noteMutator.preview_plain = previewPlain;
noteMutator.preview_html = undefined; noteMutator.preview_html = undefined;
@@ -541,7 +549,8 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
syncTakingTooLong: false, syncTakingTooLong: false,
}); });
this.setStatus({ this.setStatus({
message: 'All changes saved' + (this.application.noAccount() ? ' offline' : ''), message:
'All changes saved' + (this.application.noAccount() ? ' offline' : ''),
}); });
} }
@@ -583,7 +592,7 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
} }
contentChanged() { contentChanged() {
this.saveNote(false, true); this.save(this.note, copyEditorValues(this.editorValues), false, true);
} }
onTitleEnter($event: Event) { onTitleEnter($event: Event) {
@@ -593,7 +602,13 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
} }
onTitleChange() { onTitleChange() {
this.saveNote(false, true, true); this.save(
this.note,
copyEditorValues(this.editorValues),
false,
true,
true
);
} }
focusEditor() { focusEditor() {
@@ -653,9 +668,16 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
if (permanently) { if (permanently) {
this.performNoteDeletion(this.note); this.performNoteDeletion(this.note);
} else { } else {
this.saveNote(true, false, true, (mutator) => { this.save(
mutator.trashed = true; this.note,
}); copyEditorValues(this.editorValues),
true,
false,
true,
(mutator) => {
mutator.trashed = true;
}
);
} }
} }
} }
@@ -665,7 +687,9 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
} }
restoreTrashedNote() { restoreTrashedNote() {
this.saveNote( this.save(
this.note,
copyEditorValues(this.editorValues),
true, true,
false, false,
true, true,
@@ -698,15 +722,31 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
} }
togglePin() { togglePin() {
this.saveNote(true, false, true, (mutator) => { const note = this.note;
mutator.pinned = !this.note.pinned; this.save(
}); note,
copyEditorValues(this.editorValues),
true,
false,
true,
(mutator) => {
mutator.pinned = !note.pinned;
}
);
} }
toggleLockNote() { toggleLockNote() {
this.saveNote(true, false, true, (mutator) => { const note = this.note;
mutator.locked = !this.note.locked; this.save(
}); note,
copyEditorValues(this.editorValues),
true,
false,
true,
(mutator) => {
mutator.locked = !note.locked;
}
);
} }
async toggleProtectNote() { async toggleProtectNote() {
@@ -723,29 +763,40 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
} }
toggleNotePreview() { toggleNotePreview() {
this.saveNote(true, false, true, (mutator) => { const note = this.note;
mutator.hidePreview = !this.note.hidePreview; this.save(
}); note,
copyEditorValues(this.editorValues),
true,
false,
true,
(mutator) => {
mutator.hidePreview = !note.hidePreview;
}
);
} }
toggleArchiveNote() { toggleArchiveNote() {
if (this.note.locked) { const note = this.note;
if (note.locked) {
alertDialog({ alertDialog({
text: this.note.archived text: note.archived
? STRING_UNARCHIVE_LOCKED_ATTEMPT ? STRING_UNARCHIVE_LOCKED_ATTEMPT
: STRING_ARCHIVE_LOCKED_ATTEMPT, : STRING_ARCHIVE_LOCKED_ATTEMPT,
}); });
return; return;
} }
this.saveNote( this.save(
note,
copyEditorValues(this.editorValues),
true, true,
false, false,
true, true,
(mutator) => { (mutator) => {
mutator.archived = !this.note.archived; mutator.archived = !note.archived;
}, },
/** If we are unarchiving, and we are in the archived tag, close the editor */ /** If we are unarchiving, and we are in the archived tag, close the editor */
this.note.archived && this.appState.selectedTag?.isArchiveTag note.archived && this.appState.selectedTag?.isArchiveTag
); );
} }
@@ -1176,7 +1227,7 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
editor.selectionStart = editor.selectionEnd = start + 4; editor.selectionStart = editor.selectionEnd = start + 4;
} }
this.editorValues.text = editor.value; this.editorValues.text = editor.value;
this.saveNote(true); this.save(this.note, copyEditorValues(this.editorValues), true);
}, },
}); });