refactor(compare): unify sync and checker comparisons via task parser

This commit is contained in:
HeroBlackInk 2026-02-26 17:14:44 +08:00
parent 04139499dd
commit 5199278550
3 changed files with 46 additions and 41 deletions

View file

@ -197,22 +197,6 @@ export class DatabaseChecker {
}
}
private normalizeLabels(labels: string[] | undefined): string[] {
if (!labels || labels.length === 0) return [];
return labels
.filter(label => label && label !== 'todoist')
.map(label => label.trim())
.filter(label => label.length > 0)
.sort();
}
private labelsEqual(a: string[] | undefined, b: string[] | undefined): boolean {
const labelsA = this.normalizeLabels(a);
const labelsB = this.normalizeLabels(b);
if (labelsA.length !== labelsB.length) return false;
return labelsA.every((label, index) => label === labelsB[index]);
}
private async confirmTodoistTaskMissing(taskId: string): Promise<boolean> {
const todoistSyncAPI = this.plugin.todoistSyncAPI;
if (!todoistSyncAPI) return false;
@ -273,7 +257,8 @@ export class DatabaseChecker {
try {
const fileOperation = this.plugin.fileOperation;
const todoistSyncAPI = this.plugin.todoistSyncAPI;
if (!fileOperation || !todoistSyncAPI) {
const taskParser = this.plugin.taskParser;
if (!fileOperation || !todoistSyncAPI || !taskParser) {
throw new Error('Required modules are not initialized for database check');
}
@ -438,6 +423,11 @@ export class DatabaseChecker {
unknownIssueCount: number,
caseStats: NonNullable<DatabaseCheckResult['caseStats']>
}> {
const taskParser = this.plugin.taskParser;
if (!taskParser) {
throw new Error('TaskParser is not initialized for database comparison');
}
const issues: DatabaseCheckIssue[] = [];
const summary = {
mappingFileNotFound: 0,
@ -594,7 +584,7 @@ export class DatabaseChecker {
let hasSemanticMismatch = false;
if (vaultTask.content.trim() !== todoistTask.content.trim()) {
if (!taskParser.taskContentCompare(vaultTask, todoistTask)) {
emitIssue({
type: 'sync_content_mismatch',
filePath: vaultTask.filePath,
@ -607,8 +597,8 @@ export class DatabaseChecker {
hasSemanticMismatch = true;
}
const todoistChecked = !!(todoistTask as unknown as { checked?: boolean }).checked;
if (vaultTask.isCompleted !== todoistChecked) {
const todoistChecked = !!todoistTask.checked;
if (!taskParser.taskStatusCompare(vaultTask, todoistTask)) {
emitIssue({
type: 'sync_completion_mismatch',
filePath: vaultTask.filePath,
@ -623,7 +613,7 @@ export class DatabaseChecker {
const vaultDueDate = vaultTask.dueDate || '';
const todoistDueDate = todoistTask.dueDate || '';
if (vaultDueDate !== todoistDueDate) {
if (!taskParser.compareTaskDueDate(vaultTask, todoistTask)) {
emitIssue({
type: 'sync_due_mismatch',
filePath: vaultTask.filePath,
@ -638,7 +628,7 @@ export class DatabaseChecker {
const vaultPriority = vaultTask.priority || 1;
const todoistPriority = todoistTask.priority || 1;
if (vaultPriority !== todoistPriority) {
if (!taskParser.taskPriorityCompare(vaultTask, todoistTask)) {
emitIssue({
type: 'sync_priority_mismatch',
filePath: vaultTask.filePath,
@ -651,9 +641,9 @@ export class DatabaseChecker {
hasSemanticMismatch = true;
}
const obsidianLabels = this.normalizeLabels(vaultTask.labels);
const todoistLabels = this.normalizeLabels(todoistTask.labels);
if (!this.labelsEqual(obsidianLabels, todoistLabels)) {
const obsidianLabels = taskParser.normalizeLabelsForCompare(vaultTask.labels);
const todoistLabels = taskParser.normalizeLabelsForCompare(todoistTask.labels);
if (!taskParser.taskTagCompare(vaultTask, todoistTask)) {
emitIssue({
type: 'sync_labels_mismatch',
filePath: vaultTask.filePath,
@ -668,7 +658,7 @@ export class DatabaseChecker {
const fileMetadata = this.plugin.settings.fileMetadata?.[vaultTask.filePath];
const expectedProjectId = fileMetadata?.defaultProjectId;
if (expectedProjectId && expectedProjectId !== todoistTask.projectId) {
if (expectedProjectId && !(await taskParser.taskProjectCompare({ projectId: expectedProjectId }, todoistTask))) {
emitIssue({
type: 'sync_project_mismatch',
filePath: vaultTask.filePath,

View file

@ -52,6 +52,7 @@ type LineTaskComparable = {
isCompleted?: boolean;
dueDate?: string;
projectId?: string;
priority?: number;
};
type TodoistTaskComparable = {
@ -59,7 +60,9 @@ type TodoistTaskComparable = {
labels?: string[];
checked?: boolean;
due?: { date?: string };
dueDate?: string;
projectId?: string;
priority?: number;
};
@ -356,10 +359,10 @@ export class TaskParser {
//task content compare
taskContentCompare(lineTask:LineTaskComparable,todoistTask:TodoistTaskComparable) {
const lineTaskContent = lineTask.content
const lineTaskContent = (lineTask.content || '').trim();
//this.plugin.debugLog(dataviewTaskContent)
const todoistTaskContent = todoistTask.content
const todoistTaskContent = (todoistTask.content || '').trim();
//this.plugin.debugLog(todoistTask.content)
//content 是否修改
@ -368,29 +371,41 @@ export class TaskParser {
}
taskTagCompare(lineTask:Object,todoistTask:Object) {
const lineTaskTags = (lineTask as any).labels || [];
const todoistTaskTags = (todoistTask as any).labels || [];
const sortedLine = [...lineTaskTags].sort();
const sortedTodoist = [...todoistTaskTags].sort();
return sortedLine.length === sortedTodoist.length && sortedLine.every((val: string, index: number) => val === sortedTodoist[index]);
normalizeLabelsForCompare(labels: string[] | undefined): string[] {
if (!labels || labels.length === 0) return [];
const normalized = labels
.map(label => (label ?? '').trim())
.filter(label => label.length > 0);
const deduped = Array.from(new Set(normalized));
return deduped.sort();
}
taskTagCompare(lineTask:LineTaskComparable,todoistTask:TodoistTaskComparable) {
const lineTaskTags = this.normalizeLabelsForCompare(lineTask.labels || []);
const todoistTaskTags = this.normalizeLabelsForCompare(todoistTask.labels || []);
return lineTaskTags.length === todoistTaskTags.length && lineTaskTags.every((val: string, index: number) => val === todoistTaskTags[index]);
}
taskStatusCompare(lineTask:Object,todoistTask:Object) {
return (lineTask as any).isCompleted === !!(todoistTask as any).checked;
taskStatusCompare(lineTask:LineTaskComparable,todoistTask:TodoistTaskComparable) {
return !!lineTask.isCompleted === !!todoistTask.checked;
}
compareTaskDueDate(lineTask: object, todoistTask: object): boolean {
const lineTaskDue = (lineTask as any).dueDate || "";
const todoistDue = (todoistTask as any).due;
const todoistDueDate = todoistDue?.date || "";
compareTaskDueDate(lineTask: LineTaskComparable, todoistTask: TodoistTaskComparable): boolean {
const lineTaskDue = lineTask.dueDate || "";
const todoistDueDate = todoistTask.due?.date || todoistTask.dueDate || "";
if (lineTaskDue === "" && todoistDueDate === "") return true;
if (lineTaskDue === "" || todoistDueDate === "") return false;
return lineTaskDue === todoistDueDate;
}
taskPriorityCompare(lineTask:LineTaskComparable, todoistTask:TodoistTaskComparable): boolean {
const linePriority = lineTask.priority || 1;
const todoistPriority = todoistTask.priority || 1;
return linePriority === todoistPriority;
}
//task project id compare

View file

@ -347,7 +347,7 @@ export class ObsidianToTodoistSync {
const tagsModified = !this.plugin.taskParser.taskTagCompare(lineTask, savedTask);
const statusModified = !this.plugin.taskParser.taskStatusCompare(lineTask, savedTask);
const dueDateModified = !this.plugin.taskParser.compareTaskDueDate(lineTask, savedTask);
const priorityModified = !(lineTask.priority === savedTask.priority);
const priorityModified = !this.plugin.taskParser.taskPriorityCompare(lineTask, savedTask);
try {
let contentChanged = false;