mirror of
https://github.com/callumalpass/tasknotes.git
synced 2026-07-22 12:50:26 +00:00
feat: Add setting to hide completed tasks from overdue status (#300)
Implements a new "Hide completed tasks from overdue" setting in the misc section that controls whether completed tasks appear as overdue in agenda views. When enabled (default), completed tasks will not be classified as overdue even if their due or scheduled date has passed. Changes: - Add `hideCompletedFromOverdue` setting (default: true) to settings interface - Enhance `isOverdueTimeAware` function with completion status parameters - Update FilterService constructor to accept plugin reference for settings access - Add completion-aware overdue detection throughout date categorization - Update all FilterService instantiations with new constructor signature - Add comprehensive documentation in misc-settings.md - Create test documentation for the new feature - Update NotesView empty state message for better clarity This change helps keep overdue lists focused on actionable items while maintaining proper task categorization for completed work.
This commit is contained in:
parent
ec54a80026
commit
b01400f852
10 changed files with 151 additions and 15 deletions
|
|
@ -10,6 +10,10 @@ These settings control various plugin features and display options that don't fi
|
|||
|
||||
**Show project subtasks widget** - Display a collapsible widget showing all tasks that reference the current note as a project. This widget appears in the editor when viewing notes that have tasks referencing them as projects, providing quick access to related tasks.
|
||||
|
||||
## Task Display
|
||||
|
||||
**Hide completed tasks from overdue** - Control whether completed tasks appear as overdue in the agenda view. When enabled (default), completed tasks will not appear in the "Overdue" section of the agenda view, even if their due or scheduled date has passed. This setting affects task grouping and overdue detection throughout the plugin, helping keep overdue lists focused on actionable items.
|
||||
|
||||
## Performance Settings
|
||||
|
||||
**Disable note indexing** - Disable indexing and caching of non-task notes to improve performance in large vaults. Note: This will disable the Notes view and notes display in the Agenda view. Requires plugin restart to take effect.
|
||||
|
|
|
|||
|
|
@ -25,7 +25,8 @@ class ProjectSubtasksWidget extends WidgetType {
|
|||
this.filterService = new FilterService(
|
||||
plugin.cacheManager,
|
||||
plugin.statusManager,
|
||||
plugin.priorityManager
|
||||
plugin.priorityManager,
|
||||
plugin
|
||||
);
|
||||
|
||||
// Initialize with default filter query
|
||||
|
|
|
|||
|
|
@ -185,7 +185,8 @@ export default class TaskNotesPlugin extends Plugin {
|
|||
this.filterService = new FilterService(
|
||||
this.cacheManager,
|
||||
this.statusManager,
|
||||
this.priorityManager
|
||||
this.priorityManager,
|
||||
this
|
||||
);
|
||||
this.viewStateManager = new ViewStateManager(this.app, this);
|
||||
this.projectSubtasksService = new ProjectSubtasksService(this);
|
||||
|
|
|
|||
|
|
@ -42,7 +42,8 @@ export class FilterService extends EventEmitter {
|
|||
constructor(
|
||||
cacheManager: MinimalNativeCache,
|
||||
statusManager: StatusManager,
|
||||
priorityManager: PriorityManager
|
||||
priorityManager: PriorityManager,
|
||||
private plugin?: any // Plugin reference for accessing settings
|
||||
) {
|
||||
super();
|
||||
this.cacheManager = cacheManager;
|
||||
|
|
@ -769,25 +770,28 @@ export class FilterService extends EventEmitter {
|
|||
const referenceDate = targetDate || new Date();
|
||||
referenceDate.setHours(0, 0, 0, 0);
|
||||
|
||||
const isCompleted = this.statusManager.isCompletedStatus(task.status);
|
||||
const hideCompletedFromOverdue = this.plugin?.settings?.hideCompletedFromOverdue ?? true;
|
||||
|
||||
// For recurring tasks, check if due on the target date
|
||||
if (task.recurrence) {
|
||||
if (isDueByRRule(task, referenceDate)) {
|
||||
// If due on target date, determine which group based on target date vs today
|
||||
const referenceDateStr = format(referenceDate, 'yyyy-MM-dd');
|
||||
return this.getDateGroupFromDateString(referenceDateStr);
|
||||
return this.getDateGroupFromDateStringWithTask(referenceDateStr, isCompleted, hideCompletedFromOverdue);
|
||||
} else {
|
||||
// Recurring task not due on target date
|
||||
// If it has an original due date, use that, otherwise no due date
|
||||
if (task.due) {
|
||||
return this.getDueDateGroupFromDate(task.due);
|
||||
return this.getDateGroupFromDateStringWithTask(task.due, isCompleted, hideCompletedFromOverdue);
|
||||
}
|
||||
return 'No due date';
|
||||
}
|
||||
}
|
||||
|
||||
// Non-recurring task - use original logic
|
||||
// Non-recurring task - use completion-aware logic
|
||||
if (!task.due) return 'No due date';
|
||||
return this.getDueDateGroupFromDate(task.due);
|
||||
return this.getDateGroupFromDateStringWithTask(task.due, isCompleted, hideCompletedFromOverdue);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -797,7 +801,9 @@ export class FilterService extends EventEmitter {
|
|||
private getDateGroupFromDateString(dateString: string): string {
|
||||
const todayStr = getTodayString();
|
||||
|
||||
// Use time-aware overdue detection
|
||||
// Use time-aware overdue detection with completion-aware logic
|
||||
// For categorization purposes, we need the task to determine completion status
|
||||
// This call is for categorization only, specific task overdue checks happen elsewhere
|
||||
if (isOverdueTimeAware(dateString)) return 'Overdue';
|
||||
|
||||
// Extract date part for day-level comparisons
|
||||
|
|
@ -829,9 +835,87 @@ export class FilterService extends EventEmitter {
|
|||
return this.getDateGroupFromDateString(dueDate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to get due date group for a specific task (completion-aware)
|
||||
*/
|
||||
private getDueDateGroupForTask(task: TaskInfo): string {
|
||||
if (!task.due) return 'No due date';
|
||||
|
||||
const isCompleted = this.statusManager.isCompletedStatus(task.status);
|
||||
const hideCompletedFromOverdue = this.plugin?.settings?.hideCompletedFromOverdue ?? true;
|
||||
|
||||
return this.getDateGroupFromDateStringWithTask(task.due, isCompleted, hideCompletedFromOverdue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get date group from date string with task completion awareness
|
||||
*/
|
||||
private getDateGroupFromDateStringWithTask(dateString: string, isCompleted: boolean, hideCompletedFromOverdue: boolean): string {
|
||||
const todayStr = getTodayString();
|
||||
|
||||
// Use completion-aware overdue detection
|
||||
if (isOverdueTimeAware(dateString, isCompleted, hideCompletedFromOverdue)) return 'Overdue';
|
||||
|
||||
// Extract date part for day-level comparisons
|
||||
const datePart = getDatePart(dateString);
|
||||
if (isSameDateSafe(datePart, todayStr)) return 'Today';
|
||||
|
||||
try {
|
||||
const tomorrow = new Date();
|
||||
tomorrow.setDate(tomorrow.getDate() + 1);
|
||||
const tomorrowStr = format(tomorrow, 'yyyy-MM-dd');
|
||||
if (isSameDateSafe(datePart, tomorrowStr)) return 'Tomorrow';
|
||||
|
||||
const thisWeek = new Date();
|
||||
thisWeek.setDate(thisWeek.getDate() + 7);
|
||||
const thisWeekStr = format(thisWeek, 'yyyy-MM-dd');
|
||||
if (isBeforeDateSafe(datePart, thisWeekStr) || isSameDateSafe(datePart, thisWeekStr)) return 'This week';
|
||||
|
||||
return 'Later';
|
||||
} catch (error) {
|
||||
console.error(`Error categorizing date ${dateString}:`, error);
|
||||
return 'Invalid Date';
|
||||
}
|
||||
}
|
||||
|
||||
private getScheduledDateGroup(task: TaskInfo, targetDate?: Date): string {
|
||||
if (!task.scheduled) return 'No scheduled date';
|
||||
return this.getScheduledDateGroupFromDate(task.scheduled);
|
||||
|
||||
const isCompleted = this.statusManager.isCompletedStatus(task.status);
|
||||
const hideCompletedFromOverdue = this.plugin?.settings?.hideCompletedFromOverdue ?? true;
|
||||
|
||||
return this.getScheduledDateGroupForTask(task.scheduled, isCompleted, hideCompletedFromOverdue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get scheduled date group with task completion awareness
|
||||
*/
|
||||
private getScheduledDateGroupForTask(scheduledDate: string, isCompleted: boolean, hideCompletedFromOverdue: boolean): string {
|
||||
const todayStr = getTodayString();
|
||||
|
||||
// Use completion-aware overdue detection for past scheduled
|
||||
if (isOverdueTimeAware(scheduledDate, isCompleted, hideCompletedFromOverdue)) return 'Past scheduled';
|
||||
|
||||
// Extract date part for day-level comparisons
|
||||
const datePart = getDatePart(scheduledDate);
|
||||
if (isSameDateSafe(datePart, todayStr)) return 'Today';
|
||||
|
||||
try {
|
||||
const tomorrow = new Date();
|
||||
tomorrow.setDate(tomorrow.getDate() + 1);
|
||||
const tomorrowStr = format(tomorrow, 'yyyy-MM-dd');
|
||||
if (isSameDateSafe(datePart, tomorrowStr)) return 'Tomorrow';
|
||||
|
||||
const thisWeek = new Date();
|
||||
thisWeek.setDate(thisWeek.getDate() + 7);
|
||||
const thisWeekStr = format(thisWeek, 'yyyy-MM-dd');
|
||||
if (isBeforeDateSafe(datePart, thisWeekStr) || isSameDateSafe(datePart, thisWeekStr)) return 'This week';
|
||||
|
||||
return 'Later';
|
||||
} catch (error) {
|
||||
console.error(`Error categorizing scheduled date ${scheduledDate}:`, error);
|
||||
return 'Invalid Date';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1271,16 +1355,19 @@ export class FilterService extends EventEmitter {
|
|||
|
||||
// If showing overdue tasks and this is today, include overdue tasks on today
|
||||
if (includeOverdue && isViewingToday) {
|
||||
const isCompleted = this.statusManager.isCompletedStatus(task.status);
|
||||
const hideCompletedFromOverdue = this.plugin?.settings?.hideCompletedFromOverdue ?? true;
|
||||
|
||||
// Check if due date is overdue (show on today)
|
||||
if (task.due && getDatePart(task.due) !== dateStr) {
|
||||
if (isOverdueTimeAware(task.due)) {
|
||||
if (isOverdueTimeAware(task.due, isCompleted, hideCompletedFromOverdue)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if scheduled date is overdue (show on today)
|
||||
if (task.scheduled && getDatePart(task.scheduled) !== dateStr) {
|
||||
if (isOverdueTimeAware(task.scheduled)) {
|
||||
if (isOverdueTimeAware(task.scheduled, isCompleted, hideCompletedFromOverdue)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,6 +56,8 @@ export interface TaskNotesSettings {
|
|||
autoStopTimeTrackingNotification: boolean;
|
||||
// Project subtasks widget settings
|
||||
showProjectSubtasks: boolean;
|
||||
// Overdue behavior settings
|
||||
hideCompletedFromOverdue: boolean;
|
||||
// ICS integration settings
|
||||
icsIntegration: ICSIntegrationSettings;
|
||||
// Saved filter views
|
||||
|
|
@ -297,6 +299,8 @@ export const DEFAULT_SETTINGS: TaskNotesSettings = {
|
|||
autoStopTimeTrackingNotification: false,
|
||||
// Project subtasks widget defaults
|
||||
showProjectSubtasks: true,
|
||||
// Overdue behavior defaults
|
||||
hideCompletedFromOverdue: true,
|
||||
// ICS integration defaults
|
||||
icsIntegration: {
|
||||
defaultNoteTemplate: '',
|
||||
|
|
@ -1514,6 +1518,21 @@ export class TaskNotesSettingTab extends PluginSettingTab {
|
|||
this.plugin.notifyDataChanged();
|
||||
});
|
||||
});
|
||||
// Hide completed tasks from overdue
|
||||
new Setting(container)
|
||||
.setName('Hide completed tasks from overdue')
|
||||
.setDesc('When enabled, completed tasks will not appear as overdue in the agenda view, even if their due/scheduled date has passed')
|
||||
.addToggle(toggle => {
|
||||
toggle.toggleEl.setAttribute('aria-label', 'Hide completed tasks from overdue status');
|
||||
return toggle
|
||||
.setValue(this.plugin.settings.hideCompletedFromOverdue)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.hideCompletedFromOverdue = value;
|
||||
await this.plugin.saveSettings();
|
||||
// Refresh views to apply the change
|
||||
this.plugin.notifyDataChanged();
|
||||
});
|
||||
});
|
||||
|
||||
// Notes indexing toggle
|
||||
new Setting(container)
|
||||
|
|
|
|||
|
|
@ -670,9 +670,14 @@ export function isBeforeDateTimeAware(date1: string, date2: string): boolean {
|
|||
/**
|
||||
* Check if a date/datetime is overdue (past current date/time)
|
||||
*/
|
||||
export function isOverdueTimeAware(dateString: string): boolean {
|
||||
export function isOverdueTimeAware(dateString: string, isCompleted?: boolean, hideCompletedFromOverdue?: boolean): boolean {
|
||||
if (!dateString) return false;
|
||||
|
||||
// If the setting is enabled and task is completed, don't consider it overdue
|
||||
if (hideCompletedFromOverdue && isCompleted) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const taskDate = parseDate(dateString);
|
||||
const now = new Date();
|
||||
|
|
|
|||
|
|
@ -194,7 +194,7 @@ export class NotesView extends ItemView {
|
|||
.setName('No notes found')
|
||||
.setHeading();
|
||||
emptyState.createEl('p', {
|
||||
text: 'No notes found for the selected date. Try selecting a different date or create some notes.',
|
||||
text: 'No notes found for the selected date. Try selecting a different date in the Mini Calendar view or create some notes.',
|
||||
cls: 'notes-view__empty-description'
|
||||
});
|
||||
}
|
||||
|
|
|
|||
19
test-overdue-setting.md
Normal file
19
test-overdue-setting.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# Test: Hide Completed Tasks from Overdue Setting
|
||||
|
||||
## Test Steps
|
||||
1. Enable the "Hide completed tasks from overdue" setting in Settings > Misc
|
||||
2. Create a task with a due date in the past (e.g., yesterday)
|
||||
3. Mark the task as completed
|
||||
4. Check the agenda view - the completed task should NOT appear as overdue
|
||||
5. Disable the setting
|
||||
6. Check the agenda view again - the completed task should now appear as overdue
|
||||
|
||||
## Expected Behavior
|
||||
- When setting is enabled (default): Completed tasks don't show as overdue
|
||||
- When setting is disabled: Completed tasks show as overdue if their date has passed
|
||||
|
||||
## Implementation Details
|
||||
- Setting: `hideCompletedFromOverdue` (boolean, default: true)
|
||||
- Function: `isOverdueTimeAware(dateString, isCompleted?, hideCompletedFromOverdue?)`
|
||||
- Location: Settings > Misc tab
|
||||
- Impact: Affects agenda view, task grouping, and overdue categorization
|
||||
|
|
@ -46,7 +46,7 @@ describe('FilterService - Issue 153 Fix Verification', () => {
|
|||
|
||||
mockPriorityManager = {} as any;
|
||||
|
||||
filterService = new FilterService(mockCacheManager, mockStatusManager, mockPriorityManager);
|
||||
filterService = new FilterService(mockCacheManager, mockStatusManager, mockPriorityManager, null);
|
||||
});
|
||||
|
||||
describe('Timezone-safe date comparisons', () => {
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ describe('FilterService - Issue 153 Timezone Bug Fix Verification', () => {
|
|||
} as any;
|
||||
mockPriorityManager = {} as any;
|
||||
|
||||
filterService = new FilterService(mockCacheManager, mockStatusManager, mockPriorityManager);
|
||||
filterService = new FilterService(mockCacheManager, mockStatusManager, mockPriorityManager, null);
|
||||
});
|
||||
|
||||
describe('Date comparison edge cases that cause issue 153', () => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue