Skip to content

Commit c10ba74

Browse files
committed
resolve merge conflicts
2 parents b623e50 + 9c334fc commit c10ba74

File tree

6 files changed

+93
-20
lines changed

6 files changed

+93
-20
lines changed

src/modals/TaskEditModal.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -548,9 +548,12 @@ export class TaskEditModal extends TaskModal {
548548
// If task has recurrence, update scheduled date to next uncompleted occurrence
549549
if (this.task.recurrence) {
550550
const tempTask: TaskInfo = { ...this.task, ...changes };
551-
const nextScheduledDate = updateToNextScheduledOccurrence(tempTask);
552-
if (nextScheduledDate) {
553-
changes.scheduled = nextScheduledDate;
551+
const nextDates = updateToNextScheduledOccurrence(tempTask, this.plugin.settings.maintainDueDateOffsetInRecurring);
552+
if (nextDates.scheduled) {
553+
changes.scheduled = nextDates.scheduled;
554+
}
555+
if (nextDates.due) {
556+
changes.due = nextDates.due;
554557
}
555558
}
556559
}

src/services/TaskService.ts

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -949,9 +949,12 @@ export class TaskService {
949949
if (updates.recurrence !== undefined && updates.recurrence !== originalTask.recurrence) {
950950
// Recurrence rule changed, calculate new scheduled date
951951
const tempTask: TaskInfo = { ...originalTask, ...updates };
952-
const nextScheduledDate = updateToNextScheduledOccurrence(tempTask);
953-
if (nextScheduledDate) {
954-
recurrenceUpdates.scheduled = nextScheduledDate;
952+
const nextDates = updateToNextScheduledOccurrence(tempTask, this.plugin.settings.maintainDueDateOffsetInRecurring);
953+
if (nextDates.scheduled) {
954+
recurrenceUpdates.scheduled = nextDates.scheduled;
955+
}
956+
if (nextDates.due) {
957+
recurrenceUpdates.due = nextDates.due;
955958
}
956959

957960
// Add DTSTART to recurrence rule if it's missing (scenario 1: editing recurrence rule)
@@ -1230,16 +1233,20 @@ export class TaskService {
12301233
}
12311234

12321235
// Update scheduled date to next uncompleted occurrence
1233-
const nextScheduledDate = updateToNextScheduledOccurrence(updatedTask);
1234-
if (nextScheduledDate) {
1235-
updatedTask.scheduled = nextScheduledDate;
1236+
const nextDates = updateToNextScheduledOccurrence(updatedTask, this.plugin.settings.maintainDueDateOffsetInRecurring);
1237+
if (nextDates.scheduled) {
1238+
updatedTask.scheduled = nextDates.scheduled;
1239+
}
1240+
if (nextDates.due) {
1241+
updatedTask.due = nextDates.due;
12361242
}
12371243

12381244
// Step 2: Persist to file
12391245
await this.plugin.app.fileManager.processFrontMatter(file, (frontmatter) => {
12401246
const completeInstancesField = this.plugin.fieldMapper.toUserField('completeInstances');
12411247
const dateModifiedField = this.plugin.fieldMapper.toUserField('dateModified');
12421248
const scheduledField = this.plugin.fieldMapper.toUserField('scheduled');
1249+
const dueField = this.plugin.fieldMapper.toUserField('due');
12431250
const recurrenceField = this.plugin.fieldMapper.toUserField('recurrence');
12441251

12451252
// Ensure complete_instances array exists
@@ -1268,6 +1275,12 @@ export class TaskService {
12681275
if (updatedTask.scheduled) {
12691276
frontmatter[scheduledField] = updatedTask.scheduled;
12701277
}
1278+
1279+
// Update due date if it changed
1280+
if (updatedTask.due) {
1281+
frontmatter[dueField] = updatedTask.due;
1282+
}
1283+
12711284

12721285
frontmatter[dateModifiedField] = updatedTask.dateModified;
12731286
});
@@ -1282,6 +1295,9 @@ export class TaskService {
12821295
if (updatedTask.scheduled !== freshTask.scheduled) {
12831296
expectedChanges.scheduled = updatedTask.scheduled;
12841297
}
1298+
if (updatedTask.due !== freshTask.due) {
1299+
expectedChanges.due = updatedTask.due;
1300+
}
12851301
await this.plugin.cacheManager.waitForFreshTaskData(file, expectedChanges);
12861302
}
12871303
await this.plugin.cacheManager.updateTaskInfoInCache(freshTask.path, updatedTask);

src/settings/defaults.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,5 +241,7 @@ export const DEFAULT_SETTINGS: TaskNotesSettings = {
241241
'projects', // Projects
242242
'contexts', // Contexts
243243
'tags' // Tags
244-
]
244+
],
245+
// Recurring task behavior defaults
246+
maintainDueDateOffsetInRecurring: false
245247
};

src/settings/settings.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1876,6 +1876,20 @@ export class TaskNotesSettingTab extends PluginSettingTab {
18761876
await this.plugin.saveSettings();
18771877
});
18781878
});
1879+
1880+
// Recurring task due date behavior
1881+
new Setting(container)
1882+
.setName('Maintain due date offset in recurring tasks')
1883+
.setDesc('When completing a recurring task, move the due date by the same offset as the scheduled date to preserve the time separation between them')
1884+
.addToggle(toggle => {
1885+
toggle.toggleEl.setAttribute('aria-label', 'Maintain due date offset in recurring tasks');
1886+
return toggle
1887+
.setValue(this.plugin.settings.maintainDueDateOffsetInRecurring)
1888+
.onChange(async (value) => {
1889+
this.plugin.settings.maintainDueDateOffsetInRecurring = value;
1890+
await this.plugin.saveSettings();
1891+
});
1892+
});
18791893
}
18801894

18811895
private renderFieldMappingTab(): void {

src/types/settings.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ export interface TaskNotesSettings {
103103
userField?: UserFieldMapping;
104104
// Default visible properties for task cards (when no saved view is active)
105105
defaultVisibleProperties?: string[];
106+
// Recurring task behavior
107+
maintainDueDateOffsetInRecurring: boolean;
106108
}
107109

108110
export interface DefaultReminder {

src/utils/helpers.ts

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -669,20 +669,56 @@ export function getNextUncompletedOccurrence(task: TaskInfo): Date | null {
669669
/**
670670
* Updates the scheduled date of a recurring task to its next uncompleted occurrence
671671
* Returns the updated scheduled date or null if no next occurrence
672+
* @param task Task info object
673+
* @param maintainDueOffset Whether to maintain the due date offset (from settings)
672674
*/
673-
export function updateToNextScheduledOccurrence(task: TaskInfo): string | null {
675+
export function updateToNextScheduledOccurrence(task: TaskInfo, maintainDueOffset: boolean = true): { scheduled: string | null; due: string | null } {
674676
const nextOccurrence = getNextUncompletedOccurrence(task);
675-
if (!nextOccurrence) {
676-
return null;
677-
}
677+
let nextScheduleStr: string | null = null;
678+
let nextDueStr: string | null = null;
679+
let nextDueDate: Date | null = null;
680+
681+
if (nextOccurrence) {
682+
683+
// Calculate the offset between original scheduled and due dates (only if setting is enabled)
684+
if (maintainDueOffset) {
685+
try {
686+
const originalScheduled = task.scheduled ? parseDateToUTC(task.scheduled) : null;
687+
const originalDue = task.due ? parseDateToUTC(task.due): null;
688+
689+
if (originalScheduled && originalDue) {
690+
// Calculate the time difference
691+
const offsetMs = originalDue.getTime() - originalScheduled.getTime();
692+
if(nextOccurrence) {
693+
// Apply the same offset to get the new due date
694+
nextDueDate = new Date(nextOccurrence.getTime() + offsetMs);
695+
}
696+
}
697+
} catch (error) {
698+
console.error('Error calculating next due date with offset:', error);
699+
}
700+
}
678701

679-
// Preserve time component if original scheduled date had time
680-
if (task.scheduled && task.scheduled.includes('T')) {
681-
const timePart = task.scheduled.split('T')[1];
682-
return `${formatDateForStorage(nextOccurrence)}T${timePart}`;
683-
} else {
684-
return formatDateForStorage(nextOccurrence);
702+
// Preserve time component if original scheduled date had time
703+
if (task.scheduled && task.scheduled.includes('T')) {
704+
const timePart = task.scheduled.split('T')[1];
705+
nextScheduleStr = `${formatDateForStorage(nextOccurrence)}T${timePart}`;
706+
} else {
707+
nextScheduleStr = formatDateForStorage(nextOccurrence);
708+
}
709+
if (nextDueDate && task.due && task.due.includes('T')) {
710+
const timePart = task.due.split('T')[1];
711+
nextDueStr = `${formatDateForStorage(nextDueDate)}T${timePart}`;
712+
} else if (nextDueDate) {
713+
nextDueStr = formatDateForStorage(nextDueDate);
714+
}
685715
}
716+
717+
return {
718+
scheduled: nextScheduleStr,
719+
due: nextDueStr
720+
}
721+
686722
}
687723

688724
/**

0 commit comments

Comments
 (0)