fix(sync): await persistence calls and enforce update-before-save ordering

This commit is contained in:
HeroBlackInk 2026-02-25 11:46:32 +08:00
parent 49e8c94b43
commit e4c481c9a8
4 changed files with 74 additions and 52 deletions

View file

@ -368,9 +368,12 @@ export class CacheOperation {
this.plugin.debugLog(`new file path is`)
this.plugin.debugLog(searchResult)
//update metadata
await this.updateRenamedFilePath(filepath,searchResult)
this.plugin.saveSettings()
//update metadata
await this.updateRenamedFilePath(filepath,searchResult)
const saved = await this.plugin.saveSettings();
if (!saved) {
console.warn('[checkFileMetadata] saveSettings skipped or failed');
}
}
@ -457,16 +460,16 @@ export class CacheOperation {
* @param filepath -
* @param defaultProjectId - ID
*/
setDefaultProjectIdForFilepath(filepath:string, defaultProjectId:string){
const metadatas = { ...this.plugin.settings.fileMetadata }
if (!metadatas[filepath]) {
metadatas[filepath] = {}
}
metadatas[filepath].defaultProjectId = defaultProjectId
this.plugin.safeSettings?.update({ fileMetadata: metadatas })
async setDefaultProjectIdForFilepath(filepath:string, defaultProjectId:string): Promise<void> {
const metadatas = { ...this.plugin.settings.fileMetadata }
if (!metadatas[filepath]) {
metadatas[filepath] = {}
}
metadatas[filepath].defaultProjectId = defaultProjectId
}
await this.plugin.safeSettings?.update({ fileMetadata: metadatas }, true)
}
// ==========================================================================================

View file

@ -86,7 +86,6 @@ export class EventHandlers {
if (!await this.plugin.checkModuleClass()) return;
await this.plugin.cacheOperation!.updateRenamedFilePath(oldpath, file.path);
this.plugin.saveSettings();
this.plugin.logOperation?.log('FILE_RENAMED', `File renamed from ${oldpath} to ${file.path}`, file.path);
if (!await this.plugin.syncLockManager.acquire('obsidianToTodoist')) return;
@ -103,6 +102,8 @@ export class EventHandlers {
if (!this.plugin.settings.apiInitialized) return;
if (this.plugin.isSyncingFromTodoist) return;
if (this.plugin.isProcessingModify) return;
if (!file.path.endsWith('.md')) return;
if (file.path.startsWith('.obsidian/')) return;
this.plugin.isProcessingModify = true;
const filepath = file.path;

View file

@ -60,12 +60,15 @@ export class ObsidianToTodoistSync {
}
}
if (deletedCount > 0) {
this.plugin.saveSettings();
try {
await this.plugin.todoistSyncAPI.incrementalSync();
} catch (syncErr) {
console.error('[deletedTaskCheck] Post-push incremental sync failed:', syncErr);
if (deletedCount > 0) {
const saved = await this.plugin.saveSettings();
if (!saved) {
console.warn('[deletedTaskCheck] saveSettings skipped or failed');
}
try {
await this.plugin.todoistSyncAPI.incrementalSync();
} catch (syncErr) {
console.error('[deletedTaskCheck] Post-push incremental sync failed:', syncErr);
}
}
@ -106,7 +109,7 @@ export class ObsidianToTodoistSync {
this.plugin.logOperation?.log('OBSIDIAN_TASK_CREATED', `Created task in Obsidian: ${newTask.content}`, filepath, todoist_id, 'obsidian→todoist');
this.plugin.logOperation?.log('TODOIST_TASK_CREATED', `Created task in Todoist: ${newTask.content}`, filepath, todoist_id, 'obsidian→todoist');
this.plugin.cacheOperation.setTaskFileMapping(todoist_id, filepath || '');
await this.plugin.cacheOperation.setTaskFileMapping(todoist_id, filepath || '');
// Immediately sync so syncData contains the new task before any
// subsequent lineModifiedTaskCheck fires on the same line.
@ -148,11 +151,14 @@ export class ObsidianToTodoistSync {
return;
}
try {
this.plugin.saveSettings();
} catch (error) {
console.error(error);
}
try {
const saved = await this.plugin.saveSettings();
if (!saved) {
console.warn('[lineContentNewTaskCheck] saveSettings skipped or failed');
}
} catch (error) {
console.error(error);
}
} catch (error) {
console.error('Error adding task:', error);
@ -210,7 +216,7 @@ export class ObsidianToTodoistSync {
this.plugin.logOperation?.log('OBSIDIAN_TASK_CREATED', `Created task in Obsidian: ${newTask.content}`, filepath, todoist_id, 'obsidian\u2192todoist');
this.plugin.logOperation?.log('TODOIST_TASK_CREATED', `Created task in Todoist: ${newTask.content}`, filepath, todoist_id, 'obsidian\u2192todoist');
this.plugin.cacheOperation.setTaskFileMapping(todoist_id, filepath || '');
await this.plugin.cacheOperation.setTaskFileMapping(todoist_id, filepath || '');
if (currentTask.isCompleted === true) {
await this.plugin.todoistSyncAPI.CloseTask(newTask.id);
this.plugin.logOperation?.log('OBSIDIAN_TASK_COMPLETED', `Completed task in Obsidian: ${newTask.content}`, filepath, todoist_id, 'obsidian\u2192todoist');
@ -224,7 +230,10 @@ export class ObsidianToTodoistSync {
const newContent = lines.join('\n');
await this.plugin.backupOperation?.backupFile(filepath);
await this.app.vault.modify(file, newContent);
this.plugin.saveSettings();
const saved = await this.plugin.saveSettings();
if (!saved) {
console.warn('[fullTextNewTaskCheck] saveSettings skipped or failed');
}
try {
await this.plugin.todoistSyncAPI.incrementalSync();
const updatedTask = await this.plugin.todoistSyncAPI.GetTaskById(todoist_id);
@ -397,7 +406,10 @@ export class ObsidianToTodoistSync {
if (contentChanged || statusChanged || dueDateChanged || tagsChanged || priorityChanged) {
this.plugin.debugLog(lineTask);
this.plugin.debugLog(savedTask);
this.plugin.saveSettings();
const saved = await this.plugin.saveSettings();
if (!saved) {
console.warn('[lineModifiedTaskCheck] saveSettings skipped or failed');
}
try {
await this.plugin.todoistSyncAPI.incrementalSync();
const refreshedTask = await this.plugin.todoistSyncAPI.GetTaskById(lineTask_todoist_id);
@ -504,8 +516,11 @@ export class ObsidianToTodoistSync {
}
await this.plugin.todoistSyncAPI.CloseTask(taskId);
await this.plugin.fileOperation.completeTaskInTheFile(taskId);
this.plugin.saveSettings();
await this.plugin.fileOperation.completeTaskInTheFile(taskId);
const saved = await this.plugin.saveSettings();
if (!saved) {
console.warn('[closeTask] saveSettings skipped or failed');
}
try {
await this.plugin.todoistSyncAPI.incrementalSync();
const refreshedTask = await this.plugin.todoistSyncAPI.GetTaskById(taskId);
@ -555,8 +570,11 @@ export class ObsidianToTodoistSync {
}
await this.plugin.todoistSyncAPI.OpenTask(taskId);
await this.plugin.fileOperation.uncompleteTaskInTheFile(taskId);
this.plugin.saveSettings();
await this.plugin.fileOperation.uncompleteTaskInTheFile(taskId);
const saved = await this.plugin.saveSettings();
if (!saved) {
console.warn('[repoenTask] saveSettings skipped or failed');
}
try {
await this.plugin.todoistSyncAPI.incrementalSync();
const refreshedTask = await this.plugin.todoistSyncAPI.GetTaskById(taskId);

View file

@ -44,20 +44,20 @@ export class SetDefalutProjectInTheFilepathModal extends Modal {
new Setting(contentEl)
.setName('Default project')
//.setDesc('Set default project for todoist tasks in the current file')
.addDropdown(component =>
component
.addOption(this.defaultProjectId,this.defaultProjectName)
.addOptions(myProjectsOptions)
.onChange((value)=>{
this.plugin.debugLog(`project id is ${value}`)
this.plugin.cacheOperation.setDefaultProjectIdForFilepath(this.filepath,value)
this.plugin.setStatusBarText()
this.close();
})
new Setting(contentEl)
.setName('Default project')
//.setDesc('Set default project for todoist tasks in the current file')
.addDropdown(component =>
component
.addOption(this.defaultProjectId,this.defaultProjectName)
.addOptions(myProjectsOptions)
.onChange(async (value)=>{
this.plugin.debugLog(`project id is ${value}`)
await this.plugin.cacheOperation.setDefaultProjectIdForFilepath(this.filepath,value)
this.plugin.setStatusBarText()
this.close();
})
)
@ -749,7 +749,7 @@ export class TaskManagerModal extends Modal {
if (refreshed?.updated_at) {
await this.plugin.cacheOperation.updateTaskMappingSyncMeta(taskId, { updated_at: refreshed.updated_at });
}
this.plugin.safeSettings?.update({}, true);
await this.plugin.safeSettings?.update({}, true);
new Notice(`Conflict resolved: kept Obsidian version`);
} else {
const task = this.plugin.todoistSyncAPI.getTaskByIdLocal(taskId);
@ -840,7 +840,7 @@ export class TaskManagerModal extends Modal {
if (this._closed) return;
await this.plugin.cacheOperation.setTaskFileMapping(taskId, filePath, 'active', true);
await this.plugin.cacheOperation.updateTaskMappingSyncMeta(taskId, { updated_at: task.updated_at });
this.plugin.safeSettings?.update({}, true);
await this.plugin.safeSettings?.update({}, true);
new Notice(`Conflict resolved: kept Todoist version`);
}
} catch (e) {
@ -868,8 +868,8 @@ export class TaskManagerModal extends Modal {
if (this._closed) return;
await this.plugin.fileOperation.unbindTaskInFile(taskId);
await this.plugin.cacheOperation.deleteTaskFileMapping(taskId);
this.plugin.safeSettings?.update({}, true);
new Notice(`Issue task deleted`);
await this.plugin.safeSettings?.update({}, true);
new Notice(`Issue task deleted`);
} catch (e) {
console.error(`[TaskManagerModal] deleteIssueTask error:`, e);
new Notice(`Error deleting task: ${e}`);
@ -882,8 +882,8 @@ export class TaskManagerModal extends Modal {
private async reEnableTask(taskId: string, filePath: string, skipRerender = false) {
try {
await this.plugin.cacheOperation.setTaskFileMapping(taskId, filePath, 'active', true);
this.plugin.safeSettings?.update({}, true);
new Notice(`Task re-enabled`);
await this.plugin.safeSettings?.update({}, true);
new Notice(`Task re-enabled`);
} catch (e) {
console.error(`[TaskManagerModal] reEnableTask error:`, e);
new Notice(`Error re-enabling task: ${e}`);