mirror of
https://github.com/taskgenius/taskgenius-plugin.git
synced 2026-07-22 06:40:25 +00:00
fix(workspace): improve async handling and error recovery in module visibility
- Fix async handling in hidden modules toggle with proper error recovery - Add type imports for SidebarComponentType and FeatureComponentType - Refactor module visibility toggle to use workspace manager API methods - Add try-catch-finally structure to FluentTaskView initialization - Enhance error handling and logging in view initialization - Add DEBUG_MODE configuration for conditional logging - Improve state refresh after module visibility changes
This commit is contained in:
parent
09ed881196
commit
a0b9891e04
2 changed files with 239 additions and 114 deletions
|
|
@ -1,7 +1,12 @@
|
|||
import { Menu, Setting, setIcon } from "obsidian";
|
||||
import { TaskProgressBarSettingTab } from "@/setting";
|
||||
import TaskProgressBarPlugin from "@/index";
|
||||
import { WorkspaceData, ModuleDefinition } from "@/types/workspace";
|
||||
import {
|
||||
WorkspaceData,
|
||||
ModuleDefinition,
|
||||
SidebarComponentType,
|
||||
FeatureComponentType,
|
||||
} from "@/types/workspace";
|
||||
import { t } from "@/translations/helper";
|
||||
import {
|
||||
CreateWorkspaceModal,
|
||||
|
|
@ -351,7 +356,7 @@ function renderHiddenModulesConfig(
|
|||
});
|
||||
|
||||
const modules = getAvailableModules(plugin);
|
||||
const hiddenModules = workspace.settings.hiddenModules || {
|
||||
let hiddenModules = workspace.settings.hiddenModules || {
|
||||
views: [],
|
||||
sidebarComponents: [],
|
||||
features: [],
|
||||
|
|
@ -366,9 +371,11 @@ function renderHiddenModulesConfig(
|
|||
groupTitle: string,
|
||||
groupIcon: string,
|
||||
moduleList: ModuleDefinition[],
|
||||
hiddenList: string[],
|
||||
initialHiddenList: string[],
|
||||
moduleType: "views" | "sidebarComponents" | "features",
|
||||
) => {
|
||||
let hiddenList = [...initialHiddenList];
|
||||
|
||||
const groupEl = groupsContainer.createDiv({
|
||||
cls: "workspace-module-group",
|
||||
});
|
||||
|
|
@ -429,57 +436,100 @@ function renderHiddenModulesConfig(
|
|||
text: module.name,
|
||||
});
|
||||
|
||||
// Toggle handler
|
||||
const toggleVisibility = () => {
|
||||
const toggleVisibility = async () => {
|
||||
const newHiddenList = [...hiddenList];
|
||||
const index = newHiddenList.indexOf(module.id);
|
||||
const shouldBeVisible = checkbox.checked;
|
||||
|
||||
if (checkbox.checked && index !== -1) {
|
||||
// Remove from hidden list (make visible)
|
||||
if (shouldBeVisible && index !== -1) {
|
||||
newHiddenList.splice(index, 1);
|
||||
itemEl.removeClass("is-hidden");
|
||||
} else if (!checkbox.checked && index === -1) {
|
||||
// Add to hidden list (make hidden)
|
||||
} else if (!shouldBeVisible && index === -1) {
|
||||
newHiddenList.push(module.id);
|
||||
itemEl.addClass("is-hidden");
|
||||
}
|
||||
|
||||
// Update workspace settings
|
||||
if (!workspace.settings.hiddenModules) {
|
||||
workspace.settings.hiddenModules = {
|
||||
views: [],
|
||||
sidebarComponents: [],
|
||||
features: [],
|
||||
};
|
||||
}
|
||||
workspace.settings.hiddenModules[moduleType] = newHiddenList;
|
||||
try {
|
||||
switch (moduleType) {
|
||||
case "views":
|
||||
await plugin.workspaceManager?.setHiddenViews(
|
||||
[...newHiddenList],
|
||||
workspace.id,
|
||||
);
|
||||
break;
|
||||
case "sidebarComponents":
|
||||
await plugin.workspaceManager?.setHiddenSidebarComponents(
|
||||
newHiddenList as SidebarComponentType[],
|
||||
workspace.id,
|
||||
);
|
||||
break;
|
||||
case "features":
|
||||
await plugin.workspaceManager?.setHiddenFeatures(
|
||||
newHiddenList as FeatureComponentType[],
|
||||
workspace.id,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
// Save and trigger update
|
||||
plugin.workspaceManager?.updateWorkspace(
|
||||
workspace.id,
|
||||
workspace,
|
||||
);
|
||||
onUpdate();
|
||||
const refreshed =
|
||||
plugin.workspaceManager?.getWorkspace(workspace.id);
|
||||
if (refreshed?.settings?.hiddenModules) {
|
||||
hiddenModules = refreshed.settings.hiddenModules;
|
||||
} else {
|
||||
hiddenModules = {
|
||||
views: [],
|
||||
sidebarComponents: [],
|
||||
features: [],
|
||||
};
|
||||
}
|
||||
|
||||
// Update count badge
|
||||
const countBadge = headerEl.querySelector(
|
||||
".workspace-module-group-count",
|
||||
);
|
||||
if (countBadge) {
|
||||
countBadge.textContent = t("{{hidden}}/{{total}} hidden", {
|
||||
interpolation: {
|
||||
hidden: newHiddenList.length.toString(),
|
||||
total: totalCount.toString(),
|
||||
},
|
||||
});
|
||||
hiddenList = newHiddenList;
|
||||
switch (moduleType) {
|
||||
case "views":
|
||||
hiddenModules.views = newHiddenList;
|
||||
break;
|
||||
case "sidebarComponents":
|
||||
hiddenModules.sidebarComponents =
|
||||
newHiddenList as SidebarComponentType[];
|
||||
break;
|
||||
case "features":
|
||||
hiddenModules.features =
|
||||
newHiddenList as FeatureComponentType[];
|
||||
break;
|
||||
}
|
||||
|
||||
itemEl.toggleClass("is-hidden", !shouldBeVisible);
|
||||
|
||||
const countBadge = headerEl.querySelector(
|
||||
".workspace-module-group-count",
|
||||
);
|
||||
if (countBadge) {
|
||||
countBadge.textContent = t(
|
||||
"{{hidden}}/{{total}} hidden",
|
||||
{
|
||||
interpolation: {
|
||||
hidden: newHiddenList.length.toString(),
|
||||
total: totalCount.toString(),
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
onUpdate();
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"[WorkspaceSettings] Failed to update hidden modules",
|
||||
error,
|
||||
);
|
||||
checkbox.checked = !checkbox.checked;
|
||||
}
|
||||
};
|
||||
|
||||
checkbox.addEventListener("change", toggleVisibility);
|
||||
checkbox.addEventListener("change", () => {
|
||||
void toggleVisibility();
|
||||
});
|
||||
itemEl.addEventListener("click", (e) => {
|
||||
if (e.target !== checkbox) {
|
||||
checkbox.checked = !checkbox.checked;
|
||||
toggleVisibility();
|
||||
void toggleVisibility();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -43,6 +43,12 @@ export const FLUENT_TASK_VIEW = "fluent-task-genius-view";
|
|||
export class FluentTaskView extends ItemView {
|
||||
private plugin: TaskProgressBarPlugin;
|
||||
|
||||
// ====================
|
||||
// DEBUG CONFIGURATION
|
||||
// ====================
|
||||
// Set to false in production to reduce console output
|
||||
private readonly DEBUG_MODE = false;
|
||||
|
||||
// ====================
|
||||
// MANAGERS (added via addChild for lifecycle management)
|
||||
// ====================
|
||||
|
|
@ -131,86 +137,144 @@ export class FluentTaskView extends ItemView {
|
|||
console.log("[TG-V2] onOpen started");
|
||||
this.isInitializing = true;
|
||||
|
||||
this.contentEl.empty();
|
||||
this.contentEl.toggleClass(
|
||||
["task-genius-fluent-view", "task-genius-view"],
|
||||
true
|
||||
);
|
||||
|
||||
// Create root container (use exact same class as original)
|
||||
this.rootContainerEl = this.contentEl.createDiv({
|
||||
cls: "tg-fluent-container",
|
||||
});
|
||||
|
||||
// Add mobile class for proper styling
|
||||
if (Platform.isPhone) {
|
||||
this.rootContainerEl.addClass("is-mobile");
|
||||
}
|
||||
|
||||
// Initialize managers first (before UI)
|
||||
this.initializeManagers();
|
||||
|
||||
// Build UI structure
|
||||
await this.buildUIStructure();
|
||||
|
||||
// Subscribe to workspace and global events
|
||||
this.registerEvents();
|
||||
|
||||
// Load workspace state
|
||||
const savedWorkspaceId =
|
||||
this.workspaceStateManager.getSavedWorkspaceId();
|
||||
if (savedWorkspaceId && savedWorkspaceId !== this.workspaceId) {
|
||||
this.workspaceId = savedWorkspaceId;
|
||||
this.viewState.currentWorkspace = savedWorkspaceId;
|
||||
}
|
||||
|
||||
// Apply workspace settings and restore filter state
|
||||
await this.workspaceStateManager.applyWorkspaceSettings();
|
||||
const restored =
|
||||
this.workspaceStateManager.restoreFilterStateFromWorkspace();
|
||||
this.workspaceStateManager.syncFilterState(restored, {
|
||||
setLiveFilterState: (state) => {
|
||||
this.liveFilterState = state;
|
||||
this.app.saveLocalStorage(
|
||||
"task-genius-view-filter",
|
||||
state || null,
|
||||
try {
|
||||
// ====================
|
||||
// PHASE 1-4: UI Setup, Managers, Structure, Events
|
||||
// ====================
|
||||
if (this.DEBUG_MODE) {
|
||||
console.log(
|
||||
"[TG-V2] Initializing UI, managers, structure, and events..."
|
||||
);
|
||||
},
|
||||
setCurrentFilterState: (state) => {
|
||||
this.currentFilterState = state;
|
||||
},
|
||||
setViewPreferences: ({
|
||||
filters,
|
||||
selectedProject,
|
||||
viewMode,
|
||||
clearSearch,
|
||||
}) => {
|
||||
this.viewState.filters = filters;
|
||||
this.viewState.selectedProject = selectedProject;
|
||||
this.viewState.viewMode = viewMode;
|
||||
if (clearSearch) {
|
||||
this.viewState.searchQuery = "";
|
||||
this.viewState.filterInputValue = "";
|
||||
}
|
||||
|
||||
this.contentEl.empty();
|
||||
this.contentEl.toggleClass(
|
||||
["task-genius-fluent-view", "task-genius-view"],
|
||||
true
|
||||
);
|
||||
|
||||
// Create root container (use exact same class as original)
|
||||
this.rootContainerEl = this.contentEl.createDiv({
|
||||
cls: "tg-fluent-container",
|
||||
});
|
||||
|
||||
// Add mobile class for proper styling
|
||||
if (Platform.isPhone) {
|
||||
this.rootContainerEl.addClass("is-mobile");
|
||||
}
|
||||
|
||||
// Initialize managers first (before UI)
|
||||
this.initializeManagers();
|
||||
|
||||
// Build UI structure
|
||||
await this.buildUIStructure();
|
||||
|
||||
// Subscribe to workspace and global events
|
||||
this.registerEvents();
|
||||
|
||||
if (this.DEBUG_MODE) {
|
||||
console.log(
|
||||
"[TG-V2] ✅ UI, managers, structure, and events initialized"
|
||||
);
|
||||
}
|
||||
|
||||
// ====================
|
||||
// PHASE 5: Restore Workspace State
|
||||
// ====================
|
||||
if (this.DEBUG_MODE) {
|
||||
console.log("[TG-V2] Restoring workspace state...");
|
||||
}
|
||||
|
||||
const savedWorkspaceId =
|
||||
this.workspaceStateManager.getSavedWorkspaceId();
|
||||
if (savedWorkspaceId && savedWorkspaceId !== this.workspaceId) {
|
||||
this.workspaceId = savedWorkspaceId;
|
||||
this.viewState.currentWorkspace = savedWorkspaceId;
|
||||
if (this.DEBUG_MODE) {
|
||||
console.log(
|
||||
`[TG-V2] Restored workspace ID: ${savedWorkspaceId}`
|
||||
);
|
||||
}
|
||||
},
|
||||
onAfterSync: () => {
|
||||
this.layoutManager?.updateActionButtons();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Initial data load
|
||||
await this.dataManager.loadTasks(false); // Will trigger onTasksLoaded callback
|
||||
// Apply workspace settings and restore filter state
|
||||
await this.workspaceStateManager.applyWorkspaceSettings();
|
||||
const restored =
|
||||
this.workspaceStateManager.restoreFilterStateFromWorkspace();
|
||||
this.workspaceStateManager.syncFilterState(restored, {
|
||||
setLiveFilterState: (state) => {
|
||||
this.liveFilterState = state;
|
||||
this.app.saveLocalStorage(
|
||||
"task-genius-view-filter",
|
||||
state || null,
|
||||
);
|
||||
},
|
||||
setCurrentFilterState: (state) => {
|
||||
this.currentFilterState = state;
|
||||
},
|
||||
setViewPreferences: ({
|
||||
filters,
|
||||
selectedProject,
|
||||
viewMode,
|
||||
clearSearch,
|
||||
}) => {
|
||||
this.viewState.filters = filters;
|
||||
this.viewState.selectedProject = selectedProject;
|
||||
this.viewState.viewMode = viewMode;
|
||||
if (clearSearch) {
|
||||
this.viewState.searchQuery = "";
|
||||
this.viewState.filterInputValue = "";
|
||||
}
|
||||
},
|
||||
onAfterSync: () => {
|
||||
this.layoutManager?.updateActionButtons();
|
||||
},
|
||||
});
|
||||
|
||||
// Register dataflow listeners for real-time updates
|
||||
await this.dataManager.registerDataflowListeners();
|
||||
// ====================
|
||||
// PHASE 6: Load Data (KEY PHASE)
|
||||
// ====================
|
||||
console.log("[TG-V2] Loading tasks...");
|
||||
await this.dataManager.loadTasks(false); // Will trigger onTasksLoaded callback
|
||||
await this.dataManager.registerDataflowListeners();
|
||||
console.log(
|
||||
`[TG-V2] ✅ Loaded ${this.tasks.length} tasks, ${this.filteredTasks.length} after filters`
|
||||
);
|
||||
|
||||
// Initial render
|
||||
this.updateView();
|
||||
// ====================
|
||||
// PHASE 7-8: Initial Render & Responsive Adjustments
|
||||
// ====================
|
||||
// Initial render (will be skipped due to isInitializing)
|
||||
this.updateView();
|
||||
|
||||
// Check window size and auto-collapse sidebar if needed
|
||||
this.layoutManager.checkAndCollapseSidebar();
|
||||
// Check window size and auto-collapse sidebar if needed
|
||||
if (this.DEBUG_MODE) {
|
||||
console.log("[TG-V2] Checking sidebar collapse...");
|
||||
}
|
||||
this.layoutManager.checkAndCollapseSidebar();
|
||||
} catch (error) {
|
||||
console.error("[TG-V2] ❌ Initialization error:", error);
|
||||
console.error("[TG-V2] Error stack:", (error as Error).stack);
|
||||
this.loadError =
|
||||
(error as Error).message || "Failed to initialize view";
|
||||
} finally {
|
||||
// ====================
|
||||
// PHASE 9: Finalization (CRITICAL - Always Executes)
|
||||
// ====================
|
||||
if (this.DEBUG_MODE) {
|
||||
console.log(
|
||||
`[TG-V2] Finalizing (isInitializing was ${this.isInitializing})`
|
||||
);
|
||||
}
|
||||
|
||||
this.isInitializing = false;
|
||||
this.isInitializing = false;
|
||||
|
||||
if (this.DEBUG_MODE) {
|
||||
console.log("[TG-V2] Calling final updateView()...");
|
||||
}
|
||||
this.updateView();
|
||||
console.log("[TG-V2] ✅ Initialization complete");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -788,13 +852,24 @@ export class FluentTaskView extends ItemView {
|
|||
* Update view with current state
|
||||
*/
|
||||
private updateView() {
|
||||
// Enhanced logging with all critical state
|
||||
console.log(
|
||||
`[TG-V2] updateView called: ` +
|
||||
`isInitializing=${this.isInitializing}, ` +
|
||||
`viewId=${this.currentViewId}, ` +
|
||||
`tasks=${this.tasks.length}, ` +
|
||||
`filtered=${this.filteredTasks.length}, ` +
|
||||
`isLoading=${this.isLoading}, ` +
|
||||
`hasError=${!!this.loadError}`
|
||||
);
|
||||
|
||||
if (this.isInitializing) {
|
||||
console.log("[TG-V2] Skip update during initialization");
|
||||
console.log("[TG-V2] ⏭️ Skip update during initialization");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(
|
||||
`[TG-V2] updateView: viewId=${this.currentViewId}, tasks=${this.tasks.length}, filtered=${this.filteredTasks.length}`
|
||||
`[TG-V2] ▶️ Proceeding with view update for ${this.currentViewId}`
|
||||
);
|
||||
|
||||
// Update task count
|
||||
|
|
|
|||
Loading…
Reference in a new issue