mirror of
https://github.com/taskgenius/taskgenius-plugin.git
synced 2026-07-22 06:40:25 +00:00
fix(fluent): resolve view mode availability and navigation rendering
- Fix navigation not re-rendering when transitioning from empty to non-empty modes - Make projects view mode availability dynamic based on project selection - Ensure filtered tasks are recomputed when navigating between views - Clean up trailing commas for code consistency
This commit is contained in:
parent
e9e8979b07
commit
3fbdf4f1d2
3 changed files with 162 additions and 137 deletions
|
|
@ -32,7 +32,7 @@ export class TopNavigation extends Component {
|
|||
private onSortClick: () => void,
|
||||
private onSettingsClick: () => void,
|
||||
availableModes?: ViewMode[],
|
||||
private onToggleSidebar?: () => void,
|
||||
private onToggleSidebar?: () => void
|
||||
) {
|
||||
super();
|
||||
this.containerEl = containerEl;
|
||||
|
|
@ -60,14 +60,14 @@ export class TopNavigation extends Component {
|
|||
this.registerEvent(
|
||||
on(this.plugin.app, Events.CACHE_READY, () => {
|
||||
this.updateNotificationCount();
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
// Listen for task cache updates
|
||||
this.registerEvent(
|
||||
on(this.plugin.app, Events.TASK_CACHE_UPDATED, () => {
|
||||
this.updateNotificationCount();
|
||||
}),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -116,7 +116,7 @@ export class TopNavigation extends Component {
|
|||
} catch (error) {
|
||||
console.warn(
|
||||
"[FluentTopNavigation] Failed to update notification count:",
|
||||
error,
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -180,7 +180,7 @@ export class TopNavigation extends Component {
|
|||
badge.show();
|
||||
}
|
||||
this.registerDomEvent(notificationBtn, "click", (e) =>
|
||||
this.showNotifications(e),
|
||||
this.showNotifications(e)
|
||||
);
|
||||
|
||||
// Settings button
|
||||
|
|
@ -189,7 +189,7 @@ export class TopNavigation extends Component {
|
|||
});
|
||||
setIcon(settingsBtn, "settings");
|
||||
this.registerDomEvent(settingsBtn, "click", () =>
|
||||
this.onSettingsClick(),
|
||||
this.onSettingsClick()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -197,7 +197,7 @@ export class TopNavigation extends Component {
|
|||
container: HTMLElement,
|
||||
mode: ViewMode,
|
||||
icon: string,
|
||||
label: string,
|
||||
label: string
|
||||
) {
|
||||
const tab = container.createEl("button", {
|
||||
cls: ["fluent-view-tab", "clickable-icon"],
|
||||
|
|
@ -228,7 +228,7 @@ export class TopNavigation extends Component {
|
|||
});
|
||||
|
||||
const activeTab = this.containerEl.querySelector(
|
||||
`[data-mode="${mode}"]`,
|
||||
`[data-mode="${mode}"]`
|
||||
);
|
||||
if (activeTab) {
|
||||
activeTab.addClass("is-active");
|
||||
|
|
@ -278,7 +278,7 @@ export class TopNavigation extends Component {
|
|||
} else {
|
||||
menu.addItem((item) => {
|
||||
item.setTitle(
|
||||
`${overdueTasks.length} overdue tasks`,
|
||||
`${overdueTasks.length} overdue tasks`
|
||||
).setDisabled(true);
|
||||
});
|
||||
|
||||
|
|
@ -312,7 +312,7 @@ export class TopNavigation extends Component {
|
|||
|
||||
private updateNotificationBadge() {
|
||||
const badge = this.containerEl.querySelector(
|
||||
".fluent-notification-badge",
|
||||
".fluent-notification-badge"
|
||||
);
|
||||
if (badge instanceof HTMLElement) {
|
||||
badge.textContent = String(this.notificationCount);
|
||||
|
|
@ -358,13 +358,14 @@ export class TopNavigation extends Component {
|
|||
this.viewTabsContainer,
|
||||
mode,
|
||||
config.icon,
|
||||
config.label,
|
||||
config.label
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public updateAvailableModes(modes: ViewMode[]) {
|
||||
const wasEmpty = this.availableModes.length === 0;
|
||||
this.availableModes = modes;
|
||||
|
||||
// Hide entire navigation if no modes available
|
||||
|
|
@ -373,6 +374,12 @@ export class TopNavigation extends Component {
|
|||
return;
|
||||
}
|
||||
|
||||
// If transitioning from empty to non-empty, need to re-render entire UI
|
||||
if (wasEmpty && modes.length > 0) {
|
||||
this.render();
|
||||
return;
|
||||
}
|
||||
|
||||
// Show navigation when modes are available
|
||||
this.containerEl.show();
|
||||
|
||||
|
|
@ -385,7 +392,7 @@ export class TopNavigation extends Component {
|
|||
|
||||
// Update center section visibility (this should always be visible now since we handle empty modes above)
|
||||
const centerSection = this.containerEl.querySelector(
|
||||
".fluent-nav-center",
|
||||
".fluent-nav-center"
|
||||
) as HTMLElement;
|
||||
if (centerSection) {
|
||||
centerSection.show();
|
||||
|
|
|
|||
|
|
@ -38,7 +38,8 @@ const VIEW_MODE_CONFIG: Record<string, ViewMode[]> = {
|
|||
today: ["list", "tree", "kanban", "calendar"],
|
||||
upcoming: ["list", "tree", "kanban", "calendar"],
|
||||
flagged: ["list", "tree", "kanban", "calendar"],
|
||||
projects: ["list", "tree", "kanban", "calendar"],
|
||||
// Projects: empty by default (overview mode), but FluentTaskView enables modes when a project is selected
|
||||
projects: [],
|
||||
|
||||
// Specialized views with limited or no modes
|
||||
tags: [],
|
||||
|
|
@ -92,7 +93,7 @@ export class FluentComponentManager extends Component {
|
|||
onTaskContextMenu: (event: MouseEvent, task: Task) => void;
|
||||
onKanbanTaskStatusUpdate: (
|
||||
taskId: string,
|
||||
newStatusMark: string,
|
||||
newStatusMark: string
|
||||
) => void;
|
||||
};
|
||||
|
||||
|
|
@ -106,15 +107,15 @@ export class FluentComponentManager extends Component {
|
|||
onTaskCompleted: (task: Task) => void;
|
||||
onTaskUpdate: (
|
||||
originalTask: Task,
|
||||
updatedTask: Task,
|
||||
updatedTask: Task
|
||||
) => Promise<void>;
|
||||
onTaskContextMenu: (event: MouseEvent, task: Task) => void;
|
||||
onKanbanTaskStatusUpdate: (
|
||||
taskId: string,
|
||||
newStatusMark: string,
|
||||
newStatusMark: string
|
||||
) => void;
|
||||
},
|
||||
private selectionManager?: TaskSelectionManager,
|
||||
private selectionManager?: TaskSelectionManager
|
||||
) {
|
||||
super();
|
||||
this.viewHandlers = viewHandlers;
|
||||
|
|
@ -144,7 +145,7 @@ export class FluentComponentManager extends Component {
|
|||
this.app,
|
||||
this.plugin,
|
||||
this.contentArea,
|
||||
viewHandlers,
|
||||
viewHandlers
|
||||
);
|
||||
this.parentView.addChild(this.viewComponentManager);
|
||||
|
||||
|
|
@ -165,7 +166,7 @@ export class FluentComponentManager extends Component {
|
|||
if (task) this.viewHandlers.onTaskContextMenu(event, task);
|
||||
},
|
||||
selectionManager: this.selectionManager,
|
||||
},
|
||||
}
|
||||
);
|
||||
this.parentView.addChild(this.contentComponent);
|
||||
this.contentComponent.load();
|
||||
|
|
@ -186,14 +187,14 @@ export class FluentComponentManager extends Component {
|
|||
if (originalTask && updatedTask) {
|
||||
await this.viewHandlers.onTaskUpdate(
|
||||
originalTask,
|
||||
updatedTask,
|
||||
updatedTask
|
||||
);
|
||||
}
|
||||
},
|
||||
onTaskContextMenu: (event, task) => {
|
||||
if (task) this.viewHandlers.onTaskContextMenu(event, task);
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
this.parentView.addChild(this.forecastComponent);
|
||||
this.forecastComponent.load();
|
||||
|
|
@ -214,14 +215,14 @@ export class FluentComponentManager extends Component {
|
|||
if (originalTask && updatedTask) {
|
||||
await this.viewHandlers.onTaskUpdate(
|
||||
originalTask,
|
||||
updatedTask,
|
||||
updatedTask
|
||||
);
|
||||
}
|
||||
},
|
||||
onTaskContextMenu: (event, task) => {
|
||||
if (task) this.viewHandlers.onTaskContextMenu(event, task);
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
this.parentView.addChild(this.tagsComponent);
|
||||
this.tagsComponent.load();
|
||||
|
|
@ -242,14 +243,14 @@ export class FluentComponentManager extends Component {
|
|||
if (originalTask && updatedTask) {
|
||||
await this.viewHandlers.onTaskUpdate(
|
||||
originalTask,
|
||||
updatedTask,
|
||||
updatedTask
|
||||
);
|
||||
}
|
||||
},
|
||||
onTaskContextMenu: (event, task) => {
|
||||
if (task) this.viewHandlers.onTaskContextMenu(event, task);
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
this.parentView.addChild(this.projectsComponent);
|
||||
this.projectsComponent.load();
|
||||
|
|
@ -270,14 +271,14 @@ export class FluentComponentManager extends Component {
|
|||
if (originalTask && updatedTask) {
|
||||
await this.viewHandlers.onTaskUpdate(
|
||||
originalTask,
|
||||
updatedTask,
|
||||
updatedTask
|
||||
);
|
||||
}
|
||||
},
|
||||
onTaskContextMenu: (event, task) => {
|
||||
if (task) this.viewHandlers.onTaskContextMenu(event, task);
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
this.parentView.addChild(this.reviewComponent);
|
||||
this.reviewComponent.load();
|
||||
|
|
@ -303,7 +304,7 @@ export class FluentComponentManager extends Component {
|
|||
onEventContextMenu: (ev: MouseEvent, event: Task) => {
|
||||
if (event) this.viewHandlers.onTaskContextMenu(ev, event);
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
this.parentView.addChild(this.calendarComponent);
|
||||
|
||||
|
|
@ -317,7 +318,7 @@ export class FluentComponentManager extends Component {
|
|||
onTaskStatusUpdate: async (taskId, newStatusMark) => {
|
||||
this.viewHandlers.onKanbanTaskStatusUpdate(
|
||||
taskId,
|
||||
newStatusMark,
|
||||
newStatusMark
|
||||
);
|
||||
},
|
||||
onTaskSelected: (task) => {
|
||||
|
|
@ -329,7 +330,7 @@ export class FluentComponentManager extends Component {
|
|||
onTaskContextMenu: (event, task) => {
|
||||
if (task) this.viewHandlers.onTaskContextMenu(event, task);
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
this.parentView.addChild(this.kanbanComponent);
|
||||
|
||||
|
|
@ -344,7 +345,7 @@ export class FluentComponentManager extends Component {
|
|||
this.viewHandlers.onTaskCompleted(task),
|
||||
onTaskContextMenu: (event: MouseEvent, task: Task) =>
|
||||
this.viewHandlers.onTaskContextMenu(event, task),
|
||||
},
|
||||
}
|
||||
);
|
||||
this.parentView.addChild(this.ganttComponent);
|
||||
this.ganttComponent.load();
|
||||
|
|
@ -375,7 +376,7 @@ export class FluentComponentManager extends Component {
|
|||
// Smart hiding - only hide currently visible component (unless initial hide)
|
||||
if (!isInitialHide && this.currentVisibleComponent) {
|
||||
console.log(
|
||||
"[FluentComponent] Smart hide - only hiding current visible component",
|
||||
"[FluentComponent] Smart hide - only hiding current visible component"
|
||||
);
|
||||
this.currentVisibleComponent.containerEl?.hide();
|
||||
this.currentVisibleComponent = null;
|
||||
|
|
@ -383,7 +384,7 @@ export class FluentComponentManager extends Component {
|
|||
// Hide all components
|
||||
console.log(
|
||||
"[FluentComponent] Hiding all components",
|
||||
isInitialHide ? "(initial hide)" : "",
|
||||
isInitialHide ? "(initial hide)" : ""
|
||||
);
|
||||
this.contentComponent?.containerEl.hide();
|
||||
this.forecastComponent?.containerEl.hide();
|
||||
|
|
@ -416,20 +417,20 @@ export class FluentComponentManager extends Component {
|
|||
filteredTasks: Task[],
|
||||
currentFilterState: RootFilterState | null,
|
||||
viewMode: ViewMode,
|
||||
project?: string | null,
|
||||
project?: string | null
|
||||
): void {
|
||||
console.log(
|
||||
"[FluentComponent] switchView called with:",
|
||||
viewId,
|
||||
"viewMode:",
|
||||
viewMode,
|
||||
viewMode
|
||||
);
|
||||
|
||||
// Remove transient overlays (loading/error/empty) before showing components
|
||||
if (this.contentArea) {
|
||||
this.contentArea
|
||||
.querySelectorAll(
|
||||
".tg-fluent-loading, .tg-fluent-error-state, .tg-fluent-empty-state",
|
||||
".tg-fluent-loading, .tg-fluent-error-state, .tg-fluent-empty-state"
|
||||
)
|
||||
.forEach((el) => el.remove());
|
||||
}
|
||||
|
|
@ -439,38 +440,24 @@ export class FluentComponentManager extends Component {
|
|||
this.hideAllComponents();
|
||||
|
||||
// Check if current view supports multiple view modes and we're in a non-list mode
|
||||
const viewModes = this.getAvailableModesForView(viewId);
|
||||
const viewModes = this.getAvailableModesForView(viewId, project);
|
||||
|
||||
// If the current view mode is not available for this view, reset to first available or list
|
||||
if (viewModes.length > 0 && !viewModes.includes(viewMode)) {
|
||||
viewMode = viewModes[0];
|
||||
}
|
||||
|
||||
console.log(
|
||||
"[FluentComponent] Is content-based view?",
|
||||
this.isContentBasedView(viewId),
|
||||
"View mode:",
|
||||
viewMode,
|
||||
"Available modes:",
|
||||
viewModes,
|
||||
);
|
||||
|
||||
if (
|
||||
this.isContentBasedView(viewId) &&
|
||||
viewModes.length > 0 &&
|
||||
viewMode !== "list" &&
|
||||
viewMode !== "tree"
|
||||
) {
|
||||
// For content-based views in kanban/calendar mode, use special rendering
|
||||
console.log(
|
||||
"[FluentComponent] Using renderContentWithViewMode for non-list/tree mode:",
|
||||
viewMode,
|
||||
);
|
||||
this.renderContentWithViewMode(
|
||||
viewId,
|
||||
tasks,
|
||||
filteredTasks,
|
||||
viewMode,
|
||||
viewMode
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
@ -491,7 +478,7 @@ export class FluentComponentManager extends Component {
|
|||
this.app,
|
||||
this.plugin,
|
||||
twoColumnConfig,
|
||||
viewId,
|
||||
viewId
|
||||
);
|
||||
this.parentView.addChild(twoColumnComponent);
|
||||
|
||||
|
|
@ -509,7 +496,7 @@ export class FluentComponentManager extends Component {
|
|||
const twoColumnComponent = this.twoColumnViewComponents.get(viewId);
|
||||
if (!twoColumnComponent) {
|
||||
console.warn(
|
||||
`[FluentComponent] Missing two column component for view ${viewId}`,
|
||||
`[FluentComponent] Missing two column component for view ${viewId}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
@ -521,7 +508,7 @@ export class FluentComponentManager extends Component {
|
|||
// Check if it's a special view managed by ViewComponentManager
|
||||
if (this.viewComponentManager.isSpecialView(viewId)) {
|
||||
targetComponent = this.viewComponentManager.showComponent(
|
||||
viewId,
|
||||
viewId
|
||||
) as ManagedViewComponent | null;
|
||||
} else if (
|
||||
specificViewType === "forecast" ||
|
||||
|
|
@ -543,13 +530,13 @@ export class FluentComponentManager extends Component {
|
|||
// 2. Without project selection (from view navigation) → use ProjectsComponent for overview
|
||||
if (project) {
|
||||
console.log(
|
||||
"[FluentComponent] Projects view with selected project - using ContentComponent",
|
||||
"[FluentComponent] Projects view with selected project - using ContentComponent"
|
||||
);
|
||||
targetComponent = this.contentComponent;
|
||||
modeForComponent = viewId;
|
||||
} else {
|
||||
console.log(
|
||||
"[FluentComponent] Projects view without selection - using ProjectsComponent",
|
||||
"[FluentComponent] Projects view without selection - using ProjectsComponent"
|
||||
);
|
||||
targetComponent = this.projectsComponent;
|
||||
modeForComponent = viewId;
|
||||
|
|
@ -582,13 +569,13 @@ export class FluentComponentManager extends Component {
|
|||
|
||||
console.log(
|
||||
"[FluentComponent] Target component determined:",
|
||||
targetComponent?.constructor?.name,
|
||||
targetComponent?.constructor?.name
|
||||
);
|
||||
|
||||
if (targetComponent) {
|
||||
console.log(
|
||||
`[FluentComponent] Activating component for view ${viewId}:`,
|
||||
targetComponent.constructor.name,
|
||||
targetComponent.constructor.name
|
||||
);
|
||||
targetComponent.containerEl.show();
|
||||
this.currentVisibleComponent = targetComponent;
|
||||
|
|
@ -596,7 +583,7 @@ export class FluentComponentManager extends Component {
|
|||
// Set view mode first for ContentComponent
|
||||
if (typeof targetComponent.setViewMode === "function") {
|
||||
console.log(
|
||||
`[FluentComponent] Setting view mode for ${viewId} to ${modeForComponent}`,
|
||||
`[FluentComponent] Setting view mode for ${viewId} to ${modeForComponent}`
|
||||
);
|
||||
targetComponent.setViewMode(modeForComponent as any, project);
|
||||
}
|
||||
|
|
@ -608,7 +595,7 @@ export class FluentComponentManager extends Component {
|
|||
if (viewId === "review" || viewId === "tags") {
|
||||
console.log(
|
||||
`[FluentComponent] Calling setTasks for ${viewId} with ALL tasks:`,
|
||||
tasks.length,
|
||||
tasks.length
|
||||
);
|
||||
targetComponent.setTasks(tasks);
|
||||
} else if (viewId === "projects" && !project) {
|
||||
|
|
@ -616,7 +603,7 @@ export class FluentComponentManager extends Component {
|
|||
// and FILTERED tasks to apply filter to project task lists
|
||||
// This ensures: left sidebar shows all projects, right panel shows filtered tasks
|
||||
console.log(
|
||||
`[FluentComponent] Calling setTasks for projects with ALL tasks (${tasks.length}) and FILTERED tasks (${filteredTasks.length})`,
|
||||
`[FluentComponent] Calling setTasks for projects with ALL tasks (${tasks.length}) and FILTERED tasks (${filteredTasks.length})`
|
||||
);
|
||||
targetComponent.setTasks(tasks, filteredTasks);
|
||||
} else {
|
||||
|
|
@ -625,14 +612,14 @@ export class FluentComponentManager extends Component {
|
|||
// Forecast view: remove badge-only items
|
||||
if (viewId === "forecast") {
|
||||
filteredTasksLocal = filteredTasksLocal.filter(
|
||||
(task) => !(task as any).badge,
|
||||
(task) => !(task as any).badge
|
||||
);
|
||||
}
|
||||
console.log(
|
||||
"[FluentComponent] Calling setTasks with filtered:",
|
||||
filteredTasksLocal.length,
|
||||
"all:",
|
||||
tasks.length,
|
||||
tasks.length
|
||||
);
|
||||
targetComponent.setTasks(filteredTasksLocal, tasks);
|
||||
}
|
||||
|
|
@ -654,8 +641,8 @@ export class FluentComponentManager extends Component {
|
|||
tasks,
|
||||
viewId as any,
|
||||
this.plugin,
|
||||
filterOptions,
|
||||
),
|
||||
filterOptions
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -668,7 +655,7 @@ export class FluentComponentManager extends Component {
|
|||
}
|
||||
} else {
|
||||
console.warn(
|
||||
`[FluentComponent] No target component found for viewId: ${viewId}`,
|
||||
`[FluentComponent] No target component found for viewId: ${viewId}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -680,11 +667,11 @@ export class FluentComponentManager extends Component {
|
|||
viewId: string,
|
||||
tasks: Task[],
|
||||
filteredTasks: Task[],
|
||||
viewMode: ViewMode,
|
||||
viewMode: ViewMode
|
||||
): void {
|
||||
console.log(
|
||||
"[FluentComponent] renderContentWithViewMode called, viewMode:",
|
||||
viewMode,
|
||||
viewMode
|
||||
);
|
||||
|
||||
// Hide current component
|
||||
|
|
@ -703,7 +690,7 @@ export class FluentComponentManager extends Component {
|
|||
|
||||
console.log(
|
||||
"[FluentComponent] Setting tasks to ContentComponent, filtered:",
|
||||
filteredTasks.length,
|
||||
filteredTasks.length
|
||||
);
|
||||
this.contentComponent.setTasks(filteredTasks, tasks);
|
||||
this.currentVisibleComponent = this.contentComponent;
|
||||
|
|
@ -718,7 +705,7 @@ export class FluentComponentManager extends Component {
|
|||
console.log(
|
||||
"[FluentComponent] Setting",
|
||||
filteredTasks.length,
|
||||
"tasks to kanban",
|
||||
"tasks to kanban"
|
||||
);
|
||||
this.kanbanComponent.setTasks(filteredTasks);
|
||||
this.currentVisibleComponent = this.kanbanComponent;
|
||||
|
|
@ -727,11 +714,11 @@ export class FluentComponentManager extends Component {
|
|||
case "calendar":
|
||||
// Use CalendarComponent
|
||||
console.log(
|
||||
"[FluentComponent] Calendar mode in renderContentWithViewMode",
|
||||
"[FluentComponent] Calendar mode in renderContentWithViewMode"
|
||||
);
|
||||
if (!this.calendarComponent) {
|
||||
console.log(
|
||||
"[FluentComponent] No calendar component available!",
|
||||
"[FluentComponent] No calendar component available!"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
@ -742,7 +729,7 @@ export class FluentComponentManager extends Component {
|
|||
console.log(
|
||||
"[FluentComponent] Setting",
|
||||
filteredTasks.length,
|
||||
"tasks to calendar",
|
||||
"tasks to calendar"
|
||||
);
|
||||
this.calendarComponent.setTasks(filteredTasks);
|
||||
this.currentVisibleComponent = this.calendarComponent;
|
||||
|
|
@ -758,7 +745,7 @@ export class FluentComponentManager extends Component {
|
|||
viewId: string,
|
||||
tasks: Task[],
|
||||
filteredTasks: Task[],
|
||||
viewMode: ViewMode,
|
||||
viewMode: ViewMode
|
||||
): void {
|
||||
// Content-based views (list/tree/kanban/calendar)
|
||||
if (this.isContentBasedView(viewId)) {
|
||||
|
|
@ -775,7 +762,7 @@ export class FluentComponentManager extends Component {
|
|||
this.contentComponent?.setTasks?.(
|
||||
filteredTasks,
|
||||
tasks,
|
||||
true,
|
||||
true
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
|
@ -904,7 +891,7 @@ export class FluentComponentManager extends Component {
|
|||
emptyEl.createDiv({
|
||||
cls: "tg-fluent-empty-description",
|
||||
text: t(
|
||||
"Create your first task to get started with Task Genius",
|
||||
"Create your first task to get started with Task Genius"
|
||||
),
|
||||
});
|
||||
|
||||
|
|
@ -936,11 +923,14 @@ export class FluentComponentManager extends Component {
|
|||
/**
|
||||
* Get available view modes for a specific view
|
||||
*/
|
||||
getAvailableModesForView(viewId: string): ViewMode[] {
|
||||
getAvailableModesForView(
|
||||
viewId: string,
|
||||
selectedProject?: string | null
|
||||
): ViewMode[] {
|
||||
// Check for special two-column views
|
||||
const viewConfig = getViewSettingOrDefault(
|
||||
this.plugin,
|
||||
viewId as ViewMode,
|
||||
viewId as ViewMode
|
||||
);
|
||||
if (viewConfig?.specificConfig?.viewType === "twocolumn") {
|
||||
return [];
|
||||
|
|
@ -951,6 +941,12 @@ export class FluentComponentManager extends Component {
|
|||
return [];
|
||||
}
|
||||
|
||||
// Special handling for projects view
|
||||
// When a project is selected, enable all view modes (uses ContentComponent)
|
||||
if (viewId === "projects" && selectedProject) {
|
||||
return ["list", "tree", "kanban", "calendar"];
|
||||
}
|
||||
|
||||
// Return the configured modes for the view, or empty array
|
||||
return VIEW_MODE_CONFIG[viewId] || [];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ export class FluentTaskView extends ItemView {
|
|||
updateWorkspaceLeafWidth = debounce(() => {
|
||||
this.app.workspace.trigger(
|
||||
"task-genius:leaf-width-updated",
|
||||
this.leaf.width,
|
||||
this.leaf.width
|
||||
);
|
||||
}, 200);
|
||||
|
||||
|
|
@ -190,7 +190,7 @@ export class FluentTaskView extends ItemView {
|
|||
} catch (e) {
|
||||
console.warn(
|
||||
"[TG] Failed to sync selectedProject from filter state",
|
||||
e,
|
||||
e
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -202,7 +202,7 @@ export class FluentTaskView extends ItemView {
|
|||
this.filteredTasks = this.dataManager.applyFilters(this.tasks);
|
||||
this.updateView();
|
||||
},
|
||||
150,
|
||||
150
|
||||
);
|
||||
|
||||
onResize(): void {
|
||||
|
|
@ -211,7 +211,10 @@ export class FluentTaskView extends ItemView {
|
|||
|
||||
private ensureViewModeForView(viewId: string): ViewMode {
|
||||
const availableModes =
|
||||
this.componentManager?.getAvailableModesForView(viewId) ?? [];
|
||||
this.componentManager?.getAvailableModesForView(
|
||||
viewId,
|
||||
this.viewState.selectedProject
|
||||
) ?? [];
|
||||
const storedMode = this.viewState.viewModeByViewId?.[viewId];
|
||||
|
||||
const pickFallback = (): ViewMode => {
|
||||
|
|
@ -240,7 +243,7 @@ export class FluentTaskView extends ItemView {
|
|||
const prefersTree = getInitialViewMode(
|
||||
this.app,
|
||||
this.plugin,
|
||||
viewId,
|
||||
viewId
|
||||
);
|
||||
if (prefersTree && availableModes.includes("tree")) {
|
||||
return "tree";
|
||||
|
|
@ -258,8 +261,10 @@ export class FluentTaskView extends ItemView {
|
|||
this.viewState.viewModeByViewId = {};
|
||||
}
|
||||
|
||||
const availableModes =
|
||||
this.componentManager?.getAvailableModesForView(viewId);
|
||||
const availableModes = this.componentManager?.getAvailableModesForView(
|
||||
viewId,
|
||||
this.viewState.selectedProject
|
||||
);
|
||||
if (
|
||||
availableModes &&
|
||||
availableModes.length > 0 &&
|
||||
|
|
@ -284,14 +289,14 @@ export class FluentTaskView extends ItemView {
|
|||
// ====================
|
||||
if (this.DEBUG_MODE) {
|
||||
console.log(
|
||||
"[TG] Initializing UI, managers, structure, and events...",
|
||||
"[TG] Initializing UI, managers, structure, and events..."
|
||||
);
|
||||
}
|
||||
|
||||
this.contentEl.empty();
|
||||
this.contentEl.toggleClass(
|
||||
["task-genius-fluent-view", "task-genius-view"],
|
||||
true,
|
||||
true
|
||||
);
|
||||
|
||||
// Create root container (use exact same class as original)
|
||||
|
|
@ -315,7 +320,7 @@ export class FluentTaskView extends ItemView {
|
|||
|
||||
if (this.DEBUG_MODE) {
|
||||
console.log(
|
||||
"[TG] ✅ UI, managers, structure, and events initialized",
|
||||
"[TG] ✅ UI, managers, structure, and events initialized"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -333,7 +338,7 @@ export class FluentTaskView extends ItemView {
|
|||
this.viewState.currentWorkspace = savedWorkspaceId;
|
||||
if (this.DEBUG_MODE) {
|
||||
console.log(
|
||||
`[TG] Restored workspace ID: ${savedWorkspaceId}`,
|
||||
`[TG] Restored workspace ID: ${savedWorkspaceId}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -350,7 +355,7 @@ export class FluentTaskView extends ItemView {
|
|||
this.liveFilterState = state;
|
||||
this.app.saveLocalStorage(
|
||||
"task-genius-view-filter",
|
||||
state || null,
|
||||
state || null
|
||||
);
|
||||
},
|
||||
setCurrentFilterState: (state) => {
|
||||
|
|
@ -368,13 +373,14 @@ export class FluentTaskView extends ItemView {
|
|||
const availableModes =
|
||||
this.componentManager?.getAvailableModesForView(
|
||||
this.currentViewId,
|
||||
selectedProject
|
||||
) ?? [];
|
||||
if (
|
||||
availableModes.length > 0 &&
|
||||
!availableModes.includes(viewMode)
|
||||
) {
|
||||
return this.ensureViewModeForView(
|
||||
this.currentViewId,
|
||||
this.currentViewId
|
||||
);
|
||||
}
|
||||
return viewMode;
|
||||
|
|
@ -382,7 +388,7 @@ export class FluentTaskView extends ItemView {
|
|||
this.viewState.viewMode = normalizedMode;
|
||||
this.recordViewModeForView(
|
||||
this.currentViewId,
|
||||
normalizedMode,
|
||||
normalizedMode
|
||||
);
|
||||
this.topNavigation?.setViewMode(normalizedMode);
|
||||
if (clearSearch) {
|
||||
|
|
@ -402,7 +408,7 @@ export class FluentTaskView extends ItemView {
|
|||
await this.dataManager.loadTasks(false); // Will trigger onTasksLoaded callback
|
||||
await this.dataManager.registerDataflowListeners();
|
||||
console.log(
|
||||
`[TG] ✅ Loaded ${this.tasks.length} tasks, ${this.filteredTasks.length} after filters`,
|
||||
`[TG] ✅ Loaded ${this.tasks.length} tasks, ${this.filteredTasks.length} after filters`
|
||||
);
|
||||
|
||||
// ====================
|
||||
|
|
@ -427,7 +433,7 @@ export class FluentTaskView extends ItemView {
|
|||
// ====================
|
||||
if (this.DEBUG_MODE) {
|
||||
console.log(
|
||||
`[TG] Finalizing (isInitializing was ${this.isInitializing})`,
|
||||
`[TG] Finalizing (isInitializing was ${this.isInitializing})`
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -457,7 +463,7 @@ export class FluentTaskView extends ItemView {
|
|||
searchQuery: this.viewState.searchQuery || "",
|
||||
filterInputValue: this.viewState.filterInputValue || "",
|
||||
}),
|
||||
() => this.isInitializing,
|
||||
() => this.isInitializing
|
||||
);
|
||||
this.dataManager.setCallbacks({
|
||||
onTasksLoaded: (tasks, error) => {
|
||||
|
|
@ -473,7 +479,7 @@ export class FluentTaskView extends ItemView {
|
|||
this.isLoading = false;
|
||||
// Apply filters immediately after loading
|
||||
this.filteredTasks = this.dataManager.applyFilters(
|
||||
this.tasks,
|
||||
this.tasks
|
||||
);
|
||||
this.updateView();
|
||||
}
|
||||
|
|
@ -498,7 +504,7 @@ export class FluentTaskView extends ItemView {
|
|||
this.app,
|
||||
this.plugin,
|
||||
() => this.workspaceId,
|
||||
() => this.useSideLeaves(),
|
||||
() => this.useSideLeaves()
|
||||
);
|
||||
this.actionHandlers.setCallbacks({
|
||||
onTaskSelectionChanged: (task) => {
|
||||
|
|
@ -514,7 +520,7 @@ export class FluentTaskView extends ItemView {
|
|||
this.tasks[index] = updatedTask;
|
||||
// Re-apply filters
|
||||
this.filteredTasks = this.dataManager.applyFilters(
|
||||
this.tasks,
|
||||
this.tasks
|
||||
);
|
||||
this.updateView();
|
||||
}
|
||||
|
|
@ -545,7 +551,7 @@ export class FluentTaskView extends ItemView {
|
|||
onNavigateToView: (viewId) => {
|
||||
this.recordViewModeForView(
|
||||
this.currentViewId,
|
||||
this.viewState.viewMode,
|
||||
this.viewState.viewMode
|
||||
);
|
||||
this.currentViewId = viewId;
|
||||
|
||||
|
|
@ -553,7 +559,7 @@ export class FluentTaskView extends ItemView {
|
|||
// This enables the full project overview mode
|
||||
if (viewId === "projects") {
|
||||
console.log(
|
||||
"[TG] Navigating to projects overview - clearing project selection",
|
||||
"[TG] Navigating to projects overview - clearing project selection"
|
||||
);
|
||||
this.viewState.selectedProject = undefined;
|
||||
|
||||
|
|
@ -567,25 +573,25 @@ export class FluentTaskView extends ItemView {
|
|||
.map((g: any) => ({
|
||||
...g,
|
||||
filters: (g.filters || []).filter(
|
||||
(f: any) => f.property !== "project",
|
||||
(f: any) => f.property !== "project"
|
||||
),
|
||||
}))
|
||||
.filter(
|
||||
(g: any) =>
|
||||
g.filters && g.filters.length > 0,
|
||||
g.filters && g.filters.length > 0
|
||||
);
|
||||
|
||||
this.liveFilterState = nextState as any;
|
||||
this.currentFilterState = nextState as any;
|
||||
this.app.saveLocalStorage(
|
||||
"task-genius-view-filter",
|
||||
nextState,
|
||||
nextState
|
||||
);
|
||||
|
||||
// Broadcast filter change
|
||||
this.app.workspace.trigger(
|
||||
"task-genius:filter-changed",
|
||||
nextState,
|
||||
nextState
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
|
|
@ -593,6 +599,9 @@ export class FluentTaskView extends ItemView {
|
|||
}
|
||||
}
|
||||
|
||||
// Recompute filtered tasks for the newly selected view so all modes stay in sync
|
||||
this.filteredTasks = this.dataManager.applyFilters(this.tasks);
|
||||
|
||||
const nextMode = this.ensureViewModeForView(viewId);
|
||||
this.viewState.viewMode = nextMode;
|
||||
this.recordViewModeForView(viewId, nextMode);
|
||||
|
|
@ -613,16 +622,17 @@ export class FluentTaskView extends ItemView {
|
|||
// Switch to projects view
|
||||
this.recordViewModeForView(
|
||||
this.currentViewId,
|
||||
this.viewState.viewMode,
|
||||
this.viewState.viewMode
|
||||
);
|
||||
this.currentViewId = "projects";
|
||||
this.filteredTasks = this.dataManager.applyFilters(this.tasks);
|
||||
const projectsViewMode = this.ensureViewModeForView(
|
||||
this.currentViewId,
|
||||
this.currentViewId
|
||||
);
|
||||
this.viewState.viewMode = projectsViewMode;
|
||||
this.recordViewModeForView(
|
||||
this.currentViewId,
|
||||
projectsViewMode,
|
||||
projectsViewMode
|
||||
);
|
||||
this.topNavigation?.setViewMode(projectsViewMode);
|
||||
|
||||
|
|
@ -639,7 +649,7 @@ export class FluentTaskView extends ItemView {
|
|||
.map((g: any) => ({
|
||||
...g,
|
||||
filters: (g.filters || []).filter(
|
||||
(f: any) => f.property !== "project",
|
||||
(f: any) => f.property !== "project"
|
||||
),
|
||||
}))
|
||||
.filter((g: any) => g.filters && g.filters.length > 0); // Remove empty groups
|
||||
|
|
@ -665,23 +675,23 @@ export class FluentTaskView extends ItemView {
|
|||
this.currentFilterState = nextState as any;
|
||||
this.app.saveLocalStorage(
|
||||
"task-genius-view-filter",
|
||||
nextState,
|
||||
nextState
|
||||
);
|
||||
|
||||
// Broadcast so any open filter UI reacts and header button shows reset
|
||||
// The filter-changed event listener will handle applyFilters and updateView
|
||||
this.app.workspace.trigger(
|
||||
"task-genius:filter-changed",
|
||||
nextState,
|
||||
nextState
|
||||
);
|
||||
} catch (e) {
|
||||
console.warn(
|
||||
"[TG] Failed to project-sync filter UI state",
|
||||
e,
|
||||
e
|
||||
);
|
||||
// If filter sync fails, still update the view
|
||||
this.filteredTasks = this.dataManager.applyFilters(
|
||||
this.tasks,
|
||||
this.tasks
|
||||
);
|
||||
this.updateView();
|
||||
}
|
||||
|
|
@ -719,7 +729,7 @@ export class FluentTaskView extends ItemView {
|
|||
viewMode: this.viewState.viewMode,
|
||||
}),
|
||||
() => this.currentFilterState,
|
||||
() => this.liveFilterState,
|
||||
() => this.liveFilterState
|
||||
);
|
||||
this.addChild(this.workspaceStateManager);
|
||||
|
||||
|
|
@ -727,7 +737,7 @@ export class FluentTaskView extends ItemView {
|
|||
this.selectionManager = new TaskSelectionManager(
|
||||
this.app,
|
||||
this.plugin,
|
||||
this,
|
||||
this
|
||||
);
|
||||
this.addChild(this.selectionManager);
|
||||
|
||||
|
|
@ -795,7 +805,7 @@ export class FluentTaskView extends ItemView {
|
|||
this.rootContainerEl,
|
||||
this.headerEl, // Obsidian's view header
|
||||
this.titleEl, // Obsidian's view title element
|
||||
() => this.filteredTasks.length,
|
||||
() => this.filteredTasks.length
|
||||
);
|
||||
this.layoutManager.setOnSidebarNavigate((viewId) => {
|
||||
this.actionHandlers.handleNavigate(viewId);
|
||||
|
|
@ -813,7 +823,7 @@ export class FluentTaskView extends ItemView {
|
|||
onTaskUpdate: async (originalTask, updatedTask) => {
|
||||
await this.actionHandlers.handleTaskUpdate(
|
||||
originalTask,
|
||||
updatedTask,
|
||||
updatedTask
|
||||
);
|
||||
},
|
||||
});
|
||||
|
|
@ -833,12 +843,11 @@ export class FluentTaskView extends ItemView {
|
|||
} else {
|
||||
sidebarEl.hide();
|
||||
console.log(
|
||||
"[TG] Using workspace side leaves: skip in-view sidebar",
|
||||
"[TG] Using workspace side leaves: skip in-view sidebar"
|
||||
);
|
||||
}
|
||||
|
||||
// Create top navigation
|
||||
console.log("[TG] Initializing top navigation");
|
||||
// Initialize with empty modes first, will be updated in updateView
|
||||
this.topNavigation = new TopNavigation(
|
||||
topNavEl,
|
||||
this.plugin,
|
||||
|
|
@ -852,6 +861,7 @@ export class FluentTaskView extends ItemView {
|
|||
// Sort click callback (future extension point)
|
||||
},
|
||||
() => this.actionHandlers.handleSettingsClick(),
|
||||
[] // Start with empty modes, will be set by updateView
|
||||
);
|
||||
this.addChild(this.topNavigation);
|
||||
|
||||
|
|
@ -872,7 +882,7 @@ export class FluentTaskView extends ItemView {
|
|||
onTaskUpdate: async (originalTask, updatedTask) => {
|
||||
await this.actionHandlers.handleTaskUpdate(
|
||||
originalTask,
|
||||
updatedTask,
|
||||
updatedTask
|
||||
);
|
||||
},
|
||||
onTaskContextMenu: (event, task) => {
|
||||
|
|
@ -883,12 +893,12 @@ export class FluentTaskView extends ItemView {
|
|||
if (task) {
|
||||
this.actionHandlers.handleKanbanTaskStatusUpdate(
|
||||
task,
|
||||
newStatusMark,
|
||||
newStatusMark
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
this.selectionManager,
|
||||
this.selectionManager
|
||||
);
|
||||
this.addChild(this.componentManager);
|
||||
this.componentManager.initializeViewComponents();
|
||||
|
|
@ -937,7 +947,7 @@ export class FluentTaskView extends ItemView {
|
|||
this.workspaceStateManager.captureFilterStateSnapshot();
|
||||
if (snapshot) {
|
||||
await this.workspaceStateManager.saveFilterStateImmediately(
|
||||
snapshot,
|
||||
snapshot
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -959,7 +969,7 @@ export class FluentTaskView extends ItemView {
|
|||
this.liveFilterState = state;
|
||||
this.app.saveLocalStorage(
|
||||
"task-genius-view-filter",
|
||||
state || null,
|
||||
state || null
|
||||
);
|
||||
},
|
||||
setCurrentFilterState: (state) => {
|
||||
|
|
@ -978,13 +988,14 @@ export class FluentTaskView extends ItemView {
|
|||
const availableModes =
|
||||
this.componentManager?.getAvailableModesForView(
|
||||
this.currentViewId,
|
||||
selectedProject
|
||||
) ?? [];
|
||||
if (
|
||||
availableModes.length > 0 &&
|
||||
!availableModes.includes(viewMode)
|
||||
) {
|
||||
return this.ensureViewModeForView(
|
||||
this.currentViewId,
|
||||
this.currentViewId
|
||||
);
|
||||
}
|
||||
return viewMode;
|
||||
|
|
@ -992,7 +1003,7 @@ export class FluentTaskView extends ItemView {
|
|||
this.viewState.viewMode = normalizedMode;
|
||||
this.recordViewModeForView(
|
||||
this.currentViewId,
|
||||
normalizedMode,
|
||||
normalizedMode
|
||||
);
|
||||
this.topNavigation?.setViewMode(normalizedMode);
|
||||
if (clearSearch) {
|
||||
|
|
@ -1008,7 +1019,7 @@ export class FluentTaskView extends ItemView {
|
|||
// Reload tasks
|
||||
await this.dataManager.loadTasks();
|
||||
}
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
// Workspace overrides saved event
|
||||
|
|
@ -1018,7 +1029,7 @@ export class FluentTaskView extends ItemView {
|
|||
await this.workspaceStateManager.applyWorkspaceSettings();
|
||||
await this.dataManager.loadTasks();
|
||||
}
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
// Settings changed event (skip for filter state changes)
|
||||
|
|
@ -1026,7 +1037,7 @@ export class FluentTaskView extends ItemView {
|
|||
on(this.app, Events.SETTINGS_CHANGED, async () => {
|
||||
// Reload on settings change (unless caused by filter state save)
|
||||
await this.dataManager.loadTasks();
|
||||
}),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -1036,8 +1047,8 @@ export class FluentTaskView extends ItemView {
|
|||
"task-genius:filter-changed",
|
||||
(filterState: RootFilterState, leafId?: string) => {
|
||||
this.handleFilterChanged(filterState, leafId);
|
||||
},
|
||||
),
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// Sidebar selection changed (when using side leaves)
|
||||
|
|
@ -1050,6 +1061,9 @@ export class FluentTaskView extends ItemView {
|
|||
payload.selectionId
|
||||
) {
|
||||
this.currentViewId = payload.selectionId;
|
||||
this.filteredTasks = this.dataManager.applyFilters(
|
||||
this.tasks
|
||||
);
|
||||
this.updateView();
|
||||
}
|
||||
if (
|
||||
|
|
@ -1058,11 +1072,11 @@ export class FluentTaskView extends ItemView {
|
|||
) {
|
||||
// Use the full project selection logic via actionHandlers
|
||||
this.actionHandlers.handleProjectSelect(
|
||||
payload.selectionId || "",
|
||||
payload.selectionId || ""
|
||||
);
|
||||
}
|
||||
}
|
||||
}),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -1092,7 +1106,7 @@ export class FluentTaskView extends ItemView {
|
|||
`tasks=${this.tasks.length}, ` +
|
||||
`filtered=${this.filteredTasks.length}, ` +
|
||||
`isLoading=${this.isLoading}, ` +
|
||||
`hasError=${!!this.loadError}`,
|
||||
`hasError=${!!this.loadError}`
|
||||
);
|
||||
|
||||
if (this.isInitializing) {
|
||||
|
|
@ -1101,9 +1115,17 @@ export class FluentTaskView extends ItemView {
|
|||
}
|
||||
|
||||
console.log(
|
||||
`[TG] ▶️ Proceeding with view update for ${this.currentViewId}`,
|
||||
`[TG] ▶️ Proceeding with view update for ${this.currentViewId}`
|
||||
);
|
||||
|
||||
// Update top navigation available modes based on current view
|
||||
const availableModes =
|
||||
this.componentManager?.getAvailableModesForView(
|
||||
this.currentViewId,
|
||||
this.viewState.selectedProject
|
||||
) ?? [];
|
||||
this.topNavigation?.updateAvailableModes(availableModes);
|
||||
|
||||
// Update task count
|
||||
this.layoutManager.updateTaskMark();
|
||||
|
||||
|
|
@ -1156,7 +1178,7 @@ export class FluentTaskView extends ItemView {
|
|||
this.filteredTasks,
|
||||
this.currentFilterState,
|
||||
this.viewState.viewMode,
|
||||
this.viewState.selectedProject,
|
||||
this.viewState.selectedProject
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue