* Update a single property of a task following the deterministic data flow pattern
(
task: TaskInfo,
property: keyof TaskInfo,
value: unknown,
options: { silent?: boolean } = {}
)
| 563 | * Update a single property of a task following the deterministic data flow pattern |
| 564 | */ |
| 565 | async updateProperty( |
| 566 | task: TaskInfo, |
| 567 | property: keyof TaskInfo, |
| 568 | value: unknown, |
| 569 | options: { silent?: boolean } = {} |
| 570 | ): Promise<TaskInfo> { |
| 571 | try { |
| 572 | const file = this.plugin.app.vault.getAbstractFileByPath(task.path); |
| 573 | if (!(file instanceof TFile)) { |
| 574 | throw new Error(`Cannot find task file: ${task.path}`); |
| 575 | } |
| 576 | |
| 577 | // Get fresh task data to prevent overwrites |
| 578 | const freshTask = (await this.plugin.cacheManager.getTaskInfo(task.path)) || task; |
| 579 | |
| 580 | // Step 1: Construct new state in memory using fresh data |
| 581 | const updatePlan = buildTaskPropertyUpdatePlan({ |
| 582 | freshTask, |
| 583 | property, |
| 584 | value, |
| 585 | currentTimestamp: getCurrentTimestamp(), |
| 586 | currentDateString: this.getCompletionDateForTask(freshTask), |
| 587 | normalizeStatusValue: (candidate) => this.normalizeStatusValue(candidate), |
| 588 | isCompletedStatus: (status) => this.plugin.statusManager.isCompletedStatus(status), |
| 589 | }); |
| 590 | |
| 591 | if (property === "scheduled") { |
| 592 | applyGoogleCalendarRecurringExceptionForScheduledChange( |
| 593 | freshTask, |
| 594 | updatePlan.normalizedValue, |
| 595 | updatePlan.updatedTask |
| 596 | ); |
| 597 | } |
| 598 | if (property === "recurrence" || property === "recurrence_anchor") { |
| 599 | applyGoogleCalendarRecurringExceptionCleanup(updatePlan.updatedTask); |
| 600 | } |
| 601 | |
| 602 | // Step 2: Persist to file |
| 603 | await this.plugin.app.fileManager.processFrontMatter(file, (frontmatter) => { |
| 604 | // Use field mapper to get the correct frontmatter property name |
| 605 | const fieldName = resolveTaskPropertyFrontmatterField( |
| 606 | this.plugin.fieldMapper, |
| 607 | property |
| 608 | ); |
| 609 | const dateModifiedField = this.plugin.fieldMapper.toUserField("dateModified"); |
| 610 | const completedDateField = this.plugin.fieldMapper.toUserField("completedDate"); |
| 611 | applyTaskPropertyFrontmatterChange({ |
| 612 | frontmatter, |
| 613 | property, |
| 614 | fieldName, |
| 615 | rawValue: value, |
| 616 | normalizedValue: updatePlan.normalizedValue, |
| 617 | dateModified: updatePlan.dateModified, |
| 618 | dateModifiedField, |
| 619 | completedDateField, |
| 620 | isRecurring: !!freshTask.recurrence, |
| 621 | normalizeStatusValue: (candidate) => this.normalizeStatusValue(candidate), |
| 622 | isCompletedStatus: (status) => |