1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-24 10:27:10 +02:00

Desktop: Fixes #10284: Sort the note list soon after changing a note's title (#10512)

This commit is contained in:
Henry Heino 2024-05-30 00:40:32 -07:00 committed by GitHub
parent 55c222c577
commit 99b840da34
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 39 additions and 22 deletions

View File

@ -51,6 +51,7 @@ import getPluginSettingValue from '@joplin/lib/services/plugins/utils/getPluginS
import { MarkupLanguage } from '@joplin/renderer'; import { MarkupLanguage } from '@joplin/renderer';
import useScrollWhenReadyOptions from './utils/useScrollWhenReadyOptions'; import useScrollWhenReadyOptions from './utils/useScrollWhenReadyOptions';
import useScheduleSaveCallbacks from './utils/useScheduleSaveCallbacks'; import useScheduleSaveCallbacks from './utils/useScheduleSaveCallbacks';
const debounce = require('debounce');
const commands = [ const commands = [
require('./commands/showRevisions'), require('./commands/showRevisions'),
@ -94,7 +95,6 @@ function NoteEditor(props: NoteEditorProps) {
onAfterLoad: formNote_afterLoad, onAfterLoad: formNote_afterLoad,
}); });
setFormNoteRef.current = setFormNote; setFormNoteRef.current = setFormNote;
const formNoteRef = useRef<FormNote>(); const formNoteRef = useRef<FormNote>();
formNoteRef.current = { ...formNote }; formNoteRef.current = { ...formNote };
@ -158,9 +158,16 @@ function NoteEditor(props: NoteEditorProps) {
} }
}, [props.isProvisional, formNote.id, props.dispatch]); }, [props.isProvisional, formNote.id, props.dispatch]);
const scheduleNoteListResort = useMemo(() => {
return debounce(() => {
// Although the note list will update automatically, it may take some time. This
// forces an immediate update.
props.dispatch({ type: 'NOTE_SORT' });
}, 100);
}, [props.dispatch]);
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const onFieldChange = useCallback((field: string, value: any, changeId = 0) => { const onFieldChange = useCallback(async (field: string, value: any, changeId = 0) => {
if (!isMountedRef.current) { if (!isMountedRef.current) {
// When the component is unmounted, various actions can happen which can // When the component is unmounted, various actions can happen which can
// trigger onChange events, for example the textarea might be cleared. // trigger onChange events, for example the textarea might be cleared.
@ -200,9 +207,16 @@ function NoteEditor(props: NoteEditorProps) {
// The previously loaded note, that was modified, will be saved via saveNoteIfWillChange() // The previously loaded note, that was modified, will be saved via saveNoteIfWillChange()
} else { } else {
setFormNote(newNote); setFormNote(newNote);
void scheduleSaveNote(newNote); await scheduleSaveNote(newNote);
} }
}, [handleProvisionalFlag, formNote, setFormNote, isNewNote, titleHasBeenManuallyChanged, scheduleSaveNote]);
if (field === 'title') {
// Scheduling a resort needs to be:
// - called after scheduleSaveNote so that the new note title is used for sorting
// - debounced because many calls to scheduleSaveNote can resolve at once
scheduleNoteListResort();
}
}, [handleProvisionalFlag, formNote, setFormNote, isNewNote, titleHasBeenManuallyChanged, scheduleNoteListResort, scheduleSaveNote]);
useWindowCommandHandler({ useWindowCommandHandler({
dispatch: props.dispatch, dispatch: props.dispatch,

View File

@ -17,7 +17,7 @@ interface Props {
} }
const useScheduleSaveCallbacks = (props: Props) => { const useScheduleSaveCallbacks = (props: Props) => {
const scheduleSaveNote = useCallback(async (formNote: FormNote) => { const scheduleSaveNote = useCallback((formNote: FormNote) => {
if (!formNote.saveActionQueue) throw new Error('saveActionQueue is not set!!'); // Sanity check if (!formNote.saveActionQueue) throw new Error('saveActionQueue is not set!!'); // Sanity check
// reg.logger().debug('Scheduling...', formNote); // reg.logger().debug('Scheduling...', formNote);
@ -44,6 +44,7 @@ const useScheduleSaveCallbacks = (props: Props) => {
}; };
formNote.saveActionQueue.push(makeAction(formNote)); formNote.saveActionQueue.push(makeAction(formNote));
return formNote.saveActionQueue.waitForAllDone();
}, [props.dispatch, props.setFormNote]); }, [props.dispatch, props.setFormNote]);
const saveNoteIfWillChange = useCallback(async (formNote: FormNote) => { const saveNoteIfWillChange = useCallback(async (formNote: FormNote) => {

View File

@ -28,9 +28,19 @@ export default class AsyncActionQueue {
private scheduleProcessingIID_: any = null; private scheduleProcessingIID_: any = null;
private processing_ = false; private processing_ = false;
private processingFinishedPromise_: Promise<void>;
private onProcessingFinished_: ()=> void;
public constructor(interval = 100, intervalType: IntervalType = IntervalType.Debounce) { public constructor(interval = 100, intervalType: IntervalType = IntervalType.Debounce) {
this.interval_ = interval; this.interval_ = interval;
this.intervalType_ = intervalType; this.intervalType_ = intervalType;
this.resetFinishProcessingPromise_();
}
private resetFinishProcessingPromise_() {
this.processingFinishedPromise_ = new Promise<void>(resolve => {
this.onProcessingFinished_ = resolve;
});
} }
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
@ -77,6 +87,11 @@ export default class AsyncActionQueue {
} }
this.processing_ = false; this.processing_ = false;
if (this.queue_.length === 0) {
this.onProcessingFinished_();
this.resetFinishProcessingPromise_();
}
} }
public async reset() { public async reset() {
@ -86,30 +101,17 @@ export default class AsyncActionQueue {
} }
this.queue_ = []; this.queue_ = [];
return this.waitForAllDone(); return this.processAllNow();
} }
// Currently waitForAllDone() already finishes all the actions
// as quickly as possible so we can make it an alias.
public async processAllNow() { public async processAllNow() {
this.scheduleProcessing(1);
return this.waitForAllDone(); return this.waitForAllDone();
} }
public async waitForAllDone() { public async waitForAllDone() {
if (!this.queue_.length) return Promise.resolve(); if (!this.queue_.length) return Promise.resolve();
return this.processingFinishedPromise_;
this.scheduleProcessing(1);
return new Promise((resolve) => {
const iid = shim.setInterval(() => {
if (this.processing_) return;
if (!this.queue_.length) {
shim.clearInterval(iid);
resolve(null);
}
}, 100);
});
} }
} }

View File

@ -299,7 +299,7 @@ export default class ResourceEditWatcher {
return; return;
} }
await item.asyncSaveQueue.waitForAllDone(); await item.asyncSaveQueue.processAllNow();
try { try {
if (this.watcher_) this.watcher_.unwatch(item.path); if (this.watcher_) this.watcher_.unwatch(item.path);