feat(fluent): add automatic sorting to data manager

Integrate sorting functionality into FluentDataManager:
- Apply global and view-specific sort criteria to filtered tasks
- Add event listeners for view config and settings changes to trigger resort
- Ensure sorting updates occur on configuration modifications

Also update task dependency badge styling to use neutral colors instead of error colors for better visual clarity.
This commit is contained in:
Quorafind 2025-10-24 15:38:56 +08:00
parent 91f2eb8318
commit e9e8979b07
4 changed files with 89 additions and 10 deletions

View file

@ -5,13 +5,15 @@ import { filterTasks } from "@/utils/task/task-filter-utils";
import { RootFilterState } from "@/components/features/task/filter/ViewTaskFilter";
import { isDataflowEnabled } from "@/dataflow/createDataflow";
import { Events, on } from "@/dataflow/events/Events";
import { sortTasks } from "@/commands/sortTaskCommands";
/**
* FluentDataManager - Stateless data loading and filtering executor
* FluentDataManager - Stateless data loading, filtering, and sorting executor
*
* Responsibilities:
* - Load tasks from dataflow or preloaded cache (returns via callback)
* - Apply filters to tasks (pure function, returns filtered tasks)
* - Apply sorting to tasks (global default first, then view-specific)
* - Register dataflow event listeners for real-time updates
* - Schedule and batch updates to prevent rapid re-renders
*
@ -171,6 +173,9 @@ export class FluentDataManager extends Component {
`[FluentData] Filtered ${filteredTasks.length} tasks from ${tasks.length} total`,
);
// Apply sorting (global default first, then view-specific)
filteredTasks = this.applySorting(filteredTasks, viewId);
return filteredTasks;
}
@ -277,6 +282,39 @@ export class FluentDataManager extends Component {
return result;
}
/**
* Apply sorting to tasks (pure function - returns sorted tasks)
* Applies global default sorting first, then view-specific sorting
* @param tasks - Tasks to sort
* @param viewId - Current view ID
* @returns Sorted tasks
*/
private applySorting(tasks: Task[], viewId: string): Task[] {
let result = [...tasks]; // Copy array to avoid mutation
// Get view configuration
const viewConfig = this.plugin.settings.viewConfiguration.find(
(view) => view.id === viewId,
);
// Collect sort criteria: global first, then view-specific
const globalCriteria = this.plugin.settings.sortCriteria || [];
const viewCriteria = viewConfig?.sortCriteria || [];
// Merge criteria: global first, then view-specific (view-specific takes precedence)
const mergedCriteria = [...globalCriteria, ...viewCriteria];
// Apply sorting if criteria exist
if (mergedCriteria.length > 0) {
console.log(
`[FluentData] Applying ${mergedCriteria.length} sort criteria to ${result.length} tasks`,
);
result = sortTasks(result, mergedCriteria, this.plugin.settings);
}
return result;
}
/**
* Register dataflow event listeners for real-time updates
* Notifies parent via onUpdateNeeded callback
@ -309,6 +347,15 @@ export class FluentDataManager extends Component {
}
}, 400);
const scheduleFilterRefresh = () => {
if (this.isInitializing()) {
return;
}
// Re-use filter debounce so resorting happens once per burst
debouncedApplyFilter();
};
// Register dataflow event listeners
if (isDataflowEnabled(this.plugin)) {
// Listen for batch operation start
@ -386,6 +433,34 @@ export class FluentDataManager extends Component {
},
),
);
// Listen for view configuration changes (sort/filter updates saved from modals)
this.registerEvent(
this.plugin.app.workspace.on(
"task-genius:view-config-changed",
(payload: { reason: string; viewId?: string } | undefined) => {
const currentViewId = this.getCurrentViewId();
if (payload?.viewId && payload.viewId !== currentViewId) {
return;
}
console.log(
`[FluentData] View config changed (reason: ${payload?.reason ?? "unknown"})`,
);
scheduleFilterRefresh();
},
),
);
// Listen for global settings changes (e.g., default sort criteria updates)
this.registerEvent(
on(this.plugin.app, Events.SETTINGS_CHANGED, () => {
console.log(
"[FluentData] Settings changed event received, scheduling resort",
);
scheduleFilterRefresh();
}),
);
}
/**

View file

@ -45,7 +45,7 @@ export class TaskListItemComponent extends Component {
// Selection management
private selectionManager: TaskSelectionManager | null = null;
private isTaskSelectedState: boolean = false;
private isTaskSelectedState = false;
constructor(
private task: Task,
@ -199,7 +199,10 @@ export class TaskListItemComponent extends Component {
// The parent view should handle this via task updates
},
).catch((error) => {
console.error("Failed to show bulk operations menu:", error);
console.error(
"Failed to show bulk operations menu:",
error,
);
});
return;
}

View file

@ -262,6 +262,7 @@
border-radius: 3px;
font-size: var(--font-ui-small);
color: var(--text-muted);
background-color: var(--background-secondary);
white-space: nowrap;
}
@ -274,15 +275,15 @@
align-items: center;
padding: 2px 6px;
margin-left: 4px;
background-color: var(--background-modifier-error);
background-color: var(--background-secondary);
border-radius: 3px;
font-size: var(--font-ui-small);
color: var(--text-error);
color: var(--text-normal);
white-space: nowrap;
}
.task-dependson:hover {
background-color: var(--background-modifier-error-hover);
/*background-color: var(--background-modifier-error-hover);*/
color: var(--text-error);
}

File diff suppressed because one or more lines are too long