Skip to content

Commit 9c334fc

Browse files
committed
feat: add configurable due date offset for recurring tasks
Add setting to make recurring task due date advancement optional. When enabled, maintains time separation between scheduled and due dates. Defaults to false to preserve existing behavior. - Add maintainDueDateOffsetInRecurring setting to TaskNotesSettings - Add toggle in Misc settings tab - Update updateToNextScheduledOccurrence() to use setting - Update all call sites to pass setting value
1 parent 72d822a commit 9c334fc

File tree

6 files changed

+39
-17
lines changed

6 files changed

+39
-17
lines changed

src/modals/TaskEditModal.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,7 @@ 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 nextDates = updateToNextScheduledOccurrence(tempTask);
551+
const nextDates = updateToNextScheduledOccurrence(tempTask, this.plugin.settings.maintainDueDateOffsetInRecurring);
552552
if (nextDates.scheduled) {
553553
changes.scheduled = nextDates.scheduled;
554554
}

src/services/TaskService.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -949,7 +949,7 @@ 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 nextDates = updateToNextScheduledOccurrence(tempTask);
952+
const nextDates = updateToNextScheduledOccurrence(tempTask, this.plugin.settings.maintainDueDateOffsetInRecurring);
953953
if (nextDates.scheduled) {
954954
recurrenceUpdates.scheduled = nextDates.scheduled;
955955
}
@@ -1233,7 +1233,7 @@ export class TaskService {
12331233
}
12341234

12351235
// Update scheduled date to next uncompleted occurrence
1236-
const nextDates = updateToNextScheduledOccurrence(updatedTask);
1236+
const nextDates = updateToNextScheduledOccurrence(updatedTask, this.plugin.settings.maintainDueDateOffsetInRecurring);
12371237
if (nextDates.scheduled) {
12381238
updatedTask.scheduled = nextDates.scheduled;
12391239
}

src/settings/defaults.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,5 +231,7 @@ export const DEFAULT_SETTINGS: TaskNotesSettings = {
231231
// Webhook defaults
232232
webhooks: [],
233233
// User Fields defaults (multiple)
234-
userFields: []
234+
userFields: [],
235+
// Recurring task behavior defaults
236+
maintainDueDateOffsetInRecurring: false
235237
};

src/settings/settings.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1783,6 +1783,20 @@ export class TaskNotesSettingTab extends PluginSettingTab {
17831783
await this.plugin.saveSettings();
17841784
});
17851785
});
1786+
1787+
// Recurring task due date behavior
1788+
new Setting(container)
1789+
.setName('Maintain due date offset in recurring tasks')
1790+
.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')
1791+
.addToggle(toggle => {
1792+
toggle.toggleEl.setAttribute('aria-label', 'Maintain due date offset in recurring tasks');
1793+
return toggle
1794+
.setValue(this.plugin.settings.maintainDueDateOffsetInRecurring)
1795+
.onChange(async (value) => {
1796+
this.plugin.settings.maintainDueDateOffsetInRecurring = value;
1797+
await this.plugin.saveSettings();
1798+
});
1799+
});
17861800
}
17871801

17881802
private renderFieldMappingTab(): void {

src/types/settings.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ export interface TaskNotesSettings {
101101
userFields?: UserMappedField[];
102102
// Legacy single-field (for migration only)
103103
userField?: UserFieldMapping;
104+
// Recurring task behavior
105+
maintainDueDateOffsetInRecurring: boolean;
104106
}
105107

106108
export interface DefaultReminder {

src/utils/helpers.ts

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -669,30 +669,34 @@ 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): { scheduled: string | null; due: string | null } {
675+
export function updateToNextScheduledOccurrence(task: TaskInfo, maintainDueOffset: boolean = true): { scheduled: string | null; due: string | null } {
674676
const nextOccurrence = getNextUncompletedOccurrence(task);
675677
let nextScheduleStr: string | null = null;
676678
let nextDueStr: string | null = null;
677679
let nextDueDate: Date | null = null;
678680

679681
if (nextOccurrence) {
680682

681-
// Calculate the offset between original scheduled and due dates
682-
try {
683-
const originalScheduled = task.scheduled ? parseDateToUTC(task.scheduled) : null;
684-
const originalDue = task.due ? parseDateToUTC(task.due): null;
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;
685688

686-
if (originalScheduled && originalDue) {
687-
// Calculate the time difference
688-
const offsetMs = originalDue.getTime() - originalScheduled.getTime();
689-
if(nextOccurrence) {
690-
// Apply the same offset to get the new due date
691-
nextDueDate = new Date(nextOccurrence.getTime() + offsetMs);
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+
}
692696
}
697+
} catch (error) {
698+
console.error('Error calculating next due date with offset:', error);
693699
}
694-
} catch (error) {
695-
console.error('Error calculating next due date with offset:', error);
696700
}
697701

698702
// Preserve time component if original scheduled date had time

0 commit comments

Comments
 (0)