From 2cebfb81e82fe5020c267c3077e310c79c431ac0 Mon Sep 17 00:00:00 2001 From: Kelno Date: Sun, 4 Jan 2026 18:30:53 +0100 Subject: [PATCH 001/236] Fixed README links in section # How to Contribute --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e866b6c2..692e3114 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ Join the forum top to share your thoughts, ideas or requests and hear from other **Step 3 :** Click on the **Scan vault modal** button from the top-right corner in the Task Board view header. Then click on the run button and it will scan all your files to look for tasks. If your vault contains thousands of notes, you can apply [scanning filters](https://tu2-atmanand.github.io/task-board-docs/docs/Features/Filters_for_Scanning/) to exclude certain files from scanning. -**Step 4 :** There are already three predefined board for your convenience as an example. Feel free to delete or edit the boards and [create your own boards](https://tu2-atmanand.github.io/task-board-docs/docs/How_To/HowToCreateNewBoard/) from the [Board configure modal](https://tu2-atmanand.github.io/task-board-docs/docs/How_To/HowToUseBoardSettings/). +**Step 4 :** There are already three predefined board for your convenience as an example. Feel free to delete or edit the boards and [create your own boards](https://tu2-atmanand.github.io/docs/How_To/HowToCreateNewBoard/) from the [Board configure modal](https://tu2-atmanand.github.io/docs/How_To/HowToUseBoardSettings/). Enjoy ! @@ -124,11 +124,11 @@ This advanced and dynamic filters will help you to use boards as separate projec You can contribute to this project by : -**1. Requesting a new feature, suggesting an improvement or reporting a Bug :** [How to create a new request](https://tu2-atmanand.github.io/task-board-docs/Advanced/HowToCreateRequest.html). +**1. Requesting a new feature, suggesting an improvement or reporting a Bug :** [How to create a new request](https://tu2-atmanand.github.io/task-board-docs/docs/Advanced/HowToCreateRequest). -**2. Improving the translated languages or add a new language :** [How to Contribute for Language Translation](https://tu2-atmanand.github.io/task-board-docs/Advanced/Contribution_For_Languages.html). +**2. Improving the translated languages or add a new language :** [How to Contribute for Language Translation](https://tu2-atmanand.github.io/task-board-docs/docs/Advanced/Contribution_For_Languages). -**3. Contribute to the Development of the plugin Code :** : [How to join the plugin development](https://tu2-atmanand.github.io/task-board-docs/Advanced/HowToJoinDevelopment.html). +**3. Contribute to the Development of the plugin Code :** : [How to join the plugin development](https://tu2-atmanand.github.io/task-board-docs/docs/Advanced/HowToJoinDevelopment). ## Motivation for the Project From 083c2b56379fd001027a9dd4cf05b67ffdc21c0d Mon Sep 17 00:00:00 2001 From: Atmanand Gauns <83651531+tu2-atmanand@users.noreply.github.com> Date: Mon, 5 Jan 2026 09:15:38 +0530 Subject: [PATCH 002/236] Update README.md Co-authored-by: qodo-code-review[bot] <151058649+qodo-code-review[bot]@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 692e3114..e5158459 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ Join the forum top to share your thoughts, ideas or requests and hear from other **Step 3 :** Click on the **Scan vault modal** button from the top-right corner in the Task Board view header. Then click on the run button and it will scan all your files to look for tasks. If your vault contains thousands of notes, you can apply [scanning filters](https://tu2-atmanand.github.io/task-board-docs/docs/Features/Filters_for_Scanning/) to exclude certain files from scanning. -**Step 4 :** There are already three predefined board for your convenience as an example. Feel free to delete or edit the boards and [create your own boards](https://tu2-atmanand.github.io/docs/How_To/HowToCreateNewBoard/) from the [Board configure modal](https://tu2-atmanand.github.io/docs/How_To/HowToUseBoardSettings/). +**Step 4 :** There are already three predefined board for your convenience as an example. Feel free to delete or edit the boards and [create your own boards](https://tu2-atmanand.github.io/task-board-docs/docs/How_To/HowToCreateNewBoard/) from the [Board configure modal](https://tu2-atmanand.github.io/task-board-docs/docs/How_To/HowToUseBoardSettings/). Enjoy ! From 44f7aa90a5a3291c370cebd5f5d6cf3f644d0c29 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Mon, 19 Jan 2026 21:46:08 +0530 Subject: [PATCH 003/236] draft : basic changes to load and save .taskBoard files --- main.ts | 153 ++-- src/components/AddOrEditTaskRC.tsx | 68 +- src/components/BoardFilters/ViewTaskFilter.ts | 4 +- src/components/KanbanView/Column.tsx | 6 +- src/components/KanbanView/KanbanBoardView.tsx | 5 +- src/components/KanbanView/LazyColumn.tsx | 48 +- src/components/KanbanView/TaskItem.tsx | 42 +- src/components/KanbanView/TaskItemV2.tsx | 42 +- src/components/MapView/EdgeWithToolbar.tsx | 2 +- src/components/MapView/MapView.tsx | 26 +- .../MapView/ResizableNodeSelected.tsx | 4 +- src/components/TaskBoardViewContent.tsx | 171 ++-- .../task-operations/property-hiding.ts | 4 +- src/interfaces/BoardConfigs.ts | 243 ++++++ src/interfaces/GlobalSettings.ts | 808 ++++++------------ src/managers/BugReporter.ts | 1 + src/managers/DragDropTasksManager.ts | 32 +- src/managers/TaskBoardFileManager.ts | 406 +++++++++ src/managers/VaultScanner.ts | 75 +- src/modals/AddOrEditTaskModal.tsx | 4 +- src/modals/BoardConfigModal.tsx | 30 +- src/modals/ConfigureColumnSortingModal.ts | 2 +- src/modals/EditTagsModal.ts | 2 +- src/modals/ScanFilterModal.ts | 2 +- src/modals/ScanVaultModal.tsx | 4 +- src/modals/TaskBoardActionsModal.ts | 6 +- src/services/CommunityPlugins.ts | 4 +- src/services/OpenModals.ts | 134 +-- src/services/tasks-plugin/helpers.ts | 10 +- src/settings/SettingConstructUI.ts | 36 +- src/settings/SettingSynchronizer.ts | 2 +- src/utils/BoardOperations.ts | 24 - src/utils/CheckBoxUtils.ts | 6 +- src/utils/DateTimeCalculations.ts | 8 +- src/utils/JsonFileOperations.ts | 111 +-- src/utils/TaskItemUtils.ts | 8 +- src/utils/UserTaskEvents.ts | 24 +- src/utils/algorithms/ColumnSegregator.ts | 2 +- src/utils/algorithms/ScanningFilterer.ts | 2 +- src/utils/taskLine/TaskContentFormatter.ts | 18 +- src/utils/taskLine/TaskItemEventHandlers.ts | 6 +- src/utils/taskLine/TaskLineUtils.ts | 6 +- src/utils/taskNote/FrontmatterOperations.ts | 22 +- src/utils/taskNote/TaskNoteEventHandlers.ts | 6 +- src/utils/taskNote/TaskNoteUtils.ts | 6 +- src/views/AddOrEditTaskView.tsx | 6 +- src/views/TaskBoardView.tsx | 38 +- 47 files changed, 1573 insertions(+), 1096 deletions(-) create mode 100644 src/managers/TaskBoardFileManager.ts delete mode 100644 src/utils/BoardOperations.ts diff --git a/main.ts b/main.ts index 458e923e..850df217 100644 --- a/main.ts +++ b/main.ts @@ -26,6 +26,7 @@ import { RealTimeScanner } from "src/managers/RealTimeScanner"; import VaultScanner, { fileTypeAllowedForScanning, } from "src/managers/VaultScanner"; +import TaskBoardFileManager from "src/managers/TaskBoardFileManager"; import { TaskBoardIcon } from "src/interfaces/Icons"; import { TaskBoardSettingTab } from "./src/settings/TaskBoardSettingTab"; import { ModifiedFilesModal } from "src/modals/ModifiedFilesModal"; @@ -58,6 +59,7 @@ export default class TaskBoard extends Plugin { settings: PluginDataJson = DEFAULT_SETTINGS; vaultScanner: VaultScanner; realTimeScanner: RealTimeScanner; + taskBoardFileManager: TaskBoardFileManager; // taskBoardFileStack: string[] = []; private _editorModified: boolean = false; // Private backing field // currentModifiedFile: TFile | null; @@ -103,6 +105,9 @@ export default class TaskBoard extends Plugin { this.plugin, this.vaultScanner, ); + this.taskBoardFileManager = new TaskBoardFileManager( + this.plugin, + ); this.editorModified = false; // this.currentModifiedFile = null; // this.fileUpdatedUsingModal = ""; @@ -125,7 +130,7 @@ export default class TaskBoard extends Plugin { // Loads settings data and creating the Settings Tab in main Setting await this.loadSettings(); - this.runOnPluginUpdate(); + await this.runOnPluginUpdate(); this.addSettingTab(new TaskBoardSettingTab(this.app, this)); // this.getLanguage(); @@ -134,6 +139,8 @@ export default class TaskBoard extends Plugin { await this.vaultScanner.initializeTasksCache(); + await this.taskBoardFileManager.loadBoard(); + // Register events and commands only on Layout is ready this.app.workspace.onLayoutReady(() => { console.log("Task Board : Running onLayoutReady..."); @@ -287,12 +294,12 @@ export default class TaskBoard extends Plugin { // if (obsidianLang && obsidianLang in langCodes) { // localStorage.setItem("taskBoardLang", obsidianLang); - // this.settings.data.globalSettings.lang = obsidianLang; + // this.settings.data.lang = obsidianLang; // this.saveSettings(); // } else { // localStorage.setItem( // "taskBoardLang", - // // this.settings.data.globalSettings.lang + // // this.settings.data.lang // "en" // ); // } @@ -334,16 +341,14 @@ export default class TaskBoard extends Plugin { // this.registerEditorExtension(taskGutterExtension(this.app, this)); // Register task property hiding extension - const hiddenProperties = - this.settings.data.globalSettings?.hiddenTaskProperties || []; + const hiddenProperties = this.settings.data?.hiddenTaskProperties || []; if (hiddenProperties.length > 0) { this.registerEditorExtension(taskPropertyHidingExtension(this)); } } registerReadingModePostProcessor() { - const hiddenProperties = - this.settings.data.globalSettings?.hiddenTaskProperties || []; + const hiddenProperties = this.settings.data?.hiddenTaskProperties || []; if (hiddenProperties.length === 0) { return; } @@ -538,7 +543,7 @@ export default class TaskBoard extends Plugin { hiddenProperties.forEach((property) => { const pattern = getTaskPropertyRegexPatterns( property, - this.settings.data.globalSettings?.taskPropertyFormat, + this.settings.data?.taskPropertyFormat, ); if (pattern.test(content)) { content = content.replace(pattern, (match) => { @@ -566,7 +571,7 @@ export default class TaskBoard extends Plugin { } openAtStartup() { - if (!this.settings.data.globalSettings.openOnStartup) return; + if (!this.settings.data.openOnStartup) return; this.activateView("icon"); } @@ -583,7 +588,7 @@ export default class TaskBoard extends Plugin { id: "add-new-task", name: t("add-new-task"), callback: () => { - openAddNewTaskModal(this.app, this.plugin); + openAddNewTaskModal(this.plugin); }, }); this.addCommand({ @@ -690,8 +695,7 @@ export default class TaskBoard extends Plugin { return; } - const archivedPath = - this.settings.data.globalSettings.archivedTBNotesFolderPath; + const archivedPath = this.settings.data.archivedTBNotesFolderPath; const totalFiles = this.renameQueue.length; // Show progress notice @@ -706,10 +710,7 @@ export default class TaskBoard extends Plugin { try { if ( - fileTypeAllowedForScanning( - this.plugin.settings.data.globalSettings, - file, - ) + fileTypeAllowedForScanning(this.plugin.settings.data, file) ) { this.realTimeScanner.onFileRenamed( file, @@ -790,10 +791,7 @@ export default class TaskBoard extends Plugin { try { if ( - fileTypeAllowedForScanning( - this.plugin.settings.data.globalSettings, - file, - ) + fileTypeAllowedForScanning(this.plugin.settings.data, file) ) { this.realTimeScanner.onFileDeleted(file); } @@ -872,7 +870,7 @@ export default class TaskBoard extends Plugin { try { // if ( // fileTypeAllowedForScanning( - // this.plugin.settings.data.globalSettings, + // this.plugin.settings.data, // file // ) // ) { @@ -913,15 +911,12 @@ export default class TaskBoard extends Plugin { this.app.vault.on("modify", async (file: TAbstractFile) => { console.log("Modify event is fired..."); if ( - fileTypeAllowedForScanning( - this.plugin.settings.data.globalSettings, - file, - ) + fileTypeAllowedForScanning(this.plugin.settings.data, file) ) { if (file instanceof TFile) { if ( - this.plugin.settings.data.globalSettings - .scanMode === scanModeOptions.REAL_TIME + this.plugin.settings.data.scanMode === + scanModeOptions.REAL_TIME ) { this.vaultScanner.refreshTasksFromFiles( [file], @@ -960,10 +955,7 @@ export default class TaskBoard extends Plugin { }), ); - if ( - this.plugin.settings.data.globalSettings.scanMode !== - scanModeOptions.MANUAL - ) { + if (this.plugin.settings.data.scanMode !== scanModeOptions.MANUAL) { // Listen for editor-blur event and trigger scanning if the editor was modified this.registerEvent( this.app.workspace.on( @@ -1022,8 +1014,7 @@ export default class TaskBoard extends Plugin { .onClick(() => { if ( fileTypeAllowedForScanning( - this.plugin.settings.data - .globalSettings, + this.plugin.settings.data, file, ) ) { @@ -1034,32 +1025,26 @@ export default class TaskBoard extends Plugin { } }); }); - if ( - this.settings.data.globalSettings.scanFilters.files - .polarity === 2 - ) { + if (this.settings.data.scanFilters.files.polarity === 2) { menu.addItem((item) => { item.setTitle(t("add-file-in-scan-filter")) .setIcon(TaskBoardIcon) .setSection("action") .onClick(() => { - this.settings.data.globalSettings.scanFilters.files.values.push( + this.settings.data.scanFilters.files.values.push( file.path, ); this.saveSettings(); }); }); } - if ( - this.settings.data.globalSettings.scanFilters.files - .polarity === 1 - ) { + if (this.settings.data.scanFilters.files.polarity === 1) { menu.addItem((item) => { item.setTitle(t("add-file-in-scan-filter")) .setIcon(TaskBoardIcon) .setSection("action") .onClick(() => { - this.settings.data.globalSettings.scanFilters.files.values.push( + this.settings.data.scanFilters.files.values.push( file.path, ); this.saveSettings(); @@ -1078,32 +1063,26 @@ export default class TaskBoard extends Plugin { // }); // }); - if ( - this.settings.data.globalSettings.scanFilters.folders - .polarity === 2 - ) { + if (this.settings.data.scanFilters.folders.polarity === 2) { menu.addItem((item) => { item.setTitle(t("add-folder-in-scan-filter")) .setIcon(TaskBoardIcon) .setSection("action") .onClick(() => { - this.settings.data.globalSettings.scanFilters.folders.values.push( + this.settings.data.scanFilters.folders.values.push( file.path, ); this.saveSettings(); }); }); } - if ( - this.settings.data.globalSettings.scanFilters.folders - .polarity === 1 - ) { + if (this.settings.data.scanFilters.folders.polarity === 1) { menu.addItem((item) => { item.setTitle(t("add-folder-in-scan-filter")) .setIcon(TaskBoardIcon) .setSection("action") .onClick(() => { - this.settings.data.globalSettings.scanFilters.folders.values.push( + this.settings.data.scanFilters.folders.values.push( file.path, ); this.saveSettings(); @@ -1172,7 +1151,7 @@ export default class TaskBoard extends Plugin { // if (this.currentModifiedFile.path !== this.fileUpdatedUsingModal) { // await this.realTimeScanner.onFileModified( // this.currentModifiedFile, - // this.settings.data.globalSettings.realTimeScanner + // this.settings.data.realTimeScanner // ); // } else { // this.fileUpdatedUsingModal = ""; @@ -1186,8 +1165,7 @@ export default class TaskBoard extends Plugin { // Check if the Tasks plugin is installed and fetch the custom statuses // await fetchTasksPluginCustomStatuses(this.plugin); const tasksPlug = await isTasksPluginEnabled(this.plugin); - this.plugin.settings.data.globalSettings.compatiblePlugins.tasksPlugin = - tasksPlug; + this.plugin.settings.data.compatiblePlugins.tasksPlugin = tasksPlug; // Check if the Reminder plugin is installed isReminderPluginInstalled(this.plugin); @@ -1215,7 +1193,7 @@ export default class TaskBoard extends Plugin { // } as any) // ); // } else if (key === "boardConfigs" && Array.isArray(settings[key])) { - // // This is a temporary solution to sync the boardConfigs. I will need to replace the range object with the new 'datedBasedColumn', which will have three values 'dateType', 'from' and 'to'. So, basically I want to copy range.rangedata.from value to datedBasedColumn.from and similarly for to. And for datedBasedColumn.dateType, put the value this.settings.data.globalSettings.defaultDateType. + // // This is a temporary solution to sync the boardConfigs. I will need to replace the range object with the new 'datedBasedColumn', which will have three values 'dateType', 'from' and 'to'. So, basically I want to copy range.rangedata.from value to datedBasedColumn.from and similarly for to. And for datedBasedColumn.dateType, put the value this.settings.data.defaultDateType. // settings[key].forEach((boardConfig: Board) => { // boardConfig.columns.forEach((column: ColumnData) => { // if (!column.id) { @@ -1228,7 +1206,7 @@ export default class TaskBoard extends Plugin { // ) { // column.datedBasedColumn = { // dateType: - // this.settings.data.globalSettings + // this.settings.data // .universalDate, // from: column.datedBasedColumn?.from || 0, // to: column.datedBasedColumn?.to || 0, @@ -1420,7 +1398,7 @@ export default class TaskBoard extends Plugin { } } - private runOnPluginUpdate() { + private async runOnPluginUpdate() { // Check if the plugin version has changed const currentVersion = newReleaseVersion; // Change this whenever you will going to release a new version. const runMandatoryScan = false; // Change this whenever you will release a major version which requires user to scan the whole vault again. And to enable the notification. @@ -1450,6 +1428,63 @@ export default class TaskBoard extends Plugin { // }) // ); } + + // Check if .taskboard files exist and create missing ones + await this.checkAndCreateBoardFiles(); + } + + /** + * Check if configured board files exist, and create missing default board files + * This is called during plugin initialization + */ + private async checkAndCreateBoardFiles() { + try { + console.log("Task Board: Checking for configured board files..."); + + // Get the missing board files + const missingFiles = + await this.taskBoardFileManager.validateBoardFiles(); + + if (missingFiles.length > 0) { + console.log( + `Task Board: Found ${missingFiles.length} missing board file(s)`, + missingFiles, + ); + + // Import DEFAULT_BOARDS from BoardConfigs + const { DEFAULT_BOARDS } = + await import("src/interfaces/BoardConfigs"); + + // Try to create missing default board files + const createdCount = + await this.taskBoardFileManager.createMissingDefaultBoardFiles( + DEFAULT_BOARDS, + ); + + if (createdCount > 0) { + new Notice( + `Task Board: Created ${createdCount} missing board file(s). Please restart the plugin or reload Obsidian to load the new boards.`, + 5000, + ); + console.log( + `Task Board: Successfully created ${createdCount} board file(s)`, + ); + } + } else { + console.log("Task Board: All configured board files exist."); + } + } catch (error) { + console.error( + "Task Board: Error checking or creating board files:", + error, + ); + bugReporterManagerInsatance.showNotice( + 34, + "Error checking or creating board files", + error as string, + "main.ts/checkAndCreateBoardFiles", + ); + } } async fileExists(filePath: string): Promise { diff --git a/src/components/AddOrEditTaskRC.tsx b/src/components/AddOrEditTaskRC.tsx index 1bde4df5..c0476654 100644 --- a/src/components/AddOrEditTaskRC.tsx +++ b/src/components/AddOrEditTaskRC.tsx @@ -73,7 +73,7 @@ export const AddOrEditTaskRC: React.FC<{ const [formattedTaskContent, setFormattedTaskContent] = useState(isTaskNote ? noteContent : getFormattedTaskContentSync(task)); const frontmatterContentRef = useRef(''); const [newFilePath, setNewFilePath] = useState(filePath); - const [quickAddPluginChoice, setQuickAddPluginChoice] = useState(plugin.settings.data.globalSettings.quickAddPluginDefaultChoice || ''); + const [quickAddPluginChoice, setQuickAddPluginChoice] = useState(plugin.settings.data.quickAddPluginDefaultChoice || ''); const [markdownEditor, setMarkdownEditor] = useState(null); const [isEditorContentChanged, setIsEditorContentChanged] = useState(true); @@ -104,8 +104,8 @@ export const AddOrEditTaskRC: React.FC<{ let filteredStatusesDropdown: filterOptions[] = []; // Fetch all the custom statuses and add them to the dropdown - if (plugin.settings.data.globalSettings.customStatuses?.length > 0) { - filteredStatusesDropdown = plugin.settings.data.globalSettings.customStatuses.map((customStatus) => ({ + if (plugin.settings.data.customStatuses?.length > 0) { + filteredStatusesDropdown = plugin.settings.data.customStatuses.map((customStatus) => ({ value: customStatus.symbol, text: `${customStatus.name} [${customStatus.symbol}]`, })); @@ -160,12 +160,12 @@ export const AddOrEditTaskRC: React.FC<{ setIsEdited(true); const statusConfig = - plugin.settings.data.globalSettings.customStatuses.find( + plugin.settings.data.customStatuses.find( (status) => status.symbol === symbol ); const statusType = statusConfig ? statusConfig.type : statusTypeNames.TODO; // if (statusType === statusTypeNames.DONE) { - // const globalSettings = plugin.settings.data.globalSettings; + // const globalSettings = plugin.settings.data; // const moment = _moment as unknown as typeof _moment.default; // const currentDateValue = moment().format( // globalSettings?.taskCompletionDateTimePattern @@ -177,7 +177,7 @@ export const AddOrEditTaskRC: React.FC<{ // ); // setTitle(newTitle); // } else if (statusType === statusTypeNames.CANCELLED) { - // const globalSettings = plugin.settings.data.globalSettings; + // const globalSettings = plugin.settings.data; // const moment = _moment as unknown as typeof _moment.default; // const currentDateValue = moment().format( // globalSettings?.taskCompletionDateTimePattern @@ -190,13 +190,13 @@ export const AddOrEditTaskRC: React.FC<{ // setTitle(newTitle); // } else { // let newTitle = task.title; - // const globalSettings = plugin.settings.data.globalSettings; + // const globalSettings = plugin.settings.data; // newTitle = sanitizeCancelledDate(globalSettings, newTitle, ""); // newTitle = sanitizeCompletionDate(globalSettings, newTitle, ""); // setTitle(newTitle); // } - const globalSettings = plugin.settings.data.globalSettings; + const globalSettings = plugin.settings.data; const newTitle = sanitizeStatus(globalSettings, task.title, symbol, statusType); setTitle(newTitle); @@ -207,7 +207,7 @@ export const AddOrEditTaskRC: React.FC<{ setCreatedDate(value); if (!isTaskNote) { - const newTitle = sanitizeCreatedDate(plugin.settings.data.globalSettings, title, value, cursorLocationRef.current ?? undefined); + const newTitle = sanitizeCreatedDate(plugin.settings.data, title, value, cursorLocationRef.current ?? undefined); setTitle(newTitle); } @@ -219,7 +219,7 @@ export const AddOrEditTaskRC: React.FC<{ setStartDate(value); if (!isTaskNote) { - const newTitle = sanitizeStartDate(plugin.settings.data.globalSettings, title, value, cursorLocationRef.current ?? undefined); + const newTitle = sanitizeStartDate(plugin.settings.data, title, value, cursorLocationRef.current ?? undefined); setTitle(newTitle); } @@ -231,7 +231,7 @@ export const AddOrEditTaskRC: React.FC<{ setScheduledDate(value); if (!isTaskNote) { - const newTitle = sanitizeScheduledDate(plugin.settings.data.globalSettings, title, value, cursorLocationRef.current ?? undefined); + const newTitle = sanitizeScheduledDate(plugin.settings.data, title, value, cursorLocationRef.current ?? undefined); setTitle(newTitle); } @@ -243,7 +243,7 @@ export const AddOrEditTaskRC: React.FC<{ setDue(value); if (!isTaskNote) { - const newTitle = sanitizeDueDate(plugin.settings.data.globalSettings, title, value, cursorLocationRef.current ?? undefined); + const newTitle = sanitizeDueDate(plugin.settings.data, title, value, cursorLocationRef.current ?? undefined); setTitle(newTitle); } @@ -260,7 +260,7 @@ export const AddOrEditTaskRC: React.FC<{ // setTitle(title.replace(reminderRegex, "")); // } if (!isTaskNote) { - const newTitle = sanitizeReminder(plugin.settings.data.globalSettings, title, value, cursorLocationRef.current ?? undefined); + const newTitle = sanitizeReminder(plugin.settings.data, title, value, cursorLocationRef.current ?? undefined); setTitle(newTitle); } @@ -272,7 +272,7 @@ export const AddOrEditTaskRC: React.FC<{ setPriority(value); if (!isTaskNote) { - const newTitle = sanitizePriority(plugin.settings.data.globalSettings, title, value, cursorLocationRef.current ?? undefined); + const newTitle = sanitizePriority(plugin.settings.data, title, value, cursorLocationRef.current ?? undefined); setTitle(newTitle); } @@ -299,7 +299,7 @@ export const AddOrEditTaskRC: React.FC<{ } if (!isTaskNote) { - const newTitle = sanitizeTime(plugin.settings.data.globalSettings, title, newTime, cursorLocationRef.current ?? undefined); + const newTitle = sanitizeTime(plugin.settings.data, title, newTime, cursorLocationRef.current ?? undefined); setTitle(newTitle); } setIsEditorContentChanged(true); @@ -463,8 +463,8 @@ export const AddOrEditTaskRC: React.FC<{ let newStartDate = startDate; let newScheduledDate = scheduledDate; - if (plugin.settings.data.globalSettings.autoAddUniversalDate && !taskExists) { - const universalDateType = plugin.settings.data.globalSettings.universalDate; + if (plugin.settings.data.autoAddUniversalDate && !taskExists) { + const universalDateType = plugin.settings.data.universalDate; if (universalDateType === UniversalDateOptions.dueDate && !due) { newDue = new Date().toISOString().split('T')[0]; } else if (universalDateType === UniversalDateOptions.startDate && !startDate) { @@ -475,7 +475,7 @@ export const AddOrEditTaskRC: React.FC<{ } let newCreatedDate = createdDate; - if (plugin.settings.data.globalSettings.autoAddCreatedDate && !taskExists) { + if (plugin.settings.data.autoAddCreatedDate && !taskExists) { newCreatedDate = new Date().toISOString().split('T')[0]; } const updatedTask = { @@ -508,8 +508,8 @@ export const AddOrEditTaskRC: React.FC<{ let newStartDate = startDate; let newScheduledDate = scheduledDate; - if (plugin.settings.data.globalSettings.autoAddUniversalDate && !taskExists) { - const universalDateType = plugin.settings.data.globalSettings.universalDate; + if (plugin.settings.data.autoAddUniversalDate && !taskExists) { + const universalDateType = plugin.settings.data.universalDate; if (universalDateType === UniversalDateOptions.dueDate && !due) { newDue = new Date().toISOString().split('T')[0]; } else if (universalDateType === UniversalDateOptions.startDate && !startDate) { @@ -520,7 +520,7 @@ export const AddOrEditTaskRC: React.FC<{ } let newCreatedDate = createdDate; - if (plugin.settings.data.globalSettings.autoAddCreatedDate && !taskExists) { + if (plugin.settings.data.autoAddCreatedDate && !taskExists) { newCreatedDate = new Date().toISOString().split('T')[0]; } @@ -607,14 +607,14 @@ export const AddOrEditTaskRC: React.FC<{ }, [plugin.app]); const handleOpenTaskInMapView = () => { - // if (!plugin.settings.data.globalSettings.experimentalFeatures) { + // if (!plugin.settings.data.experimentalFeatures) { // new Notice(t("enable-experimental-features-message")); // return; // } applyIdToTaskItem(plugin, task).then((newId) => { - plugin.settings.data.globalSettings.lastViewHistory.viewedType = 'map'; - plugin.settings.data.globalSettings.lastViewHistory.taskId = newId ? String(newId) : (task.legacyId ? task.legacyId : String(plugin.settings.data.globalSettings.uniqueIdCounter)); + plugin.settings.data.lastViewHistory.viewedType = 'map'; + plugin.settings.data.lastViewHistory.taskId = newId ? String(newId) : (task.legacyId ? task.legacyId : String(plugin.settings.data.uniqueIdCounter)); // console.log("Preparing to open task in kanban view. Current file path:", newFilePath, "\nTask ID:", task.id, "\nLegacy ID:", task.legacyId, "\nnewId:", newId); @@ -972,7 +972,7 @@ export const AddOrEditTaskRC: React.FC<{ if (!prev.includes(task.legacyId ? task.legacyId : task.id)) { if (newId === undefined && !selectedTask?.legacyId) { bugReporterManagerInsatance.showNotice(24, "Both newId and legacyId are undefined", `Both newId and legacyId are undefined for the selected task titled ${selectedTask.title}.`, "AddOrEditTaskModal.tsx/EditTaskContent/childTaskInputRef useEffect/getUpdatedDependsOnIds"); - return [...prev, String(plugin.settings.data.globalSettings.uniqueIdCounter)]; + return [...prev, String(plugin.settings.data.uniqueIdCounter)]; } else if (newId === undefined) { return [...prev, selectedTask.legacyId]; } else if (newId) { @@ -986,11 +986,11 @@ export const AddOrEditTaskRC: React.FC<{ setDependsOn(prev => { const updated = getUpdatedDependsOnIds(prev); if (!isTaskNote) { - const newTitle = sanitizeDependsOn(plugin.settings.data.globalSettings, title, updated, cursorLocationRef.current ?? undefined); + const newTitle = sanitizeDependsOn(plugin.settings.data, title, updated, cursorLocationRef.current ?? undefined); setTitle(newTitle); } - selectedTask.legacyId = selectedTask.legacyId ? selectedTask.legacyId : (newId ? String(newId) : String(plugin.settings.data.globalSettings.uniqueIdCounter)); + selectedTask.legacyId = selectedTask.legacyId ? selectedTask.legacyId : (newId ? String(newId) : String(plugin.settings.data.uniqueIdCounter)); setChildTasks(prevChildTasks => { // Avoid adding duplicates if (!prevChildTasks.find(t => t.id === selectedTask.id)) { @@ -1061,7 +1061,7 @@ export const AddOrEditTaskRC: React.FC<{ // // Clear existing children in the leaf // await leaf.open(new AddOrEditTaskModal(plugin, childTask, onSave, onClose, true, activeNote)); - const settingOption = plugin.settings.data.globalSettings.editButtonAction; + const settingOption = plugin.settings.data.editButtonAction; switch (settingOption) { case EditButtonMode.NoteInSplit: case EditButtonMode.NoteInTab: @@ -1077,7 +1077,7 @@ export const AddOrEditTaskRC: React.FC<{ case EditButtonMode.View: case EditButtonMode.TasksPluginModal: default: - const isTaskNotePresent = isTaskNotePresentInTags(plugin.settings.data.globalSettings.taskNoteIdentifierTag, childTask.tags); + const isTaskNotePresent = isTaskNotePresentInTags(plugin.settings.data.taskNoteIdentifierTag, childTask.tags); openEditTaskView(plugin, isTaskNotePresent, false, true, childTask, childTask.filePath, "window"); break; } @@ -1110,7 +1110,7 @@ export const AddOrEditTaskRC: React.FC<{ const newDependsOn = dependsOn.filter(id => id !== taskId); setDependsOn(newDependsOn); if (!isTaskNote) { - const newTitle = sanitizeDependsOn(plugin.settings.data.globalSettings, title, newDependsOn, cursorLocationRef.current ?? undefined); + const newTitle = sanitizeDependsOn(plugin.settings.data, title, newDependsOn, cursorLocationRef.current ?? undefined); setTitle(newTitle); } setIsEdited(true); @@ -1145,7 +1145,7 @@ export const AddOrEditTaskRC: React.FC<{ // Update the title with the new dependsOn order if not a task note if (!isTaskNote) { - const newTitle = sanitizeDependsOn(plugin.settings.data.globalSettings, title, updatedDependsOn, cursorLocationRef.current ?? undefined); + const newTitle = sanitizeDependsOn(plugin.settings.data, title, updatedDependsOn, cursorLocationRef.current ?? undefined); setTitle(newTitle); } @@ -1345,7 +1345,7 @@ export const AddOrEditTaskRC: React.FC<{ {/* Task Created Date */} - {!plugin.settings.data.globalSettings.autoAddCreatedDate && + {!plugin.settings.data.autoAddCreatedDate &&
handleCreatedDateChange(e.target.value)} /> @@ -1371,7 +1371,7 @@ export const AddOrEditTaskRC: React.FC<{
{/* Task reminder date-time selector */} - {plugin.settings.data.globalSettings.notificationService !== NotificationService.None && ( + {plugin.settings.data.notificationService !== NotificationService.None && (
{tags.map((tag: string) => { const tagName = tag.replace('#', ''); - const customTagData = plugin.settings.data.globalSettings.tagColors.find(t => t.name === tagName); + const customTagData = plugin.settings.data.tagColors.find(t => t.name === tagName); const tagColor = customTagData?.color; const backgroundColor = tagColor ? updateRGBAOpacity(plugin, tagColor, 0.1) : `var(--tag-background)`; const borderColor = tagColor ? updateRGBAOpacity(plugin, tagColor, 0.5) : `var(--tag-color-hover)`; diff --git a/src/components/BoardFilters/ViewTaskFilter.ts b/src/components/BoardFilters/ViewTaskFilter.ts index 48463f34..0fcb9eda 100644 --- a/src/components/BoardFilters/ViewTaskFilter.ts +++ b/src/components/BoardFilters/ViewTaskFilter.ts @@ -858,7 +858,7 @@ export class TaskFilterComponent extends Component { // } valueSelect.addOptions( getCustomStatusOptionsForDropdown( - this.plugin.settings.data.globalSettings + this.plugin.settings.data .customStatuses ).reduce( ( @@ -1275,7 +1275,7 @@ export class TaskFilterComponent extends Component { switch (property) { // case "status": // suggestions = getStatusSuggestions( - // this.pluginSettings.data.globalSettings + // this.pluginSettings.data // .customStatuses // ); // break; diff --git a/src/components/KanbanView/Column.tsx b/src/components/KanbanView/Column.tsx index dd79a24d..61885b95 100644 --- a/src/components/KanbanView/Column.tsx +++ b/src/components/KanbanView/Column.tsx @@ -93,11 +93,11 @@ const Column: React.FC = ({ // } // }, [colType, columnData, allTasksExternal]); - const columnWidth = plugin.settings.data.globalSettings.columnWidth || '273px'; + const columnWidth = plugin.settings.data.columnWidth || '273px'; // const activeBoardSettings = plugin.settings.data.boardConfigs[activeBoardIndex]; // Extra code to provide special data-types for theme support. - const tagColors = plugin.settings.data.globalSettings.tagColors; + const tagColors = plugin.settings.data.tagColors; const tagColorMap = new Map(tagColors.map((t) => [t.name, t])); let tagData = tagColorMap.get(columnData?.coltag || ''); if (!tagData) { @@ -727,7 +727,7 @@ const Column: React.FC = ({
)}
{ handleDragOver(e); }} onDragLeave={handleDragLeave} diff --git a/src/components/KanbanView/KanbanBoardView.tsx b/src/components/KanbanView/KanbanBoardView.tsx index ff2f81ce..ef08ea1d 100644 --- a/src/components/KanbanView/KanbanBoardView.tsx +++ b/src/components/KanbanView/KanbanBoardView.tsx @@ -12,7 +12,6 @@ import type TaskBoard from "main"; import { t } from "src/utils/lang/helper"; interface KanbanBoardProps { - app: App; plugin: TaskBoard; board: Board; allTasks: taskJsonMerged | undefined; @@ -23,7 +22,7 @@ interface KanbanBoardProps { const KanbanBoard: React.FC = ({ plugin, board, allTasks, tasksPerColumn, loading, freshInstall }) => { // Check if lazy loading is enabled - const lazyLoadingEnabled = plugin.settings.data.globalSettings.kanbanView?.lazyLoadingEnabled ?? false; + const lazyLoadingEnabled = plugin.settings.data.kanbanView?.lazyLoadingEnabled ?? false; const ColumnComponent = lazyLoadingEnabled ? LazyColumn : Column; return ( @@ -249,7 +248,7 @@ export default memo(KanbanBoard); // // Memoized refreshBoardButton to avoid re-creating the function on every render // const refreshBoardButton = useCallback(async () => { -// if (plugin.settings.data.globalSettings.realTimeScanner) { +// if (plugin.settings.data.realTimeScanner) { // eventEmitter.emit("REFRESH_BOARD"); // } else { // if ( diff --git a/src/components/KanbanView/LazyColumn.tsx b/src/components/KanbanView/LazyColumn.tsx index 7ed7d1db..8c2fd9bb 100644 --- a/src/components/KanbanView/LazyColumn.tsx +++ b/src/components/KanbanView/LazyColumn.tsx @@ -11,7 +11,6 @@ import { taskItem } from 'src/interfaces/TaskItem'; import { Menu, Notice, Platform } from 'obsidian'; import { ViewTaskFilterPopover } from 'src/components/BoardFilters/ViewTaskFilterPopover'; import { eventEmitter } from 'src/services/EventEmitter'; -import { bugReporter } from 'src/services/OpenModals'; import { ViewTaskFilterModal } from 'src/components/BoardFilters'; import { ConfigureColumnSortingModal } from 'src/modals/ConfigureColumnSortingModal'; import { matchTagsWithWildcards } from 'src/utils/algorithms/ScanningFilterer'; @@ -53,7 +52,7 @@ const LazyColumn: React.FC = ({ } // Lazy loading settings from plugin - const lazySettings = plugin.settings.data.globalSettings.kanbanView; + const lazySettings = plugin.settings.data.kanbanView; const initialTaskCount = lazySettings.initialTaskCount || 20; const loadMoreCount = lazySettings.loadMoreCount || 10; const scrollThresholdPercent = lazySettings.scrollThresholdPercent || 80; @@ -140,10 +139,10 @@ const LazyColumn: React.FC = ({ }; }, [handleScroll]); - const columnWidth = plugin.settings.data.globalSettings.columnWidth || '273px'; + const columnWidth = plugin.settings.data.columnWidth || '273px'; // Extra code to provide special data-types for theme support. - const tagColors = plugin.settings.data.globalSettings.tagColors; + const tagColors = plugin.settings.data.tagColors; const tagColorMap = new Map(tagColors.map((t) => [t.name, t])); let tagData = tagColorMap.get(columnData?.coltag || ''); if (!tagData) { @@ -197,8 +196,10 @@ const LazyColumn: React.FC = ({ const columnIndex = columnData.index - 1; if (columnIndex !== -1) { - plugin.settings.data.boardConfigs[boardIndex].columns[columnIndex].minimized = !plugin.settings.data.boardConfigs[boardIndex].columns[columnIndex].minimized; - await plugin.saveSettings(); + let newBoardData = activeBoardData; + newBoardData.columns[columnIndex].minimized = !newBoardData.columns[columnIndex].minimized; + plugin.taskBoardFileManager.saveBoard(newBoardData); + eventEmitter.emit('REFRESH_BOARD'); } } @@ -232,21 +233,17 @@ const LazyColumn: React.FC = ({ columnData, (updatedColumnConfiguration: ColumnData) => { // Update the column configuration in the board data - const boardIndex = plugin.settings.data.boardConfigs.findIndex( - (board: Board) => board.index === activeBoardData.index - ); - if (boardIndex !== -1) { - const columnIndex = plugin.settings.data.boardConfigs[boardIndex].columns.findIndex( + if (activeBoardData.index !== -1) { + const columnIndex = activeBoardData.columns.findIndex( (col: ColumnData) => col.id === columnData.id ); if (columnIndex !== -1) { // Update the column configuration - plugin.settings.data.boardConfigs[boardIndex].columns[columnIndex] = updatedColumnConfiguration; - - // Save the settings - plugin.saveSettings(); + let newBoardData = activeBoardData; + newBoardData.columns[columnIndex] = updatedColumnConfiguration; + plugin.taskBoardFileManager.saveBoard(newBoardData); eventEmitter.emit('REFRESH_BOARD'); } @@ -285,10 +282,10 @@ const LazyColumn: React.FC = ({ if (filterState && boardIndex !== -1) { if (columnIndex !== -1) { // Update the column filters - plugin.settings.data.boardConfigs[boardIndex].columns[columnIndex].filters = filterState; + let newBoardData = activeBoardData; + newBoardData.columns[columnIndex].filters = filterState; - // Save the settings - await plugin.saveSettings(); + plugin.taskBoardFileManager.saveBoard(newBoardData); // Refresh the board view eventEmitter.emit('REFRESH_BOARD'); @@ -322,10 +319,10 @@ const LazyColumn: React.FC = ({ if (filterState && boardIndex !== -1) { if (columnIndex !== -1) { // Update the column filters - plugin.settings.data.boardConfigs[boardIndex].columns[columnIndex].filters = filterState; + let newBoardData = activeBoardData; + newBoardData.columns[columnIndex].filters = filterState; - // Save the settings - await plugin.saveSettings(); + plugin.taskBoardFileManager.saveBoard(newBoardData); // Refresh the board view eventEmitter.emit('REFRESH_BOARD'); @@ -365,7 +362,10 @@ const LazyColumn: React.FC = ({ if (columnIndex !== -1) { // Set the active property to false - plugin.settings.data.boardConfigs[boardIndex].columns[columnIndex].active = false; + let newBoardData = activeBoardData; + newBoardData.columns[columnIndex].active = false; + + plugin.taskBoardFileManager.saveBoard(newBoardData); // Save the settings await plugin.saveSettings(); @@ -735,7 +735,7 @@ const LazyColumn: React.FC = ({ // Render // ------------------------------------------------- - const taskItemComponent = plugin.settings.data.globalSettings.taskCardStyle === taskCardStyleNames.EMOJI ? TaskItem : TaskItemV2; + const taskItemComponent = plugin.settings.data.taskCardStyle === taskCardStyleNames.EMOJI ? TaskItem : TaskItemV2; return (
= ({
)}
{ handleDragOver(e); }} onDragLeave={handleDragLeave} diff --git a/src/components/KanbanView/TaskItem.tsx b/src/components/KanbanView/TaskItem.tsx index ed5f5e82..c06f5dfc 100644 --- a/src/components/KanbanView/TaskItem.tsx +++ b/src/components/KanbanView/TaskItem.tsx @@ -51,8 +51,8 @@ export interface TaskCardComponentProps { } const TaskItem: React.FC = ({ dataAttributeIndex, plugin, task, activeBoardSettings, columnIndex, swimlaneData }) => { - const globalSettings = plugin.settings.data.globalSettings; - const taskNoteIdentifierTag = plugin.settings.data.globalSettings.taskNoteIdentifierTag; + const globalSettings = plugin.settings.data; + const taskNoteIdentifierTag = plugin.settings.data.taskNoteIdentifierTag; const isTaskNote = isTaskNotePresentInTags(taskNoteIdentifierTag, task.tags); const isThistaskCompleted = isTaskNote ? isTaskCompleted(task.status, true, plugin.settings) : isTaskCompleted(task.title, false, plugin.settings) const columnData = columnIndex !== undefined ? activeBoardSettings?.columns[columnIndex - 1] : undefined; @@ -62,14 +62,14 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin const [isChecked, setIsChecked] = useState(isThistaskCompleted); const [cardLoadingAnimation, setCardLoadingAnimation] = useState(false); const [isDescriptionExpanded, setIsDescriptionExpanded] = useState(false); - const [showSubtasks, setShowSubtasks] = useState(plugin.settings.data.globalSettings.visiblePropertiesList?.includes(taskPropertiesNames.SubTasks)); + const [showSubtasks, setShowSubtasks] = useState(plugin.settings.data.visiblePropertiesList?.includes(taskPropertiesNames.SubTasks)); useEffect(() => { - if (plugin.settings.data.globalSettings.visiblePropertiesList?.includes(taskPropertiesNames.SubTasks)) { + if (plugin.settings.data.visiblePropertiesList?.includes(taskPropertiesNames.SubTasks)) { setShowSubtasks(true); } else { setShowSubtasks(false); } - }, [plugin.settings.data.globalSettings]); + }, [plugin.settings.data]); const [universalDate, setUniversalDate] = useState(() => getUniversalDateFromTask(task, plugin)); useEffect(() => { @@ -116,7 +116,7 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin // if (titleElement && task.title !== "") { // let cleanedTitle = cleanTaskTitleLegacy(task); // // NOTE : This search method is not working smoothly, hence using the first approach in file TaskBoardViewContent.tsx - // // const searchQuery = plugin.settings.data.globalSettings.searchQuery || ''; + // // const searchQuery = plugin.settings.data.searchQuery || ''; // // if (searchQuery) { // // const escapedQuery = searchQuery.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // // const regex = new RegExp(`(${escapedQuery})`, "gi"); @@ -180,7 +180,7 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin // return () => { // cancelled = true; // }; - }, [task.id, task.title, task.filePath, plugin.settings.data.globalSettings.searchQuery]); + }, [task.id, task.title, task.filePath, plugin.settings.data.searchQuery]); // useEffect(() => { // const allSubTasks = task.body.filter(line => isTaskLine(line.trim())); @@ -196,7 +196,7 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin // element.empty(); // Clear previous content // // NOTE : This search method is not working smoothly, hence using the first approach in file TaskBoardViewContent.tsx - // // const searchQuery = plugin.settings.data.globalSettings.searchQuery || ''; + // // const searchQuery = plugin.settings.data.searchQuery || ''; // // if (searchQuery) { // // const escapedQuery = searchQuery.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // // const regex = new RegExp(`(${escapedQuery})`, "gi"); @@ -416,11 +416,11 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin // Function to get the card background color based on tags function getCardBgBasedOnTag(tags: string[]): string | undefined { - if (plugin.settings.data.globalSettings.tagColorsType === "text") { + if (plugin.settings.data.tagColorsType === "text") { return undefined; } - const tagColors = plugin.settings.data.globalSettings.tagColors; + const tagColors = plugin.settings.data.tagColors; if (!Array.isArray(tagColors) || tagColors.length === 0) { return undefined; @@ -590,7 +590,7 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin }; const onEditButtonClicked = (event: React.MouseEvent) => { - const settingOption = plugin.settings.data.globalSettings.editButtonAction; + const settingOption = plugin.settings.data.editButtonAction; if (settingOption !== EditButtonMode.NoteInHover) { handleEditTask(plugin, task, settingOption); } else { @@ -601,7 +601,7 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin } const handleDoubleClickOnCard = (event: React.MouseEvent) => { - const settingOption = plugin.settings.data.globalSettings.doubleClickCardToEdit; + const settingOption = plugin.settings.data.doubleClickCardToEdit; if (settingOption === EditButtonMode.None) return; if (settingOption !== EditButtonMode.NoteInHover) { @@ -622,7 +622,7 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin return; } - const settingOption = plugin.settings.data.globalSettings.editButtonAction; + const settingOption = plugin.settings.data.editButtonAction; if (settingOption !== EditButtonMode.NoteInHover) { handleEditTask(plugin, childTask, settingOption); } else { @@ -649,7 +649,7 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin item.setIcon("info"); const statusMenu = item.setSubmenu() - const customStatues = getCustomStatusOptionsForDropdown(plugin.settings.data.globalSettings.customStatuses); + const customStatues = getCustomStatusOptionsForDropdown(plugin.settings.data.customStatuses); customStatues.forEach((status) => { statusMenu.addItem((item) => { item.setTitle(status.text); @@ -994,7 +994,7 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin {/* Render line tags (editable) */} {task.tags.map((tag: string) => { const tagName = tag.replace('#', ''); - const customTag = plugin.settings.data.globalSettings.tagColorsType === "text" ? plugin.settings.data.globalSettings.tagColors.find(t => t.name === tagName) : undefined; + const customTag = plugin.settings.data.tagColorsType === "text" ? plugin.settings.data.tagColors.find(t => t.name === tagName) : undefined; const tagColor = customTag?.color || `var(--tag-color)`; const backgroundColor = customTag ? updateRGBAOpacity(plugin, customTag.color, 0.1) : `var(--tag-background)`; // 10% opacity background const borderColor = customTag ? updateRGBAOpacity(plugin, customTag.color, 0.5) : `var(--tag-color-hover)`; @@ -1261,7 +1261,7 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin const renderChildTasks = () => { try { // Render only if the last viewed history is Kanban and there are child tasks - if (plugin.settings.data.globalSettings.lastViewHistory.viewedType === viewTypeNames.kanban && task?.dependsOn && task.dependsOn.length > 0) { + if (plugin.settings.data.lastViewHistory.viewedType === viewTypeNames.kanban && task?.dependsOn && task.dependsOn.length > 0) { return (
{/* Placeholder for future child tasks rendering */} @@ -1302,10 +1302,10 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin }; // Memoize the render functions to prevent unnecessary re-renders - const memoizedRenderHeader = useMemo(() => renderHeader(), [plugin.settings.data.globalSettings.visiblePropertiesList, task.priority, task.tags, activeBoardSettings]); - const memoizedRenderSubTasks = useMemo(() => renderSubTasks(), [plugin.settings.data.globalSettings.visiblePropertiesList, task.body, showSubtasks]); + const memoizedRenderHeader = useMemo(() => renderHeader(), [plugin.settings.data.visiblePropertiesList, task.priority, task.tags, activeBoardSettings]); + const memoizedRenderSubTasks = useMemo(() => renderSubTasks(), [plugin.settings.data.visiblePropertiesList, task.body, showSubtasks]); const memoizedRenderChildTasks = useMemo(() => renderChildTasks(), [task.dependsOn, childTasksData]); - // const memoizedRenderFooter = useMemo(() => renderFooter(), [plugin.settings.data.globalSettings.showFooter, task.completion, universalDate, task.time]); + // const memoizedRenderFooter = useMemo(() => renderFooter(), [plugin.settings.data.showFooter, task.completion, universalDate, task.time]); // ======================================== // RETURN STATEMENT (UPDATED) @@ -1326,10 +1326,10 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin {memoizedRenderHeader} {/* Drag Handle and Task Menu button */} - {plugin.settings.data.globalSettings.experimentalFeatures && ( + {plugin.settings.data.experimentalFeatures && ( <> { - Platform.isPhone || plugin.settings.data.globalSettings.lastViewHistory.viewedType === viewTypeNames.map ? ( + Platform.isPhone || plugin.settings.data.lastViewHistory.viewedType === viewTypeNames.map ? ( <>
diff --git a/src/components/KanbanView/TaskItemV2.tsx b/src/components/KanbanView/TaskItemV2.tsx index e0a72c1d..034f996f 100644 --- a/src/components/KanbanView/TaskItemV2.tsx +++ b/src/components/KanbanView/TaskItemV2.tsx @@ -51,8 +51,8 @@ export interface TaskProps { } const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, activeBoardSettings, columnIndex, swimlaneData }) => { - const globalSettings = plugin.settings.data.globalSettings; - const taskNoteIdentifierTag = plugin.settings.data.globalSettings.taskNoteIdentifierTag; + const globalSettings = plugin.settings.data; + const taskNoteIdentifierTag = plugin.settings.data.taskNoteIdentifierTag; const isTaskNote = isTaskNotePresentInTags(taskNoteIdentifierTag, task.tags); const isThistaskCompleted = isTaskNote ? isTaskCompleted(task.status, true, plugin.settings) : isTaskCompleted(task.title, false, plugin.settings) const columnData = columnIndex !== undefined ? activeBoardSettings?.columns[columnIndex - 1] : undefined; @@ -62,14 +62,14 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act const [isChecked, setIsChecked] = useState(isThistaskCompleted); const [cardLoadingAnimation, setCardLoadingAnimation] = useState(false); const [isDescriptionExpanded, setIsDescriptionExpanded] = useState(false); - const [showSubtasks, setShowSubtasks] = useState(plugin.settings.data.globalSettings.visiblePropertiesList?.includes(taskPropertiesNames.SubTasks)); + const [showSubtasks, setShowSubtasks] = useState(plugin.settings.data.visiblePropertiesList?.includes(taskPropertiesNames.SubTasks)); useEffect(() => { - if (plugin.settings.data.globalSettings.visiblePropertiesList?.includes(taskPropertiesNames.SubTasks)) { + if (plugin.settings.data.visiblePropertiesList?.includes(taskPropertiesNames.SubTasks)) { setShowSubtasks(true); } else { setShowSubtasks(false); } - }, [plugin.settings.data.globalSettings]); + }, [plugin.settings.data]); const [universalDate, setUniversalDate] = useState(() => getUniversalDateFromTask(task, plugin)); useEffect(() => { @@ -116,7 +116,7 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act // if (titleElement && task.title !== "") { // let cleanedTitle = cleanTaskTitleLegacy(task); // // NOTE : This search method is not working smoothly, hence using the first approach in file TaskBoardViewContent.tsx - // // const searchQuery = plugin.settings.data.globalSettings.searchQuery || ''; + // // const searchQuery = plugin.settings.data.searchQuery || ''; // // if (searchQuery) { // // const escapedQuery = searchQuery.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // // const regex = new RegExp(`(${escapedQuery})`, "gi"); @@ -180,7 +180,7 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act // return () => { // cancelled = true; // }; - }, [task.id, task.title, task.filePath, plugin.settings.data.globalSettings.searchQuery]); + }, [task.id, task.title, task.filePath, plugin.settings.data.searchQuery]); // useEffect(() => { // const allSubTasks = task.body.filter(line => isTaskLine(line.trim())); @@ -196,7 +196,7 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act // element.empty(); // Clear previous content // // NOTE : This search method is not working smoothly, hence using the first approach in file TaskBoardViewContent.tsx - // // const searchQuery = plugin.settings.data.globalSettings.searchQuery || ''; + // // const searchQuery = plugin.settings.data.searchQuery || ''; // // if (searchQuery) { // // const escapedQuery = searchQuery.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // // const regex = new RegExp(`(${escapedQuery})`, "gi"); @@ -416,11 +416,11 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act // Function to get the card background color based on tags function getCardBgBasedOnTag(tags: string[]): string | undefined { - if (plugin.settings.data.globalSettings.tagColorsType === "text") { + if (plugin.settings.data.tagColorsType === "text") { return undefined; } - const tagColors = plugin.settings.data.globalSettings.tagColors; + const tagColors = plugin.settings.data.tagColors; if (!Array.isArray(tagColors) || tagColors.length === 0) { return undefined; @@ -590,7 +590,7 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act }; const onEditButtonClicked = (event: React.MouseEvent) => { - const settingOption = plugin.settings.data.globalSettings.editButtonAction; + const settingOption = plugin.settings.data.editButtonAction; if (settingOption !== EditButtonMode.NoteInHover) { handleEditTask(plugin, task, settingOption); } else { @@ -601,7 +601,7 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act } const handleDoubleClickOnCard = (event: React.MouseEvent) => { - const settingOption = plugin.settings.data.globalSettings.doubleClickCardToEdit; + const settingOption = plugin.settings.data.doubleClickCardToEdit; if (settingOption === EditButtonMode.None) return; if (settingOption !== EditButtonMode.NoteInHover) { @@ -622,7 +622,7 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act return; } - const settingOption = plugin.settings.data.globalSettings.editButtonAction; + const settingOption = plugin.settings.data.editButtonAction; if (settingOption !== EditButtonMode.NoteInHover) { handleEditTask(plugin, childTask, settingOption); } else { @@ -649,7 +649,7 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act item.setIcon("info"); const statusMenu = item.setSubmenu() - const customStatues = getCustomStatusOptionsForDropdown(plugin.settings.data.globalSettings.customStatuses); + const customStatues = getCustomStatusOptionsForDropdown(plugin.settings.data.customStatuses); customStatues.forEach((status) => { statusMenu.addItem((item) => { item.setTitle(status.text); @@ -997,7 +997,7 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act {/* Render line tags (editable) */} {task.tags.map((tag: string) => { const tagName = tag.replace('#', ''); - const customTag = plugin.settings.data.globalSettings.tagColorsType === "text" ? plugin.settings.data.globalSettings.tagColors.find(t => t.name === tagName) : undefined; + const customTag = plugin.settings.data.tagColorsType === "text" ? plugin.settings.data.tagColors.find(t => t.name === tagName) : undefined; const tagColor = customTag?.color || null; // const backgroundColor = customTag ? updateRGBAOpacity(plugin, customTag.color, 0.1) : `var(--tag-background)`; // 10% opacity background // const borderColor = customTag ? updateRGBAOpacity(plugin, customTag.color, 0.5) : `var(--tag-color-hover)`; @@ -1272,7 +1272,7 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act const renderChildTasks = () => { try { // Render only if the last viewed history is Kanban and there are child tasks - if (plugin.settings.data.globalSettings.lastViewHistory.viewedType === viewTypeNames.kanban && task?.dependsOn && task.dependsOn.length > 0) { + if (plugin.settings.data.lastViewHistory.viewedType === viewTypeNames.kanban && task?.dependsOn && task.dependsOn.length > 0) { return (
{/* Placeholder for future child tasks rendering */} @@ -1313,10 +1313,10 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act }; // Memoize the render functions to prevent unnecessary re-renders - const memoizedRenderHeader = useMemo(() => renderHeader(), [plugin.settings.data.globalSettings.visiblePropertiesList, task.priority, task.tags, activeBoardSettings]); - const memoizedRenderSubTasks = useMemo(() => renderSubTasks(), [plugin.settings.data.globalSettings.visiblePropertiesList, task.body, showSubtasks]); + const memoizedRenderHeader = useMemo(() => renderHeader(), [plugin.settings.data.visiblePropertiesList, task.priority, task.tags, activeBoardSettings]); + const memoizedRenderSubTasks = useMemo(() => renderSubTasks(), [plugin.settings.data.visiblePropertiesList, task.body, showSubtasks]); const memoizedRenderChildTasks = useMemo(() => renderChildTasks(), [task.dependsOn, childTasksData]); - // const memoizedRenderFooter = useMemo(() => renderFooter(), [plugin.settings.data.globalSettings.showFooter, task.completion, universalDate, task.time]); + // const memoizedRenderFooter = useMemo(() => renderFooter(), [plugin.settings.data.showFooter, task.completion, universalDate, task.time]); // ======================================== // RETURN STATEMENT (UPDATED) @@ -1337,7 +1337,7 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act {memoizedRenderHeader} {/* Drag Handle and Task Menu button */} - {plugin.settings.data.globalSettings.experimentalFeatures && ( + {plugin.settings.data.experimentalFeatures && ( <> { Platform.isPhone ? ( @@ -1347,7 +1347,7 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act ) : ( <> {/* Drag Handle */} - {columnData?.colType !== colTypeNames.allPending && plugin.settings.data.globalSettings.lastViewHistory.viewedType === viewTypeNames.kanban && ( + {columnData?.colType !== colTypeNames.allPending && plugin.settings.data.lastViewHistory.viewedType === viewTypeNames.kanban && (
= ({ plugin, activeBoardIndex, allTasksArranged, focusOnTaskId }) => { - plugin.settings.data.globalSettings.lastViewHistory.taskId = ""; // Clear the taskId after focusing once - const mapViewSettings = plugin.settings.data.globalSettings.mapView; - const taskNoteIdentifierTag = plugin.settings.data.globalSettings.taskNoteIdentifierTag; + plugin.settings.data.lastViewHistory.taskId = ""; // Clear the taskId after focusing once + const mapViewSettings = plugin.settings.data.mapView; + const taskNoteIdentifierTag = plugin.settings.data.taskNoteIdentifierTag; const userBackgroundVariant: BackgroundVariant | undefined = (() => { switch (mapViewSettings.background) { @@ -85,7 +85,7 @@ const MapView: React.FC = ({ return undefined; } })(); - const tagColors = plugin.settings.data.globalSettings.tagColors; + const tagColors = plugin.settings.data.tagColors; const activeBoardSettings = plugin.settings.data.boardConfigs[activeBoardIndex]; // Loading state for localStorage data @@ -230,7 +230,7 @@ const MapView: React.FC = ({ // Get default width with proper validation const getDefaultWidth = () => { try { - const columnWidth = plugin.settings.data.globalSettings.columnWidth; + const columnWidth = plugin.settings.data.columnWidth; if (!columnWidth || typeof columnWidth !== 'string') { return 300; // Fallback if missing or not a string } @@ -546,7 +546,7 @@ const MapView: React.FC = ({ }; eventEmitter.emit("UPDATE_TASK", eventData); if (!isTaskNotePresentInTags(taskNoteIdentifierTag, updatedTargetTask.tags)) { - const updatedTargetTaskTitle = sanitizeDependsOn(plugin.settings.data.globalSettings, updatedTargetTask.title, updatedTargetTask.dependsOn); + const updatedTargetTaskTitle = sanitizeDependsOn(plugin.settings.data, updatedTargetTask.title, updatedTargetTask.dependsOn); updatedTargetTask.title = updatedTargetTaskTitle; // console.log('Updated source task :', updatedSourceTask, "\nOld source task:", sourceTask); @@ -681,7 +681,7 @@ const MapView: React.FC = ({ item.setTitle(t("transparent")); item.setIcon("eye-off"); item.onClick(() => { - plugin.settings.data.globalSettings.mapView.background = mapViewBackgrounVariantTypes.transparent; + plugin.settings.data.mapView.background = mapViewBackgrounVariantTypes.transparent; plugin.saveSettings(); // Refresh the board view @@ -694,7 +694,7 @@ const MapView: React.FC = ({ item.setTitle(t("dots")); item.setIcon("grip"); item.onClick(() => { - plugin.settings.data.globalSettings.mapView.background = mapViewBackgrounVariantTypes.dots; + plugin.settings.data.mapView.background = mapViewBackgrounVariantTypes.dots; plugin.saveSettings(); eventEmitter.emit('REFRESH_BOARD'); @@ -706,7 +706,7 @@ const MapView: React.FC = ({ item.setTitle(t("lines")); item.setIcon("grid-3x3"); item.onClick(() => { - plugin.settings.data.globalSettings.mapView.background = mapViewBackgrounVariantTypes.lines; + plugin.settings.data.mapView.background = mapViewBackgrounVariantTypes.lines; plugin.saveSettings(); eventEmitter.emit('REFRESH_BOARD'); @@ -718,7 +718,7 @@ const MapView: React.FC = ({ item.setTitle(t("cross")); item.setIcon("x"); item.onClick(() => { - plugin.settings.data.globalSettings.mapView.background = mapViewBackgrounVariantTypes.cross; + plugin.settings.data.mapView.background = mapViewBackgrounVariantTypes.cross; plugin.saveSettings(); eventEmitter.emit('REFRESH_BOARD'); @@ -732,7 +732,7 @@ const MapView: React.FC = ({ item.setTitle(t("show-minimap")); item.setIcon("map"); item.onClick(async () => { - plugin.settings.data.globalSettings.mapView.showMinimap = !plugin.settings.data.globalSettings.mapView.showMinimap; + plugin.settings.data.mapView.showMinimap = !plugin.settings.data.mapView.showMinimap; plugin.saveSettings(); eventEmitter.emit('REFRESH_BOARD'); @@ -744,7 +744,7 @@ const MapView: React.FC = ({ item.setTitle(t("animate-links")); item.setIcon("worm"); item.onClick(async () => { - plugin.settings.data.globalSettings.mapView.animatedEdges = !plugin.settings.data.globalSettings.mapView.animatedEdges; + plugin.settings.data.mapView.animatedEdges = !plugin.settings.data.mapView.animatedEdges; plugin.saveSettings(); eventEmitter.emit('REFRESH_BOARD'); @@ -812,7 +812,7 @@ const MapView: React.FC = ({ try { if (!isTaskNotePresentInTags(taskNoteIdentifierTag, updatedTargetTask.tags)) { - const updatedTargetTaskTitle = sanitizeDependsOn(plugin.settings.data.globalSettings, updatedTargetTask.title, updatedTargetTask.dependsOn); + const updatedTargetTaskTitle = sanitizeDependsOn(plugin.settings.data, updatedTargetTask.title, updatedTargetTask.dependsOn); updatedTargetTask.title = updatedTargetTaskTitle; await updateTaskInFile(plugin, updatedTargetTask, targetTask); diff --git a/src/components/MapView/ResizableNodeSelected.tsx b/src/components/MapView/ResizableNodeSelected.tsx index 61f2bbc8..03773693 100644 --- a/src/components/MapView/ResizableNodeSelected.tsx +++ b/src/components/MapView/ResizableNodeSelected.tsx @@ -17,7 +17,7 @@ interface ResizableNodeSelectedProps { } const ResizableNodeSelected: FC = ({ id, data, selected, width, height }) => { - const mapViewSettings = data.label?.props?.plugin.settings.data.globalSettings.mapView; + const mapViewSettings = data.label?.props?.plugin.settings.data.mapView; const orientationHorizontal = mapViewSettings.mapOrientation === mapViewNodeMapOrientation.horizontal; // console.log('Rendering ResizableNodeSelected for node:', id, { data, selected, width, height }); @@ -36,7 +36,7 @@ const ResizableNodeSelected: FC = ({ id, try { const sizeData: Record = JSON.parse(localStorage.getItem(NODE_SIZE_STORAGE_KEY) || '{}'); sizeData[id] = { - width: params.width ?? data.label.props.plugin.settings.data.globalSettings.columnWidth ?? 300 + width: params.width ?? data.label.props.plugin.settings.data.columnWidth ?? 300 // height: params.height ?? 30 }; localStorage.setItem(NODE_SIZE_STORAGE_KEY, JSON.stringify(sizeData)); diff --git a/src/components/TaskBoardViewContent.tsx b/src/components/TaskBoardViewContent.tsx index d59642b0..e52290e8 100644 --- a/src/components/TaskBoardViewContent.tsx +++ b/src/components/TaskBoardViewContent.tsx @@ -3,13 +3,12 @@ import { Board, ColumnData, RootFilterState } from "../interfaces/BoardConfigs"; import { CirclePlus, RefreshCcw, Search, SearchX, Filter, Menu as MenuICon, Settings, EllipsisVertical, List } from 'lucide-react'; import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; -import { loadBoardsData, loadTasksAndMerge } from "src/utils/JsonFileOperations"; +import { loadTasksAndMerge } from "src/utils/JsonFileOperations"; import { taskItem, taskJsonMerged } from "src/interfaces/TaskItem"; import { App, debounce, Platform, Menu } from "obsidian"; import type TaskBoard from "main"; import { eventEmitter } from "src/services/EventEmitter"; -import { handleUpdateBoards } from "../utils/BoardOperations"; import { bugReporter, openAddNewTaskModal, openBoardConfigModal, openScanVaultModal, openTaskBoardActionsModal } from "../services/OpenModals"; import { columnSegregator } from 'src/utils/algorithms/ColumnSegregator'; import { t } from "src/utils/lang/helper"; @@ -23,19 +22,21 @@ import { taskPropertiesNames, viewTypeNames } from "src/interfaces/Enums"; import { ScanVaultIcon, funnelIcon } from "src/interfaces/Icons"; import { bugReporterManagerInsatance } from "src/managers/BugReporter"; -const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs: Board[] }> = ({ app, plugin, boardConfigs }) => { - const [boards, setBoards] = useState(boardConfigs); - const [activeBoardIndex, setActiveBoardIndex] = useState(plugin.settings.data.globalSettings.lastViewHistory.boardIndex ?? 0); +const TaskBoardViewContent: React.FC<{ plugin: TaskBoard }> = ({ plugin }) => { + // const [boards, setBoards] = useState(boardConfigs); + const [activeBoardIndex, setActiveBoardIndex] = useState(plugin.settings.data.lastViewHistory.boardIndex ?? 0); + const [currentBoardData, setCurrentBoardData] = useState(); + const [allBoardsData, setAllBoardsData] = useState(); const [allTasks, setAllTasks] = useState(); const [filteredTasks, setFilteredTasks] = useState(null); const [filteredTasksPerColumn, setFilteredTasksPerColumn] = useState([]); - const [viewType, setViewType] = useState(plugin.settings.data.globalSettings.lastViewHistory.viewedType || viewTypeNames.kanban); + const [viewType, setViewType] = useState(plugin.settings.data.lastViewHistory.viewedType || viewTypeNames.kanban); const [refreshCount, setRefreshCount] = useState(0); const [loading, setLoading] = useState(true); const [freshInstall, setFreshInstall] = useState(false); - const [showSearchInput, setShowSearchInput] = useState(plugin.settings.data.globalSettings.searchQuery ? true : false); - const [searchQuery, setSearchQuery] = useState(plugin.settings.data.globalSettings.searchQuery ?? ""); + const [showSearchInput, setShowSearchInput] = useState(plugin.settings.data.searchQuery ? true : false); + const [searchQuery, setSearchQuery] = useState(plugin.settings.data.searchQuery ?? ""); const filterPopoverRef = useRef(null); @@ -86,14 +87,18 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs useEffect(() => { const fetchData = async () => { try { - const data = await loadBoardsData(plugin); - setBoards(data); + const data = await plugin.taskBoardFileManager.loadBoard(activeBoardIndex); + if (!data) throw "Board data not found."; + + setCurrentBoardData(data); const allTasks = await loadTasksAndMerge(plugin, true); if (allTasks) { setAllTasks(allTasks); setFreshInstall(false); } + + const allBoardsData = plugin.taskBoardFileManager.loadAllBoards(); } catch (error) { console.error( "Error loading tasks cache from disk\nIf this is appearing on a fresh install then no need to worry.\n", @@ -109,9 +114,9 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs const allTasksArrangedPerColumn = useMemo(() => { // console.log("Calculating allTasksArrangedPerColumn..."); setFilteredTasksPerColumn([]); - if (allTasks && boards[activeBoardIndex]) { + if (allTasks && currentBoardData) { // Apply board filters to pending tasks - const currentBoard = boards[activeBoardIndex]; + const currentBoard = currentBoardData; const boardFilter = currentBoard.boardFilter; // Create a copy of allTasks with filtered pending tasks @@ -120,7 +125,7 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs Pending: boardFilterer(allTasks.Pending, boardFilter), Completed: boardFilterer(allTasks.Completed, boardFilter), }; - plugin.settings.data.boardConfigs[activeBoardIndex].taskCount = { + currentBoardData.taskCount = { pending: filteredAllTasks.Pending.length, completed: filteredAllTasks.Completed.length, }; @@ -141,11 +146,11 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs // I think this below code is not required as we simply want to update the data on the disk. // setBoards((prevBoards) => { // const updatedBoards = [...prevBoards]; - // updatedBoards[activeBoardIndex] = updatedBoardData; + // updatedcurrentBoardData = updatedBoardData; // return updatedBoards; // }); - plugin.settings.data.boardConfigs[activeBoardIndex] = updatedBoardData; + plugin.taskBoardFileManager.saveBoard(updatedBoardData); // Technically, at later point in time, when user will make any changes, the latest data will be updated on the disk, so we need not have to update it everytime during this column seggregation. // const newSettings = plugin.settings; // plugin.saveSettings(newSettings); @@ -189,7 +194,7 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs useEffect(() => { const refreshView = (viewType: string) => { setViewType(viewType); - plugin.settings.data.globalSettings.lastViewHistory.viewedType = viewType; + plugin.settings.data.lastViewHistory.viewedType = viewType; plugin.saveSettings(); }; eventEmitter.on("SWITCH_VIEW", refreshView); @@ -219,7 +224,7 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs }, []); function handleOpenAddNewTaskModal() { - openAddNewTaskModal(app, plugin); + openAddNewTaskModal(plugin); } function handleOpenTaskBoardActionsModal() { @@ -231,13 +236,13 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs setSearchQuery(""); // el.currentTarget.focus(); setFilteredTasksPerColumn([]); - plugin.settings.data.globalSettings.searchQuery = ""; + plugin.settings.data.searchQuery = ""; eventEmitter.emit("REFRESH_COLUMN"); plugin.saveSettings(); setShowSearchInput(false); } else { - setSearchQuery(plugin.settings.data.globalSettings.searchQuery || ""); + setSearchQuery(plugin.settings.data.searchQuery || ""); handleSearchSubmit(); setShowSearchInput(true); } @@ -255,7 +260,7 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs return null; } - // plugin.settings.data.globalSettings.searchQuery = searchQuery; + // plugin.settings.data.searchQuery = searchQuery; const lowerQuery = searchQuery.toLowerCase(); let searchFilteredTasks: taskJsonMerged | null = null; @@ -310,7 +315,7 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs setFilteredTasksPerColumn(filtered); setTimeout(() => { - plugin.settings.data.globalSettings.searchQuery = lowerQuery; + plugin.settings.data.searchQuery = lowerQuery; plugin.saveSettings(); }, 100); } @@ -322,26 +327,26 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs function handleViewTypeChange(e: React.ChangeEvent) { const newViewType = e.target.value; setViewType(newViewType); - plugin.settings.data.globalSettings.lastViewHistory.viewedType = newViewType; + plugin.settings.data.lastViewHistory.viewedType = newViewType; plugin.saveSettings(); } function handleFilterButtonClick(event: React.MouseEvent) { try { - const currentBoardConfig = boards[activeBoardIndex]; + const currentBoardConfig = currentBoardData; if (Platform.isMobile || Platform.isMacOS) { // If its a mobile platform, then we will open a modal instead of popover. const filterModal = new ViewTaskFilterModal( - plugin, false, undefined, activeBoardIndex, currentBoardConfig.name + plugin, false, undefined, activeBoardIndex, currentBoardConfig!.name ); // Set initial filter state - if (currentBoardConfig.boardFilter) { + if (currentBoardConfig!.boardFilter) { setTimeout(() => { // Use type assertion to resolve non-null issues // const filterState = filterModal.liveFilterState as RootFilterState; if (filterModal.taskFilterComponent) { - filterModal.taskFilterComponent.loadFilterState(currentBoardConfig.boardFilter!); + filterModal.taskFilterComponent.loadFilterState(currentBoardConfig!.boardFilter); } }, 100); } @@ -350,12 +355,12 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs filterModal.filterCloseCallback = async (filterState) => { if (filterState) { // Save the filter state to the board - const updatedBoards = [...boards]; - updatedBoards[activeBoardIndex].boardFilter = filterState; - setBoards(updatedBoards); + const updatedcurrentBoardData = currentBoardData; + updatedcurrentBoardData!.boardFilter = filterState; + setCurrentBoardData(updatedcurrentBoardData); // Persist to settings - plugin.settings.data.boardConfigs[activeBoardIndex].boardFilter = filterState; + currentBoardData!.boardFilter = filterState; await plugin.saveSettings(); // Refresh the board view @@ -388,15 +393,15 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs false, // forColumn = false since this is for board-level filter undefined, activeBoardIndex, - boards[activeBoardIndex]?.name || "Board", + currentBoardData?.name || "Board", ); // Load existing filter state if available - if (currentBoardConfig.boardFilter) { + if (currentBoardConfig!.boardFilter) { // Wait for component to be created and loaded setTimeout(() => { if (popover.taskFilterComponent) { - popover.taskFilterComponent.loadFilterState(currentBoardConfig.boardFilter!); + popover.taskFilterComponent.loadFilterState(currentBoardConfig!.boardFilter!); } }, 100); } @@ -405,12 +410,12 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs popover.onClose = async (filterState?: RootFilterState) => { if (filterState) { // Save the filter state to the board - const updatedBoards = [...boards]; - updatedBoards[activeBoardIndex].boardFilter = filterState; - setBoards(updatedBoards); + const updatedcurrentBoardData = currentBoardData; + updatedcurrentBoardData!.boardFilter = filterState; + setCurrentBoardData(updatedcurrentBoardData); // Persist to settings - plugin.settings.data.boardConfigs[activeBoardIndex].boardFilter = filterState; + currentBoardData!.boardFilter = filterState; await plugin.saveSettings(); // Refresh the board view @@ -428,12 +433,12 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs } function togglePropertyNameInSettings(propertyName: string) { - let visibleProperties = plugin.settings.data.globalSettings.visiblePropertiesList || []; + let visibleProperties = plugin.settings.data.visiblePropertiesList || []; console.log("Current properties list :", visibleProperties, "\nRemove following property :", propertyName, "\nWill remove from the following index :", visibleProperties.indexOf(propertyName)); if (visibleProperties.includes(propertyName)) { visibleProperties.splice(visibleProperties.indexOf(propertyName), 1); - plugin.settings.data.globalSettings.visiblePropertiesList = visibleProperties; + plugin.settings.data.visiblePropertiesList = visibleProperties; } else { console.log("Property Name:", propertyName); @@ -462,7 +467,7 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs } visibleProperties.push(propertyName); - plugin.settings.data.globalSettings.visiblePropertiesList = visibleProperties; + plugin.settings.data.visiblePropertiesList = visibleProperties; } plugin.saveSettings(); @@ -484,7 +489,7 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs item.onClick(async () => { togglePropertyNameInSettings(taskPropertiesNames.ID); }) - item.setChecked(plugin.settings.data.globalSettings.visiblePropertiesList?.includes(taskPropertiesNames.ID)) + item.setChecked(plugin.settings.data.visiblePropertiesList?.includes(taskPropertiesNames.ID)) }); propertyMenu.addItem((item) => { @@ -492,7 +497,7 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs item.onClick(async () => { togglePropertyNameInSettings(taskPropertiesNames.Checkbox); }) - item.setChecked(plugin.settings.data.globalSettings.visiblePropertiesList?.includes(taskPropertiesNames.Checkbox)) + item.setChecked(plugin.settings.data.visiblePropertiesList?.includes(taskPropertiesNames.Checkbox)) }); propertyMenu.addItem((item) => { @@ -501,7 +506,7 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs togglePropertyNameInSettings(taskPropertiesNames.Status); }) - item.setChecked(plugin.settings.data.globalSettings.visiblePropertiesList?.includes(taskPropertiesNames.Status)) + item.setChecked(plugin.settings.data.visiblePropertiesList?.includes(taskPropertiesNames.Status)) }); propertyMenu.addItem((item) => { @@ -510,7 +515,7 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs togglePropertyNameInSettings(taskPropertiesNames.Priority); }) - item.setChecked(plugin.settings.data.globalSettings.visiblePropertiesList?.includes(taskPropertiesNames.Priority)) + item.setChecked(plugin.settings.data.visiblePropertiesList?.includes(taskPropertiesNames.Priority)) }); propertyMenu.addItem((item) => { @@ -519,7 +524,7 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs togglePropertyNameInSettings(taskPropertiesNames.Tags); }) - item.setChecked(plugin.settings.data.globalSettings.visiblePropertiesList?.includes(taskPropertiesNames.Tags)) + item.setChecked(plugin.settings.data.visiblePropertiesList?.includes(taskPropertiesNames.Tags)) }); propertyMenu.addItem((item) => { item.setTitle(t("time")); @@ -527,7 +532,7 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs togglePropertyNameInSettings(taskPropertiesNames.Time); }) - item.setChecked(plugin.settings.data.globalSettings.visiblePropertiesList?.includes(taskPropertiesNames.Time)) + item.setChecked(plugin.settings.data.visiblePropertiesList?.includes(taskPropertiesNames.Time)) }); propertyMenu.addItem((item) => { item.setTitle(t("reminder")); @@ -535,7 +540,7 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs togglePropertyNameInSettings(taskPropertiesNames.Reminder); }) - item.setChecked(plugin.settings.data.globalSettings.visiblePropertiesList?.includes(taskPropertiesNames.Reminder)) + item.setChecked(plugin.settings.data.visiblePropertiesList?.includes(taskPropertiesNames.Reminder)) }); propertyMenu.addItem((item) => { item.setTitle(t("created-date")); @@ -543,7 +548,7 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs togglePropertyNameInSettings(taskPropertiesNames.CreatedDate); }) - item.setChecked(plugin.settings.data.globalSettings.visiblePropertiesList?.includes(taskPropertiesNames.CreatedDate)) + item.setChecked(plugin.settings.data.visiblePropertiesList?.includes(taskPropertiesNames.CreatedDate)) }); propertyMenu.addItem((item) => { item.setTitle(t("start-date")); @@ -551,7 +556,7 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs togglePropertyNameInSettings(taskPropertiesNames.StartDate); }) - item.setChecked(plugin.settings.data.globalSettings.visiblePropertiesList?.includes(taskPropertiesNames.StartDate)) + item.setChecked(plugin.settings.data.visiblePropertiesList?.includes(taskPropertiesNames.StartDate)) }); propertyMenu.addItem((item) => { item.setTitle(t("scheduled-date")); @@ -559,7 +564,7 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs togglePropertyNameInSettings(taskPropertiesNames.ScheduledDate); }) - item.setChecked(plugin.settings.data.globalSettings.visiblePropertiesList?.includes(taskPropertiesNames.ScheduledDate)) + item.setChecked(plugin.settings.data.visiblePropertiesList?.includes(taskPropertiesNames.ScheduledDate)) }); propertyMenu.addItem((item) => { item.setTitle(t("due-date")); @@ -567,7 +572,7 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs togglePropertyNameInSettings(taskPropertiesNames.DueDate); }) - item.setChecked(plugin.settings.data.globalSettings.visiblePropertiesList?.includes(taskPropertiesNames.DueDate)) + item.setChecked(plugin.settings.data.visiblePropertiesList?.includes(taskPropertiesNames.DueDate)) }); propertyMenu.addItem((item) => { item.setTitle(t("completed-date")); @@ -575,7 +580,7 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs togglePropertyNameInSettings(taskPropertiesNames.CompletionDate); }) - item.setChecked(plugin.settings.data.globalSettings.visiblePropertiesList?.includes(taskPropertiesNames.CompletionDate)) + item.setChecked(plugin.settings.data.visiblePropertiesList?.includes(taskPropertiesNames.CompletionDate)) }); propertyMenu.addItem((item) => { item.setTitle(t("cancelled-date")); @@ -583,7 +588,7 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs togglePropertyNameInSettings(taskPropertiesNames.CancelledDate); }) - item.setChecked(plugin.settings.data.globalSettings.visiblePropertiesList?.includes(taskPropertiesNames.CancelledDate)) + item.setChecked(plugin.settings.data.visiblePropertiesList?.includes(taskPropertiesNames.CancelledDate)) }); propertyMenu.addItem((item) => { item.setTitle(t("dependencies")); @@ -591,7 +596,7 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs togglePropertyNameInSettings(taskPropertiesNames.Dependencies); }) - item.setChecked(plugin.settings.data.globalSettings.visiblePropertiesList?.includes(taskPropertiesNames.Dependencies)) + item.setChecked(plugin.settings.data.visiblePropertiesList?.includes(taskPropertiesNames.Dependencies)) }); propertyMenu.addItem((item) => { item.setTitle(t("file-name")); @@ -599,7 +604,7 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs togglePropertyNameInSettings(taskPropertiesNames.FilePath); }) - item.setChecked(plugin.settings.data.globalSettings.visiblePropertiesList?.includes(taskPropertiesNames.FilePath)) + item.setChecked(plugin.settings.data.visiblePropertiesList?.includes(taskPropertiesNames.FilePath)) }); propertyMenu.addSeparator(); @@ -614,7 +619,7 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs togglePropertyNameInSettings(taskPropertiesNames.SubTasks); }) - item.setChecked(plugin.settings.data.globalSettings.visiblePropertiesList?.includes(taskPropertiesNames.SubTasks)); + item.setChecked(plugin.settings.data.visiblePropertiesList?.includes(taskPropertiesNames.SubTasks)); }); subTasksMenu.addItem((item) => { @@ -623,7 +628,7 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs togglePropertyNameInSettings(taskPropertiesNames.SubTasksMinimized); }) - item.setChecked(plugin.settings.data.globalSettings.visiblePropertiesList?.includes(taskPropertiesNames.SubTasksMinimized)); + item.setChecked(plugin.settings.data.visiblePropertiesList?.includes(taskPropertiesNames.SubTasksMinimized)); }); // subTasksMenu.addItem((item) => { @@ -633,7 +638,7 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs // togglePropertyNameInSettings(taskPropertiesNames.SubTasksMinimized); // }) - // item.setChecked(!plugin.settings.data.globalSettings.visiblePropertiesList?.includes(taskPropertiesNames.SubTasks) && !plugin.settings.data.globalSettings.visiblePropertiesList?.includes(taskPropertiesNames.SubTasksMinimized)); + // item.setChecked(!plugin.settings.data.visiblePropertiesList?.includes(taskPropertiesNames.SubTasks) && !plugin.settings.data.visiblePropertiesList?.includes(taskPropertiesNames.SubTasksMinimized)); // }); }); @@ -648,7 +653,7 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs plugin.saveSettings(); }) - item.setChecked(plugin.settings.data.globalSettings.visiblePropertiesList?.includes(taskPropertiesNames.Description)); + item.setChecked(plugin.settings.data.visiblePropertiesList?.includes(taskPropertiesNames.Description)); }); subTasksMenu.addItem((item) => { @@ -658,7 +663,7 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs plugin.saveSettings(); }) - item.setChecked(plugin.settings.data.globalSettings.visiblePropertiesList?.includes(taskPropertiesNames.DescriptionMinimized)); + item.setChecked(plugin.settings.data.visiblePropertiesList?.includes(taskPropertiesNames.DescriptionMinimized)); }); }); @@ -672,8 +677,8 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs if (index !== activeBoardIndex) { setSearchQuery(""); setFilteredTasksPerColumn([]); - plugin.settings.data.globalSettings.searchQuery = ""; - plugin.settings.data.globalSettings.lastViewHistory.boardIndex = index; + plugin.settings.data.searchQuery = ""; + plugin.settings.data.lastViewHistory.boardIndex = index; setActiveBoardIndex(index); setTimeout(() => { eventEmitter.emit("REFRESH_BOARD"); @@ -737,9 +742,14 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs item.setTitle(t("open-board-configuration-modal")); item.setIcon("settings"); item.onClick(async () => { - openBoardConfigModal(plugin, boards, activeBoardIndex, (updatedBoards) => - handleUpdateBoards(plugin, updatedBoards, setBoards) - ); + openBoardConfigModal(plugin, allBoardsData!, activeBoardIndex, (updatedBoards, boardIndex) => { + // handleUpdateBoards(plugin, updatedBoards, setCurrentBoardData) + if (activeBoardIndex === boardIndex) { + setCurrentBoardData(updatedBoards[boardIndex]); + } + plugin.taskBoardFileManager.saveBoard(updatedBoards[boardIndex], boardIndex); + } + ) }); }); sortMenu.addItem((item) => { @@ -811,13 +821,13 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs {!showSearchInput && ( - {boards[activeBoardIndex]?.name} + {currentBoardData?.name} )}
) : ( // Desktop view: Original board titles
- {boards.map((board, index) => ( + {allBoardsData && allBoardsData.map((board, index) => (
- {boards.map((board, index) => ( + {allBoardsData && allBoardsData.map((board, index) => (
- openBoardConfigModal(plugin, boards, activeBoardIndex, (updatedBoards) => - handleUpdateBoards(plugin, updatedBoards, setBoards) + openBoardConfigModal(plugin, allBoardsData!, activeBoardIndex, (updatedBoards, boardIndex) => { + // handleUpdateBoards(plugin, updatedBoards, setCurrentBoardData) + if (activeBoardIndex === boardIndex) { + setCurrentBoardData(updatedBoards[boardIndex]); + } + plugin.taskBoardFileManager.saveBoard(updatedBoards[boardIndex], boardIndex); + } ) } > @@ -987,12 +1007,11 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs }
- {boards[activeBoardIndex] ? ( + {currentBoardData ? ( viewType === viewTypeNames.kanban ? ( 0 ? filteredTasksPerColumn : allTasksArrangedPerColumn} loading={loading} @@ -1023,7 +1042,7 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs plugin={plugin} activeBoardIndex={activeBoardIndex} allTasksArranged={filteredTasksPerColumn.length > 0 ? filteredTasksPerColumn : allTasksArrangedPerColumn} - focusOnTaskId={plugin.settings.data.globalSettings.lastViewHistory.taskId || ""} + focusOnTaskId={plugin.settings.data.lastViewHistory.taskId || ""} /> ) ) : ( diff --git a/src/editor-extensions/task-operations/property-hiding.ts b/src/editor-extensions/task-operations/property-hiding.ts index dd1fa322..dbdccaf4 100644 --- a/src/editor-extensions/task-operations/property-hiding.ts +++ b/src/editor-extensions/task-operations/property-hiding.ts @@ -239,7 +239,7 @@ function createPropertyDecorations( ): DecorationSet { const decorations: Range[] = []; const hiddenProperties = - plugin.settings.data.globalSettings.hiddenTaskProperties || []; + plugin.settings.data.hiddenTaskProperties || []; const cursorPos = view.state.selection.main.head; const doc = view.state.doc; @@ -274,7 +274,7 @@ function createPropertyDecorations( hiddenProperties.forEach((property) => { const pattern = getTaskPropertyRegexPatterns( property, - plugin.settings.data.globalSettings?.taskPropertyFormat + plugin.settings.data?.taskPropertyFormat ); const matches = Array.from(lineText.matchAll(pattern)); // console.log( diff --git a/src/interfaces/BoardConfigs.ts b/src/interfaces/BoardConfigs.ts index 4ea22bdd..1828c095 100644 --- a/src/interfaces/BoardConfigs.ts +++ b/src/interfaces/BoardConfigs.ts @@ -1,3 +1,5 @@ +import { colTypeNames, defaultTaskStatuses } from "./Enums"; + export interface columnSortingCriteria { criteria: | "status" @@ -145,3 +147,244 @@ export type Board = { }; export type BoardConfigs = Board[]; + +export const DEFAULT_BOARDS: BoardConfigs = [ + { + columns: [ + { + id: 1, + colType: colTypeNames.undated, + active: true, + collapsed: false, + name: "Undated Tasks", + index: 1, + datedBasedColumn: { + dateType: "due", + from: 0, + to: 0, + }, + }, + { + id: 2, + colType: colTypeNames.dated, + active: true, + collapsed: false, + name: "Over Due", + index: 2, + datedBasedColumn: { + dateType: "due", + from: -300, + to: -1, + }, + }, + { + id: 3, + colType: colTypeNames.dated, + active: true, + collapsed: false, + name: "Today", + index: 3, + datedBasedColumn: { + dateType: "due", + from: 0, + to: 0, + }, + }, + { + id: 4, + colType: colTypeNames.dated, + active: true, + collapsed: false, + name: "Tomorrow", + index: 4, + datedBasedColumn: { + dateType: "due", + from: 1, + to: 1, + }, + }, + { + id: 5, + colType: colTypeNames.dated, + active: true, + collapsed: false, + name: "Future", + index: 5, + datedBasedColumn: { + dateType: "due", + from: 2, + to: 300, + }, + }, + { + id: 6, + colType: colTypeNames.completed, + active: true, + collapsed: false, + limit: 20, + name: "Completed", + index: 6, + }, + ], + name: "Time Based Workflow", + index: 0, + showColumnTags: false, + showFilteredTags: true, + hideEmptyColumns: false, + boardFilter: { + rootCondition: "any", + filterGroups: [], + }, + swimlanes: { + enabled: false, + hideEmptySwimlanes: false, + property: "tags", + sortCriteria: "asc", + minimized: [], + maxHeight: "300px", + verticalHeaderUI: false, + }, + }, + { + columns: [ + { + id: 7, + colType: colTypeNames.untagged, + active: true, + collapsed: false, + name: "Backlogs", + index: 1, + }, + { + id: 8, + colType: colTypeNames.namedTag, + active: true, + collapsed: false, + name: "Important", + index: 2, + coltag: "important", + }, + { + id: 9, + colType: colTypeNames.namedTag, + active: true, + collapsed: false, + name: "WIP", + index: 3, + coltag: "wip", + }, + { + id: 11, + colType: colTypeNames.namedTag, + active: true, + collapsed: false, + name: "In Review", + index: 5, + coltag: "review", + }, + { + id: 12, + colType: colTypeNames.completed, + active: true, + collapsed: false, + index: 6, + limit: 20, + name: "Completed", + }, + ], + name: "Tag Based Workflow", + index: 1, + showColumnTags: false, + showFilteredTags: true, + hideEmptyColumns: false, + boardFilter: { + rootCondition: "any", + filterGroups: [], + }, + swimlanes: { + enabled: false, + hideEmptySwimlanes: false, + property: "tags", + sortCriteria: "asc", + minimized: [], + maxHeight: "300px", + verticalHeaderUI: false, + }, + }, + { + columns: [ + { + id: 7, + colType: colTypeNames.taskStatus, + taskStatus: defaultTaskStatuses.unchecked, + active: true, + collapsed: false, + name: "Backlogs", + index: 1, + }, + { + id: 8, + colType: colTypeNames.taskStatus, + taskStatus: defaultTaskStatuses.scheduled, + active: true, + collapsed: false, + name: "Ready to start", + index: 2, + }, + { + id: 9, + colType: colTypeNames.taskStatus, + taskStatus: defaultTaskStatuses.inprogress, + active: true, + collapsed: false, + name: "In Progress", + index: 3, + }, + { + id: 11, + colType: colTypeNames.taskStatus, + taskStatus: defaultTaskStatuses.question, + active: true, + collapsed: false, + name: "In Review", + index: 5, + }, + { + id: 12, + colType: colTypeNames.completed, + active: true, + collapsed: false, + index: 6, + limit: 20, + name: "Completed", + }, + { + id: 13, + colType: colTypeNames.taskStatus, + taskStatus: defaultTaskStatuses.dropped, + active: true, + collapsed: false, + name: "Cancelled", + index: 7, + }, + ], + name: "Status Based Workflow", + index: 1, + showColumnTags: false, + showFilteredTags: true, + hideEmptyColumns: false, + boardFilter: { + rootCondition: "any", + filterGroups: [], + }, + swimlanes: { + enabled: false, + hideEmptySwimlanes: false, + property: "tags", + sortCriteria: "asc", + minimized: [], + maxHeight: "300px", + verticalHeaderUI: false, + }, + }, +]; diff --git a/src/interfaces/GlobalSettings.ts b/src/interfaces/GlobalSettings.ts index 037897c6..b17d5050 100644 --- a/src/interfaces/GlobalSettings.ts +++ b/src/interfaces/GlobalSettings.ts @@ -1,5 +1,4 @@ import { TaskRegularExpressions } from "src/regularExpressions/TasksPluginRegularExpr"; -import { BoardConfigs } from "./BoardConfigs"; import { EditButtonMode, TagColorType, @@ -19,6 +18,7 @@ import { scanModeOptions, } from "./Enums"; import { taskItemKeyToNameMapping } from "./Mapping"; +import { DEFAULT_BOARDS } from "./BoardConfigs"; export interface scanFilters { files: { @@ -122,6 +122,7 @@ export interface globalSettingsData { uniqueIdCounter: number; // Counter to generate unique IDs for tasks. This will keep track of the last used ID. experimentalFeatures: boolean; safeGuardFeature: boolean; + boardFilesLocation: string[]; lastViewHistory: { viewedType: string; boardIndex: number; @@ -151,547 +152,306 @@ export interface globalSettingsData { // Define the interface for GlobalSettings based on your JSON structure export interface PluginDataJson { version: string; - data: { - boardConfigs: BoardConfigs; - globalSettings: globalSettingsData; - }; + data: globalSettingsData; } export const DEFAULT_SETTINGS: PluginDataJson = { version: "", // Keep this empty only. Change the version number in the runOnPluginUpdate function inside main.ts file whenever you will going to release a new version. data: { - boardConfigs: [ + lang: "en", + openOnStartup: false, + scanFilters: { + files: { + polarity: 3, + values: [], + }, + folders: { + polarity: 3, + values: [], + }, + frontMatter: { + polarity: 3, + values: [], + }, + tags: { + polarity: 3, + values: [], + }, + }, + firstDayOfWeek: "Mon", + showTaskWithoutMetadata: true, + ignoreFileNameDates: false, + taskPropertyFormat: taskPropertyFormatOptions.tasksPlugin, + taskCompletionDateTimePattern: TaskRegularExpressions.dateTimeFormat, + dailyNotesPluginComp: false, + universalDateFormat: TaskRegularExpressions.dateFormat, + defaultStartTime: "", + taskCompletionInLocalTime: true, + taskCompletionShowUtcOffset: false, + autoAddUniversalDate: true, + autoAddCreatedDate: false, + autoAddCompletedDate: false, + autoAddCancelledDate: false, + showModifiedFilesNotice: true, + scanMode: scanModeOptions.AUTOMATIC, + columnWidth: "300px", + visiblePropertiesList: [ + taskPropertiesNames.ID, + taskPropertiesNames.Title, + taskPropertiesNames.SubTasks, + taskPropertiesNames.Description, + taskPropertiesNames.Status, + taskPropertiesNames.Tags, + taskPropertiesNames.Priority, + taskPropertiesNames.CreatedDate, + taskPropertiesNames.StartDate, + taskPropertiesNames.ScheduledDate, + taskPropertiesNames.DueDate, + taskPropertiesNames.CompletionDate, + taskPropertiesNames.CancelledDate, + taskPropertiesNames.Reminder, + taskPropertiesNames.FilePath, + ], + taskCardStyle: taskCardStyleNames.EMOJI, + showVerticalScroll: true, + tagColors: [ + { + name: "bug", + color: "rgba(255, 0, 0, 0.55)", + priority: 1, + }, + { + name: "important", + color: "rgba(246, 255, 0, 0.53)", + priority: 2, + }, + { + name: "wip", + color: "rgba(0, 255, 0, 0.53)", + priority: 2, + }, + { + name: "review", + color: "rgba(0, 0, 255, 0.49)", + priority: 3, + }, + ], + editButtonAction: EditButtonMode.Modal, + doubleClickCardToEdit: EditButtonMode.None, + universalDate: UniversalDateOptions.dueDate, + tagColorsType: TagColorType.Background, + customStatuses: [ + { + symbol: defaultTaskStatuses.todo, + name: "Todo", + nextStatusSymbol: defaultTaskStatuses.done, + availableAsCommand: false, + type: "TODO", + }, + { + symbol: defaultTaskStatuses.scheduled, + name: "Ready to start", + nextStatusSymbol: defaultTaskStatuses.done, + availableAsCommand: false, + type: "TODO", + }, + { + symbol: defaultTaskStatuses.question, + name: "In Review", + nextStatusSymbol: defaultTaskStatuses.done, + availableAsCommand: false, + type: "TODO", + }, + { + symbol: defaultTaskStatuses.inprogress, + name: "In Progress", + nextStatusSymbol: defaultTaskStatuses.done, + availableAsCommand: true, + type: "IN_PROGRESS", + }, + { + symbol: defaultTaskStatuses.done, + name: "Done", + nextStatusSymbol: defaultTaskStatuses.todo, + availableAsCommand: true, + type: "DONE", + }, + { + symbol: defaultTaskStatuses.checked, + name: "Completed", + nextStatusSymbol: defaultTaskStatuses.todo, + availableAsCommand: true, + type: "DONE", + }, + { + symbol: defaultTaskStatuses.dropped, + name: "Cancelled", + nextStatusSymbol: defaultTaskStatuses.done, + availableAsCommand: true, + type: "CANCELLED", + }, + ], + compatiblePlugins: { + dailyNotesPlugin: false, + dayPlannerPlugin: false, + tasksPlugin: false, + reminderPlugin: false, + quickAddPlugin: false, + }, + preDefinedNote: "Meta/Task_Board/New_Tasks.md", + taskNoteIdentifierTag: "taskNote", + taskNoteDefaultLocation: "Meta/Task_Board/Task_Notes", + quickAddPluginDefaultChoice: "", + archivedTasksFilePath: "", + archivedTBNotesFolderPath: "Meta/Task_Board/Archived_Task_Notes", + frontmatterFormatting: [ { - columns: [ - { - id: 1, - colType: colTypeNames.undated, - active: true, - collapsed: false, - name: "Undated Tasks", - index: 1, - datedBasedColumn: { - dateType: "due", - from: 0, - to: 0, - }, - }, - { - id: 2, - colType: colTypeNames.dated, - active: true, - collapsed: false, - name: "Over Due", - index: 2, - datedBasedColumn: { - dateType: "due", - from: -300, - to: -1, - }, - }, - { - id: 3, - colType: colTypeNames.dated, - active: true, - collapsed: false, - name: "Today", - index: 3, - datedBasedColumn: { - dateType: "due", - from: 0, - to: 0, - }, - }, - { - id: 4, - colType: colTypeNames.dated, - active: true, - collapsed: false, - name: "Tomorrow", - index: 4, - datedBasedColumn: { - dateType: "due", - from: 1, - to: 1, - }, - }, - { - id: 5, - colType: colTypeNames.dated, - active: true, - collapsed: false, - name: "Future", - index: 5, - datedBasedColumn: { - dateType: "due", - from: 2, - to: 300, - }, - }, - { - id: 6, - colType: colTypeNames.completed, - active: true, - collapsed: false, - limit: 20, - name: "Completed", - index: 6, - }, - ], - name: "Time Based Workflow", index: 0, - showColumnTags: false, - showFilteredTags: true, - hideEmptyColumns: false, - boardFilter: { - rootCondition: "any", - filterGroups: [], - }, - swimlanes: { - enabled: false, - hideEmptySwimlanes: false, - property: "tags", - sortCriteria: "asc", - minimized: [], - maxHeight: "300px", - verticalHeaderUI: false, - }, + property: taskItemKeyToNameMapping["id"], + key: DEFAULT_TASK_NOTE_FRONTMATTER_KEYS.id, + taskItemKey: "id", }, { - columns: [ - { - id: 7, - colType: colTypeNames.untagged, - active: true, - collapsed: false, - name: "Backlogs", - index: 1, - }, - { - id: 8, - colType: colTypeNames.namedTag, - active: true, - collapsed: false, - name: "Important", - index: 2, - coltag: "important", - }, - { - id: 9, - colType: colTypeNames.namedTag, - active: true, - collapsed: false, - name: "WIP", - index: 3, - coltag: "wip", - }, - { - id: 11, - colType: colTypeNames.namedTag, - active: true, - collapsed: false, - name: "In Review", - index: 5, - coltag: "review", - }, - { - id: 12, - colType: colTypeNames.completed, - active: true, - collapsed: false, - index: 6, - limit: 20, - name: "Completed", - }, - ], - name: "Tag Based Workflow", index: 1, - showColumnTags: false, - showFilteredTags: true, - hideEmptyColumns: false, - boardFilter: { - rootCondition: "any", - filterGroups: [], - }, - swimlanes: { - enabled: false, - hideEmptySwimlanes: false, - property: "tags", - sortCriteria: "asc", - minimized: [], - maxHeight: "300px", - verticalHeaderUI: false, - }, + property: taskItemKeyToNameMapping["title"], + key: DEFAULT_TASK_NOTE_FRONTMATTER_KEYS.title, + taskItemKey: "title", }, { - columns: [ - { - id: 7, - colType: colTypeNames.taskStatus, - taskStatus: defaultTaskStatuses.unchecked, - active: true, - collapsed: false, - name: "Backlogs", - index: 1, - }, - { - id: 8, - colType: colTypeNames.taskStatus, - taskStatus: defaultTaskStatuses.scheduled, - active: true, - collapsed: false, - name: "Ready to start", - index: 2, - }, - { - id: 9, - colType: colTypeNames.taskStatus, - taskStatus: defaultTaskStatuses.inprogress, - active: true, - collapsed: false, - name: "In Progress", - index: 3, - }, - { - id: 11, - colType: colTypeNames.taskStatus, - taskStatus: defaultTaskStatuses.question, - active: true, - collapsed: false, - name: "In Review", - index: 5, - }, - { - id: 12, - colType: colTypeNames.completed, - active: true, - collapsed: false, - index: 6, - limit: 20, - name: "Completed", - }, - { - id: 13, - colType: colTypeNames.taskStatus, - taskStatus: defaultTaskStatuses.dropped, - active: true, - collapsed: false, - name: "Cancelled", - index: 7, - }, - ], - name: "Status Based Workflow", - index: 1, - showColumnTags: false, - showFilteredTags: true, - hideEmptyColumns: false, - boardFilter: { - rootCondition: "any", - filterGroups: [], - }, - swimlanes: { - enabled: false, - hideEmptySwimlanes: false, - property: "tags", - sortCriteria: "asc", - minimized: [], - maxHeight: "300px", - verticalHeaderUI: false, - }, + index: 2, + property: taskItemKeyToNameMapping["status"], + key: DEFAULT_TASK_NOTE_FRONTMATTER_KEYS.status, + taskItemKey: "status", }, - ], - globalSettings: { - lang: "en", - openOnStartup: false, - scanFilters: { - files: { - polarity: 3, - values: [], - }, - folders: { - polarity: 3, - values: [], - }, - frontMatter: { - polarity: 3, - values: [], - }, - tags: { - polarity: 3, - values: [], - }, + { + index: 3, + property: taskItemKeyToNameMapping["priority"], + key: DEFAULT_TASK_NOTE_FRONTMATTER_KEYS.priority, + taskItemKey: "priority", }, - firstDayOfWeek: "Mon", - showTaskWithoutMetadata: true, - ignoreFileNameDates: false, - taskPropertyFormat: taskPropertyFormatOptions.tasksPlugin, - taskCompletionDateTimePattern: - TaskRegularExpressions.dateTimeFormat, - dailyNotesPluginComp: false, - universalDateFormat: TaskRegularExpressions.dateFormat, - defaultStartTime: "", - taskCompletionInLocalTime: true, - taskCompletionShowUtcOffset: false, - autoAddUniversalDate: true, - autoAddCreatedDate: false, - autoAddCompletedDate: false, - autoAddCancelledDate: false, - showModifiedFilesNotice: true, - scanMode: scanModeOptions.AUTOMATIC, - columnWidth: "300px", - visiblePropertiesList: [ - taskPropertiesNames.ID, - taskPropertiesNames.Title, - taskPropertiesNames.SubTasks, - taskPropertiesNames.Description, - taskPropertiesNames.Status, - taskPropertiesNames.Tags, - taskPropertiesNames.Priority, - taskPropertiesNames.CreatedDate, - taskPropertiesNames.StartDate, - taskPropertiesNames.ScheduledDate, - taskPropertiesNames.DueDate, - taskPropertiesNames.CompletionDate, - taskPropertiesNames.CancelledDate, - taskPropertiesNames.Reminder, - taskPropertiesNames.FilePath, - ], - taskCardStyle: taskCardStyleNames.EMOJI, - showVerticalScroll: true, - tagColors: [ - { - name: "bug", - color: "rgba(255, 0, 0, 0.55)", - priority: 1, - }, - { - name: "important", - color: "rgba(246, 255, 0, 0.53)", - priority: 2, - }, - { - name: "wip", - color: "rgba(0, 255, 0, 0.53)", - priority: 2, - }, - { - name: "review", - color: "rgba(0, 0, 255, 0.49)", - priority: 3, - }, - ], - editButtonAction: EditButtonMode.Modal, - doubleClickCardToEdit: EditButtonMode.None, - universalDate: UniversalDateOptions.dueDate, - tagColorsType: TagColorType.Background, - customStatuses: [ - { - symbol: defaultTaskStatuses.todo, - name: "Todo", - nextStatusSymbol: defaultTaskStatuses.done, - availableAsCommand: false, - type: "TODO", - }, - { - symbol: defaultTaskStatuses.scheduled, - name: "Ready to start", - nextStatusSymbol: defaultTaskStatuses.done, - availableAsCommand: false, - type: "TODO", - }, - { - symbol: defaultTaskStatuses.question, - name: "In Review", - nextStatusSymbol: defaultTaskStatuses.done, - availableAsCommand: false, - type: "TODO", - }, - { - symbol: defaultTaskStatuses.inprogress, - name: "In Progress", - nextStatusSymbol: defaultTaskStatuses.done, - availableAsCommand: true, - type: "IN_PROGRESS", - }, - { - symbol: defaultTaskStatuses.done, - name: "Done", - nextStatusSymbol: defaultTaskStatuses.todo, - availableAsCommand: true, - type: "DONE", - }, - { - symbol: defaultTaskStatuses.checked, - name: "Completed", - nextStatusSymbol: defaultTaskStatuses.todo, - availableAsCommand: true, - type: "DONE", - }, - { - symbol: defaultTaskStatuses.dropped, - name: "Cancelled", - nextStatusSymbol: defaultTaskStatuses.done, - availableAsCommand: true, - type: "CANCELLED", - }, - ], - compatiblePlugins: { - dailyNotesPlugin: false, - dayPlannerPlugin: false, - tasksPlugin: false, - reminderPlugin: false, - quickAddPlugin: false, + { + index: 4, + property: taskItemKeyToNameMapping["tags"], + key: DEFAULT_TASK_NOTE_FRONTMATTER_KEYS.tags, + taskItemKey: "tags", }, - preDefinedNote: "Meta/Task_Board/New_Tasks.md", - taskNoteIdentifierTag: "taskNote", - taskNoteDefaultLocation: "Meta/Task_Board/Task_Notes", - quickAddPluginDefaultChoice: "", - archivedTasksFilePath: "", - archivedTBNotesFolderPath: "Meta/Task_Board/Archived_Task_Notes", - frontmatterFormatting: [ - { - index: 0, - property: taskItemKeyToNameMapping["id"], - key: DEFAULT_TASK_NOTE_FRONTMATTER_KEYS.id, - taskItemKey: "id", - }, - { - index: 1, - property: taskItemKeyToNameMapping["title"], - key: DEFAULT_TASK_NOTE_FRONTMATTER_KEYS.title, - taskItemKey: "title", - }, - { - index: 2, - property: taskItemKeyToNameMapping["status"], - key: DEFAULT_TASK_NOTE_FRONTMATTER_KEYS.status, - taskItemKey: "status", - }, - { - index: 3, - property: taskItemKeyToNameMapping["priority"], - key: DEFAULT_TASK_NOTE_FRONTMATTER_KEYS.priority, - taskItemKey: "priority", - }, - { - index: 4, - property: taskItemKeyToNameMapping["tags"], - key: DEFAULT_TASK_NOTE_FRONTMATTER_KEYS.tags, - taskItemKey: "tags", - }, - { - index: 5, - property: taskItemKeyToNameMapping["time"], - key: DEFAULT_TASK_NOTE_FRONTMATTER_KEYS.time, - taskItemKey: "time", - }, - { - index: 6, - property: taskItemKeyToNameMapping["reminder"], - key: DEFAULT_TASK_NOTE_FRONTMATTER_KEYS.reminder, - taskItemKey: "reminder", - }, - { - index: 7, - property: taskItemKeyToNameMapping["createdDate"], - key: DEFAULT_TASK_NOTE_FRONTMATTER_KEYS.createdDate, - taskItemKey: "createdDate", - }, - { - index: 8, - property: taskItemKeyToNameMapping["startDate"], - key: DEFAULT_TASK_NOTE_FRONTMATTER_KEYS.startDate, - taskItemKey: "startDate", - }, - { - index: 9, - property: taskItemKeyToNameMapping["scheduledDate"], - key: DEFAULT_TASK_NOTE_FRONTMATTER_KEYS.scheduledDate, - taskItemKey: "scheduledDate", - }, - { - index: 10, - property: taskItemKeyToNameMapping["due"], - key: DEFAULT_TASK_NOTE_FRONTMATTER_KEYS.due, - taskItemKey: "due", - }, - { - index: 11, - property: taskItemKeyToNameMapping["dependsOn"], - key: DEFAULT_TASK_NOTE_FRONTMATTER_KEYS.dependsOn, - taskItemKey: "dependsOn", - }, - { - index: 12, - property: taskItemKeyToNameMapping["completion"], - key: DEFAULT_TASK_NOTE_FRONTMATTER_KEYS.cancelledDate, - taskItemKey: "cancelledDate", - }, - { - index: 13, - property: taskItemKeyToNameMapping["completionDate"], - key: DEFAULT_TASK_NOTE_FRONTMATTER_KEYS.completionDate, - taskItemKey: "completionDate", - }, - // TODO : The below properties will be available once the TBNote feature has been implemented. The filePath will be actually the path of the task-note or the tb-note. A new property will be required to be added inside the taskItem interface to store the sourcePath. - // { - // index: 14, - // property: taskItemKeyToNameMapping["filePath"], - // key: DEFAULT_TASK_NOTE_FRONTMATTER_KEYS.filePath, - // taskItemKey: "filePath", - // }, - // { - // index: 15, - // property: taskItemKeyToNameMapping["taskLocation"], - // key: DEFAULT_TASK_NOTE_FRONTMATTER_KEYS.taskLocation, - // taskItemKey: "taskLocation", - // }, - // { - // index: 14, - // property: taskItemKeyToNameMapping["dateModified"], - // key: DEFAULT_TASK_NOTE_FRONTMATTER_KEYS.dateModified, - // taskItemKey: "", - // }, - ], - showFrontmatterTagsOnCards: false, - tasksCacheFilePath: "", - notificationService: NotificationService.None, - actions: [ - { - enabled: true, - trigger: "Complete", - type: "move", - targetColumn: "Completed", - }, - ], - hiddenTaskProperties: [], - autoAddUniqueID: false, - uniqueIdCounter: 0, // Counter to generate unique IDs for tasks. This will keep track of the last used ID. --- IGNORE --- - experimentalFeatures: false, - safeGuardFeature: true, - lastViewHistory: { - viewedType: "kanban", - boardIndex: 0, - settingTab: 0, + { + index: 5, + property: taskItemKeyToNameMapping["time"], + key: DEFAULT_TASK_NOTE_FRONTMATTER_KEYS.time, + taskItemKey: "time", }, - boundTaskCompletionToChildTasks: false, - kanbanView: { - lazyLoadingEnabled: true, - initialTaskCount: 20, - loadMoreCount: 10, - scrollThresholdPercent: 80, + { + index: 6, + property: taskItemKeyToNameMapping["reminder"], + key: DEFAULT_TASK_NOTE_FRONTMATTER_KEYS.reminder, + taskItemKey: "reminder", + }, + { + index: 7, + property: taskItemKeyToNameMapping["createdDate"], + key: DEFAULT_TASK_NOTE_FRONTMATTER_KEYS.createdDate, + taskItemKey: "createdDate", }, - mapView: { - background: mapViewBackgrounVariantTypes.none, - mapOrientation: mapViewNodeMapOrientation.horizontal, - optimizedRender: false, - arrowDirection: mapViewArrowDirection.childToParent, - animatedEdges: true, - scrollAction: mapViewScrollAction.zoom, - showMinimap: true, - renderVisibleNodes: false, - edgeType: mapViewEdgeType.bezier, + { + index: 8, + property: taskItemKeyToNameMapping["startDate"], + key: DEFAULT_TASK_NOTE_FRONTMATTER_KEYS.startDate, + taskItemKey: "startDate", }, + { + index: 9, + property: taskItemKeyToNameMapping["scheduledDate"], + key: DEFAULT_TASK_NOTE_FRONTMATTER_KEYS.scheduledDate, + taskItemKey: "scheduledDate", + }, + { + index: 10, + property: taskItemKeyToNameMapping["due"], + key: DEFAULT_TASK_NOTE_FRONTMATTER_KEYS.due, + taskItemKey: "due", + }, + { + index: 11, + property: taskItemKeyToNameMapping["dependsOn"], + key: DEFAULT_TASK_NOTE_FRONTMATTER_KEYS.dependsOn, + taskItemKey: "dependsOn", + }, + { + index: 12, + property: taskItemKeyToNameMapping["completion"], + key: DEFAULT_TASK_NOTE_FRONTMATTER_KEYS.cancelledDate, + taskItemKey: "cancelledDate", + }, + { + index: 13, + property: taskItemKeyToNameMapping["completionDate"], + key: DEFAULT_TASK_NOTE_FRONTMATTER_KEYS.completionDate, + taskItemKey: "completionDate", + }, + // TODO : The below properties will be available once the TBNote feature has been implemented. The filePath will be actually the path of the task-note or the tb-note. A new property will be required to be added inside the taskItem interface to store the sourcePath. + // { + // index: 14, + // property: taskItemKeyToNameMapping["filePath"], + // key: DEFAULT_TASK_NOTE_FRONTMATTER_KEYS.filePath, + // taskItemKey: "filePath", + // }, + // { + // index: 15, + // property: taskItemKeyToNameMapping["taskLocation"], + // key: DEFAULT_TASK_NOTE_FRONTMATTER_KEYS.taskLocation, + // taskItemKey: "taskLocation", + // }, + // { + // index: 14, + // property: taskItemKeyToNameMapping["dateModified"], + // key: DEFAULT_TASK_NOTE_FRONTMATTER_KEYS.dateModified, + // taskItemKey: "", + // }, + ], + showFrontmatterTagsOnCards: false, + tasksCacheFilePath: "", + notificationService: NotificationService.None, + actions: [ + { + enabled: true, + trigger: "Complete", + type: "move", + targetColumn: "Completed", + }, + ], + hiddenTaskProperties: [], + autoAddUniqueID: false, + uniqueIdCounter: 0, // Counter to generate unique IDs for tasks. This will keep track of the last used ID. --- IGNORE --- + experimentalFeatures: false, + safeGuardFeature: true, + boardFilesLocation: [ + `TaskBoard/Boards/${DEFAULT_BOARDS[0].name}.taskboard`, + `TaskBoard/Boards/${DEFAULT_BOARDS[1].name}.taskboard`, + `TaskBoard/Boards/${DEFAULT_BOARDS[2].name}.taskboard`, + ], + lastViewHistory: { + viewedType: "kanban", + boardIndex: 0, + settingTab: 0, + }, + boundTaskCompletionToChildTasks: false, + kanbanView: { + lazyLoadingEnabled: true, + initialTaskCount: 20, + loadMoreCount: 10, + scrollThresholdPercent: 80, + }, + mapView: { + background: mapViewBackgrounVariantTypes.none, + mapOrientation: mapViewNodeMapOrientation.horizontal, + optimizedRender: false, + arrowDirection: mapViewArrowDirection.childToParent, + animatedEdges: true, + scrollAction: mapViewScrollAction.zoom, + showMinimap: true, + renderVisibleNodes: false, + edgeType: mapViewEdgeType.bezier, }, }, }; diff --git a/src/managers/BugReporter.ts b/src/managers/BugReporter.ts index d08c7e55..1017abb4 100644 --- a/src/managers/BugReporter.ts +++ b/src/managers/BugReporter.ts @@ -29,6 +29,7 @@ class BugReporterManager { private alreadyShownBugsIDs: number[] = []; private LOG_FILE_PATH = ""; private readonly MAX_RECENT_LOGS = 10; + private TRACK_MAX_ID = 90; // This is not being used in the code, its manually tracked to refer it here and update it when I will be adding new IDs. private constructor() { // Private constructor to enforce singleton pattern diff --git a/src/managers/DragDropTasksManager.ts b/src/managers/DragDropTasksManager.ts index 66a59605..d2d3f455 100644 --- a/src/managers/DragDropTasksManager.ts +++ b/src/managers/DragDropTasksManager.ts @@ -152,7 +152,7 @@ class DragDropTasksManager { newTask, sourceColumnSwimlaneData, targetColumnSwimlaneData, - plugin.settings.data.globalSettings + plugin.settings.data ); eventEmitter.emit("UPDATE_TASK", { taskID: oldTask.id, @@ -160,7 +160,7 @@ class DragDropTasksManager { }); const isThisTaskNote = isTaskNotePresentInTags( - plugin.settings.data.globalSettings.taskNoteIdentifierTag, + plugin.settings.data.taskNoteIdentifierTag, oldTask.tags ); @@ -244,7 +244,7 @@ class DragDropTasksManager { newTask, sourceColumnSwimlaneData, targetColumnSwimlaneData, - plugin.settings.data.globalSettings + plugin.settings.data ); console.log("newTask after swimlane change:", newTask); } @@ -270,7 +270,7 @@ class DragDropTasksManager { // newTask.tags = newTags; // newTask = await updateTaskItemProperty( // oldTask, - // plugin.settings.data.globalSettings, + // plugin.settings.data, // "tags", // oldTask.tags, // newTask.tags @@ -289,7 +289,7 @@ class DragDropTasksManager { // eventEmitter.emit("UPDATE_TASK", { taskID: oldTask.id, state: true }); // const isThisTaskNote = isTaskNotePresentInTags( - // plugin.settings.data.globalSettings.taskNoteIdentifierTag, + // plugin.settings.data.taskNoteIdentifierTag, // oldTask.tags // ); @@ -363,7 +363,7 @@ class DragDropTasksManager { newTask, sourceColumnSwimlaneData, targetColumnSwimlaneData, - plugin.settings.data.globalSettings + plugin.settings.data ); console.log("newTask after swimlane change:", newTask); } @@ -468,7 +468,7 @@ class DragDropTasksManager { newTask, sourceColumnSwimlaneData, targetColumnSwimlaneData, - plugin.settings.data.globalSettings + plugin.settings.data ); console.log("newTask after swimlane change:", newTask); } @@ -523,7 +523,7 @@ class DragDropTasksManager { newTask, sourceColumnSwimlaneData, targetColumnSwimlaneData, - plugin.settings.data.globalSettings + plugin.settings.data ); console.log("newTask after swimlane change:", newTask); } @@ -548,12 +548,12 @@ class DragDropTasksManager { }; if ( !isTaskNotePresentInTags( - plugin.settings.data.globalSettings.taskNoteIdentifierTag, + plugin.settings.data.taskNoteIdentifierTag, task.tags ) ) { newTask.title = sanitizeStatus( - plugin.settings.data.globalSettings, + plugin.settings.data, task.title, " ", statusTypeNames.TODO @@ -605,7 +605,7 @@ class DragDropTasksManager { newTask, sourceColumnSwimlaneData, targetColumnSwimlaneData, - plugin.settings.data.globalSettings + plugin.settings.data ); console.log("newTask after swimlane change:", newTask); } @@ -707,7 +707,7 @@ class DragDropTasksManager { newTask, sourceColumnSwimlaneData, targetColumnSwimlaneData, - plugin.settings.data.globalSettings + plugin.settings.data ); console.log("newTask after swimlane change:", newTask); } @@ -775,7 +775,7 @@ class DragDropTasksManager { newTask, sourceColumnSwimlaneData, targetColumnSwimlaneData, - plugin.settings.data.globalSettings + plugin.settings.data ); console.log("newTask after swimlane change:", newTask); } @@ -832,7 +832,7 @@ class DragDropTasksManager { newTask, sourceColumnSwimlaneData, targetColumnSwimlaneData, - plugin.settings.data.globalSettings + plugin.settings.data ); console.log("newTask after swimlane change:", newTask); } @@ -884,13 +884,13 @@ class DragDropTasksManager { newTask, sourceColumnSwimlaneData, targetColumnSwimlaneData, - plugin.settings.data.globalSettings + plugin.settings.data ); console.log("newTask after swimlane change:", newTask); } const newStatus = - plugin.settings.data.globalSettings.customStatuses.find( + plugin.settings.data.customStatuses.find( (status) => status.type === statusTypeNames.DONE ); diff --git a/src/managers/TaskBoardFileManager.ts b/src/managers/TaskBoardFileManager.ts new file mode 100644 index 00000000..43ca2839 --- /dev/null +++ b/src/managers/TaskBoardFileManager.ts @@ -0,0 +1,406 @@ +/** + * TaskBoardFileManager.ts + * Manages loading and saving individual board configurations from/to .taskboard files + * This replaces the previous approach of storing all board data in data.json + */ + +import { App, TFile, Notice } from "obsidian"; +import { Board } from "src/interfaces/BoardConfigs"; +import type TaskBoard from "main"; +import { bugReporterManagerInsatance } from "./BugReporter"; + +export default class TaskBoardFileManager { + private app: App; + private plugin: TaskBoard; + private currentBoardIndex: number; + private currentBoardData: Board | null = null; + private allBoardsData: Board[] | [] = []; + + constructor(plugin: TaskBoard) { + this.plugin = plugin; + this.app = plugin.app; + this.currentBoardIndex = + this.plugin.settings.data.lastViewHistory.boardIndex; + } + + /** + * Load board configuration from a .taskboard file + * @param filePath - The path to the .taskboard file + * @returns The board configuration object, or null if file doesn't exist or cannot be parsed + */ + async loadBoardFromFile(filePath: string): Promise { + try { + // Check if file exists + const fileExists = await this.app.vault.adapter.exists(filePath); + if (!fileExists) { + console.warn(`TaskBoard file not found: ${filePath}`); + return null; + } + + // Read the file + const file = this.app.vault.getAbstractFileByPath(filePath); + if (!file || !(file instanceof TFile)) { + console.warn(`Invalid TaskBoard file: ${filePath}`); + return null; + } + + const fileContent = await this.app.vault.read(file); + if (!fileContent || fileContent.trim() === "") { + console.warn(`TaskBoard file is empty: ${filePath}`); + return null; + } + + // Parse JSON content + const boardData: Board = JSON.parse(fileContent); + console.log( + `Successfully loaded board from: ${filePath}`, + boardData, + ); + return boardData; + } catch (error) { + console.error(`Error loading board from file ${filePath}:`, error); + return null; + } + } + + /** + * Save board configuration to a .taskboard file + * @param filePath - The path to the .taskboard file + * @param boardData - The board configuration object to save + * @returns boolean - True if saved successfully, false otherwise + */ + async saveBoardToFile( + filePath: string, + boardData: Board, + ): Promise { + try { + // Check if file exists, if not create it + const fileExists = await this.app.vault.adapter.exists(filePath); + + const jsonContent = JSON.stringify(boardData, null, 2); + + if (!fileExists) { + // Create new file + await this.app.vault.create(filePath, jsonContent); + console.log(`Created new TaskBoard file: ${filePath}`); + } else { + // Update existing file + const file = this.app.vault.getAbstractFileByPath(filePath); + if (!file || !(file instanceof TFile)) { + console.error(`Cannot find file to update: ${filePath}`); + return false; + } + await this.app.vault.modify(file, jsonContent); + console.log(`Updated TaskBoard file: ${filePath}`); + } + + return true; + } catch (error) { + console.error(`Error saving board to file ${filePath}:`, error); + return false; + } + } + + /** + * Check if a .taskboard file exists + * @param filePath - The path to the .taskboard file + * @returns boolean - True if file exists, false otherwise + */ + async boardFileExists(filePath: string): Promise { + try { + return await this.app.vault.adapter.exists(filePath); + } catch (error) { + console.error(`Error checking if file exists ${filePath}:`, error); + return false; + } + } + + /** + * Create a new .taskboard file with initial board configuration + * @param filePath - The path where the new .taskboard file should be created + * @param boardData - The initial board configuration + * @returns boolean - True if created successfully, false otherwise + */ + async createNewBoardFile( + filePath: string, + boardData: Board, + ): Promise { + try { + // Check if file already exists + const exists = await this.boardFileExists(filePath); + if (exists) { + console.warn(`TaskBoard file already exists: ${filePath}`); + return false; + } + + return await this.saveBoardToFile(filePath, boardData); + } catch (error) { + console.error(`Error creating new board file ${filePath}:`, error); + return false; + } + } + + /** + * Delete a .taskboard file + * @param filePath - The path to the .taskboard file + * @returns boolean - True if deleted successfully, false otherwise + */ + async deleteBoardFile(filePath: string): Promise { + try { + const file = this.app.vault.getAbstractFileByPath(filePath); + if (!file || !(file instanceof TFile)) { + console.warn(`Cannot find file to delete: ${filePath}`); + return false; + } + + await this.app.vault.delete(file); + console.log(`Deleted TaskBoard file: ${filePath}`); + return true; + } catch (error) { + console.error(`Error deleting board file ${filePath}:`, error); + return false; + } + } + + /** + * Get all .taskboard files from the vault + * Filters files with .taskboard extension + * @returns Array of .taskboard file paths + */ + getAllTaskboardFiles(): string[] { + try { + const allFiles = this.app.vault.getAllLoadedFiles(); + const taskboardFiles = allFiles + .filter( + (file) => + file instanceof TFile && file.extension === "taskboard", + ) + .map((file) => (file as TFile).path); + + console.log( + `Found ${taskboardFiles.length} .taskboard files:`, + taskboardFiles, + ); + return taskboardFiles; + } catch (error) { + console.error("Error getting all .taskboard files:", error); + return []; + } + } + + /** + * Validate that all board files path present in settings also exist in the vault + * @returns Array of missing file paths + */ + async validateBoardFiles(): Promise { + const boardFilesLocation = + this.plugin.settings.data.boardFilesLocation || []; + const missingFiles: string[] = []; + + for (const filePath of boardFilesLocation) { + const exists = await this.boardFileExists(filePath); + if (!exists) { + missingFiles.push(filePath); + console.warn( + `Expected board file not found: ${filePath}. It may have been moved or deleted.`, + ); + } + } + + return missingFiles; + } + + /** + * Create default board files that are configured in settings but don't exist yet + * This is called during plugin initialization + * @param defaultBoards - The default board configurations to create + * @returns number - Count of files created + */ + async createMissingDefaultBoardFiles( + defaultBoards: Board[], + ): Promise { + const boardFilesLocation = + this.plugin.settings.data.boardFilesLocation || []; + let createdCount = 0; + + for (let i = 0; i < boardFilesLocation.length; i++) { + const filePath = boardFilesLocation[i]; + const exists = await this.boardFileExists(filePath); + + if (!exists && i < defaultBoards.length) { + const created = await this.createNewBoardFile( + filePath, + defaultBoards[i], + ); + if (created) { + createdCount++; + console.log( + `Created default board file: ${filePath}`, + defaultBoards[i].name, + ); + } + } + } + + return createdCount; + } + + /** + * Load board configuration from disk by board index + * Reads the board file from boardFilesLocation based on the provided index + * Caches the board data in memory for quick access + * @param boardIndex - The index of the board to load + * @returns The board configuration object, or null if file cannot be loaded + */ + async loadBoard(boardIndex?: number): Promise { + try { + const boardFilesLocation = + this.plugin.settings.data.boardFilesLocation || []; + const currentBoardINdex = boardIndex ?? this.currentBoardIndex; + + // Validate board index + if ( + currentBoardINdex < 0 || + currentBoardINdex >= boardFilesLocation.length + ) { + console.error( + `Invalid board index: ${currentBoardINdex}. Available boards: ${boardFilesLocation.length}`, + ); + return null; + } + + const boardFilePath = boardFilesLocation[currentBoardINdex]; + if (!boardFilePath || boardFilePath.trim() === "") { + console.error( + `No board file path configured for index: ${currentBoardINdex}`, + ); + return null; + } + + // Load board from file + const boardData = await this.loadBoardFromFile(boardFilePath); + + if (boardData) { + // Cache the board data in memory + this.currentBoardData = boardData; + this.currentBoardIndex = currentBoardINdex; + console.log( + `Loaded and cached board "${boardData.name}" (index: ${currentBoardINdex}) from: ${boardFilePath}`, + ); + } + + return boardData; + } catch (error) { + console.error( + `Error loading board at index ${this.currentBoardIndex}:`, + error, + ); + return null; + } + } + + /** + * Save board configuration to disk by board index passed + * If no boardIndex has been passed, it will update currentBoard data + * Also updates the cached board data in memory + * @param updatedBoardData - The updated board configuration object + * @param boardIndex - (Optional) The index of the board to save + * @returns boolean - True if saved successfully, false otherwise + */ + async saveBoard( + updatedBoardData: Board, + boardIndex?: number, + ): Promise { + try { + const boardFilesLocation = + this.plugin.settings.data.boardFilesLocation || []; + + let boardIndexToUse = boardIndex; + if (!boardIndexToUse) { + boardIndexToUse = this.currentBoardIndex; + if (!boardIndexToUse) { + bugReporterManagerInsatance.showNotice( + 90, + "The TaskBoardFileManager instance dont contain an currentBoardIndex number", + "ERROR : this.currentBoardIndex not found", + "TaskBoardFileManager/saveCurrentBoard", + ); + return false; + } + } + + // Validate board index + if ( + boardIndexToUse < 0 || + boardIndexToUse >= boardFilesLocation.length + ) { + console.error( + `Invalid board index: ${boardIndexToUse}. Available boards: ${boardFilesLocation.length}`, + ); + return false; + } + + const boardFilePath = boardFilesLocation[boardIndexToUse]; + if (!boardFilePath || boardFilePath.trim() === "") { + console.error( + `No board file path configured for index: ${boardIndexToUse}`, + ); + return false; + } + + // Save board to file + const success = await this.saveBoardToFile( + boardFilePath, + updatedBoardData, + ); + + if (success) { + // Update cached board data if this is the currently active board + if (boardIndexToUse === this.currentBoardIndex) { + this.currentBoardData = updatedBoardData; + } + + console.log( + `Saved board "${updatedBoardData.name}" (index: ${boardIndexToUse}) to: ${boardFilePath}`, + ); + } + + return success; + } catch (error) { + console.error( + `Error saving board at index ${boardIndex ?? this.currentBoardData}:`, + error, + ); + return false; + } + } + + async loadAllBoards() {} + + /** + * Get the currently cached board data + * Returns the board that was last loaded with loadBoard() + * @returns The cached board data, or null if no board has been loaded + */ + async getCurrentBoardData(): Promise { + return this.currentBoardData; + } + + /** + * Get the index of the currently cached board + * @returns The index of the currently loaded board, or null if no board has been loaded + */ + getCurrentBoardIndex(): number | null { + return this.currentBoardIndex; + } + + /** + * Clear the cached board data + * Useful when switching between different boards or clearing state + */ + clearCurrentBoardCache(): void { + this.currentBoardData = null; + this.allBoardsData = []; + console.log("Cleared cached board data"); + } +} diff --git a/src/managers/VaultScanner.ts b/src/managers/VaultScanner.ts index 907bc70f..ff0b933d 100644 --- a/src/managers/VaultScanner.ts +++ b/src/managers/VaultScanner.ts @@ -121,11 +121,11 @@ export default class VaultScanner { */ // Below code is to detect if the reminder property is present in the frontmatter. If present, then add this file in the tasks.Notes list. This is specifically for Notifian integration and for other plugins which might want to use this reminder property for notes. // if ( - // this.plugin.settings.data.globalSettings + // this.plugin.settings.data // .frontmatterPropertyForReminder && // frontmatter && // frontmatter[ - // this.plugin.settings.data.globalSettings + // this.plugin.settings.data // .frontmatterPropertyForReminder // ] // ) { @@ -134,7 +134,7 @@ export default class VaultScanner { // frontmatter: frontmatter, // reminder: // frontmatter[ - // this.plugin.settings.data.globalSettings + // this.plugin.settings.data // .frontmatterPropertyForReminder // ], // }; @@ -156,7 +156,7 @@ export default class VaultScanner { if ( frontmatter && isTaskNotePresentInFrontmatter( - this.plugin.settings.data.globalSettings + this.plugin.settings.data .taskNoteIdentifierTag, frontmatter, ) @@ -325,23 +325,23 @@ export default class VaultScanner { ); if ( - this.plugin.settings.data.globalSettings + this.plugin.settings.data .dailyNotesPluginComp && - ((this.plugin.settings.data.globalSettings + ((this.plugin.settings.data .universalDate === UniversalDateOptions.dueDate && dueDate === "") || - (this.plugin.settings.data.globalSettings + (this.plugin.settings.data .universalDate === UniversalDateOptions.startDate && startDate === "") || - (this.plugin.settings.data.globalSettings + (this.plugin.settings.data .universalDate === UniversalDateOptions.scheduledDate && scheduledDate === "")) ) { const universalDateFormat = - this.plugin.settings.data.globalSettings + this.plugin.settings.data .universalDateFormat; const basename = file.basename; @@ -356,19 +356,19 @@ export default class VaultScanner { ).isValid() ) { if ( - this.plugin.settings.data.globalSettings + this.plugin.settings.data .universalDate === UniversalDateOptions.dueDate ) { dueDate = basename; // If the basename matches the dueFormat, assign it to due } else if ( - this.plugin.settings.data.globalSettings + this.plugin.settings.data .universalDate === UniversalDateOptions.startDate ) { startDate = basename; // If the basename matches the dueFormat, assign it to startDate } else if ( - this.plugin.settings.data.globalSettings + this.plugin.settings.data .universalDate === UniversalDateOptions.scheduledDate ) { @@ -379,7 +379,7 @@ export default class VaultScanner { let frontmatterTags: string[] = []; // Initialize frontmatterTags if ( - this.plugin.settings.data.globalSettings + this.plugin.settings.data .showFrontmatterTagsOnCards ) { // Extract frontmatter tags @@ -491,7 +491,7 @@ export default class VaultScanner { // // The second sub-condition is to check if the older file was a task-note. This second sub-condition is required in the case when user simply removes the taskNoteIdentifierTag from the frontmatter of the note, so its not longer a task-note now and also if the note doesnt have any tasks in its content, then this task-note cache should be removed. // (oldPendingFileCache.length === 1 && // isTaskNotePresentInTags( - // this.plugin.settings.data.globalSettings + // this.plugin.settings.data // .taskNoteIdentifierTag, // oldPendingFileCache[0].tags // )); @@ -508,7 +508,7 @@ export default class VaultScanner { // (oldCompletedFileCache && // oldCompletedFileCache.length === 1 && // isTaskNotePresentInTags( - // this.plugin.settings.data.globalSettings + // this.plugin.settings.data // .taskNoteIdentifierTag, // oldCompletedFileCache[0].tags // )); @@ -545,7 +545,7 @@ export default class VaultScanner { try { const scanFilters = - this.plugin.settings.data.globalSettings.scanFilters; + this.plugin.settings.data.scanFilters; let isFileScanned: string = ""; for (const file of files) { if ( @@ -626,7 +626,7 @@ export default class VaultScanner { // ); // // this.plugin.saveSettings(); // This was to save the uniqueIdCounter in settings, but moved that to be saved immediately when the ID is generated. // if ( - // this.plugin.settings.data.globalSettings.realTimeScanner && + // this.plugin.settings.data.realTimeScanner && // (Object.values(this.tasksCache.Pending).flat().length > 0 || // Object.values(this.tasksCache.Completed).flat().length > 0) // ) { @@ -651,10 +651,10 @@ export default class VaultScanner { setTimeout(() => { eventEmitter.emit("REFRESH_COLUMN"); - // if (this.plugin.settings.data.globalSettings.searchQuery) { + // if (this.plugin.settings.data.searchQuery) { // console.log( // "Refreshing the board now after saving...\nSetting : ", - // this.plugin.settings.data.globalSettings.searchQuery + // this.plugin.settings.data.searchQuery // ); // eventEmitter.emit("REFRESH_BOARD"); // } else { @@ -667,25 +667,50 @@ export default class VaultScanner { // const result = this.saveTasksToJsonCacheDebounced(); } + + /** + * Discover all .taskboard files in the vault + * This method scans through all files and identifies board configuration files + * @returns Array of discovered .taskboard file paths + */ + discoverTaskboardFiles(): string[] { + try { + const allFiles = this.app.vault.getAllLoadedFiles(); + const taskboardFiles = allFiles + .filter((file) => file instanceof TFile && file.extension === "taskboard") + .map((file) => (file as TFile).path); + + console.log(`VaultScanner: Discovered ${taskboardFiles.length} .taskboard files:`, taskboardFiles); + return taskboardFiles; + } catch (error) { + console.error("VaultScanner: Error discovering .taskboard files:", error); + return []; + } + } } /** * Checks if the file is allowed for scanning based on its extension and plugin settings. * It also checks if the file is not the archived tasks file or inside the archived TB notes folder. - * @param plugin - The TaskBoard plugin instance - * @param file - The file to check (TFile) + * Additionally, it excludes .taskboard files from scanning as they are board configuration files. + * @param globalSettings - The global settings from the plugin + * @param file - The file to check (TFile or TAbstractFile) * @returns boolean - True if the file is allowed for scanning, false otherwise */ export function fileTypeAllowedForScanning( globalSettings: globalSettingsData, file: TFile | TAbstractFile, ): boolean { - // console.log("Condition 1 :", notAllowedFileExtensionsRegEx.test(file.path), "\nCondition 2 :", file.path === - // plugin.settings.data.globalSettings.archivedTasksFilePath, "\nCondition 3 :", , "\nCondition 4 :", ) - if (!globalSettings.archivedTBNotesFolderPath.trim()) return true; - const filePath = file.path.toLocaleLowerCase(); + // Exclude .taskboard files from task scanning (they are board configuration files, not task files) + if (file instanceof TFile && file.extension === "taskboard") { + console.log(`Excluding .taskboard file from scanning: ${file.path}`); + return false; + } + + if (!globalSettings.archivedTBNotesFolderPath.trim()) return true; + if ( // notAllowedFileExtensionsRegEx.test(file.path) || allowedFileExtensionsRegEx.test(file.path) === false || diff --git a/src/modals/AddOrEditTaskModal.tsx b/src/modals/AddOrEditTaskModal.tsx index 04da09c3..758e713e 100644 --- a/src/modals/AddOrEditTaskModal.tsx +++ b/src/modals/AddOrEditTaskModal.tsx @@ -61,7 +61,7 @@ export class AddOrEditTaskModal extends Modal { this.setTitle(this.taskExists ? t("edit-task") : t("add-new-task")); - if (this.plugin.settings.data.globalSettings.autoAddUniqueID && (!this.taskExists || !this.task.id)) { + if (this.plugin.settings.data.autoAddUniqueID && (!this.taskExists || !this.task.id)) { this.task.id = generateTaskId(this.plugin); this.task.legacyId = this.task.id; } @@ -74,7 +74,7 @@ export class AddOrEditTaskModal extends Modal { } else { noteContent = "---\ntitle: \n---\n"; - const defaultLocation = this.plugin.settings.data.globalSettings.taskNoteDefaultLocation || 'Meta/Task_Board/Task_Notes'; + const defaultLocation = this.plugin.settings.data.taskNoteDefaultLocation || 'Meta/Task_Board/Task_Notes'; const noteName = this.task.title || getLocalDateTimeString(); // Sanitize filename const sanitizedName = noteName.replace(/[<>:"/\\|?*]/g, '_'); diff --git a/src/modals/BoardConfigModal.tsx b/src/modals/BoardConfigModal.tsx index 88b049c9..2b1027a3 100644 --- a/src/modals/BoardConfigModal.tsx +++ b/src/modals/BoardConfigModal.tsx @@ -27,7 +27,7 @@ interface ConfigModalProps { settingManager: SettingsManager; boards: Board[]; activeBoardIndex: number; - onSave: (updatedBoards: Board[]) => void; + onSave: (updatedBoards: Board[], boardIndex: number) => void; onClose: () => void; setIsEdited: (value: boolean) => void; } @@ -306,7 +306,7 @@ const ConfigModalContent: React.FC = ({ // Function to save changes const handleSave = () => { - onSave(localBoards); + onSave(localBoards, selectedBoardIndex); // onClose(); }; @@ -541,7 +541,7 @@ const ConfigModalContent: React.FC = ({ />
- {plugin.settings.data.globalSettings.experimentalFeatures && ( + {plugin.settings.data.experimentalFeatures && (
{t("configure-kanban-swimlanes")}
@@ -786,7 +786,7 @@ const ConfigModalContent: React.FC = ({ /> handleColumnChange( boardIndex, @@ -955,14 +955,14 @@ export class BoardConfigureModal extends Modal { boards: Board[]; activeBoardIndex: number; isEdited: boolean; - onSave: (updatedBoards: Board[]) => void; + onSave: (updatedBoards: Board[], boardIndex: number) => void; plugin: TaskBoard; constructor( plugin: TaskBoard, boards: Board[], activeBoardIndex: number, - onSave: (updatedBoards: Board[]) => void + onSave: (updatedBoards: Board[], boardIndex: number) => void ) { super(plugin.app); this.plugin = plugin; @@ -984,9 +984,9 @@ export class BoardConfigureModal extends Modal { settingManager={this.settingsManager} boards={this.boards} activeBoardIndex={this.activeBoardIndex} - onSave={(updatedBoards: Board[]) => { + onSave={(updatedBoards: Board[], boardIndex: number) => { this.isEdited = false; - this.onSave(updatedBoards); + this.onSave(updatedBoards, boardIndex); this.close(); }} onClose={() => this.close()} @@ -1012,12 +1012,12 @@ export class BoardConfigureModal extends Modal { closeConfirmModal.open(); } - handleSave() { - // Trigger save functionality if required before closing - this.onSave(this.boards); - this.isEdited = false; - this.close(); - } + // handleSave() { + // // Trigger save functionality if required before closing + // this.onSave(this.boards); + // this.isEdited = false; + // this.close(); + // } onClose() { // Clean up React rendering diff --git a/src/modals/ConfigureColumnSortingModal.ts b/src/modals/ConfigureColumnSortingModal.ts index 58157670..416b7916 100644 --- a/src/modals/ConfigureColumnSortingModal.ts +++ b/src/modals/ConfigureColumnSortingModal.ts @@ -140,7 +140,7 @@ export class ConfigureColumnSortingModal extends Modal { ) .addDropdown((dropdown) => { if ( - this.plugin.settings.data.globalSettings + this.plugin.settings.data .experimentalFeatures ) { dropdown.addOption( diff --git a/src/modals/EditTagsModal.ts b/src/modals/EditTagsModal.ts index c6298bff..ba2d284a 100644 --- a/src/modals/EditTagsModal.ts +++ b/src/modals/EditTagsModal.ts @@ -36,7 +36,7 @@ export default class EditTagsModal extends Modal { // MultiSuggest for tag suggestions (best-effort) try { // @ts-ignore - MultiSuggest is available globally in project - const suggestionContent = this.plugin.settings.data.globalSettings.tagColors?.map(tc => tc.name) || []; + const suggestionContent = this.plugin.settings.data.tagColors?.map(tc => tc.name) || []; // @ts-ignore new (window as any).MultiSuggest(this.inputEl, new Set(suggestionContent), (choice: string) => { this.handleAddTag(choice); diff --git a/src/modals/ScanFilterModal.ts b/src/modals/ScanFilterModal.ts index d5e40d9f..9f119887 100644 --- a/src/modals/ScanFilterModal.ts +++ b/src/modals/ScanFilterModal.ts @@ -23,7 +23,7 @@ export class ScanFilterModal extends Modal { ) { super(plugin.app); this.selectedValues = new Set( - this.plugin.settings.data.globalSettings.scanFilters[ + this.plugin.settings.data.scanFilters[ this.filterType ].values ); diff --git a/src/modals/ScanVaultModal.tsx b/src/modals/ScanVaultModal.tsx index b7213481..0f693bfd 100644 --- a/src/modals/ScanVaultModal.tsx +++ b/src/modals/ScanVaultModal.tsx @@ -39,7 +39,7 @@ export const findMaxIdCounterAndUpdateSettings = (plugin: TaskBoard) => { }); // Update the uniqueIdCounter in settings to be one more than the max found ID - plugin.settings.data.globalSettings.uniqueIdCounter = maxId + 1; + plugin.settings.data.uniqueIdCounter = maxId + 1; plugin.saveSettings(); } @@ -67,7 +67,7 @@ const ScanVaultModalContent: React.FC<{ app: App, plugin: TaskBoard, vaultScanne const files = app.vault.getFiles(); setProgress(0); // Reset progress - const globalSettings = plugin.settings.data.globalSettings; + const globalSettings = plugin.settings.data; const scanFilters = globalSettings.scanFilters; for (let i = 0; i < files.length; i++) { diff --git a/src/modals/TaskBoardActionsModal.ts b/src/modals/TaskBoardActionsModal.ts index 27a49a8b..3301c708 100644 --- a/src/modals/TaskBoardActionsModal.ts +++ b/src/modals/TaskBoardActionsModal.ts @@ -43,7 +43,7 @@ export class TaskBoardActionsModal extends Modal { type: "move", targetColumn: this.columns[0].name || "", }; - this.plugin.settings.data.globalSettings.actions.push(newAction); + this.plugin.settings.data.actions.push(newAction); this.plugin.saveSettings(); this.refresh(); }; @@ -63,7 +63,7 @@ export class TaskBoardActionsModal extends Modal { }); const actions: TaskBoardAction[] = - this.plugin.settings.data.globalSettings.actions; + this.plugin.settings.data.actions; for (let i = 0; i < actions.length; i++) { const action = actions[i]; @@ -119,7 +119,7 @@ export class TaskBoardActionsModal extends Modal { .setIcon("trash") .setTooltip("Delete Action") .onClick(() => { - this.plugin.settings.data.globalSettings.actions.splice( + this.plugin.settings.data.actions.splice( i, 1 ); diff --git a/src/services/CommunityPlugins.ts b/src/services/CommunityPlugins.ts index 98f0f4b1..26d7f53b 100644 --- a/src/services/CommunityPlugins.ts +++ b/src/services/CommunityPlugins.ts @@ -24,7 +24,7 @@ export class CommunityPlugins extends TaskBoardSubmodule { isQuickAddPluginIntegrationEnabled() { return ( - this.settings.data.globalSettings.compatiblePlugins + this.settings.data.compatiblePlugins .quickAddPlugin && this.isQuickAddPluginEnabled() ); } @@ -39,6 +39,6 @@ export class CommunityPlugins extends TaskBoardSubmodule { export function isReminderPluginInstalled(plugin: TaskBoard) { const reminderPlugin = new CommunityPlugins(plugin); - plugin.settings.data.globalSettings.compatiblePlugins.reminderPlugin = + plugin.settings.data.compatiblePlugins.reminderPlugin = reminderPlugin.isReminderPluginEnabled(); } diff --git a/src/services/OpenModals.ts b/src/services/OpenModals.ts index 86e1a231..ce20884c 100644 --- a/src/services/OpenModals.ts +++ b/src/services/OpenModals.ts @@ -31,11 +31,16 @@ import { bugReporterManagerInsatance } from "src/managers/BugReporter"; // Function to open the BoardConfigModal export const openBoardConfigModal = ( plugin: TaskBoard, - boards: Board[], + allBoardData: Board[], activeBoardIndex: number, - onSave: (updatedBoards: Board[]) => void + onSave: (updatedBoards: Board[], boardIndex: number) => void, ) => { - new BoardConfigureModal(plugin, boards, activeBoardIndex, onSave).open(); + new BoardConfigureModal( + plugin, + allBoardData, + activeBoardIndex, + onSave, + ).open(); }; // Function to open the BoardConfigModal @@ -47,7 +52,7 @@ export const openAddNewTaskInCurrentFileModal = ( app: App, plugin: TaskBoard, activeFile: TFile, - cursorPosition?: { line: number; ch: number } | undefined + cursorPosition?: { line: number; ch: number } | undefined, ) => { const AddTaskModal = new AddOrEditTaskModal( plugin, @@ -55,9 +60,9 @@ export const openAddNewTaskInCurrentFileModal = ( addTaskInNote(plugin, newTask, true, cursorPosition).then( (newId) => { plugin.realTimeScanner.processAllUpdatedFiles( - newTask.filePath + newTask.filePath, ); - } + }, ); // DEPRECATED : See notes from //src/utils/TaskItemCacheOperations.ts file @@ -77,19 +82,18 @@ export const openAddNewTaskInCurrentFileModal = ( true, false, undefined, - activeFile.path + activeFile.path, ); AddTaskModal.open(); return true; }; export const openAddNewTaskModal = ( - app: App, plugin: TaskBoard, - activeFile?: TFile + activeFile?: TFile, ) => { const preDefinedNoteFile = plugin.app.vault.getAbstractFileByPath( - plugin.settings.data.globalSettings.preDefinedNote + plugin.settings.data.preDefinedNote, ); const activeTFile = activeFile ? activeFile : preDefinedNoteFile; const AddTaskModal = new AddOrEditTaskModal( @@ -107,12 +111,12 @@ export const openAddNewTaskModal = ( quickAddPluginChoice, { value: completeTask + "\n", - } + }, ); } else { await addTaskInNote(plugin, newTask, false).then((newId) => { plugin.realTimeScanner.processAllUpdatedFiles( - newTask.filePath + newTask.filePath, ); }); } @@ -132,9 +136,7 @@ export const openAddNewTaskModal = ( false, false, undefined, - activeTFile - ? activeTFile.path - : plugin.settings.data.globalSettings.preDefinedNote + activeTFile ? activeTFile.path : plugin.settings.data.preDefinedNote, ); AddTaskModal.open(); }; @@ -145,7 +147,7 @@ export const openAddNewTaskNoteModal = (app: App, plugin: TaskBoard) => { async ( newTask: taskItem, quickAddPluginChoice: string, - noteContent: string | undefined + noteContent: string | undefined, ) => { if (!noteContent) { // console.warn("This code should not run..."); @@ -164,7 +166,7 @@ export const openAddNewTaskNoteModal = (app: App, plugin: TaskBoard) => { // Create or update the file const existingFile = plugin.app.vault.getFileByPath( - newTask.filePath + newTask.filePath, ); if (!existingFile) { await plugin.app.vault @@ -172,7 +174,7 @@ export const openAddNewTaskNoteModal = (app: App, plugin: TaskBoard) => { .then(() => { // This is required to rescan the updated file and refresh the board. plugin.realTimeScanner.onFileModified( - newTask.filePath + newTask.filePath, ); sleep(1000).then(() => { // TODO : Is 1 seconds really required ? @@ -183,17 +185,17 @@ export const openAddNewTaskNoteModal = (app: App, plugin: TaskBoard) => { new Notice( t("file-note-already-exists") + t( - "creating a new file with the following name :" + "creating a new file with the following name :", ) + ` Copy-${newTask.filePath}`, - 10000 + 10000, ); await plugin.app.vault .create(`Copy-${newTask.filePath}`, noteContent) .then(() => { // This is required to rescan the updated file and refresh the board. plugin.realTimeScanner.onFileModified( - `Copy-${newTask.filePath}` + `Copy-${newTask.filePath}`, ); sleep(1000).then(() => { plugin.realTimeScanner.processAllUpdatedFiles(); @@ -203,7 +205,7 @@ export const openAddNewTaskNoteModal = (app: App, plugin: TaskBoard) => { } catch (error) { console.error( "Error creating or updating task note:", - error + error, ); new Notice(t("error-creating-task-note"), 5000); return false; @@ -216,14 +218,14 @@ export const openAddNewTaskNoteModal = (app: App, plugin: TaskBoard) => { false, false, undefined, - "" + "", ); AddTaskModal.open(); }; export const openEditTaskModal = async ( plugin: TaskBoard, - existingTask: taskItem + existingTask: taskItem, ) => { const EditTaskModal = new AddOrEditTaskModal( plugin, @@ -240,9 +242,9 @@ export const openEditTaskModal = async ( (newId) => { plugin.realTimeScanner.processAllUpdatedFiles( updatedTask.filePath, - existingTask.id + existingTask.id, ); - } + }, ); // DEPRECATED : See notes from //src/utils/TaskItemCacheOperations.ts file @@ -259,21 +261,21 @@ export const openEditTaskModal = async ( false, true, existingTask, - existingTask.filePath + existingTask.filePath, ); EditTaskModal.open(); }; export const openEditTaskNoteModal = ( plugin: TaskBoard, - existingTask: taskItem + existingTask: taskItem, ) => { const EditTaskModal = new AddOrEditTaskModal( plugin, async ( updatedTask: taskItem, quickAddPluginChoice: string, - newTaskContent: string | undefined + newTaskContent: string | undefined, ) => { let eventData: UpdateTaskEventData = { taskID: existingTask.id, @@ -285,14 +287,14 @@ export const openEditTaskNoteModal = ( // Update frontmatter with task properties await updateFrontmatterInMarkdownFile( plugin, - updatedTask + updatedTask, ).then(() => { sleep(1000).then(() => { // TODO : Is 1 sec really required ? // This is required to rescan the updated file and refresh the board. plugin.realTimeScanner.processAllUpdatedFiles( updatedTask.filePath, - existingTask.id + existingTask.id, ); }); }); @@ -300,14 +302,14 @@ export const openEditTaskNoteModal = ( writeDataToVaultFile( plugin, updatedTask.filePath, - newTaskContent + newTaskContent, ).then(() => { sleep(1000).then(() => { // TODO : Is 1 sec really required ? // This is required to rescan the updated file and refresh the board. plugin.realTimeScanner.processAllUpdatedFiles( updatedTask.filePath, - existingTask.id + existingTask.id, ); }); }); @@ -323,7 +325,7 @@ export const openEditTaskNoteModal = ( 41, "Error updating task note", error as string, - "TaskNoteEventHandlers.ts/handleTaskNoteEdit" + "TaskNoteEventHandlers.ts/handleTaskNoteEdit", ); } }, @@ -331,7 +333,7 @@ export const openEditTaskNoteModal = ( false, true, existingTask, - existingTask.filePath + existingTask.filePath, ); EditTaskModal.open(); }; @@ -343,14 +345,14 @@ export const openEditTaskNoteModal = ( * @param message - The short message of the bug, shown to the user. * @param bugContent - The detailed information related to the bug. * @param context - The location where the bug was generated and some other context. - * + * * @deprecated v1.9.0 */ export const bugReporter = ( plugin: TaskBoard, message: string, bugContent: string, - context: string + context: string, ) => { // const leaves = plugin.app.workspace.getLeavesOfType(VIEW_TYPE_TASKBOARD); // if (leaves.length > 0) { @@ -397,7 +399,7 @@ export const bugReporter = ( plugin, message, bugContent, - context + context, ); bugReportModal.open(); el.hide(); @@ -412,7 +414,7 @@ export const bugReporter = ( }); }); }), - 0 + 0, ); bugReportNotice.messageEl.onClickEvent((e) => { @@ -458,7 +460,7 @@ export const openDiffContentCompareModal = ( cachedTaskContent: string, EditedTaskContent: string, taskContentFromFile: string, - onSelect: (which: "old" | "new") => void + onSelect: (which: "old" | "new") => void, ) => { const contentMismatchNotice = new Notice( createFragment((f) => { @@ -478,7 +480,7 @@ export const openDiffContentCompareModal = ( cachedTaskContent, EditedTaskContent, taskContentFromFile, - onSelect + onSelect, ); modal.open(); el.hide(); @@ -486,7 +488,7 @@ export const openDiffContentCompareModal = ( }); }); }), - 0 + 0, ); contentMismatchNotice.messageEl.onClickEvent((e) => { @@ -500,19 +502,19 @@ export const openDiffContentCompareModal = ( export const openTaskBoardActionsModal = ( plugin: TaskBoard, - activeBoardIndex: number + activeBoardIndex: number, ) => { - const actionModal = new TaskBoardActionsModal( - plugin, - plugin.settings.data.boardConfigs[activeBoardIndex].columns - ); - actionModal.open(); + // const actionModal = new TaskBoardActionsModal( + // plugin, + // plugin.taskBoardFileManager.getCurrentBoardData()?.columns, + // ); + // actionModal.open(); }; export const openScanFiltersModal = ( plugin: TaskBoard, filterType: "files" | "frontMatter" | "folders" | "tags", - onSave: (scanFilters: string[]) => void + onSave: (scanFilters: string[]) => void, ) => { new ScanFilterModal(plugin, filterType, async (newValues) => { onSave(newValues); @@ -572,7 +574,7 @@ export const openEditTaskView = async ( taskExists: boolean, task: taskItem, filePath: string, - location: "tab" | "split" | "window" = "tab" + location: "tab" | "split" | "window" = "tab", ): Promise => { const { workspace } = plugin.app; @@ -644,17 +646,17 @@ export const openEditTaskView = async ( async ( updatedTask, quickAddPluginChoice, - updatedNoteContent + updatedNoteContent, ) => { if (!isTaskNote) { // Update the task in the file and JSON updateTaskInFile( plugin, updatedTask, - task + task, ).then((newId) => { plugin.realTimeScanner.processAllUpdatedFiles( - updatedTask.filePath + updatedTask.filePath, ); }); } else { @@ -662,14 +664,14 @@ export const openEditTaskView = async ( // Update frontmatter with task properties await updateFrontmatterInMarkdownFile( plugin, - updatedTask + updatedTask, ).then(() => { // This is required to rescan the updated file and refresh the board. sleep(1000).then(() => { // TODO : Is 1 sec really required ? // This is required to rescan the updated file and refresh the board. plugin.realTimeScanner.processAllUpdatedFiles( - updatedTask.filePath + updatedTask.filePath, ); }); }); @@ -677,13 +679,13 @@ export const openEditTaskView = async ( writeDataToVaultFile( plugin, updatedTask.filePath, - updatedNoteContent + updatedNoteContent, ).then(() => { sleep(1000).then(() => { // TODO : Is 1 sec really required ? // This is required to rescan the updated file and refresh the board. plugin.realTimeScanner.processAllUpdatedFiles( - updatedTask.filePath + updatedTask.filePath, ); }); }); @@ -694,7 +696,7 @@ export const openEditTaskView = async ( activeNote, taskExists, task, - filePath + filePath, ); }); } catch { @@ -705,29 +707,29 @@ export const openEditTaskView = async ( async ( updatedTask, quickAddPluginChoice, - updatedNoteContent + updatedNoteContent, ) => { if (!isTaskNote) { // Update the task in the file and JSON updateTaskInFile(plugin, updatedTask, task).then( (newId) => { plugin.realTimeScanner.processAllUpdatedFiles( - updatedTask.filePath + updatedTask.filePath, ); - } + }, ); } else { if (!updatedNoteContent) { // Update frontmatter with task properties await updateFrontmatterInMarkdownFile( plugin, - updatedTask + updatedTask, ).then(() => { // This is required to rescan the updated file and refresh the board. sleep(1000).then(() => { // This is required to rescan the updated file and refresh the board. plugin.realTimeScanner.processAllUpdatedFiles( - updatedTask.filePath + updatedTask.filePath, ); }); }); @@ -735,13 +737,13 @@ export const openEditTaskView = async ( writeDataToVaultFile( plugin, updatedTask.filePath, - updatedNoteContent + updatedNoteContent, ).then(() => { sleep(1000).then(() => { // TODO : Is 1 sec really required ? // This is required to rescan the updated file and refresh the board. plugin.realTimeScanner.processAllUpdatedFiles( - updatedTask.filePath + updatedTask.filePath, ); }); }); @@ -752,7 +754,7 @@ export const openEditTaskView = async ( activeNote, taskExists, task, - filePath + filePath, ); // Replace the leaf's view with our configured view diff --git a/src/services/tasks-plugin/helpers.ts b/src/services/tasks-plugin/helpers.ts index 90f8602b..e1248046 100644 --- a/src/services/tasks-plugin/helpers.ts +++ b/src/services/tasks-plugin/helpers.ts @@ -36,7 +36,7 @@ export async function fetchTasksPluginCustomStatuses( ); // if( plugin.app.plugins.getPlugin("obsidian-tasks-plugin")) { if (tasksPluginO.isTasksPluginEnabled()) { - plugin.settings.data.globalSettings.compatiblePlugins.tasksPlugin = + plugin.settings.data.compatiblePlugins.tasksPlugin = true; // Define the path to the tasks plugin data.json file @@ -67,10 +67,10 @@ export async function fetchTasksPluginCustomStatuses( // "Fetched custom statuses from tasks plugin:", // statuses, // "\nTask Board old statuses:", - // plugin.settings.data.globalSettings.customStatuses, + // plugin.settings.data.customStatuses, // "\nCondition :", // JSON.stringify( - // plugin.settings.data.globalSettings + // plugin.settings.data // .customStatuses // ) !== JSON.stringify(statuses) // ); @@ -78,10 +78,10 @@ export async function fetchTasksPluginCustomStatuses( // Store it in the plugin settings if there is a difference if ( JSON.stringify( - plugin.settings.data.globalSettings.customStatuses + plugin.settings.data.customStatuses ) !== JSON.stringify(statuses) ) { - plugin.settings.data.globalSettings.customStatuses = statuses; + plugin.settings.data.customStatuses = statuses; await plugin.saveSettings(plugin.settings); } } diff --git a/src/settings/SettingConstructUI.ts b/src/settings/SettingConstructUI.ts index b6a599b1..aa17a9c9 100644 --- a/src/settings/SettingConstructUI.ts +++ b/src/settings/SettingConstructUI.ts @@ -121,7 +121,7 @@ export class SettingsManager { // Function to load the settings from data.json async loadSettings(): Promise { try { - const settingsData = this.plugin.settings.data.globalSettings; + const settingsData = this.plugin.settings.data; this.globalSettings = settingsData; } catch (err) { bugReporterManagerInsatance.showNotice( @@ -138,7 +138,7 @@ export class SettingsManager { if (!this.globalSettings) return; try { - this.plugin.settings.data.globalSettings = this.globalSettings; + this.plugin.settings.data = this.globalSettings; this.plugin.saveSettings(); } catch (err) { bugReporterManagerInsatance.showNotice( @@ -227,7 +227,7 @@ export class SettingsManager { tabContent.empty(); sections[tabName](); - // Store the tabIndex inside `this.plugin.settings.data.globalSettings.lastViewHistory.settingTab` and call this.saveSetting() + // Store the tabIndex inside `this.plugin.settings.data.lastViewHistory.settingTab` and call this.saveSetting() const tabKeys = Object.keys(sections); const tabIndex = tabKeys.indexOf(tabName); if (this.globalSettings) { @@ -245,7 +245,7 @@ export class SettingsManager { // Set the last viewed tab const defaultTab = Object.keys(sections)[ - this.plugin.settings.data.globalSettings.lastViewHistory + this.plugin.settings.data.lastViewHistory .settingTab ]; tabs[defaultTab].click(); @@ -346,7 +346,7 @@ export class SettingsManager { }); configureBtn.addEventListener("click", () => { openScanFiltersModal(this.plugin, filterType, (newValues) => { - this.plugin.settings.data.globalSettings.scanFilters[ + this.plugin.settings.data.scanFilters[ filterType ].values = newValues; this.plugin.saveSettings(); @@ -400,7 +400,7 @@ export class SettingsManager { const newPolarity = Number( (e.target as HTMLSelectElement).value, ); - this.plugin.settings.data.globalSettings.scanFilters[ + this.plugin.settings.data.scanFilters[ filterType ].polarity = newPolarity; this.plugin.saveSettings(); @@ -1045,7 +1045,7 @@ export class SettingsManager { ) .then(() => { const colorMap = - this.plugin.settings.data.globalSettings.tagColors.map( + this.plugin.settings.data.tagColors.map( (tagColor) => ({ color: this.plugin.settings.data @@ -1269,7 +1269,7 @@ export class SettingsManager { customStatusesContainer.empty(); // Clear existing rendered rows const customStatuses = - this.plugin.settings.data.globalSettings.customStatuses; + this.plugin.settings.data.customStatuses; if (!customStatuses || customStatuses.length === 0) { customStatusesContainer.createDiv({ @@ -1373,7 +1373,7 @@ export class SettingsManager { updatedStatus.availableAsCommand, type: updatedStatus.type, }; - this.plugin.settings.data.globalSettings!.customStatuses[ + this.plugin.settings.data!.customStatuses[ index ] = customStatus; await this.saveSettings(); @@ -1402,7 +1402,7 @@ export class SettingsManager { renderCustomStatuses(); const isTasksPluginEnabled = - this.plugin.settings.data.globalSettings.compatiblePlugins + this.plugin.settings.data.compatiblePlugins .tasksPlugin; // Add "Add New Status" button new Setting(contentEl) @@ -1842,7 +1842,7 @@ export class SettingsManager { frontmatterItem !== null, ); - this.plugin.settings.data.globalSettings.frontmatterFormatting = + this.plugin.settings.data.frontmatterFormatting = newOrder; }, }); @@ -1882,7 +1882,7 @@ export class SettingsManager { text.setValue(sortfrontmatterItem.key) .setPlaceholder(t("Enter property key")) .onChange((value) => { - this.plugin.settings.data.globalSettings.frontmatterFormatting[ + this.plugin.settings.data.frontmatterFormatting[ index ].key = value.trim(); }); @@ -1897,10 +1897,10 @@ export class SettingsManager { // .setTooltip(t("remove-sort-criterion")) // .onClick(async () => { // if ( - // this.plugin.settings.data.globalSettings + // this.plugin.settings.data // .frontmatterFormatting // ) { - // this.plugin.settings.data.globalSettings.frontmatterFormatting.splice( + // this.plugin.settings.data.frontmatterFormatting.splice( // index, // 1 // ); @@ -1925,18 +1925,18 @@ export class SettingsManager { // addNewSortingButton.addEventListener("click", async () => { // const newfrontmatterItem: frontmatterFormatting = { // index: - // (this.plugin.settings.data.globalSettings + // (this.plugin.settings.data // .frontmatterFormatting?.length || 0) + 1, // property: "", // key: "", // }; // if ( - // !this.plugin.settings.data.globalSettings.frontmatterFormatting + // !this.plugin.settings.data.frontmatterFormatting // ) { - // this.plugin.settings.data.globalSettings.frontmatterFormatting = + // this.plugin.settings.data.frontmatterFormatting = // []; // } - // this.plugin.settings.data.globalSettings.frontmatterFormatting.push( + // this.plugin.settings.data.frontmatterFormatting.push( // newfrontmatterItem // ); // renderFrontmatterFormattingItems(); diff --git a/src/settings/SettingSynchronizer.ts b/src/settings/SettingSynchronizer.ts index a015517f..8c368aed 100644 --- a/src/settings/SettingSynchronizer.ts +++ b/src/settings/SettingSynchronizer.ts @@ -40,7 +40,7 @@ export function migrateSettings(defaults: any, settings: any): PluginDataJson { } as any) ); } else if (key === "boardConfigs" && Array.isArray(settings[key])) { - // This is a temporary solution to sync the boardConfigs. Will need to replace the range object with the new 'datedBasedColumn', which will have three values 'dateType', 'from' and 'to'. So, basically I want to copy `range.rangedata.from` value to `datedBasedColumn.from` and similarly for `range.rangedatato`. And for `datedBasedColumn.dateType`, put the value this.settings.data.globalSettings.universalDate + // This is a temporary solution to sync the boardConfigs. Will need to replace the range object with the new 'datedBasedColumn', which will have three values 'dateType', 'from' and 'to'. So, basically I want to copy `range.rangedata.from` value to `datedBasedColumn.from` and similarly for `range.rangedatato`. And for `datedBasedColumn.dateType`, put the value this.settings.data.universalDate // This migration was applied since version 1.5.0. settings[key].forEach((boardConfig: Board) => { boardConfig.columns.forEach((column: ColumnData) => { diff --git a/src/utils/BoardOperations.ts b/src/utils/BoardOperations.ts deleted file mode 100644 index 8a0e2816..00000000 --- a/src/utils/BoardOperations.ts +++ /dev/null @@ -1,24 +0,0 @@ -// /src/utils/BoardOperations.ts - -import { Board } from "../interfaces/BoardConfigs"; -import TaskBoard from "main"; -import { saveBoardsData } from "./JsonFileOperations"; - -// // Function to refresh the board data -// export const refreshBoardData = async ( -// setBoards: React.Dispatch>, -// callback: () => void // Add this callback -// ) => { -// callback(); -// }; - -// Function to handle saving boards -export const handleUpdateBoards = async ( - plugin: TaskBoard, - updatedBoards: Board[], - setBoards: React.Dispatch> -) => { - setBoards(updatedBoards); - saveBoardsData(plugin, updatedBoards); - // await refreshBoardData(setBoards, () => {}); // this wont work anymore, use some different way to refresh the whole shit, maybe load the everything again from TaskBoardView.tsx. -}; diff --git a/src/utils/CheckBoxUtils.ts b/src/utils/CheckBoxUtils.ts index 5c6f42d6..e583854b 100644 --- a/src/utils/CheckBoxUtils.ts +++ b/src/utils/CheckBoxUtils.ts @@ -14,7 +14,7 @@ export function checkboxStateSwitcher( plugin: TaskBoard, symbol: string ): { newSymbol: string; newSymbolType: string } { - const { customStatuses } = plugin.settings.data.globalSettings; + const { customStatuses } = plugin.settings.data; // Check if customStatuses is available and has entries if (customStatuses?.length > 0) { @@ -86,7 +86,7 @@ export function isTaskCompleted( // ); const tasksPluginStatusConfigs = - settings.data.globalSettings.customStatuses; + settings.data.customStatuses; let flag = false; tasksPluginStatusConfigs.some((customStatus: CustomStatus) => { // console.log("customStatus :", customStatus, "\nsymbol :", symbol); @@ -102,7 +102,7 @@ export function isTaskCompleted( return flag; } else { const tasksPluginStatusConfigs = - settings.data.globalSettings.customStatuses; + settings.data.customStatuses; let flag = false; tasksPluginStatusConfigs.some((customStatus: CustomStatus) => { if ( diff --git a/src/utils/DateTimeCalculations.ts b/src/utils/DateTimeCalculations.ts index aa5614d0..ba9123fb 100644 --- a/src/utils/DateTimeCalculations.ts +++ b/src/utils/DateTimeCalculations.ts @@ -66,7 +66,7 @@ export const getUniversalDateFromTask = ( ): string => { // Method 1 - Comparing const universalDateChoice = - plugin.settings.data.globalSettings.universalDate; + plugin.settings.data.universalDate; if (universalDateChoice === UniversalDateOptions.dueDate) { return task.due; @@ -77,9 +77,9 @@ export const getUniversalDateFromTask = ( } return ""; - // Method 2 - directly fetching the key of the task object which is same as that saved as string inside plugin.settings.data.globalSettings.universalDate + // Method 2 - directly fetching the key of the task object which is same as that saved as string inside plugin.settings.data.universalDate // const universalDateChoice = - // plugin.settings.data.globalSettings.universalDate; + // plugin.settings.data.universalDate; // if ( // !universalDateChoice || // !task[universalDateChoice] || @@ -99,7 +99,7 @@ export const getUniversalDateFromTask = ( */ export const getUniversalDateEmoji = (plugin: TaskBoard): string => { const universalDateChoice = - plugin.settings.data.globalSettings.universalDate; + plugin.settings.data.universalDate; if (universalDateChoice === UniversalDateOptions.dueDate) { return "📅"; } else if (universalDateChoice === UniversalDateOptions.scheduledDate) { diff --git a/src/utils/JsonFileOperations.ts b/src/utils/JsonFileOperations.ts index 1a771f35..e8c93bc8 100644 --- a/src/utils/JsonFileOperations.ts +++ b/src/utils/JsonFileOperations.ts @@ -17,7 +17,7 @@ import { bugReporterManagerInsatance } from "src/managers/BugReporter"; export const loadGlobalSettings = async (plugin: TaskBoard) => { try { await plugin.loadSettings(); - const globalSettings = plugin.settings.data.globalSettings || {}; + const globalSettings = plugin.settings.data || {}; return globalSettings; } catch (error) { bugReporterManagerInsatance.showNotice( @@ -30,50 +30,63 @@ export const loadGlobalSettings = async (plugin: TaskBoard) => { } }; -// Function to load boards data from the JSON file -export const loadBoardsData = async (plugin: TaskBoard): Promise => { - try { - // Fetch settings via Obsidian's loadData method - await plugin.loadSettings(); - - const boardConfigs = plugin.settings.data.boardConfigs || []; - - return boardConfigs; - } catch (error) { - bugReporterManagerInsatance.showNotice( - 69, - "Failed to load board configurations from data.json", - String(error), - "JsonFileOperations.ts/loadBoardsData" - ); - throw error; - } -}; - -// Function to save boards data to the JSON file -export const saveBoardsData = async ( - plugin: TaskBoard, - updatedBoards: Board[] -) => { - try { - // Fetch current settings - await plugin.loadSettings(); - - // Update the boardConfigs in settings - plugin.settings.data.boardConfigs = updatedBoards; +/** + * Loads all boards configurations from the data.json file. + * @param plugin - Plugin instance. + * @returns - Returns the board configurations. + * + * @deprecated v2.0.0 - The individual board data will stored in their respective files. All laoding and saving of board configurations will be handled by the TaskBOardFileManager. + */ +// export const loadBoardsData = async (plugin: TaskBoard): Promise => { +// try { +// // Fetch settings via Obsidian's loadData method +// await plugin.loadSettings(); + +// const boardConfigs = plugin.settings.data.boardConfigs || []; + +// return boardConfigs; +// } catch (error) { +// bugReporterManagerInsatance.showNotice( +// 69, +// "Failed to load board configurations from data.json", +// String(error), +// "JsonFileOperations.ts/loadBoardsData" +// ); +// throw error; +// } +// }; - // Save updated settings - await plugin.saveSettings(); - } catch (error) { - bugReporterManagerInsatance.showNotice( - 70, - "Failed to save board configurations to data.json", - String(error), - "JsonFileOperations.ts/saveBoardsData" - ); - throw error; - } -}; +/** + * Saves the updated boards configurations from the data.json file. + * @param plugin - Plugin instance. + * @param updatedBoards - Updated boards configurations list. + * @returns - Returns the board configurations. + * + * @deprecated v2.0.0 - The individual board data will stored in their respective files. All laoding and saving of board configurations will be handled by the TaskBOardFileManager. + */ +// export const saveBoardsData = async ( +// plugin: TaskBoard, +// updatedBoards: Board[] +// ) => { +// try { +// // Fetch current settings +// await plugin.loadSettings(); + +// // Update the boardConfigs in settings +// plugin.settings.data.boardConfigs = updatedBoards; + +// // Save updated settings +// await plugin.saveSettings(); +// } catch (error) { +// bugReporterManagerInsatance.showNotice( +// 70, +// "Failed to save board configurations to data.json", +// String(error), +// "JsonFileOperations.ts/saveBoardsData" +// ); +// throw error; +// } +// }; // ------------ Operations with tasks.json ---------------- @@ -105,8 +118,8 @@ export const loadJsonCacheDataFromDisk = async ( ): Promise => { try { let path = `${plugin.app.vault.configDir}/plugins/task-board/tasks.json`; - if (plugin.settings.data.globalSettings.tasksCacheFilePath !== "") { - path = plugin.settings.data.globalSettings.tasksCacheFilePath; + if (plugin.settings.data.tasksCacheFilePath !== "") { + path = plugin.settings.data.tasksCacheFilePath; } const data: string = await plugin.app.vault.adapter.read(path); const cacheData: jsonCacheData = JSON.parse(data); @@ -196,8 +209,8 @@ export const writeJsonCacheDataToDisk = async ( ): Promise => { try { let path = `${plugin.app.vault.configDir}/plugins/task-board/tasks.json`; - if (plugin.settings.data.globalSettings.tasksCacheFilePath !== "") { - path = plugin.settings.data.globalSettings.tasksCacheFilePath; + if (plugin.settings.data.tasksCacheFilePath !== "") { + path = plugin.settings.data.tasksCacheFilePath; } // const cleanedTasksData = tasksData; //await dataCleanup(tasksData); @@ -257,7 +270,7 @@ export const moveTasksCacheFileToNewPath = ( .rename(oldPath, newPath) // .then(() => { // // Update the tasksCacheFilePath in globalSettings - // plugin.settings.data.globalSettings.tasksCacheFilePath = + // plugin.settings.data.tasksCacheFilePath = // newPath; // // Save the updated settings // return plugin.saveSettings(); diff --git a/src/utils/TaskItemUtils.ts b/src/utils/TaskItemUtils.ts index aba13360..aab90fe0 100644 --- a/src/utils/TaskItemUtils.ts +++ b/src/utils/TaskItemUtils.ts @@ -94,13 +94,13 @@ export function generateRandomTempTaskId(): string { * @returns A string representing the unique ID for the task */ export function generateTaskId(plugin: TaskBoard): string { - plugin.settings.data.globalSettings.uniqueIdCounter = - plugin.settings.data.globalSettings.uniqueIdCounter + 1 || 0; + plugin.settings.data.uniqueIdCounter = + plugin.settings.data.uniqueIdCounter + 1 || 0; // Save the updated uniqueIdCounter back to settings plugin.saveSettings(); // Return the current counter value and then increment it for the next ID - return String(plugin.settings.data.globalSettings.uniqueIdCounter); + return String(plugin.settings.data.uniqueIdCounter); } /** @@ -117,7 +117,7 @@ export const applyIdToTaskItem = async ( ): Promise => { if ( isTaskNotePresentInTags( - plugin.settings.data.globalSettings.taskNoteIdentifierTag, + plugin.settings.data.taskNoteIdentifierTag, task.tags ) ) { diff --git a/src/utils/UserTaskEvents.ts b/src/utils/UserTaskEvents.ts index d36deb40..ae633271 100644 --- a/src/utils/UserTaskEvents.ts +++ b/src/utils/UserTaskEvents.ts @@ -45,7 +45,7 @@ export const handleEditTask = ( settingOption: string ) => { const taskNoteIdentifierTag = - plugin.settings.data.globalSettings.taskNoteIdentifierTag; + plugin.settings.data.taskNoteIdentifierTag; const isThisATaskNote = isTaskNotePresentInTags( taskNoteIdentifierTag, task.tags @@ -211,7 +211,7 @@ export const updateTaskItemStatus = ( eventEmitter.emit("UPDATE_TASK", eventData); const isThisTaskNote = isTaskNotePresentInTags( - plugin.settings.data.globalSettings.taskNoteIdentifierTag, + plugin.settings.data.taskNoteIdentifierTag, oldTask.tags ); if (isThisTaskNote) { @@ -227,11 +227,11 @@ export const updateTaskItemStatus = ( }); } else { const newStatusType = - plugin.settings.data.globalSettings.customStatuses.find( + plugin.settings.data.customStatuses.find( (status) => status.symbol === newStatus )?.type ?? statusTypeNames.TODO; newTask.title = sanitizeStatus( - plugin.settings.data.globalSettings, + plugin.settings.data, newTask.title, newStatus, newStatusType @@ -270,7 +270,7 @@ export const updateTaskItemPriority = ( eventEmitter.emit("UPDATE_TASK", eventData); const isThisTaskNote = isTaskNotePresentInTags( - plugin.settings.data.globalSettings.taskNoteIdentifierTag, + plugin.settings.data.taskNoteIdentifierTag, oldTask.tags ); @@ -285,7 +285,7 @@ export const updateTaskItemPriority = ( }); } else { newTask.title = sanitizePriority( - plugin.settings.data.globalSettings, + plugin.settings.data, newTask.title, newPriority ); @@ -335,7 +335,7 @@ export const updateTaskItemDate = ( eventEmitter.emit("UPDATE_TASK", { taskID: oldTask.id, state: true }); const isThisTaskNote = isTaskNotePresentInTags( - plugin.settings.data.globalSettings.taskNoteIdentifierTag, + plugin.settings.data.taskNoteIdentifierTag, oldTask.tags ); @@ -352,21 +352,21 @@ export const updateTaskItemDate = ( switch (dateType) { case UniversalDateOptions.startDate: newTask.title = sanitizeStartDate( - plugin.settings.data.globalSettings, + plugin.settings.data, newTask.title, newDate ); break; case UniversalDateOptions.scheduledDate: newTask.title = sanitizeScheduledDate( - plugin.settings.data.globalSettings, + plugin.settings.data, newTask.title, newDate ); break; case UniversalDateOptions.dueDate: newTask.title = sanitizeDueDate( - plugin.settings.data.globalSettings, + plugin.settings.data, newTask.title, newDate ); @@ -407,7 +407,7 @@ export const updateTaskItemReminder = ( eventEmitter.emit("UPDATE_TASK", { taskID: oldTask.id, state: true }); const isThisTaskNote = isTaskNotePresentInTags( - plugin.settings.data.globalSettings.taskNoteIdentifierTag, + plugin.settings.data.taskNoteIdentifierTag, oldTask.tags ); @@ -452,7 +452,7 @@ export const updateTaskItemTags = ( eventEmitter.emit("UPDATE_TASK", { taskID: oldTask.id, state: true }); const isThisTaskNote = isTaskNotePresentInTags( - plugin.settings.data.globalSettings.taskNoteIdentifierTag, + plugin.settings.data.taskNoteIdentifierTag, oldTask.tags ); diff --git a/src/utils/algorithms/ColumnSegregator.ts b/src/utils/algorithms/ColumnSegregator.ts index fbd308b3..dad87c97 100644 --- a/src/utils/algorithms/ColumnSegregator.ts +++ b/src/utils/algorithms/ColumnSegregator.ts @@ -364,7 +364,7 @@ export const columnSegregator = ( } else { // Default algorithm for other criteria tasksToDisplay = columnSortingAlgorithm( - settings.data.globalSettings.defaultStartTime, + settings.data.defaultStartTime, tasksToDisplay, columnData.sortCriteria ); diff --git a/src/utils/algorithms/ScanningFilterer.ts b/src/utils/algorithms/ScanningFilterer.ts index fa1ec778..cc8dbbc5 100644 --- a/src/utils/algorithms/ScanningFilterer.ts +++ b/src/utils/algorithms/ScanningFilterer.ts @@ -310,7 +310,7 @@ export async function verifySubtasksAndChildtasksAreComplete( plugin: TaskBoard, task: taskItem ): Promise { - if (!plugin.settings.data.globalSettings.boundTaskCompletionToChildTasks) + if (!plugin.settings.data.boundTaskCompletionToChildTasks) return true; let flag = true; diff --git a/src/utils/taskLine/TaskContentFormatter.ts b/src/utils/taskLine/TaskContentFormatter.ts index fea51af9..fe8a7df9 100644 --- a/src/utils/taskLine/TaskContentFormatter.ts +++ b/src/utils/taskLine/TaskContentFormatter.ts @@ -79,11 +79,11 @@ export const addIdToTaskContent = async ( const taskId = extractTaskId(formattedTaskContent); let newId = undefined; if ( - (!taskId && Plugin.settings.data.globalSettings.autoAddUniqueID) || + (!taskId && Plugin.settings.data.autoAddUniqueID) || forcefullyAddId ) { newId = generateTaskId(Plugin); - const format = Plugin.settings.data.globalSettings.taskPropertyFormat; + const format = Plugin.settings.data.taskPropertyFormat; switch (format) { case taskPropertyFormatOptions.tasksPlugin: case taskPropertyFormatOptions.default: @@ -173,7 +173,7 @@ export const getSanitizedTaskContent = ( // return ""; // } - const globalSettings = plugin.settings.data.globalSettings; + const globalSettings = plugin.settings.data; const checkBoxStat = `- [${updatedTask.status}]`; // TODO : Sanitizations not only correcting the format and replacing the old content with the latest one, but also very important is to clean if any old properties are there. @@ -1170,8 +1170,8 @@ export const sanitizeDependsOn = ( // } // const dayPlannerPlugin = -// plugin.settings.data.globalSettings.dayPlannerPlugin; -// const globalSettings = plugin.settings.data.globalSettings; +// plugin.settings.data.dayPlannerPlugin; +// const globalSettings = plugin.settings.data; // let dueDateWithFormat: string = ""; // let completedWitFormat: string = ""; @@ -1269,12 +1269,12 @@ export const sanitizeDependsOn = ( export const cleanTaskTitle = (plugin: TaskBoard, task: taskItem): string => { // Get the list of properties to hide const hiddenProperties = - plugin.settings.data.globalSettings.hiddenTaskProperties || []; + plugin.settings.data.hiddenTaskProperties || []; // If no properties are configured to hide and the legacy setting is false, return original title if ( hiddenProperties.length === 0 && - !plugin.settings.data.globalSettings.showTaskWithoutMetadata + !plugin.settings.data.showTaskWithoutMetadata ) { return task.title; } @@ -1290,7 +1290,7 @@ export const cleanTaskTitle = (plugin: TaskBoard, task: taskItem): string => { .trim(); // If legacy showTaskWithoutMetadata is enabled, hide all properties (backward compatibility) - if (plugin.settings.data.globalSettings.showTaskWithoutMetadata) { + if (plugin.settings.data.showTaskWithoutMetadata) { return cleanTaskTitleLegacy(task); } @@ -1409,7 +1409,7 @@ export const cleanTaskTitle = (plugin: TaskBoard, task: taskItem): string => { // Remove reminder if it's in the hidden properties list if ( hiddenProperties.includes(taskPropertiesNames.Dependencies) || - plugin.settings.data.globalSettings.showTaskWithoutMetadata + plugin.settings.data.showTaskWithoutMetadata ) { const reminderRegex = /\(\@(\d{4}-\d{2}-\d{2}( \d{2}:\d{2})?|\d{2}:\d{2})\)/; diff --git a/src/utils/taskLine/TaskItemEventHandlers.ts b/src/utils/taskLine/TaskItemEventHandlers.ts index 4fc8135d..61311a3c 100644 --- a/src/utils/taskLine/TaskItemEventHandlers.ts +++ b/src/utils/taskLine/TaskItemEventHandlers.ts @@ -39,7 +39,7 @@ export const handleCheckboxChange = (plugin: TaskBoard, task: taskItem) => { // Check if the task is completed const newStatus = checkboxStateSwitcher(plugin, task.status); const newTitle = sanitizeStatus( - plugin.settings.data.globalSettings, + plugin.settings.data, task.title, newStatus.newSymbol, newStatus.newSymbolType @@ -64,7 +64,7 @@ export const handleCheckboxChange = (plugin: TaskBoard, task: taskItem) => { // if (isTaskCompleted(`- [${task.status}]`, false, plugin.settings)) { // const newStatusType = - // plugin.settings.data.globalSettings.customStatuses.find( + // plugin.settings.data.customStatuses.find( // (status) => status.symbol === newStatus // )?.type ?? statusTypeNames.TODO; // let newTitle = sanitizeStatus(task.title, newStatus.newSymbol); @@ -88,7 +88,7 @@ export const handleCheckboxChange = (plugin: TaskBoard, task: taskItem) => { // } // ); // } else { - // const globalSettings = plugin.settings.data.globalSettings; + // const globalSettings = plugin.settings.data; // const moment = _moment as unknown as typeof _moment.default; // const currentDateValue = moment().format( // globalSettings?.taskCompletionDateTimePattern diff --git a/src/utils/taskLine/TaskLineUtils.ts b/src/utils/taskLine/TaskLineUtils.ts index 9a5b29ed..c0d351b4 100644 --- a/src/utils/taskLine/TaskLineUtils.ts +++ b/src/utils/taskLine/TaskLineUtils.ts @@ -475,7 +475,7 @@ export const deleteTaskFromFile = async ( /** * Archives a task by moving it to a specified archived tasks file or marking it as archived in the same file. * - * This function first checks the value of `plugin.settings.data.globalSettings.archivedTasksFilePath`. + * This function first checks the value of `plugin.settings.data.archivedTasksFilePath`. * If this setting contains the path of the file, it removes the task from its original file and adds it * to the top of the specified archived file with a timestamp. If the setting does not contain a file path, * it marks the task as archived in the original file by surrounding the task content with '%%'. @@ -491,7 +491,7 @@ export const archiveTask = async ( task: taskItem ): Promise => { const archivedFilePath = - plugin.settings.data.globalSettings.archivedTasksFilePath; + plugin.settings.data.archivedTasksFilePath; // Prepare the task content to be archived const oldTaskContent = await getFormattedTaskContent(task); if (oldTaskContent === "") @@ -766,7 +766,7 @@ export const replaceOldTaskWithNewTask = async ( // Replace the old task block with the updated content await writeDataToVaultFile(plugin, filePath, newContent); } else { - if (plugin.settings.data.globalSettings.safeGuardFeature) { + if (plugin.settings.data.safeGuardFeature) { // Ask user to choose between old and new content openDiffContentCompareModal( plugin, diff --git a/src/utils/taskNote/FrontmatterOperations.ts b/src/utils/taskNote/FrontmatterOperations.ts index 5679ed14..cbf00d77 100644 --- a/src/utils/taskNote/FrontmatterOperations.ts +++ b/src/utils/taskNote/FrontmatterOperations.ts @@ -211,19 +211,19 @@ export function createFrontmatterFromTask( frontmatterObj[getCustomFrontmatterKey("status", frontmatterFormatting)] = getStatusNameFromStatusSymbol( task?.status, - plugin.settings.data.globalSettings + plugin.settings.data ) || "pending"; frontmatterObj[getCustomFrontmatterKey("tags", frontmatterFormatting)] = [ - plugin.settings.data.globalSettings.taskNoteIdentifierTag, + plugin.settings.data.taskNoteIdentifierTag, ...(task?.tags?.filter( (tag) => tag.includes( - plugin.settings.data.globalSettings.taskNoteIdentifierTag + plugin.settings.data.taskNoteIdentifierTag ) === false ) ?? []), ]; - if (task.id && plugin.settings.data.globalSettings.autoAddUniqueID) + if (task.id && plugin.settings.data.autoAddUniqueID) frontmatterObj[getCustomFrontmatterKey("id", frontmatterFormatting)] = task.legacyId ? task.legacyId : task.id; if (task.priority && task.priority > 0) { @@ -291,7 +291,7 @@ export function updateFrontmatterProperties( forceId?: boolean ): Partial { const frontmatterFormatting: frontmatterFormatting[] = - plugin.settings.data.globalSettings.frontmatterFormatting; + plugin.settings.data.frontmatterFormatting; const oldFrontmatter = existingFrontmatter; // Step 1: Build a temporary object with all the updated values @@ -344,7 +344,7 @@ export function updateFrontmatterProperties( .filter(Boolean); const identifierTag = - plugin.settings.data.globalSettings.taskNoteIdentifierTag; + plugin.settings.data.taskNoteIdentifierTag; // // Step 1: Add any new tags from task.tags that are not in existingTags // let newTagsToAdd = taskTags.filter((t) => !existingTags.includes(t)); @@ -369,7 +369,7 @@ export function updateFrontmatterProperties( // Update or add unique ID const idKey = getCustomFrontmatterKey("id", frontmatterFormatting); if (!existingFrontmatter?.[idKey]) { - if (forceId || plugin.settings.data.globalSettings.autoAddUniqueID) { + if (forceId || plugin.settings.data.autoAddUniqueID) { tempUpdates[idKey] = task.legacyId ? task.legacyId : generateTaskId(plugin); @@ -383,7 +383,7 @@ export function updateFrontmatterProperties( if (task.status) { const statusName = getStatusNameFromStatusSymbol( task.status, - plugin.settings.data.globalSettings + plugin.settings.data ); tempUpdates[statusKey] = statusName ?? `"${task.status}"`; } @@ -442,7 +442,7 @@ export function updateFrontmatterProperties( } const statusConfig = - plugin.settings.data.globalSettings.customStatuses.find( + plugin.settings.data.customStatuses.find( (status) => status.symbol === task.status ); const statusType = statusConfig ? statusConfig.type : undefined; @@ -455,7 +455,7 @@ export function updateFrontmatterProperties( if (task.cancelledDate) { tempUpdates[cancelledDateKey] = task.cancelledDate; } else { - const globalSettings = plugin.settings.data.globalSettings; + const globalSettings = plugin.settings.data; const moment = _moment as unknown as typeof _moment.default; const currentDateValue = moment().format( globalSettings?.taskCompletionDateTimePattern @@ -476,7 +476,7 @@ export function updateFrontmatterProperties( if (task.cancelledDate) { tempUpdates[completionKey] = task.completion; } else { - const globalSettings = plugin.settings.data.globalSettings; + const globalSettings = plugin.settings.data; const moment = _moment as unknown as typeof _moment.default; const currentDateValue = moment().format( globalSettings?.taskCompletionDateTimePattern diff --git a/src/utils/taskNote/TaskNoteEventHandlers.ts b/src/utils/taskNote/TaskNoteEventHandlers.ts index 2f9497f0..bbda2d15 100644 --- a/src/utils/taskNote/TaskNoteEventHandlers.ts +++ b/src/utils/taskNote/TaskNoteEventHandlers.ts @@ -27,7 +27,7 @@ export const handleTaskNoteStatusChange = async ( ) => { try { const newStatus = checkboxStateSwitcher(plugin, task.status); - const globalSettings = plugin.settings.data.globalSettings; + const globalSettings = plugin.settings.data; const moment = _moment as unknown as typeof _moment.default; const currentDateValue = moment().format( globalSettings?.taskCompletionDateTimePattern @@ -56,7 +56,7 @@ export const handleTaskNoteStatusChange = async ( } const newStatusName = getStatusNameFromStatusSymbol( newStatus.newSymbol, - plugin.settings.data.globalSettings + plugin.settings.data ); // Update frontmatter with new status @@ -134,7 +134,7 @@ export const handleTaskNoteDelete = async ( tags = tags.filter( (tag: string) => tag.includes( - plugin.settings.data.globalSettings + plugin.settings.data .taskNoteIdentifierTag ) === false ); diff --git a/src/utils/taskNote/TaskNoteUtils.ts b/src/utils/taskNote/TaskNoteUtils.ts index d5894a30..cd566765 100644 --- a/src/utils/taskNote/TaskNoteUtils.ts +++ b/src/utils/taskNote/TaskNoteUtils.ts @@ -75,7 +75,7 @@ export function extractTaskNoteProperties( } const frontmatterFormatting: frontmatterFormatting[] = - settings.data.globalSettings.frontmatterFormatting; + settings.data.frontmatterFormatting; return { id: @@ -229,7 +229,7 @@ export function getStatusSymbolFromStatusName( // return " "; const tasksPluginStatusConfigs = - settings.data.globalSettings.customStatuses; + settings.data.customStatuses; let statusSymbol = ""; tasksPluginStatusConfigs.some((customStatus: CustomStatus) => { if (customStatus.name === statusName) { @@ -458,7 +458,7 @@ export async function archiveTaskNote( // Get the archive folder path from settings const archiveFolderPath = - plugin.settings.data.globalSettings.archivedTBNotesFolderPath; + plugin.settings.data.archivedTBNotesFolderPath; if (!archiveFolderPath || archiveFolderPath.trim() === "") { new Notice("Archive folder path is not configured in settings"); diff --git a/src/views/AddOrEditTaskView.tsx b/src/views/AddOrEditTaskView.tsx index c465cab1..b1dd7a6b 100644 --- a/src/views/AddOrEditTaskView.tsx +++ b/src/views/AddOrEditTaskView.tsx @@ -66,7 +66,7 @@ export class AddOrEditTaskView extends ItemView { // container.empty(); container.setAttribute('data-type', 'task-board-view'); - if (!this.isTaskNote && this.plugin.settings.data.globalSettings.autoAddUniqueID && (!this.taskExists || !this.task.id)) { + if (!this.isTaskNote && this.plugin.settings.data.autoAddUniqueID && (!this.taskExists || !this.task.id)) { this.task.id = generateTaskId(this.plugin); this.task.legacyId = String(this.task.id); } @@ -79,7 +79,7 @@ export class AddOrEditTaskView extends ItemView { } else { noteContent = "---\ntitle: \n---\n"; - const defaultLocation = this.plugin.settings.data.globalSettings.taskNoteDefaultLocation || 'Meta/Task_Board/Task_Notes'; + const defaultLocation = this.plugin.settings.data.taskNoteDefaultLocation || 'Meta/Task_Board/Task_Notes'; const noteName = this.task.title || getLocalDateTimeString(); // Sanitize filename const sanitizedName = noteName.replace(/[<>:"/\\|?*]/g, '_'); @@ -88,7 +88,7 @@ export class AddOrEditTaskView extends ItemView { if (!this.task.title) this.task.title = this.filePath.split('/').pop()?.replace(allowedFileExtensionsRegEx, "") ?? "Untitled"; - if (this.plugin.settings.data.globalSettings.autoAddUniqueID && (!this.taskExists || !this.task.legacyId)) { + if (this.plugin.settings.data.autoAddUniqueID && (!this.taskExists || !this.task.legacyId)) { this.task.id = generateTaskId(this.plugin); this.task.legacyId = this.task.id; } diff --git a/src/views/TaskBoardView.tsx b/src/views/TaskBoardView.tsx index e61293f5..3162b5f2 100644 --- a/src/views/TaskBoardView.tsx +++ b/src/views/TaskBoardView.tsx @@ -9,22 +9,20 @@ import { Board } from "src/interfaces/BoardConfigs"; import TaskBoardViewContent from "src/components/TaskBoardViewContent"; import type TaskBoard from "../../main"; import { PENDING_SCAN_FILE_STACK, VIEW_TYPE_TASKBOARD } from "src/interfaces/Constants"; -import { loadBoardsData } from "src/utils/JsonFileOperations"; -import { bugReporter, openScanVaultModal } from "../services/OpenModals"; +import { openScanVaultModal } from "../services/OpenModals"; import { t } from "src/utils/lang/helper"; import { eventEmitter } from "src/services/EventEmitter"; -import { bugReporterManagerInsatance } from "src/managers/BugReporter"; export class TaskBoardView extends ItemView { plugin: TaskBoard; - boards: Board[]; + // boards: Board[]; root: Root | null = null; constructor(plugin: TaskBoard, leaf: WorkspaceLeaf) { super(leaf); this.app = plugin.app; this.plugin = plugin; - this.boards = []; + // this.boards = []; this.icon = TaskBoardIcon; } @@ -63,8 +61,10 @@ export class TaskBoardView extends ItemView { if (mandatoryScanSignal) this.highlighgtScanvaultIcon(); - await this.loadBoards(); + // await this.loadBoards(); this.renderBoard(); + + } async highlighgtScanvaultIcon() { @@ -79,27 +79,25 @@ export class TaskBoardView extends ItemView { } } - private async loadBoards() { - try { - this.boards = await loadBoardsData(this.plugin); - } catch (err) { - bugReporterManagerInsatance.showNotice( - 89, - "Failed to load board configurations from data.json", - String(err), - "TaskBoardView.tsx/loadBoards" - ); - } - } + // private async loadBoards() { + // try { + // this.boards = await loadBoardsData(this.plugin); + // } catch (err) { + // bugReporterManagerInsatance.showNotice( + // 89, + // "Failed to load board configurations from data.json", + // String(err), + // "TaskBoardView.tsx/loadBoards" + // ); + // } + // } private renderBoard() { this.root = createRoot(this.containerEl.children[1]); this.root.render( , , ); From f6618516b962cb8f32ce7d3acc0737ed01b595b4 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Tue, 20 Jan 2026 10:58:38 +0530 Subject: [PATCH 004/236] delete Column component --- src/components/KanbanView/Column.tsx | 809 ------------------ src/components/KanbanView/KanbanBoardView.tsx | 8 +- .../KanbanView/KanbanSwimlanesContainer.tsx | 6 +- 3 files changed, 6 insertions(+), 817 deletions(-) delete mode 100644 src/components/KanbanView/Column.tsx diff --git a/src/components/KanbanView/Column.tsx b/src/components/KanbanView/Column.tsx deleted file mode 100644 index 61885b95..00000000 --- a/src/components/KanbanView/Column.tsx +++ /dev/null @@ -1,809 +0,0 @@ -// /src/components/Column.tsx - -/** - * Column component - DEPRECATED - * This component has been now deprecated, since the LazyColum.tsx component is performing its intended functionality of lazy-loading without any issue in all the tested environments. - * Will still keep this file in the project and update it with the changes from LazyColumn.tsx, just to have a fallback backup ready. And for other testing. - * @kind component - * @deprecated - */ - -import React, { memo, useCallback, useEffect, useMemo, useState, useRef } from 'react'; - -import { CSSProperties } from 'react'; -import TaskItem, { swimlaneDataProp } from './TaskItem'; -import { t } from 'src/utils/lang/helper'; -import TaskBoard from 'main'; -import { Board, ColumnData, RootFilterState } from 'src/interfaces/BoardConfigs'; -import { taskItem } from 'src/interfaces/TaskItem'; -import { Menu, Platform } from 'obsidian'; -import { ViewTaskFilterPopover } from 'src/components/BoardFilters/ViewTaskFilterPopover'; -import { eventEmitter } from 'src/services/EventEmitter'; -import { ViewTaskFilterModal } from 'src/components/BoardFilters'; -import { ConfigureColumnSortingModal } from 'src/modals/ConfigureColumnSortingModal'; -import { matchTagsWithWildcards } from 'src/utils/algorithms/ScanningFilterer'; -import { isRootFilterStateEmpty } from 'src/utils/algorithms/BoardFilterer'; -import { dragDropTasksManagerInsatance } from 'src/managers/DragDropTasksManager'; -import { bugReporterManagerInsatance } from 'src/managers/BugReporter'; - -type CustomCSSProperties = CSSProperties & { - '--task-board-column-width': string; -}; - -export interface ColumnProps { - plugin: TaskBoard; - columnIndex: number; - activeBoardData: Board; - collapsed?: boolean; - columnData: ColumnData; - tasksForThisColumn: taskItem[]; - swimlaneData?: swimlaneDataProp; - hideColumnHeader?: boolean; - headerOnly?: boolean; -} - - -const Column: React.FC = ({ - plugin, - columnIndex, - activeBoardData, - columnData, - tasksForThisColumn, - swimlaneData, - hideColumnHeader = false, - headerOnly = false, -}) => { - if (activeBoardData?.hideEmptyColumns && (tasksForThisColumn === undefined || tasksForThisColumn.length === 0)) { - return null; // Don't render the column if it has no tasks and empty columns are hidden - } - const [isDragOver, setIsDragOver] = useState(false); - const [insertIndex, setInsertIndex] = useState(null); - const insertIndexRef = useRef(null); - const rafRef = useRef(null); - const tasksContainerRef = useRef(null); - - const scheduleSetInsertIndex = (pos: number | null) => { - if (insertIndexRef.current === pos) return; - if (rafRef.current) { - cancelAnimationFrame(rafRef.current); - rafRef.current = null; - } - rafRef.current = requestAnimationFrame(() => { - insertIndexRef.current = pos; - setInsertIndex(pos); - rafRef.current = null; - }); - }; - - // Local tasks state, initially set from external tasks - // const [tasks, setTasks] = useState(tasksForThisColumn); - // const tasks = useMemo(() => tasksForThisColumn, [tasksForThisColumn]); - // Local tasks state, initially set from external tasks - // console.log("Column.tsx : Data in tasks :", tasks); - const [localTasks, setLocalTasks] = useState(tasksForThisColumn); - // Detect external changes in tasksForThisColumn - useEffect(() => { - setLocalTasks(tasksForThisColumn); - }, [tasksForThisColumn]); - - // // Render tasks using the tasks passed from KanbanBoard - // useEffect(() => { - // if (allTasksExternal.Pending.length > 0 || allTasksExternal.Completed.length > 0) { - // columnSegregator(plugin, setTasks, activeBoardIndex, colType, columnData, allTasksExternal); - // } - // }, [colType, columnData, allTasksExternal]); - - const columnWidth = plugin.settings.data.columnWidth || '273px'; - // const activeBoardSettings = plugin.settings.data.boardConfigs[activeBoardIndex]; - - // Extra code to provide special data-types for theme support. - const tagColors = plugin.settings.data.tagColors; - const tagColorMap = new Map(tagColors.map((t) => [t.name, t])); - let tagData = tagColorMap.get(columnData?.coltag || ''); - if (!tagData) { - tagColorMap.forEach((tagColor, tagNameKey, mapValue) => { - const result = matchTagsWithWildcards(tagNameKey, columnData?.coltag || ''); - // console.log("Column.tsx : Matching tag result : ", { tagNameKey, columnTag: columnData?.coltag, result }); - // Return the first match found - if (result) tagData = tagColor; - }); - } - - // Determine whether an advanced filter is applied (used by header count UI) - const isAdvancedFilterApplied = !isRootFilterStateEmpty(columnData.filters); - - // If this column is requested to render header-only (used by swimlane top header), return just the header UI - if (headerOnly) { - return ( -
- {columnData.minimized ? ( -
openColumnMenu(evt)} aria-label={t("open-column-menu")}>{localTasks?.length ?? 0}
- ) : ( -
-
-
{columnData.name}
-
-
openColumnMenu(evt)} aria-label={t("open-column-menu")}> - {localTasks?.length ?? 0} -
-
- )} -
- ); - } - - async function handleMinimizeColumn() { - // const boardIndex = plugin.settings.data.boardConfigs.findIndex( - // (board: Board) => board.name === activeBoardData.name - // ); - const boardIndex = activeBoardData.index; - - if (boardIndex !== -1) { - // NOTE : This extra thing we need to do because, the columnData.index is stored starting with 1 and not 0. Hence, I we will need to subtract 1 from it. - // const columnIndex = plugin.settings.data.boardConfigs[boardIndex].columns.findIndex( - // (col: ColumnData) => col.id === columnData.id - // ); - const columnIndex = columnData.index - 1; - - if (columnIndex !== -1) { - // Set the minimized property to true - plugin.settings.data.boardConfigs[boardIndex].columns[columnIndex].minimized = !plugin.settings.data.boardConfigs[boardIndex].columns[columnIndex].minimized; - - // Save the settings - await plugin.saveSettings(); - - // Refresh the board view - eventEmitter.emit('REFRESH_BOARD'); - } - } - } - - /** - * Opens the column menu, which allows the user to sort and filter the tasks in the column, - * configure the column's sorting and filtering, and hide the column. - * - * @param {MouseEvent | React.MouseEvent} event - The event that triggered the menu - */ - function openColumnMenu(event: MouseEvent | React.MouseEvent) { - const columnMenu = new Menu(); - - columnMenu.addItem((item) => { - item.setTitle(t("sort-and-filter")); - item.setIsLabel(true); - }); - columnMenu.addItem((item) => { - item.setTitle(t("configure-column-sorting")); - item.setIcon("arrow-up-down"); - item.onClick(async () => { - // open sorting modal - const modal = new ConfigureColumnSortingModal( - plugin, - columnData, - (updatedColumnConfiguration: ColumnData) => { - // Update the column configuration in the board data - // const boardIndex = plugin.settings.data.boardConfigs.findIndex( - // (board: Board) => board.name === activeBoardData.name - // ); - const boardIndex = activeBoardData.index; - - if (boardIndex !== -1) { - // NOTE : This extra thing we need to do because, the columnData.index is stored starting with 1 and not 0. Hence, I we will need to subtract 1 from it. - // const columnIndex = plugin.settings.data.boardConfigs[boardIndex].columns.findIndex( - // (col: ColumnData) => col.id === columnData.id - // ); - const columnIndex = columnData.index - 1; - - if (columnIndex !== -1) { - // Update the column configuration - plugin.settings.data.boardConfigs[boardIndex].columns[columnIndex] = updatedColumnConfiguration; - - // Save the settings - plugin.saveSettings(); - - eventEmitter.emit('REFRESH_BOARD'); - } - } - }, - () => { - // onCancel callback - nothing to do - } - ); - modal.open(); - }); - }); - columnMenu.addItem((item) => { - item.setTitle(t("configure-column-filtering")); - item.setIcon("list-filter"); - item.onClick(async () => { - try { - // const boardIndex = plugin.settings.data.boardConfigs.findIndex( - // (board: Board) => board.name === activeBoardData.name - // ); - const boardIndex = activeBoardData.index; - // NOTE : This extra thing we need to do because, the columnData.index is stored starting with 1 and not 0. Hence, I we will need to subtract 1 from it. - // const columnIndex = plugin.settings.data.boardConfigs[boardIndex].columns.findIndex( - // (col: ColumnData) => col.id === columnData.id - // ); - const columnIndex = columnData.index - 1; - - if (Platform.isMobile || Platform.isMacOS) { - // If its a mobile platform, then we will open a modal instead of popover. - const filterModal = new ViewTaskFilterModal( - plugin, true, undefined, boardIndex, columnData.name, columnData.filters - ); - - // Set the close callback - mainly used for handling cancel actions - filterModal.filterCloseCallback = async (filterState) => { - if (filterState && boardIndex !== -1) { - if (columnIndex !== -1) { - // Update the column filters - plugin.settings.data.boardConfigs[boardIndex].columns[columnIndex].filters = filterState; - - // Save the settings - await plugin.saveSettings(); - - // Refresh the board view - eventEmitter.emit('REFRESH_BOARD'); - } - } - }; - - filterModal.open(); - } else { - // Get the position of the menu (approximate column position) - // Use CSS.escape to properly escape the selector value - const escapedTag = columnData.coltag ? CSS.escape(columnData.coltag) : ''; - const columnElement = document.querySelector(`[data-column-tag-name="${escapedTag}"]`) as HTMLElement; - const position = columnElement - ? { x: columnElement.getBoundingClientRect().left, y: columnElement.getBoundingClientRect().top + 40 } - : { x: 100, y: 100 }; // Fallback position - - // Create and show filter popover - // leafId is undefined for column filters (not tied to a specific leaf) - const popover = new ViewTaskFilterPopover( - plugin, - true, // forColumn is true - undefined, - boardIndex, - columnData.name, - columnData.filters - ); - - // Set up close callback to save filter state - popover.onClose = async (filterState?: RootFilterState) => { - if (filterState && boardIndex !== -1) { - if (columnIndex !== -1) { - // Update the column filters - plugin.settings.data.boardConfigs[boardIndex].columns[columnIndex].filters = filterState; - - // Save the settings - await plugin.saveSettings(); - - // Refresh the board view - eventEmitter.emit('REFRESH_BOARD'); - } - } - }; - - popover.showAtPosition(position); - } - } catch (error) { - bugReporterManagerInsatance.showNotice(1, "Error showing filter popover", String(error), "Column.tsx/column-menu/configure-conlum-filters"); - } - }); - }); - - columnMenu.addSeparator(); - - columnMenu.addItem((item) => { - item.setTitle(t("quick-actions")); - item.setIsLabel(true); - }); - columnMenu.addItem((item) => { - item.setTitle(t("hide-column")); - item.setIcon("eye-off"); - item.onClick(async () => { - // const boardIndex = plugin.settings.data.boardConfigs.findIndex( - // (board: Board) => board.name === activeBoardData.name - // ); - const boardIndex = activeBoardData.index; - - if (boardIndex !== -1) { - // NOTE : This extra thing we need to do because, the columnData.index is stored starting with 1 and not 0. Hence, I we will need to subtract 1 from it. - // const columnIndex = plugin.settings.data.boardConfigs[boardIndex].columns.findIndex( - // (col: ColumnData) => col.id === columnData.id - // ); - const columnIndex = columnData.index - 1; - - if (columnIndex !== -1) { - // Set the active property to false - plugin.settings.data.boardConfigs[boardIndex].columns[columnIndex].active = false; - - // Save the settings - await plugin.saveSettings(); - - // Refresh the board view - eventEmitter.emit('REFRESH_BOARD'); - } - } - }); - }); - - // Show minimize or maximize option based on current state - if (columnData.minimized) { - columnMenu.addItem((item) => { - item.setTitle(t("maximize-column")); - item.setIcon("panel-left-open"); - item.onClick(async () => { - await handleMinimizeColumn(); - }); - }); - } else { - columnMenu.addItem((item) => { - item.setTitle(t("minimize-column")); - item.setIcon("panel-left-close"); - item.onClick(async () => { - await handleMinimizeColumn(); - }); - }); - } - - // Use native event if available (React event has nativeEvent property) - columnMenu.showAtMouseEvent( - (event instanceof MouseEvent ? event : event.nativeEvent) - ); - } - - // ------------------------------------------------- - // ALL DRAG AND DROP RELATED FUNCTIONS - // ------------------------------------------------- - - /** - * Handles the drop event of a task in this column. - * Moves the task from its original position (dragIndex) to the new position (dropIndex). - * Updates the localTasks state and the columnData.tasksIdManualOrder if the column uses manualOrder. - * Clears the raf timer to prevent any pending raf calls. - * @param {React.DragEvent} e - The drag event. - * @param {number} dropIndex - The index at which to drop the task. - */ - const handleTaskDrop = async (e: React.DragEvent, dropIndex: number) => { - e.preventDefault(); - setIsDragOver(false); - setInsertIndex(null); - - const targetColumnContainer = tasksContainerRef.current; - if (!targetColumnContainer) { - return; - } - - // We are basically doing same thing from the handleDrop function below. - dragDropTasksManagerInsatance.handleDropEvent( - e.nativeEvent, - columnData, - targetColumnContainer, - swimlaneData - ); - - // Clear manager payload (drag finished) - dragDropTasksManagerInsatance.clearCurrentDragData(); - dragDropTasksManagerInsatance.clearDesiredDropIndex(); - - // const dragIndex = parseInt(e.dataTransfer.getData('text/plain')); - // if (isNaN(dragIndex) || dragIndex === dropIndex) return; - // const updated = [...localTasks]; - // const [moved] = updated.splice(dragIndex, 1); - // updated.splice(dropIndex, 0, moved); - // setLocalTasks(updated); - // // If this column uses manualOrder, update the columnData.tasksIdManualOrder to reflect new order - // const hasManualOrder = Array.isArray(columnData.sortCriteria) && columnData.sortCriteria.some((c) => c.criteria === 'manualOrder'); - // if (hasManualOrder) { - // columnData.tasksIdManualOrder = updated.map(t => t.id); - // } - - // clear any pending raf - if (rafRef.current) { - cancelAnimationFrame(rafRef.current); - rafRef.current = null; - } - }; - - const handleDrop = useCallback((e: React.DragEvent) => { - e.preventDefault(); - setIsDragOver(false); - - try { - // Get the data of the dragged task -- No need anymore, since its already stored in the dragdropmanager. - // const taskData = e.dataTransfer.getData('application/json'); - // if (taskData) { - // const { task, sourceColumnData } = JSON.parse(taskData); - - // // Ensure we have valid data - // if (!task || !sourceColumnData) return; - - // Get the target column container - const targetColumnContainer = (e.currentTarget) as HTMLDivElement; - - - // Try to locate the source container by stable column id first (works for all colTypes) -- No need to find this anymore, since I am not making use of sourceColumnContainer in dragdropmanager. - // let sourceColumnContainer: HTMLDivElement | null = null; - // if (sourceColumnData?.id) { - // try { - // const escapedId = CSS.escape(String(sourceColumnData.id)); - // sourceColumnContainer = document.querySelector(`.TaskBoardColumnsSection[data-column-id="${escapedId}"]`) as HTMLDivElement | null; - // } catch (err) { - // // fallback to tag-based lookup below - // } - // } - // if (!sourceColumnContainer) { - // // Fallback: find by tag name (legacy behavior) - // console.log("------------- I hope this fall-back mechanism is never running -------------"); - // const allColumnContainers = Array.from(document.querySelectorAll('.TaskBoardColumnsSection')) as HTMLDivElement[]; - // sourceColumnContainer = allColumnContainers.find(container => { - // const containerTag = container.getAttribute('data-column-tag-name'); - // return containerTag === sourceColumnData.coltag || sourceColumnData.coltag?.includes(containerTag || ''); - // }) || targetColumnContainer; - // } - - // we will allow cross-column drops now with target column having manualOrder sortCriteria. Disabling below code. - // const hasManualOrder = Array.isArray(columnData.sortCriteria) && columnData.sortCriteria.some((c) => c.criteria === 'manualOrder'); - // if (hasManualOrder && sourceColumnData.id !== columnData.id) { - // // Not allowed: ignore drop - // dragDropTasksManagerInsatance.clearCurrentDragData(); - // dragDropTasksManagerInsatance.clearDesiredDropIndex(); - // return; - // } - - // // Use the DragDropTasksManager to handle the drop - // try { - // const dragIdxStr = e.dataTransfer.getData('text/plain'); - // const dragIdx = dragIdxStr ? parseInt(dragIdxStr) : NaN; - // if (sourceColumnData.coltag === columnData.coltag && !isNaN(dragIdx) && insertIndexRef.current !== null) { - // // Reorder locally - // const updated = [...localTasks]; - // const [moved] = updated.splice(dragIdx, 1); - // updated.splice(insertIndexRef.current!, 0, moved); - // setLocalTasks(updated); - // setInsertIndex(null); - // insertIndexRef.current = null; - // // Update manual order if applicable - // const hasManualOrderLocal = Array.isArray(columnData.sortCriteria) && columnData.sortCriteria.some((c) => c.criteria === 'manualOrder'); - // if (hasManualOrderLocal) { - // columnData.tasksIdManualOrder = updated.map(t => t.id); - // } - // // Clear manager payload and skip default handling - // dragDropTasksManagerInsatance.clearCurrentDragData(); - // dragDropTasksManagerInsatance.clearDesiredDropIndex(); - // return; - // } - // } catch (err) { - // // ignore and continue to default handling - // } - - dragDropTasksManagerInsatance.handleDropEvent( - e.nativeEvent, - columnData, - targetColumnContainer, - swimlaneData - ); - - // Clear manager payload (drag finished) - dragDropTasksManagerInsatance.clearCurrentDragData(); - dragDropTasksManagerInsatance.clearDesiredDropIndex(); - // } - } catch (error) { - console.error('Error handling task drop:', error); - } - }, [columnData, plugin]); - - // This function will be only run when user will drag the taskItem on another taskItem. - // Compute insertion index based on mouse Y relative to task items inside the container. - const handleTaskItemDragOver = useCallback((e: React.DragEvent) => { - e.preventDefault(); - setIsDragOver(true); - try { - // Only compute insertion index for columns that use "manualOrder" as the sorting criteria. - const hasManualOrder = Array.isArray(columnData.sortCriteria) && columnData.sortCriteria.some((c) => c.criteria === 'manualOrder'); - if (!hasManualOrder) { - // Clear any visual placeholder and desired index - if (insertIndexRef.current !== null) { - scheduleSetInsertIndex(null); - } - dragDropTasksManagerInsatance.clearDesiredDropIndex(); - return; - } else { - // APPROACH 1 - COMPUTE INSERTION INDEX BASED ON MOUSE Y POSITION BY COMPARING WITH TASK ITEM BOUNDING RECTANGLES - // Else will proceed with finding the insertion index - // const container = e.currentTarget.parentElement as HTMLDivElement; - // const children = Array.from(container.querySelectorAll('.taskItemFadeIn')) as HTMLElement[]; - // let pos = children.length; // default to end - // const clientY = e.clientY; - // for (let i = 0; i < children.length; i++) { - // const child = children[i]; - // const rect = child.getBoundingClientRect(); - // const midpoint = rect.top + rect.height / 2; - // if (clientY < midpoint) { - // pos = i; - // break; - // } - // } - - // APPROACH 2 - DIRECTLY FETCH THE INDEX FROM THE DATA ATTRIBUTE OF THE HOVERED ELEMENT - let pos = 0 // Default to top of the column - const hoveredElement = e.currentTarget; - const draggedOverItemIndex = hoveredElement.getAttribute('data-taskitem-index'); - const draggedOverItemKey = hoveredElement.getAttribute('data-taskitem-id'); - const draggedItemKey = dragDropTasksManagerInsatance.getCurrentDragData()?.task.id; - // console.log('handleTaskItemDragOver... \ndataAttribute', draggedOverItemIndex, "\ndraggedItemIndex", draggedItemIndex); - if (draggedOverItemKey && draggedOverItemIndex && draggedOverItemKey !== draggedItemKey) { - const clientY = e.clientY; - const rect = hoveredElement.getBoundingClientRect(); - const midpoint = rect.top + rect.height / 2; - if (clientY < midpoint) { - pos = parseInt(draggedOverItemIndex, 10); - } else { - pos = parseInt(draggedOverItemIndex, 10) + 1; - } - - // Throttle updates via RAF - scheduleSetInsertIndex(pos); - // Store desired drop index in manager - dragDropTasksManagerInsatance.setDesiredDropIndex(pos); - } else { - // Clear any visual placeholder and desired index - if (insertIndexRef.current !== null) { - scheduleSetInsertIndex(null); - } - dragDropTasksManagerInsatance.clearDesiredDropIndex(); - } - - - // // Use the DragDropTasksManager to handle the drag over (this sets classes and dropEffect) - // dragDropTasksManagerInsatance.handleDragOver( - // e.nativeEvent, - // columnData, - // container - // ); - - const targetColumnContainer = tasksContainerRef.current as HTMLDivElement; - dragDropTasksManagerInsatance.handleCardDragOverEvent(e.nativeEvent as DragEvent, e.currentTarget as HTMLDivElement, targetColumnContainer, columnData); - } - } catch (error) { - console.error('Error computing insert index:', error); - } - }, [scheduleSetInsertIndex, columnData]); - - const handleDragOver = useCallback((e: React.DragEvent) => { - e.preventDefault(); - setIsDragOver(true); - try { - // // Try to read payload from the DataTransfer first - // let taskDataStr = ''; - // try { - // taskDataStr = e.dataTransfer.getData('application/json'); - // } catch (err) { - // // ignore - some environments restrict access - // } - - // let payload: any = null; - // if (taskDataStr) { - // try { payload = JSON.parse(taskDataStr); } catch { } - // } - - // // Fallback to manager-stored payload if dataTransfer is empty - // if (!payload) { - // payload = dragDropTasksManagerInsatance.getCurrentDragData(); - // } - - // if (!payload) return; - - // const { task, sourceColumnData } = payload; - // if (!task || !sourceColumnData) return; - - // Get the target column container - const targetColumnContainer = (e.currentTarget) as HTMLDivElement; - - // // Try id-based lookup first - // let sourceColumnContainer: HTMLDivElement | null = null; - // if (sourceColumnData?.id) { - // try { - // const escapedId = CSS.escape(String(sourceColumnData.id)); - // sourceColumnContainer = document.querySelector(`.TaskBoardColumnsSection[data-column-id="${escapedId}"]`) as HTMLDivElement | null; - // } catch (err) { - // // ignore and fall back to tag-based lookup - // } - // } - // if (!sourceColumnContainer) { - // const allColumnContainers = Array.from(document.querySelectorAll('.TaskBoardColumnsSection')) as HTMLDivElement[]; - // sourceColumnContainer = allColumnContainers.find(container => { - // const containerTag = container.getAttribute('data-column-tag-name'); - // return containerTag === sourceColumnData.coltag || sourceColumnData.coltag?.includes(containerTag || ''); - // }) || targetColumnContainer; - // } - - // Use the DragDropTasksManager to handle the drag over (this sets classes and dropEffect) - dragDropTasksManagerInsatance.handleColumnDragOverEvent( - e.nativeEvent, - columnData, - targetColumnContainer - ); - - // Below code is not required, since, I will call the dragDropTasksManagerInsatance.handleCardDragOverEvent from handleTaskItemDragOver. - // // If hovering over an actual card element, show card drop indicator - // try { - // const hovered = (e.target as HTMLElement).closest('.taskItem') as HTMLElement | null; - // if (hovered) { - // dragDropTasksManagerInsatance.handleCardDragOverEvent(e.nativeEvent as DragEvent, hovered); - // } - // } catch (err) { - // // ignore - // } - - // // Ensure cursor reflects allowed/not-allowed (best-effort fallback) - // const allowed = dragDropTasksManagerInsatance.isTaskDropAllowed(sourceColumnData, columnData); - // e.dataTransfer!.dropEffect = allowed ? 'move' : 'none'; - } catch (error) { - console.error('Error handling drag over:', error); - } - }, [columnData]); - - // Cleanup any pending RAF on unmount - useEffect(() => { - return () => { - if (rafRef.current) { - cancelAnimationFrame(rafRef.current); - rafRef.current = null; - } - }; - }, []); - - // Handle the dragleave event to remove the visual effect - const handleDragLeave = useCallback((e: React.DragEvent) => { - // Avoid flicker: if the drag event indicates the pointer is still within the container bounds, - // ignore this dragleave (this happens when moving between child elements). - try { - const container = e.currentTarget as HTMLElement; - const x = e.clientX; - const y = e.clientY; - if (typeof x === 'number' && typeof y === 'number') { - const rect = container.getBoundingClientRect(); - if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) { - // still inside container — ignore to prevent CSS flicker - return; - } - } - } catch (err) { - // ignore and continue cleanup - } - - setIsDragOver(false); - setInsertIndex(null); - dragDropTasksManagerInsatance.clearDesiredDropIndex(); - // Let manager clean up the dropindicator and column highlight - dragDropTasksManagerInsatance.handleDragLeaveEvent(e.currentTarget as HTMLDivElement); - }, []); - - // ------------------------------------------------- - // Render - // ------------------------------------------------- - - return ( -
- {columnData.minimized && !hideColumnHeader ? ( - // Minimized view - vertical bar with count and rotated text -
-
openColumnMenu(evt)} aria-label={t("open-column-menu")}> - {localTasks.length} -
-
{ - await handleMinimizeColumn(); - eventEmitter.emit('REFRESH_BOARD'); - }}>{columnData.name}
-
- ) : ( - // Normal view - <> - {!hideColumnHeader && ( -
-
-
{columnData.name}
-
-
openColumnMenu(evt)} aria-label={t("open-column-menu")}> - {localTasks?.length ?? 0} -
-
- )} -
{ handleDragOver(e); }} - onDragLeave={handleDragLeave} - onDrop={handleDrop} - onDragEnd={(e) => { setIsDragOver(false); setInsertIndex(null); dragDropTasksManagerInsatance.clearAllDragStyling(); }} - > - {columnData.minimized ? <> : ( - <> - {localTasks.length > 0 ? ( - (() => { - const elements: React.ReactNode[] = []; - for (let i = 0; i < localTasks.length; i++) { - // If insertIndex points to this position, render placeholder - if (insertIndex === i) { - elements.push( -
Drop here
- ); - } - const task = localTasks[i]; - elements.push( -
{ handleTaskItemDragOver(e); } - } - onDrop={e => handleTaskDrop(e, i)} - > - -
- ); - } - // If insertIndex points to end (after last item) - if (insertIndex === localTasks.length) { - elements.push( -
Drop here
- ); - } - return elements; - })() - ) : ( -

{t("no-tasks-available")}

- )} - - ) - } -
- - )} -
- ); - -}; - -// const MemoizedTaskItem = memo(TaskItem, (prevProps, nextProps) => { -// return ( -// prevProps.task.id === nextProps.task.id && // Immutable check -// prevProps.task.title === nextProps.task.title && -// prevProps.task.body === nextProps.task.body && -// prevProps.task.due === nextProps.task.due && -// prevProps.task.tags.join(",") === nextProps.task.tags.join(",") && -// prevProps.task.priority === nextProps.task.priority && -// prevProps.task.completed === nextProps.task.completed && -// prevProps.task.filePath === nextProps.task.filePath && -// prevProps.columnIndex === nextProps.columnIndex && -// prevProps.activeBoardSettings === nextProps.activeBoardSettings -// ); -// }); - -export default memo(Column); diff --git a/src/components/KanbanView/KanbanBoardView.tsx b/src/components/KanbanView/KanbanBoardView.tsx index ef08ea1d..92290445 100644 --- a/src/components/KanbanView/KanbanBoardView.tsx +++ b/src/components/KanbanView/KanbanBoardView.tsx @@ -3,9 +3,6 @@ import { Board } from "../../interfaces/BoardConfigs"; import React, { memo } from "react"; import { taskItem, taskJsonMerged } from "src/interfaces/TaskItem"; - -import { App } from "obsidian"; -import Column from "./Column"; import LazyColumn from "./LazyColumn"; import KanbanSwimlanesContainer from "./KanbanSwimlanesContainer"; import type TaskBoard from "main"; @@ -23,7 +20,8 @@ interface KanbanBoardProps { const KanbanBoard: React.FC = ({ plugin, board, allTasks, tasksPerColumn, loading, freshInstall }) => { // Check if lazy loading is enabled const lazyLoadingEnabled = plugin.settings.data.kanbanView?.lazyLoadingEnabled ?? false; - const ColumnComponent = lazyLoadingEnabled ? LazyColumn : Column; + // const ColumnComponent = lazyLoadingEnabled ? LazyColumn : Column; + const ColumnComponent = LazyColumn; return (
@@ -85,7 +83,7 @@ const MemoizedColumn = memo<{ activeBoardData: Board; columnData: any; tasksForThisColumn: taskItem[]; - Component: typeof Column | typeof LazyColumn; + Component: typeof LazyColumn; }>(({ Component, ...props }) => { return ; }, (prevProps, nextProps) => { diff --git a/src/components/KanbanView/KanbanSwimlanesContainer.tsx b/src/components/KanbanView/KanbanSwimlanesContainer.tsx index e521d2ce..4e02d275 100644 --- a/src/components/KanbanView/KanbanSwimlanesContainer.tsx +++ b/src/components/KanbanView/KanbanSwimlanesContainer.tsx @@ -3,7 +3,7 @@ import React, { useMemo, memo } from 'react'; import { Board, ColumnData } from 'src/interfaces/BoardConfigs'; import { taskItem, taskJsonMerged } from 'src/interfaces/TaskItem'; -import Column from './Column'; +// import Column from './Column'; import LazyColumn from './LazyColumn'; import type TaskBoard from 'main'; import { t } from 'src/utils/lang/helper'; @@ -32,7 +32,7 @@ const KanbanSwimlanesContainer: React.FC = ({ tasksPerColumn, lazyLoadingEnabled, }) => { - const ColumnComponent = lazyLoadingEnabled ? LazyColumn : Column; + const ColumnComponent = LazyColumn; // Extract and organize swimlanes using tasksPerColumn (already segregated per active column) const { @@ -453,7 +453,7 @@ const MemoizedSwimlanColumn = memo<{ activeBoardData: Board; columnData: ColumnData; tasksForThisColumn: taskItem[]; - Component: typeof Column | typeof LazyColumn; + Component: typeof LazyColumn; hideColumnHeader?: boolean; swimlaneData?: { property: string, value: string }; headerOnly?: boolean; From e99e3d7046b18d49c5cf8430c22ab0e90356d822 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Tue, 20 Jan 2026 10:59:53 +0530 Subject: [PATCH 005/236] loading and saving one board file working --- data.json | 1422 +++-------------- main.ts | 8 +- .../BoardFilters/FilterConfigModal.ts | 55 +- .../KanbanView/KanbanSwimlanesContainer.tsx | 8 +- src/components/MapView/MapView.tsx | 8 +- src/components/TaskBoardViewContent.tsx | 6 +- src/interfaces/BoardConfigs.ts | 2 +- src/managers/DragDropTasksManager.ts | 225 ++- src/managers/TaskBoardFileManager.ts | 74 +- src/settings/SettingConstructUI.ts | 30 +- src/utils/algorithms/ColumnSegregator.ts | 44 +- task-board-logs.log | 36 + temp.data.json | 1193 ++++++++++++++ 13 files changed, 1730 insertions(+), 1381 deletions(-) create mode 100644 temp.data.json diff --git a/data.json b/data.json index c9c0770c..521af9a8 100644 --- a/data.json +++ b/data.json @@ -1,1193 +1,279 @@ { "version": "1.8.7", "data": { - "boardConfigs": [ + "lang": "en", + "openOnStartup": false, + "scanFilters": { + "files": { + "polarity": 3, + "values": [] + }, + "folders": { + "polarity": 3, + "values": [] + }, + "frontMatter": { + "polarity": 3, + "values": [] + }, + "tags": { + "polarity": 3, + "values": [] + } + }, + "firstDayOfWeek": "Mon", + "showTaskWithoutMetadata": true, + "ignoreFileNameDates": false, + "taskPropertyFormat": "2", + "taskCompletionDateTimePattern": "YYYY-MM-DDTHH:mm", + "dailyNotesPluginComp": false, + "universalDateFormat": "YYYY-MM-DD", + "defaultStartTime": "", + "taskCompletionInLocalTime": true, + "taskCompletionShowUtcOffset": false, + "autoAddUniversalDate": true, + "autoAddCreatedDate": false, + "autoAddCompletedDate": false, + "autoAddCancelledDate": false, + "showModifiedFilesNotice": true, + "scanMode": "automatic", + "columnWidth": "300px", + "visiblePropertiesList": [ + "id", + "title", + "status", + "tags", + "priority", + "createdDate", + "startDate", + "scheduledDate", + "dueDate", + "completionDate", + "cancelledDate", + "reminder", + "filePath" + ], + "taskCardStyle": "emoji", + "showVerticalScroll": true, + "tagColors": [ + { + "name": "bug", + "color": "rgba(255, 0, 0, 0.55)", + "priority": 1 + }, + { + "name": "important", + "color": "rgba(246, 255, 0, 0.53)", + "priority": 2 + }, + { + "name": "wip", + "color": "rgba(0, 255, 0, 0.53)", + "priority": 2 + }, + { + "name": "review", + "color": "rgba(0, 0, 255, 0.49)", + "priority": 3 + } + ], + "editButtonAction": "popUp", + "doubleClickCardToEdit": "none", + "universalDate": "due", + "tagColorsType": "background", + "customStatuses": [ + { + "symbol": " ", + "name": "Todo", + "nextStatusSymbol": "x", + "availableAsCommand": false, + "type": "TODO" + }, + { + "symbol": "<", + "name": "Ready to start", + "nextStatusSymbol": "x", + "availableAsCommand": false, + "type": "TODO" + }, + { + "symbol": "?", + "name": "In Review", + "nextStatusSymbol": "x", + "availableAsCommand": false, + "type": "TODO" + }, + { + "symbol": "/", + "name": "In Progress", + "nextStatusSymbol": "x", + "availableAsCommand": true, + "type": "IN_PROGRESS" + }, + { + "symbol": "x", + "name": "Done", + "nextStatusSymbol": " ", + "availableAsCommand": true, + "type": "DONE" + }, + { + "symbol": "X", + "name": "Completed", + "nextStatusSymbol": " ", + "availableAsCommand": true, + "type": "DONE" + }, + { + "symbol": "-", + "name": "Cancelled", + "nextStatusSymbol": "x", + "availableAsCommand": true, + "type": "CANCELLED" + } + ], + "compatiblePlugins": { + "dailyNotesPlugin": false, + "dayPlannerPlugin": false, + "tasksPlugin": false, + "reminderPlugin": false, + "quickAddPlugin": false + }, + "preDefinedNote": "Meta/Task_Board/New_Tasks.md", + "taskNoteIdentifierTag": "taskNote", + "taskNoteDefaultLocation": "Meta/Task_Board/Task_Notes", + "quickAddPluginDefaultChoice": "", + "archivedTasksFilePath": "", + "archivedTBNotesFolderPath": "Meta/Task_Board/Archived_Task_Notes", + "frontmatterFormatting": [ { - "name": "Path filtered", "index": 0, - "columns": [ - { - "id": 3061361157, - "index": 1, - "colType": "pathFiltered", - "active": true, - "collapsed": false, - "name": "TaskNotes", - "filePaths": "TaskNotes/", - "sortCriteria": [ - { - "criteria": "manualOrder", - "order": "asc", - "priority": 1, - "uid": "ur7dguyt" - } - ], - "tasksIdManualOrder": [ - "367", - "372", - "398696308", - "373", - "370", - "3077781059", - "4184100420", - "3052793295", - "1506909345", - 274, - "4031700447", - "362" - ] - }, - { - "id": 1312742179, - "index": 2, - "colType": "pathFiltered", - "active": true, - "collapsed": false, - "name": "Indented Bug", - "filePaths": "Testing Indented Task Bug.md", - "filters": { - "rootCondition": "any", - "filterGroups": [] - } - }, - { - "id": 2025781698, - "index": 3, - "colType": "pathFiltered", - "active": true, - "collapsed": false, - "name": "QMD", - "filePaths": "A QMD file.qmd" - }, - { - "id": 1623496994, - "index": 4, - "colType": "completed", - "active": true, - "collapsed": false, - "name": "Completed", - "limit": 20 - } - ], - "hideEmptyColumns": false, - "boardFilter": { - "rootCondition": "any", - "filterGroups": [] - }, - "taskCount": { - "pending": 65, - "completed": 19 - } + "property": "ID", + "key": "id", + "taskItemKey": "id" }, { - "columns": [ - { - "id": 1799377278, - "index": 1, - "colType": "undated", - "active": true, - "collapsed": false, - "name": "Backlog", - "datedBasedColumn": { - "from": 0, - "to": 0, - "dateType": "scheduledDate" - }, - "filters": { - "rootCondition": "any", - "filterGroups": [] - } - }, - { - "id": 3492, - "colType": "dated", - "active": true, - "collapsed": false, - "name": "Over Due", - "index": 2, - "datedBasedColumn": { - "dateType": "scheduledDate", - "from": -300, - "to": -1 - }, - "sortCriteria": [ - { - "criteria": "scheduledDate", - "order": "asc", - "priority": 1 - }, - { - "criteria": "time", - "order": "asc", - "priority": 2 - } - ], - "filters": { - "rootCondition": "any", - "filterGroups": [] - } - }, - { - "id": 3494, - "colType": "dated", - "active": true, - "collapsed": false, - "name": "Today", - "index": 3, - "datedBasedColumn": { - "dateType": "scheduledDate", - "from": 0, - "to": 0 - }, - "sortCriteria": [ - { - "criteria": "time", - "order": "asc", - "priority": 1 - } - ], - "filters": { - "rootCondition": "any", - "filterGroups": [] - }, - "minimized": true - }, - { - "id": 3493, - "colType": "dated", - "active": true, - "collapsed": false, - "name": "Tomorrow", - "index": 4, - "datedBasedColumn": { - "dateType": "scheduledDate", - "from": 1, - "to": 1 - } - }, - { - "id": 3495, - "colType": "dated", - "active": true, - "collapsed": false, - "name": "Future", - "index": 5, - "datedBasedColumn": { - "dateType": "scheduledDate", - "from": 2, - "to": 300 - } - }, - { - "id": 3497, - "colType": "completed", - "active": true, - "collapsed": false, - "limit": 20, - "name": "Completed, updated", - "index": 6, - "filters": { - "rootCondition": "any", - "filterGroups": [] - } - } - ], - "filters": [ - "#Test", - "#working", - "#new" - ], - "filterPolarity": "0", - "filterScope": "Both", - "name": "Time based workflow", "index": 1, - "showColumnTags": true, - "showFilteredTags": false, - "hideEmptyColumns": false, - "boardFilter": { - "rootCondition": "any", - "filterGroups": [ - { - "id": "id-1763871967163-8a21jnuiq", - "groupCondition": "any", - "filters": [ - { - "id": "id-1763871967165-lct3l84v6", - "property": "tags", - "condition": "doesNotContain", - "value": "#task" - }, - { - "id": "id-1763871980225-nbzhu6dnv", - "property": "tags", - "condition": "doesNotContain", - "value": "#taskNote" - } - ] - } - ] - }, - "description": "Project to manage all the tasks related to plugin release.", - "taskCount": { - "pending": 4, - "completed": 1 - } + "property": "Title", + "key": "title", + "taskItemKey": "title" }, { - "columns": [ - { - "id": 3187486162, - "index": 1, - "colType": "otherTags", - "active": true, - "collapsed": false, - "name": "Other tags", - "minimized": false - }, - { - "colType": "untagged", - "active": true, - "collapsed": false, - "name": "Backlogs", - "index": 2, - "id": 226119, - "sortCriterias": [], - "filters": { - "rootCondition": "any", - "filterGroups": [] - }, - "sortCriteria": [ - { - "criteria": "id", - "order": "desc", - "priority": 1, - "uid": "k6wjlc63" - } - ], - "minimized": false - }, - { - "id": 2485661779, - "index": 3, - "colType": "namedTag", - "active": false, - "collapsed": false, - "name": "#Task", - "coltag": "#Task", - "sortCriteria": [ - { - "criteria": "content", - "order": "asc", - "priority": 1, - "uid": "bmlpsogj" - } - ], - "tasksIdManualOrder": [ - "365", - 274, - "362" - ], - "minimized": false, - "filters": { - "rootCondition": "any", - "filterGroups": [] - } - }, - { - "colType": "namedTag", - "active": true, - "collapsed": false, - "name": "Can be implemented", - "index": 4, - "coltag": "#pending", - "id": 47626, - "filters": { - "rootCondition": "any", - "filterGroups": [] - }, - "minimized": false, - "sortCriteria": [ - { - "criteria": "manualOrder", - "order": "asc", - "priority": 1, - "uid": "285zohor" - } - ], - "tasksIdManualOrder": [ - "224", - "276", - "389", - "378", - "355", - "258", - "375" - ] - }, - { - "id": 3130768414, - "index": 5, - "colType": "namedTag", - "active": true, - "collapsed": false, - "name": "In Progress", - "coltag": "wip" - }, - { - "colType": "namedTag", - "active": true, - "collapsed": false, - "name": "Ready to publish", - "index": 6, - "coltag": "done", - "id": 328227, - "minimized": false - }, - { - "id": 1936828579, - "index": 7, - "colType": "namedTag", - "active": false, - "collapsed": false, - "name": "*/seeding/*", - "coltag": "*/seeding/*", - "minimized": false - }, - { - "id": 3193261849, - "index": 8, - "colType": "completed", - "active": true, - "collapsed": false, - "name": "Completed", - "limit": 20, - "filters": { - "rootCondition": "any", - "filterGroups": [] - }, - "minimized": false - } - ], - "filters": [ - "#Test", - "#working", - "*/seeding/*" - ], - "filterPolarity": "0", - "filterScope": "Both", - "name": "Tag based board", "index": 2, - "showColumnTags": true, - "showFilteredTags": true, - "hideEmptyColumns": false, - "boardFilter": { - "rootCondition": "all", - "filterGroups": [] - }, - "filterConfig": { - "enableSavedFilters": true, - "savedConfigs": [ - { - "id": "filter-config-1760103432145-fevybeh8j", - "name": "Destinatio", - "description": "Filtering all tasks from \"Destinatio\" folder", - "filterState": { - "rootCondition": "any", - "filterGroups": [ - { - "id": "id-1760029960520-z2a1g0ka4", - "groupCondition": "all", - "filters": [ - { - "id": "id-1760029960521-t33fwt542", - "property": "filePath", - "condition": "startsWith", - "value": "Destinatio" - } - ] - } - ] - }, - "createdAt": "2025-10-10T13:37:12.145Z", - "updatedAt": "2025-10-10T13:37:12.145Z" - }, - { - "id": "filter-config-1764176875594-8gv5ee69p", - "name": "This", - "description": "If \"This\" is present in the content.", - "filterState": { - "rootCondition": "any", - "filterGroups": [ - { - "id": "id-1760365709344-buyyxwhhh", - "groupCondition": "all", - "filters": [ - { - "id": "id-1760365709345-8uyz54hm3", - "property": "content", - "condition": "contains", - "value": "This" - } - ] - } - ] - }, - "createdAt": "2025-11-26T17:07:55.594Z", - "updatedAt": "2025-11-26T17:07:55.594Z" - }, - { - "id": "filter-config-1764176933714-tm6ozdue7", - "name": "This and No", - "description": "Content contains this and no", - "filterState": { - "rootCondition": "any", - "filterGroups": [ - { - "id": "id-1760029960520-z2a1g0ka4", - "groupCondition": "all", - "filters": [ - { - "id": "id-1760029960521-t33fwt542", - "property": "content", - "condition": "contains", - "value": "This" - } - ] - }, - { - "id": "id-1764176913802-bwvk75kr4", - "groupCondition": "all", - "filters": [ - { - "id": "id-1764176913803-1ugczxozs", - "property": "content", - "condition": "contains", - "value": "No" - } - ] - } - ] - }, - "createdAt": "2025-11-26T17:08:53.714Z", - "updatedAt": "2025-11-26T17:08:53.714Z" - }, - { - "id": "filter-config-1764177013691-5uj7krjmm", - "name": "This and No, both", - "description": "Both", - "filterState": { - "rootCondition": "all", - "filterGroups": [ - { - "id": "id-1760029960520-z2a1g0ka4", - "groupCondition": "all", - "filters": [ - { - "id": "id-1760029960521-t33fwt542", - "property": "content", - "condition": "contains", - "value": "This" - } - ] - }, - { - "id": "id-1764176913802-bwvk75kr4", - "groupCondition": "all", - "filters": [ - { - "id": "id-1764176913803-1ugczxozs", - "property": "content", - "condition": "contains", - "value": "No" - } - ] - } - ] - }, - "createdAt": "2025-11-26T17:10:13.691Z", - "updatedAt": "2025-11-26T17:10:13.691Z" - } - ] - }, - "taskCount": { - "pending": 63, - "completed": 14 - }, - "swimlanes": { - "enabled": true, - "hideEmptySwimlanes": false, - "property": "tags", - "maxHeight": "500px", - "sortCriteria": "custom", - "customSortOrder": [ - { - "value": "first", - "index": 1 - }, - { - "value": "second", - "index": 2 - }, - { - "value": "third", - "index": 3 - } - ], - "groupAllRest": true, - "verticalHeaderUI": true, - "minimized": [] - } + "property": "Status", + "key": "status", + "taskItemKey": "status" }, { - "columns": [ - { - "colType": "untagged", - "active": true, - "collapsed": false, - "name": "Backlogs", - "index": 1, - "id": 226119, - "sortCriterias": [], - "filters": { - "rootCondition": "any", - "filterGroups": [] - }, - "sortCriteria": [ - { - "criteria": "content", - "order": "desc", - "priority": 1, - "uid": "k6wjlc63" - } - ], - "minimized": false - }, - { - "id": 2485661779, - "index": 2, - "colType": "namedTag", - "active": false, - "collapsed": false, - "name": "#Task", - "coltag": "#Task", - "sortCriteria": [ - { - "criteria": "content", - "order": "asc", - "priority": 1, - "uid": "bmlpsogj" - } - ], - "tasksIdManualOrder": [ - "365", - 274, - "362" - ], - "minimized": false, - "filters": { - "rootCondition": "any", - "filterGroups": [] - } - }, - { - "id": 1936828579, - "index": 3, - "colType": "namedTag", - "active": false, - "collapsed": false, - "name": "*/seeding/*", - "coltag": "*/seeding/*" - }, - { - "colType": "namedTag", - "active": true, - "collapsed": false, - "name": "Can be implemented", - "index": 4, - "coltag": "#pending", - "id": 47626, - "filters": { - "rootCondition": "any", - "filterGroups": [] - }, - "minimized": false, - "sortCriteria": [ - { - "criteria": "manualOrder", - "order": "asc", - "priority": 1, - "uid": "pn3wvwal" - } - ], - "tasksIdManualOrder": [ - "375", - "389", - "224", - "355", - "276", - "258", - "378" - ] - }, - { - "colType": "namedTag", - "active": true, - "collapsed": false, - "name": "In Progress", - "index": 5, - "coltag": "working", - "id": 908753, - "filters": { - "rootCondition": "any", - "filterGroups": [] - }, - "minimized": false, - "workLimit": 5 - }, - { - "colType": "namedTag", - "active": true, - "collapsed": false, - "name": "In Review", - "index": 6, - "coltag": "Test", - "id": 396902, - "minimized": false, - "workLimit": 3 - }, - { - "colType": "namedTag", - "active": true, - "collapsed": false, - "name": "Ready to publish", - "index": 7, - "coltag": "done", - "id": 328227, - "minimized": false - }, - { - "id": 3193261849, - "index": 8, - "colType": "completed", - "active": true, - "collapsed": false, - "name": "Completed", - "limit": 20, - "filters": { - "rootCondition": "any", - "filterGroups": [] - }, - "minimized": false - } - ], - "filters": [ - "#Test", - "#working", - "*/seeding/*" - ], - "filterPolarity": "0", - "filterScope": "Both", - "name": "Tag based board (copy)", "index": 3, - "showColumnTags": true, - "showFilteredTags": true, - "hideEmptyColumns": false, - "boardFilter": { - "rootCondition": "any", - "filterGroups": [] - }, - "taskCount": { - "pending": 60, - "completed": 15 - }, - "swimlanes": { - "enabled": false, - "showEmptySwimlanes": true, - "property": "tags", - "maxHeight": "500px", - "sortCriteria": "custom", - "customSortOrder": [ - { - "value": "first", - "index": 1 - }, - { - "value": "second", - "index": 2 - }, - { - "value": "third", - "index": 3 - } - ], - "groupAllRest": true, - "verticalHeaderUI": false - } + "property": "Priority", + "key": "priority", + "taskItemKey": "priority" }, { - "name": "Only column filters", "index": 4, - "columns": [ - { - "id": 2050194674, - "index": 1, - "colType": "allPending", - "active": true, - "collapsed": false, - "name": "Backlogs", - "filters": { - "rootCondition": "any", - "filterGroups": [ - { - "id": "id-1768140055334-efs743ce1", - "groupCondition": "all", - "filters": [ - { - "id": "id-1768140055335-r98019pcb", - "property": "tags", - "condition": "isEmpty" - } - ] - } - ] - } - }, - { - "id": 850996788, - "index": 2, - "colType": "allPending", - "active": true, - "collapsed": false, - "name": "Important", - "filters": { - "rootCondition": "any", - "filterGroups": [ - { - "id": "id-1768140070366-ymlrpxfd5", - "groupCondition": "all", - "filters": [ - { - "id": "id-1768140070367-26fzfh3fi", - "property": "tags", - "condition": "contains", - "value": "#important" - } - ] - } - ] - } - }, - { - "id": 3370807172, - "index": 3, - "colType": "allPending", - "active": true, - "collapsed": false, - "name": "WIP", - "filters": { - "rootCondition": "any", - "filterGroups": [ - { - "id": "id-1768140095718-9u02uoxai", - "groupCondition": "all", - "filters": [ - { - "id": "id-1768140095719-sfcnddjpj", - "property": "tags", - "condition": "contains", - "value": "#wip" - } - ] - } - ] - } - }, - { - "id": 1957870296, - "index": 4, - "colType": "allPending", - "active": true, - "collapsed": false, - "name": "In Review", - "filters": { - "rootCondition": "any", - "filterGroups": [ - { - "id": "id-1768140118917-x50nyv0rm", - "groupCondition": "any", - "filters": [ - { - "id": "id-1768140118918-q6snn1uk7", - "property": "tags", - "condition": "contains", - "value": "#Test" - }, - { - "id": "id-1768140126093-wn5bwcpwz", - "property": "tags", - "condition": "contains", - "value": "#working" - } - ] - } - ] - }, - "workLimit": 3 - }, - { - "id": 4265650565, - "index": 5, - "colType": "completed", - "active": true, - "collapsed": false, - "name": "Completed", - "limit": 20 - } - ], - "hideEmptyColumns": false, - "showColumnTags": true, - "showFilteredTags": true, - "boardFilter": { - "rootCondition": "any", - "filterGroups": [] - }, - "swimlanes": { - "enabled": false, - "hideEmptySwimlanes": false, - "property": "tags", - "sortCriteria": "asc", - "minimized": [], - "maxHeight": "300px", - "verticalHeaderUI": false - }, - "taskCount": { - "pending": 63, - "completed": 14 - } - } - ], - "globalSettings": { - "lang": "zh-TW", - "scanFilters": { - "files": { - "polarity": 3, - "values": [ - "Testing Indented Task Bug.md", - "/\\b2025-\\d{2}-\\d{2}\\b/" - ] - }, - "folders": { - "polarity": 3, - "values": [ - "Task Board/1.7.0", - "/Notes/", - "Bible" - ] - }, - "tags": { - "polarity": 3, - "values": [ - "#CS", - "#placeholder/author", - "*/seeding/*" - ] - }, - "frontMatter": { - "polarity": 3, - "values": [ - "[\"created\": 2025-02-27]", - "[\"tags\": #TEST]", - "[\"background\": yellow]" - ] - } + "property": "Tags", + "key": "tags", + "taskItemKey": "tags" }, - "firstDayOfWeek": "2", - "ignoreFileNameDates": false, - "taskCompletionFormat": "2", - "taskCompletionDateTimePattern": "YYYY-MM-DD/HH:mm", - "dailyNotesPluginComp": true, - "universalDateFormat": "YYYY-MM-DD", - "taskCompletionInLocalTime": true, - "taskCompletionShowUtcOffset": false, - "autoAddDue": true, - "scanVaultAtStartup": false, - "dayPlannerPlugin": false, - "realTimeScanner": true, - "columnWidth": "300px", - "showHeader": true, - "showFooter": true, - "showVerticalScroll": false, - "tagColors": [ - { - "name": "Bug", - "color": "rgba(131, 10, 18, 0.84)", - "priority": 1 - }, - { - "name": "feat", - "color": "rgba(115, 15, 151, 0.9490196078431372)", - "priority": 3 - }, - { - "name": "Test", - "color": "rgba(142, 157, 24, 1)", - "priority": 4 - }, - { - "name": "working", - "color": "rgba(22, 85, 17, 1)", - "priority": 5 - }, - { - "name": "New", - "color": "rgba(16, 50, 117, 1)", - "priority": 6 - }, - { - "name": "pending", - "color": "rgba(64, 20, 144, 1)", - "priority": 7 - }, - { - "name": "done", - "color": "rgba(14, 117, 84, 1)", - "priority": 7 - }, - { - "name": "*/seeding/*", - "color": "rgba(15, 121, 110, 1)", - "priority": 8 - }, - { - "name": "first", - "color": "rgba(13, 103, 37, 1)", - "priority": 9 - } - ], - "editButtonAction": "popUp", - "openOnStartup": false, - "customStatuses": [ - { - "symbol": " ", - "name": "Incomplete", - "nextStatusSymbol": "x", - "availableAsCommand": false, - "type": "TODO" - }, - { - "symbol": "X", - "name": "Complete", - "nextStatusSymbol": " ", - "availableAsCommand": false, - "type": "DONE" - }, - { - "symbol": "/", - "name": "In Progress", - "nextStatusSymbol": "x", - "availableAsCommand": true, - "type": "IN_PROGRESS" - }, - { - "symbol": "-", - "name": "Cancelled", - "nextStatusSymbol": " ", - "availableAsCommand": true, - "type": "CANCELLED" - }, - { - "symbol": ">", - "name": "Deferred", - "nextStatusSymbol": "x", - "availableAsCommand": false, - "type": "TODO" - }, - { - "symbol": "!", - "name": "Important", - "nextStatusSymbol": "x", - "availableAsCommand": false, - "type": "TODO" - }, - { - "symbol": "x", - "name": "Finished", - "nextStatusSymbol": " ", - "availableAsCommand": false, - "type": "DONE" - } - ], - "showTaskWithoutMetadata": false, - "tagColorsType": "text", - "compatiblePlugins": { - "dailyNotesPlugin": false, - "tasksPlugin": false, - "reminderPlugin": false, - "dayPlannerPlugin": true, - "quickAddPlugin": false + { + "index": 5, + "property": "Time", + "key": "time", + "taskItemKey": "time" + }, + { + "index": 6, + "property": "Reminder", + "key": "reminder", + "taskItemKey": "reminder" + }, + { + "index": 7, + "property": "Created date", + "key": "created-date", + "taskItemKey": "createdDate" + }, + { + "index": 8, + "property": "Start date", + "key": "start-date", + "taskItemKey": "startDate" }, - "preDefinedNote": "Task_Board_Temp_Tasks.md", - "quickAddPluginDefaultChoice": "Task Board Temp Tasks", - "autoAddCreatedDate": true, - "autoAddUniversalDate": true, - "universalDate": "startDate", - "archivedTasksFilePath": "", - "showFileNameInCard": true, - "showFrontmatterTagsOnCards": true, - "tasksCacheFilePath": ".obsidian/plugins/task-board/tasks.json", - "notificationService": "obsiApp", - "frontmatterPropertyForReminder": "remind at", - "actions": [ - { - "enabled": true, - "trigger": "Complete", - "type": "move", - "targetColumn": "6" - } - ], - "cardSectionsVisibility": "hideBoth", - "hiddenTaskProperties": [], - "taskPropertyFormat": "3", - "taskNoteDefaultLocation": "", - "autoAddUniqueID": true, - "uniqueIdCounter": 586, - "experimentalFeatures": true, - "searchQuery": "", - "lastViewHistory": { - "viewedType": "kanban", - "boardIndex": 4, - "taskId": "", - "settingTab": 1 + { + "index": 9, + "property": "Scheduled date", + "key": "scheduled-date", + "taskItemKey": "scheduledDate" }, - "taskNoteIdentifierTag": "task", - "doubleClickCardToEdit": "noteInTab", - "boundTaskCompletionToChildTasks": true, - "defaultStartTime": "23:59", - "archivedTBNotesFolderPath": "", - "frontmatterFormatting": [ - { - "index": 0, - "property": "ID", - "key": "id", - "taskItemKey": "id" - }, - { - "index": 1, - "property": "Title", - "key": "title", - "taskItemKey": "title" - }, - { - "index": 2, - "property": "Status", - "key": "status", - "taskItemKey": "status" - }, - { - "index": 3, - "property": "Priority", - "key": "priority", - "taskItemKey": "priority" - }, - { - "index": 4, - "property": "Tags", - "key": "tags", - "taskItemKey": "tags" - }, - { - "index": 5, - "property": "Time", - "key": "time", - "taskItemKey": "time" - }, - { - "index": 6, - "property": "Reminder", - "key": "reminder", - "taskItemKey": "reminder" - }, - { - "index": 7, - "property": "Created date", - "key": "created-date", - "taskItemKey": "createdDate" - }, - { - "index": 8, - "property": "Start date", - "key": "start-date", - "taskItemKey": "startDate" - }, - { - "index": 9, - "property": "Scheduled date", - "key": "scheduled", - "taskItemKey": "scheduledDate" - }, - { - "index": 10, - "property": "Due date", - "key": "due", - "taskItemKey": "due" - }, - { - "index": 11, - "property": "Depends on", - "key": "depends-on", - "taskItemKey": "dependsOn" - }, - { - "index": 12, - "property": "Cancelled date", - "key": "cancelled-date", - "taskItemKey": "cancelledDate" - }, - { - "index": 13, - "key": "completed-date", - "taskItemKey": "completionDate" - } - ], - "mapView": { - "background": "transparent", - "mapOrientation": "hor", - "optimizedRender": false, - "arrowDirection": "c2p", - "animatedEdges": false, - "scrollAction": "pan", - "showMinimap": true, - "renderVisibleNodes": true, - "edgeType": "default" + { + "index": 10, + "property": "Due date", + "key": "due-date", + "taskItemKey": "due" }, - "kanbanView": { - "lazyLoadingEnabled": true, - "initialTaskCount": 20, - "loadMoreCount": 10, - "scrollThresholdPercent": 80 + { + "index": 11, + "property": "Depends on", + "key": "depends-on", + "taskItemKey": "dependsOn" }, - "safeGuardFeature": true, - "visiblePropertiesList": [ - "id", - "priority", - "tags", - "time", - "reminder", - "createdDate", - "startDate", - "scheduledDate", - "dueDate", - "completionDate", - "cancelledDate", - "dependsOn", - "filePath", - "status", - "checkbox" - ], - "taskCardStyle": "emoji", - "autoAddCompletedDate": true, - "autoAddCancelledDate": true, - "scanMode": "manual" + { + "index": 12, + "property": "Completed date", + "key": "cancelled-date", + "taskItemKey": "cancelledDate" + }, + { + "index": 13, + "key": "completed-date", + "taskItemKey": "completionDate" + } + ], + "showFrontmatterTagsOnCards": false, + "tasksCacheFilePath": "", + "notificationService": "none", + "actions": [ + { + "enabled": true, + "trigger": "Complete", + "type": "move", + "targetColumn": "Completed" + } + ], + "hiddenTaskProperties": [], + "autoAddUniqueID": false, + "uniqueIdCounter": 586, + "experimentalFeatures": false, + "safeGuardFeature": true, + "boardFilesLocation": [ + "TaskBoard/Boards/Time Based Workflow.taskboard", + "TaskBoard/Boards/Tag Based Workflow.taskboard", + "TaskBoard/Boards/Status Based Workflow.taskboard" + ], + "lastViewHistory": { + "viewedType": "kanban", + "boardIndex": 0, + "settingTab": 0, + "taskId": "" + }, + "boundTaskCompletionToChildTasks": false, + "kanbanView": { + "lazyLoadingEnabled": true, + "initialTaskCount": 20, + "loadMoreCount": 10, + "scrollThresholdPercent": 80 + }, + "mapView": { + "background": "none", + "mapOrientation": "hor", + "optimizedRender": false, + "arrowDirection": "c2p", + "animatedEdges": true, + "scrollAction": "zoom", + "showMinimap": true, + "renderVisibleNodes": false, + "edgeType": "default" } } } \ No newline at end of file diff --git a/main.ts b/main.ts index 850df217..82028984 100644 --- a/main.ts +++ b/main.ts @@ -105,9 +105,7 @@ export default class TaskBoard extends Plugin { this.plugin, this.vaultScanner, ); - this.taskBoardFileManager = new TaskBoardFileManager( - this.plugin, - ); + this.taskBoardFileManager = new TaskBoardFileManager(this.plugin); this.editorModified = false; // this.currentModifiedFile = null; // this.fileUpdatedUsingModal = ""; @@ -1279,7 +1277,9 @@ export default class TaskBoard extends Plugin { ); const deletedFilesList = [...deletedFiles]; - const changed_files = [...modifiedCreatedRenamedFiles]; + const changed_files = modifiedCreatedRenamedFiles.filter((file) => + fileTypeAllowedForScanning(this.plugin.settings.data, file), + ); console.log( "Task Board : Fetching complete.\nModified files :", changed_files, diff --git a/src/components/BoardFilters/FilterConfigModal.ts b/src/components/BoardFilters/FilterConfigModal.ts index add49268..c3dccc47 100644 --- a/src/components/BoardFilters/FilterConfigModal.ts +++ b/src/components/BoardFilters/FilterConfigModal.ts @@ -22,7 +22,7 @@ export class FilterConfigModal extends Modal { activeBoardIndex: number, currentFilterState?: RootFilterState, onSave?: (config: SavedFilterConfig) => void, - onLoad?: (config: SavedFilterConfig) => void + onLoad?: (config: SavedFilterConfig) => void, ) { super(app); this.plugin = plugin; @@ -90,20 +90,20 @@ export class FilterConfigModal extends Modal { }); } - private renderLoadMode() { + private async renderLoadMode() { const { contentEl } = this; contentEl.createEl("h2", { text: t("load-filter-configuration") }); const board = - this.plugin.settings.data.boardConfigs[this.activeBoardIndex]; - if (!board.filterConfig) { + await this.plugin.taskBoardFileManager.getCurrentBoardData(); + if (board && !board?.filterConfig) { board.filterConfig = { enableSavedFilters: true, savedConfigs: [], }; } - const savedConfigs = board.filterConfig.savedConfigs; + const savedConfigs = board!.filterConfig!.savedConfigs; if (savedConfigs.length === 0) { contentEl.createEl("p", { @@ -124,7 +124,7 @@ export class FilterConfigModal extends Modal { .addDropdown((dropdown: DropdownComponent) => { dropdown.addOption( "", - t("select-a-saved-filter-configuration") + t("select-a-saved-filter-configuration"), ); savedConfigs.forEach((config) => { @@ -172,7 +172,7 @@ export class FilterConfigModal extends Modal { (this as any).detailsContainer = detailsContainer; } - private updateConfigDetails(configId: string) { + private async updateConfigDetails(configId: string) { const detailsContainer = (this as any).detailsContainer; if (!detailsContainer) return; @@ -181,11 +181,11 @@ export class FilterConfigModal extends Modal { if (!configId) return; const board = - this.plugin.settings.data.boardConfigs[this.activeBoardIndex]; - if (!board.filterConfig) return; + await this.plugin.taskBoardFileManager.getCurrentBoardData(); + if (board && !board.filterConfig) return; - const config = board.filterConfig.savedConfigs.find( - (c: SavedFilterConfig) => c.id === configId + const config = board!.filterConfig!.savedConfigs.find( + (c: SavedFilterConfig) => c.id === configId, ); if (!config) return; @@ -198,14 +198,14 @@ export class FilterConfigModal extends Modal { detailsContainer.createEl("p", { text: `${t("created")}: ${new Date( - config.createdAt + config.createdAt, ).toLocaleString()}`, cls: "filter-config-meta", }); detailsContainer.createEl("p", { text: `${t("updated")}: ${new Date( - config.updatedAt + config.updatedAt, ).toLocaleString()}`, cls: "filter-config-meta", }); @@ -219,7 +219,7 @@ export class FilterConfigModal extends Modal { const groupCount = config.filterState.filterGroups.length; const totalFilters = config.filterState.filterGroups.reduce( (sum: number, group: FilterGroup) => sum + group.filters.length, - 0 + 0, ); filterSummary.createEl("p", { @@ -258,14 +258,14 @@ export class FilterConfigModal extends Modal { try { const board = - this.plugin.settings.data.boardConfigs[this.activeBoardIndex]; - if (!board.filterConfig) { + await this.plugin.taskBoardFileManager.getCurrentBoardData(); + if (board && !board.filterConfig) { board.filterConfig = { enableSavedFilters: true, savedConfigs: [], }; } - board.filterConfig.savedConfigs.push(config); + board!.filterConfig!.savedConfigs.push(config); await this.plugin.saveSettings(); if (this.onSave) { @@ -286,11 +286,11 @@ export class FilterConfigModal extends Modal { } const board = - this.plugin.settings.data.boardConfigs[this.activeBoardIndex]; - if (!board.filterConfig) return; + await this.plugin.taskBoardFileManager.getCurrentBoardData(); + if (!board || !board.filterConfig) return; const config = board.filterConfig.savedConfigs.find( - (c: SavedFilterConfig) => c.id === configId + (c: SavedFilterConfig) => c.id === configId, ); if (!config) { @@ -316,12 +316,11 @@ export class FilterConfigModal extends Modal { return; } - const board = - this.plugin.settings.data.boardConfigs[this.activeBoardIndex]; - if (!board.filterConfig) return; + const board = await this.plugin.taskBoardFileManager.getCurrentBoardData(); + if (!board || !board.filterConfig) return; const config = board.filterConfig.savedConfigs.find( - (c: SavedFilterConfig) => c.id === configId + (c: SavedFilterConfig) => c.id === configId, ); if (!config) { @@ -366,12 +365,12 @@ export class FilterConfigModal extends Modal { try { const board = - this.plugin.settings.data.boardConfigs[this.activeBoardIndex]; - if (!board.filterConfig) return; + await this.plugin.taskBoardFileManager.getCurrentBoardData(); + if (!board || !board.filterConfig) return; board.filterConfig.savedConfigs = board.filterConfig.savedConfigs.filter( - (c: SavedFilterConfig) => c.id !== configId + (c: SavedFilterConfig) => c.id !== configId, ); await this.plugin.saveSettings(); @@ -389,7 +388,7 @@ export class FilterConfigModal extends Modal { this.activeBoardIndex, undefined, this.onSave, - this.onLoad + this.onLoad, ); newModal.open(); } catch (error) { diff --git a/src/components/KanbanView/KanbanSwimlanesContainer.tsx b/src/components/KanbanView/KanbanSwimlanesContainer.tsx index 4e02d275..1f7491d9 100644 --- a/src/components/KanbanView/KanbanSwimlanesContainer.tsx +++ b/src/components/KanbanView/KanbanSwimlanesContainer.tsx @@ -232,12 +232,14 @@ const KanbanSwimlanesContainer: React.FC = ({ if (!swimlaneName) return; const boardIndex = board.index; // plugin.settings.data.boardConfigs.findIndex((b) => b.index === board.index); if (boardIndex === -1) return; - const swimCfg = plugin.settings.data.boardConfigs[boardIndex].swimlanes || { minimized: [] }; + const swimCfg = board.swimlanes || { minimized: [] }; const arr = Array.isArray(swimCfg.minimized) ? [...swimCfg.minimized] : []; const idx = arr.indexOf(swimlaneName); if (idx === -1) arr.push(swimlaneName); else arr.splice(idx, 1); - plugin.settings.data.boardConfigs[boardIndex].swimlanes.minimized = arr; - await plugin.saveSettings(); + board.swimlanes.minimized = arr; + + + await plugin.taskBoardFileManager.saveBoard(board, boardIndex); eventEmitter.emit('REFRESH_BOARD'); } catch (err) { console.error('Error toggling swimlane minimize:', err); diff --git a/src/components/MapView/MapView.tsx b/src/components/MapView/MapView.tsx index 0512c585..ead2962c 100644 --- a/src/components/MapView/MapView.tsx +++ b/src/components/MapView/MapView.tsx @@ -34,10 +34,11 @@ import { TasksImporterPanel } from './TasksImporterPanel'; import { isTaskNotePresentInTags, updateFrontmatterInMarkdownFile } from 'src/utils/taskNote/TaskNoteUtils'; import { isTaskCompleted } from 'src/utils/CheckBoxUtils'; import { bugReporterManagerInsatance } from 'src/managers/BugReporter'; +import { Board } from 'src/interfaces/BoardConfigs'; type MapViewProps = { plugin: TaskBoard; - activeBoardIndex: number; + activeBoardData: Board; allTasksArranged: taskItem[][]; // loading: boolean; // freshInstall: boolean; @@ -67,7 +68,7 @@ const nodeTypes = { const MapView: React.FC = ({ - plugin, activeBoardIndex, allTasksArranged, focusOnTaskId + plugin, activeBoardData, allTasksArranged, focusOnTaskId }) => { plugin.settings.data.lastViewHistory.taskId = ""; // Clear the taskId after focusing once const mapViewSettings = plugin.settings.data.mapView; @@ -86,7 +87,8 @@ const MapView: React.FC = ({ } })(); const tagColors = plugin.settings.data.tagColors; - const activeBoardSettings = plugin.settings.data.boardConfigs[activeBoardIndex]; + const activeBoardSettings = activeBoardData; + const activeBoardIndex = activeBoardSettings.index; // Loading state for localStorage data const [storageLoaded, setStorageLoaded] = useState(false); diff --git a/src/components/TaskBoardViewContent.tsx b/src/components/TaskBoardViewContent.tsx index e52290e8..ce142587 100644 --- a/src/components/TaskBoardViewContent.tsx +++ b/src/components/TaskBoardViewContent.tsx @@ -136,13 +136,13 @@ const TaskBoardViewContent: React.FC<{ plugin: TaskBoard }> = ({ plugin }) => { return currentBoard.columns .filter((column) => column.active) .map((column: ColumnData) => - columnSegregator(plugin.settings, activeBoardIndex, column, searchQueryFilteredTasks) + columnSegregator(plugin.settings, currentBoard, column, searchQueryFilteredTasks) ); } else { return currentBoard.columns .filter((column) => column.active) .map((column: ColumnData) => - columnSegregator(plugin.settings, activeBoardIndex, column, filteredAllTasks, (updatedBoardData: Board) => { + columnSegregator(plugin.settings, currentBoard, column, filteredAllTasks, (updatedBoardData: Board) => { // I think this below code is not required as we simply want to update the data on the disk. // setBoards((prevBoards) => { // const updatedBoards = [...prevBoards]; @@ -1040,7 +1040,7 @@ const TaskBoardViewContent: React.FC<{ plugin: TaskBoard }> = ({ plugin }) => { ) : ( 0 ? filteredTasksPerColumn : allTasksArrangedPerColumn} focusOnTaskId={plugin.settings.data.lastViewHistory.taskId || ""} /> diff --git a/src/interfaces/BoardConfigs.ts b/src/interfaces/BoardConfigs.ts index 1828c095..1b41a279 100644 --- a/src/interfaces/BoardConfigs.ts +++ b/src/interfaces/BoardConfigs.ts @@ -369,7 +369,7 @@ export const DEFAULT_BOARDS: BoardConfigs = [ }, ], name: "Status Based Workflow", - index: 1, + index: 2, showColumnTags: false, showFilteredTags: true, hideEmptyColumns: false, diff --git a/src/managers/DragDropTasksManager.ts b/src/managers/DragDropTasksManager.ts index d2d3f455..5e5fc9f7 100644 --- a/src/managers/DragDropTasksManager.ts +++ b/src/managers/DragDropTasksManager.ts @@ -131,14 +131,14 @@ class DragDropTasksManager { sourceColumnData: ColumnData, targetColumnData: ColumnData, sourceColumnSwimlaneData: swimlaneDataProp | null | undefined, - targetColumnSwimlaneData: swimlaneDataProp | null | undefined + targetColumnSwimlaneData: swimlaneDataProp | null | undefined, ): Promise => { // This means, user either wants to change the order of the taskItems within the column or is changing the swimlanes. this.handleTasksOrderChange( this.plugin!, currentDragData, sourceColumnData, - this.desiredDropIndex + this.desiredDropIndex, ); if ( @@ -152,7 +152,7 @@ class DragDropTasksManager { newTask, sourceColumnSwimlaneData, targetColumnSwimlaneData, - plugin.settings.data + plugin.settings.data, ); eventEmitter.emit("UPDATE_TASK", { taskID: oldTask.id, @@ -161,7 +161,7 @@ class DragDropTasksManager { const isThisTaskNote = isTaskNotePresentInTags( plugin.settings.data.taskNoteIdentifierTag, - oldTask.tags + oldTask.tags, ); if (isThisTaskNote) { @@ -169,7 +169,7 @@ class DragDropTasksManager { sleep(1000).then(() => { plugin.realTimeScanner.processAllUpdatedFiles( oldTask.filePath, - oldTask.id + oldTask.id, ); }); }); @@ -177,13 +177,13 @@ class DragDropTasksManager { newTask.title = sanitizeTags( newTask.title, oldTask.tags, - newTask.tags + newTask.tags, ); console.log("Sanitized title after tag update:", newTask.title); updateTaskInFile(plugin, newTask, oldTask).then(() => { plugin.realTimeScanner.processAllUpdatedFiles( oldTask.filePath, - oldTask.id + oldTask.id, ); }); } @@ -210,14 +210,14 @@ class DragDropTasksManager { sourceColumn: ColumnData, targetColumn: ColumnData, sourceColumnSwimlaneData: swimlaneDataProp | null | undefined, - targetColumnSwimlaneData: swimlaneDataProp | null | undefined + targetColumnSwimlaneData: swimlaneDataProp | null | undefined, ): Promise => { if ( sourceColumn.coltag == undefined || targetColumn.coltag == undefined ) { console.error( - "handleTaskMove_namedTag_to_namedTag: coltag undefined" + "handleTaskMove_namedTag_to_namedTag: coltag undefined", ); return; } @@ -233,7 +233,7 @@ class DragDropTasksManager { plugin, currentDragData, targetColumn, - this.desiredDropIndex + this.desiredDropIndex, ); // ----------------------------------------------- @@ -244,7 +244,7 @@ class DragDropTasksManager { newTask, sourceColumnSwimlaneData, targetColumnSwimlaneData, - plugin.settings.data + plugin.settings.data, ); console.log("newTask after swimlane change:", newTask); } @@ -258,7 +258,7 @@ class DragDropTasksManager { let newTags = newTask.tags.filter( (tag: string) => tag.replace("#", "").toLowerCase() !== - sourceTag.replace("#", "").toLowerCase() + sourceTag.replace("#", "").toLowerCase(), ); // Add the target column tag if it doesn't exist @@ -278,7 +278,7 @@ class DragDropTasksManager { console.log( "handleTaskMove_namedTag_to_namedTag...\nnewTask=", - newTask + newTask, ); // ----------------------------------------------- @@ -334,14 +334,14 @@ class DragDropTasksManager { sourceColumn: ColumnData, targetColumn: ColumnData, sourceColumnSwimlaneData: swimlaneDataProp | null | undefined, - targetColumnSwimlaneData: swimlaneDataProp | null | undefined + targetColumnSwimlaneData: swimlaneDataProp | null | undefined, ): Promise => { if (!currentDragData || !targetColumn.datedBasedColumn) { console.error( "No current drag data available for reordering : ", JSON.stringify(currentDragData), "\nOr the target column data : ", - JSON.stringify(targetColumn) + JSON.stringify(targetColumn), ); return; } @@ -355,7 +355,7 @@ class DragDropTasksManager { plugin, currentDragData, targetColumn, - this.desiredDropIndex + this.desiredDropIndex, ); if (sourceColumnSwimlaneData && targetColumnSwimlaneData) { @@ -363,7 +363,7 @@ class DragDropTasksManager { newTask, sourceColumnSwimlaneData, targetColumnSwimlaneData, - plugin.settings.data + plugin.settings.data, ); console.log("newTask after swimlane change:", newTask); } @@ -385,7 +385,7 @@ class DragDropTasksManager { const newDateValue = getAllDatesInRelativeRange( targetColumn.datedBasedColumn?.from, - targetColumn.datedBasedColumn?.to + targetColumn.datedBasedColumn?.to, )[0]; // newTask[dateType] = newDateValue; @@ -419,7 +419,7 @@ class DragDropTasksManager { 30, "The column configurations are currupted. Configurations are not valid for this operation. Kindly verify the column configuration in which you just dropped the task.", `Column configuration : ${JSON.stringify(targetColumn)}`, - "DragDropTasksManager.ts/handleTaskMove_dated_to_dated" + "DragDropTasksManager.ts/handleTaskMove_dated_to_dated", ); } }; @@ -439,19 +439,18 @@ class DragDropTasksManager { sourceColumn: ColumnData, targetColumn: ColumnData, sourceColumnSwimlaneData: swimlaneDataProp | null | undefined, - targetColumnSwimlaneData: swimlaneDataProp | null | undefined + targetColumnSwimlaneData: swimlaneDataProp | null | undefined, ): Promise => { if (!targetColumn.taskPriority) { console.error( "The priority value not found in the target column configuration : ", - JSON.stringify(targetColumn) + JSON.stringify(targetColumn), ); return; } - const { updateTaskItemPriority } = await import( - "src/utils/UserTaskEvents" - ); + const { updateTaskItemPriority } = + await import("src/utils/UserTaskEvents"); const oldTask = currentDragData.task; let newTask = { ...oldTask } as taskItem; @@ -460,7 +459,7 @@ class DragDropTasksManager { plugin, currentDragData, targetColumn, - this.desiredDropIndex + this.desiredDropIndex, ); if (sourceColumnSwimlaneData && targetColumnSwimlaneData) { @@ -468,7 +467,7 @@ class DragDropTasksManager { newTask, sourceColumnSwimlaneData, targetColumnSwimlaneData, - plugin.settings.data + plugin.settings.data, ); console.log("newTask after swimlane change:", newTask); } @@ -494,19 +493,18 @@ class DragDropTasksManager { sourceColumn: ColumnData, targetColumn: ColumnData, sourceColumnSwimlaneData: swimlaneDataProp | null | undefined, - targetColumnSwimlaneData: swimlaneDataProp | null | undefined + targetColumnSwimlaneData: swimlaneDataProp | null | undefined, ): Promise => { if (!targetColumn.taskStatus) { console.error( "The status value not found in the target column configuration : ", - JSON.stringify(targetColumn) + JSON.stringify(targetColumn), ); return; } - const { updateTaskItemStatus } = await import( - "src/utils/UserTaskEvents" - ); + const { updateTaskItemStatus } = + await import("src/utils/UserTaskEvents"); const oldTask = currentDragData.task; let newTask = { ...oldTask } as taskItem; @@ -515,7 +513,7 @@ class DragDropTasksManager { plugin, currentDragData, targetColumn, - this.desiredDropIndex + this.desiredDropIndex, ); if (sourceColumnSwimlaneData && targetColumnSwimlaneData) { @@ -523,7 +521,7 @@ class DragDropTasksManager { newTask, sourceColumnSwimlaneData, targetColumnSwimlaneData, - plugin.settings.data + plugin.settings.data, ); console.log("newTask after swimlane change:", newTask); } @@ -537,7 +535,7 @@ class DragDropTasksManager { handleTaskMove_DONE_to_TODO = ( plugin: TaskBoard, - task: taskItem + task: taskItem, ): taskItem => { const newTitle = task.title; let newTask: taskItem = { @@ -549,14 +547,14 @@ class DragDropTasksManager { if ( !isTaskNotePresentInTags( plugin.settings.data.taskNoteIdentifierTag, - task.tags + task.tags, ) ) { newTask.title = sanitizeStatus( plugin.settings.data, task.title, " ", - statusTypeNames.TODO + statusTypeNames.TODO, ); } @@ -576,14 +574,14 @@ class DragDropTasksManager { sourceColumn: ColumnData, targetColumn: ColumnData, sourceColumnSwimlaneData: swimlaneDataProp | null | undefined, - targetColumnSwimlaneData: swimlaneDataProp | null | undefined + targetColumnSwimlaneData: swimlaneDataProp | null | undefined, ): Promise => { if (!currentDragData || !targetColumn.datedBasedColumn) { console.error( "No current drag data available for reordering : ", JSON.stringify(currentDragData), "\nOr the target column data : ", - JSON.stringify(targetColumn) + JSON.stringify(targetColumn), ); return; } @@ -597,7 +595,7 @@ class DragDropTasksManager { plugin, currentDragData, targetColumn, - this.desiredDropIndex + this.desiredDropIndex, ); if (sourceColumnSwimlaneData && targetColumnSwimlaneData) { @@ -605,7 +603,7 @@ class DragDropTasksManager { newTask, sourceColumnSwimlaneData, targetColumnSwimlaneData, - plugin.settings.data + plugin.settings.data, ); console.log("newTask after swimlane change:", newTask); } @@ -629,7 +627,7 @@ class DragDropTasksManager { const newDateValue = getAllDatesInRelativeRange( targetColumn.datedBasedColumn?.from, - targetColumn.datedBasedColumn?.to + targetColumn.datedBasedColumn?.to, )[0]; // newTask[dateType] = newDateValue; @@ -663,7 +661,7 @@ class DragDropTasksManager { 31, "The column configurations are currupted. Configurations are not valid for this operation. Kindly verify the column configuration in which you just dropped the task.", `Column configuration : ${JSON.stringify(targetColumn)}`, - "DragDropTasksManager.ts/handleTaskMove_dated_to_dated" + "DragDropTasksManager.ts/handleTaskMove_dated_to_dated", ); } }; @@ -681,11 +679,11 @@ class DragDropTasksManager { sourceColumn: ColumnData, targetColumn: ColumnData, sourceColumnSwimlaneData: swimlaneDataProp | null | undefined, - targetColumnSwimlaneData: swimlaneDataProp | null | undefined + targetColumnSwimlaneData: swimlaneDataProp | null | undefined, ): Promise => { if (!targetColumn?.coltag) { console.error( - "handleTaskMove_to_namedTag: coltag undefined in the target column configs" + "handleTaskMove_to_namedTag: coltag undefined in the target column configs", ); return; } @@ -698,7 +696,7 @@ class DragDropTasksManager { plugin, currentDragData, targetColumn, - this.desiredDropIndex + this.desiredDropIndex, ); // STEP 2 - Check if swimlanes are enabled and if user is moving from one swimlane to another. @@ -707,7 +705,7 @@ class DragDropTasksManager { newTask, sourceColumnSwimlaneData, targetColumnSwimlaneData, - plugin.settings.data + plugin.settings.data, ); console.log("newTask after swimlane change:", newTask); } @@ -746,19 +744,18 @@ class DragDropTasksManager { sourceColumn: ColumnData, targetColumn: ColumnData, sourceColumnSwimlaneData: swimlaneDataProp | null | undefined, - targetColumnSwimlaneData: swimlaneDataProp | null | undefined + targetColumnSwimlaneData: swimlaneDataProp | null | undefined, ): Promise => { if (!targetColumn.taskPriority) { console.error( "The priority value not found in the target column configuration : ", - JSON.stringify(targetColumn) + JSON.stringify(targetColumn), ); return; } - const { updateTaskItemPriority } = await import( - "src/utils/UserTaskEvents" - ); + const { updateTaskItemPriority } = + await import("src/utils/UserTaskEvents"); const oldTask = currentDragData.task; let newTask = { ...oldTask } as taskItem; @@ -767,7 +764,7 @@ class DragDropTasksManager { plugin, currentDragData, targetColumn, - this.desiredDropIndex + this.desiredDropIndex, ); if (sourceColumnSwimlaneData && targetColumnSwimlaneData) { @@ -775,7 +772,7 @@ class DragDropTasksManager { newTask, sourceColumnSwimlaneData, targetColumnSwimlaneData, - plugin.settings.data + plugin.settings.data, ); console.log("newTask after swimlane change:", newTask); } @@ -803,19 +800,18 @@ class DragDropTasksManager { sourceColumn: ColumnData, targetColumn: ColumnData, sourceColumnSwimlaneData: swimlaneDataProp | null | undefined, - targetColumnSwimlaneData: swimlaneDataProp | null | undefined + targetColumnSwimlaneData: swimlaneDataProp | null | undefined, ): Promise => { if (!targetColumn.taskStatus) { console.error( "The status value not found in the target column configuration : ", - JSON.stringify(targetColumn) + JSON.stringify(targetColumn), ); return; } - const { updateTaskItemStatus } = await import( - "src/utils/UserTaskEvents" - ); + const { updateTaskItemStatus } = + await import("src/utils/UserTaskEvents"); const oldTask = currentDragData.task; let newTask = { ...oldTask } as taskItem; @@ -824,7 +820,7 @@ class DragDropTasksManager { plugin, currentDragData, targetColumn, - this.desiredDropIndex + this.desiredDropIndex, ); if (sourceColumnSwimlaneData && targetColumnSwimlaneData) { @@ -832,7 +828,7 @@ class DragDropTasksManager { newTask, sourceColumnSwimlaneData, targetColumnSwimlaneData, - plugin.settings.data + plugin.settings.data, ); console.log("newTask after swimlane change:", newTask); } @@ -861,11 +857,10 @@ class DragDropTasksManager { sourceColumn: ColumnData, targetColumn: ColumnData, sourceColumnSwimlaneData: swimlaneDataProp | null | undefined, - targetColumnSwimlaneData: swimlaneDataProp | null | undefined + targetColumnSwimlaneData: swimlaneDataProp | null | undefined, ): Promise => { - const { updateTaskItemStatus } = await import( - "src/utils/UserTaskEvents" - ); + const { updateTaskItemStatus } = + await import("src/utils/UserTaskEvents"); const oldTask = currentDragData.task; let newTask = { ...oldTask } as taskItem; @@ -875,7 +870,7 @@ class DragDropTasksManager { plugin, currentDragData, targetColumn, - this.desiredDropIndex + this.desiredDropIndex, ); // STEP 2 - Check if swimlanes are enabled and if user is moving from one swimlane to another. @@ -884,15 +879,14 @@ class DragDropTasksManager { newTask, sourceColumnSwimlaneData, targetColumnSwimlaneData, - plugin.settings.data + plugin.settings.data, ); console.log("newTask after swimlane change:", newTask); } - const newStatus = - plugin.settings.data.customStatuses.find( - (status) => status.type === statusTypeNames.DONE - ); + const newStatus = plugin.settings.data.customStatuses.find( + (status) => status.type === statusTypeNames.DONE, + ); // FINALLY - Update the task in the note. updateTaskItemStatus(plugin, newTask, newStatus?.symbol ?? "x"); @@ -905,19 +899,19 @@ class DragDropTasksManager { * @param targetColumnData The column data with manualOrder sorting * @param desiredIndex The desired index to insert the task at */ - handleTasksOrderChange = ( + handleTasksOrderChange = async ( plugin: TaskBoard, currentDragData: currentDragDataPayload, targetColumnData: ColumnData, - desiredIndex: number | null - ): void => { + desiredIndex: number | null, + ) => { console.log( "handleTasksOrderChange called...\ncurrentDragData=", currentDragData, "\ntargetColumnData=", targetColumnData, "\ndesiredIndex=", - desiredIndex + desiredIndex, ); if ( !( @@ -948,20 +942,25 @@ class DragDropTasksManager { targetColumnData.tasksIdManualOrder.splice( desiredIndex, 0, - task.id + task.id, ); } else { targetColumnData.tasksIdManualOrder.push(task.id); } - let newSettings = plugin.settings; - newSettings.data.boardConfigs[ - currentDragData.currentBoardIndex - ].columns[targetColumnData.index - 1] = targetColumnData; + let newBoardData = + await this.plugin?.taskBoardFileManager.getCurrentBoardData(); + + if (!newBoardData) { + throw "Board data not found"; + return; + } + + newBoardData!.columns[targetColumnData.index - 1] = targetColumnData; // Persist settings and refresh the board try { - plugin.saveSettings(newSettings); + this.plugin?.taskBoardFileManager.saveBoard(newBoardData); } catch (err) { console.error("Error saving settings after task reorder:", err); } @@ -978,7 +977,7 @@ class DragDropTasksManager { task: taskItem, sourceColumnSwimlaneData: swimlaneDataProp, targetColumnSwimlaneData: swimlaneDataProp, - globalSettings: globalSettingsData + globalSettings: globalSettingsData, ): Promise => { const property = sourceColumnSwimlaneData.property; const oldValue = sourceColumnSwimlaneData.value; @@ -994,14 +993,14 @@ class DragDropTasksManager { newTags = newTags.filter( (tag) => tag.replace("#", "").toLowerCase() !== - oldValue.replace("#", "").toLowerCase() + oldValue.replace("#", "").toLowerCase(), ); } // Add new tag of target swimlane if (newValue !== "All rest") newTags.push( - newValue.startsWith("#") ? newValue : `#${newValue}` + newValue.startsWith("#") ? newValue : `#${newValue}`, ); newTags = Array.from(new Set(newTags)); @@ -1010,7 +1009,7 @@ class DragDropTasksManager { globalSettings, property, oldTags, - newTags + newTags, ); } else { newTask = await updateTaskItemProperty( @@ -1018,7 +1017,7 @@ class DragDropTasksManager { globalSettings, property, oldValue, - newValue + newValue, ); } @@ -1046,7 +1045,7 @@ class DragDropTasksManager { */ isTaskDropAllowed( sourceColumnData: ColumnData, - targetColumnData: ColumnData + targetColumnData: ColumnData, ): boolean { // Since there are more positive rules then negative ones. // Hence this function will only mention the negative ones and return false. @@ -1158,12 +1157,12 @@ class DragDropTasksManager { // For column we can do this kind of heavy DOM traversing, // since there will be less columns, so querySelecting them all is not so big issue. const allColumnContainers = Array.from( - document.querySelectorAll(".TaskBoardColumnsSection") + document.querySelectorAll(".TaskBoardColumnsSection"), ) as HTMLDivElement[]; allColumnContainers.forEach((container) => { container.classList.remove( "drag-over-allowed", - "drag-over-not-allowed" + "drag-over-not-allowed", ); }); @@ -1200,7 +1199,7 @@ class DragDropTasksManager { e: DragEvent, draggedTaskItem: HTMLDivElement, currentDragData: currentDragDataPayload, - dragIndex: number + dragIndex: number, ): void { if (!e.dataTransfer) return; @@ -1254,7 +1253,7 @@ class DragDropTasksManager { e: DragEvent, cardEl: HTMLElement, columnContainerEl: HTMLDivElement, - ColumnData: ColumnData + ColumnData: ColumnData, ): void { if (!this.getCurrentDragData() || this.getCurrentDragData() === null) return; @@ -1279,7 +1278,7 @@ class DragDropTasksManager { const dropAllowed = this.handleColumnDragOverEvent( e, ColumnData, - columnContainerEl + columnContainerEl, ); if (!dropAllowed) return; @@ -1304,7 +1303,7 @@ class DragDropTasksManager { public handleColumnDragOverEvent( e: DragEvent, targetColumnData: ColumnData, - targetColumnContainer: HTMLDivElement + targetColumnContainer: HTMLDivElement, ): boolean { // console.log("DragDropTasksManager : handleDragOver called..."); e.preventDefault(); @@ -1320,7 +1319,7 @@ class DragDropTasksManager { // Check if drop is allowed const isDropAllowed = this.isTaskDropAllowed( sourceColumnData, - targetColumnData + targetColumnData, ); // console.log("isDropAllowed", isDropAllowed); @@ -1333,7 +1332,7 @@ class DragDropTasksManager { } else { console.log( "Task drop not allowed from column:", - sourceColumnData.name + sourceColumnData.name, ); // Apply CSS styling for not allowed drop targetColumnContainer.classList.add("drag-over-not-allowed"); @@ -1359,7 +1358,7 @@ class DragDropTasksManager { columnContainerEl.classList.remove( "drag-over-allowed", - "drag-over-not-allowed" + "drag-over-not-allowed", ); // Clear drag-over styling from all columns @@ -1393,7 +1392,7 @@ class DragDropTasksManager { e: DragEvent, targetColumnData: ColumnData, targetColumnContainer: HTMLDivElement, - targetColumnSwimlaneData: swimlaneDataProp | null | undefined + targetColumnSwimlaneData: swimlaneDataProp | null | undefined, ): void { console.log("DragDropTasksManager : handleDrop called..."); e.preventDefault(); @@ -1408,7 +1407,7 @@ class DragDropTasksManager { const sourceColumnSwimlaneData = this.currentDragData.swimlaneData; if (!sourceColumnData) { console.error( - "There was an error while capturing the source column data." + "There was an error while capturing the source column data.", ); return; } @@ -1416,13 +1415,13 @@ class DragDropTasksManager { // Remove drag-over styling from target targetColumnContainer.classList.remove( "drag-over-allowed", - "drag-over-not-allowed" + "drag-over-not-allowed", ); // Check if drop is allowed const isDropAllowed = this.isTaskDropAllowed( sourceColumnData, - targetColumnData + targetColumnData, ); if (!isDropAllowed) { @@ -1430,10 +1429,10 @@ class DragDropTasksManager { "Task drop not allowed from column:", sourceColumnData.name, "to column:", - targetColumnData.name + targetColumnData.name, ); new Notice( - `Task drop not allowed from column: ${sourceColumnData.name} to column: ${targetColumnData.name}` + `Task drop not allowed from column: ${sourceColumnData.name} to column: ${targetColumnData.name}`, ); return; } @@ -1457,7 +1456,7 @@ class DragDropTasksManager { sourceColumnData, targetColumnData, sourceColumnSwimlaneData, - targetColumnSwimlaneData + targetColumnSwimlaneData, ); return; } else if (targetColumnData.colType === colTypeNames.namedTag) { @@ -1467,7 +1466,7 @@ class DragDropTasksManager { sourceColumnData, targetColumnData, sourceColumnSwimlaneData, - targetColumnSwimlaneData + targetColumnSwimlaneData, ); } else if (targetColumnData.colType === colTypeNames.dated) { this.handleTaskMove_to_dated( @@ -1476,7 +1475,7 @@ class DragDropTasksManager { sourceColumnData, targetColumnData, sourceColumnSwimlaneData, - targetColumnSwimlaneData + targetColumnSwimlaneData, ); } else if (targetColumnData.colType === colTypeNames.taskPriority) { this.handleTaskMove_priority_to_priority( @@ -1485,7 +1484,7 @@ class DragDropTasksManager { sourceColumnData, targetColumnData, sourceColumnSwimlaneData, - targetColumnSwimlaneData + targetColumnSwimlaneData, ); } else if (targetColumnData.colType === colTypeNames.taskStatus) { this.handleTaskMove_status_to_status( @@ -1494,11 +1493,11 @@ class DragDropTasksManager { sourceColumnData, targetColumnData, sourceColumnSwimlaneData, - targetColumnSwimlaneData + targetColumnSwimlaneData, ); } else { new Notice( - "This operation is not possible in the current version. Please request this idea to the developer." + "This operation is not possible in the current version. Please request this idea to the developer.", ); } } else if (targetColumnData.colType === colTypeNames.completed) { @@ -1510,7 +1509,7 @@ class DragDropTasksManager { sourceColumnData, targetColumnData, sourceColumnSwimlaneData, - targetColumnSwimlaneData + targetColumnSwimlaneData, ); } else if (targetColumnData.colType === colTypeNames.dated) { // This means user is moving task to a dated column from any other type of column. @@ -1521,7 +1520,7 @@ class DragDropTasksManager { sourceColumnData, targetColumnData, sourceColumnSwimlaneData, - targetColumnSwimlaneData + targetColumnSwimlaneData, ); } else if (targetColumnData.colType === colTypeNames.namedTag) { // This means user is moving task to a namedTag column from any other type of column. @@ -1532,7 +1531,7 @@ class DragDropTasksManager { sourceColumnData, targetColumnData, sourceColumnSwimlaneData, - targetColumnSwimlaneData + targetColumnSwimlaneData, ); } else if (targetColumnData.colType === colTypeNames.taskPriority) { // This means user is moving task to a priority column from any other type of column. @@ -1543,7 +1542,7 @@ class DragDropTasksManager { sourceColumnData, targetColumnData, sourceColumnSwimlaneData, - targetColumnSwimlaneData + targetColumnSwimlaneData, ); } else if (targetColumnData.colType === colTypeNames.taskStatus) { // This means user is moving task to a status column from any other type of column. @@ -1554,11 +1553,11 @@ class DragDropTasksManager { sourceColumnData, targetColumnData, sourceColumnSwimlaneData, - targetColumnSwimlaneData + targetColumnSwimlaneData, ); } else { new Notice( - "This operation is not possible in the current version. Please request this idea to the developer." + "This operation is not possible in the current version. Please request this idea to the developer.", ); } } diff --git a/src/managers/TaskBoardFileManager.ts b/src/managers/TaskBoardFileManager.ts index 43ca2839..43231cf6 100644 --- a/src/managers/TaskBoardFileManager.ts +++ b/src/managers/TaskBoardFileManager.ts @@ -77,20 +77,23 @@ export default class TaskBoardFileManager { // Check if file exists, if not create it const fileExists = await this.app.vault.adapter.exists(filePath); - const jsonContent = JSON.stringify(boardData, null, 2); + // Convert board data to JSON string, then to Uint8Array for binary storage + const jsonString = JSON.stringify(boardData); + const uint8Array = new TextEncoder().encode(jsonString); + const arrayBuffer = uint8Array.buffer.slice(uint8Array.byteOffset, uint8Array.byteOffset + uint8Array.byteLength); if (!fileExists) { - // Create new file - await this.app.vault.create(filePath, jsonContent); + // Create new file with binary data + await this.app.vault.createBinary(filePath, arrayBuffer); console.log(`Created new TaskBoard file: ${filePath}`); } else { - // Update existing file + // Update existing file with binary data const file = this.app.vault.getAbstractFileByPath(filePath); if (!file || !(file instanceof TFile)) { console.error(`Cannot find file to update: ${filePath}`); return false; } - await this.app.vault.modify(file, jsonContent); + await this.app.vault.modifyBinary(file, arrayBuffer); console.log(`Updated TaskBoard file: ${filePath}`); } @@ -238,6 +241,9 @@ export default class TaskBoardFileManager { `Created default board file: ${filePath}`, defaultBoards[i].name, ); + new Notice( + `Created default board file: ${filePath} : ${defaultBoards[i].name}`, + ); } } } @@ -318,15 +324,15 @@ export default class TaskBoardFileManager { let boardIndexToUse = boardIndex; if (!boardIndexToUse) { boardIndexToUse = this.currentBoardIndex; - if (!boardIndexToUse) { - bugReporterManagerInsatance.showNotice( - 90, - "The TaskBoardFileManager instance dont contain an currentBoardIndex number", - "ERROR : this.currentBoardIndex not found", - "TaskBoardFileManager/saveCurrentBoard", - ); - return false; - } + // if (!boardIndexToUse) { + // bugReporterManagerInsatance.showNotice( + // 90, + // "The TaskBoardFileManager instance dont contain an currentBoardIndex number", + // "ERROR : this.currentBoardIndex not found", + // "TaskBoardFileManager/saveCurrentBoard", + // ); + // return false; + // } } // Validate board index @@ -375,7 +381,45 @@ export default class TaskBoardFileManager { } } - async loadAllBoards() {} + loadAllBoards(): Board[] | [] { + try { + const boardFilesLocations = + this.plugin.settings.data.boardFilesLocation || []; + let allBoardsData: Board[] | [] = []; + + boardFilesLocations.forEach(async (boardFilePath: string) => { + if (!boardFilePath || boardFilePath.trim() === "") { + console.error( + `No board file path configured for index: ${this.currentBoardIndex}`, + ); + return []; + } + + // Load board from file + const boardData = await this.loadBoardFromFile(boardFilePath); + + if (boardData) { + // Cache the board data in memory + allBoardsData[boardData.index] = boardData; + console.log( + `Loaded and cached board "${boardData.name}" (index: ${boardData.index}) from: ${boardFilePath}`, + ); + } else { + new Notice( + `Task Board : Error loading all boards data. Following board not found : ${boardFilePath}`, + ); + } + }); + + return allBoardsData; + } catch (error) { + console.error( + `Error loading board at index ${this.currentBoardIndex}:`, + error, + ); + return []; + } + } /** * Get the currently cached board data diff --git a/src/settings/SettingConstructUI.ts b/src/settings/SettingConstructUI.ts index aa17a9c9..0d3a0d56 100644 --- a/src/settings/SettingConstructUI.ts +++ b/src/settings/SettingConstructUI.ts @@ -245,8 +245,7 @@ export class SettingsManager { // Set the last viewed tab const defaultTab = Object.keys(sections)[ - this.plugin.settings.data.lastViewHistory - .settingTab + this.plugin.settings.data.lastViewHistory.settingTab ]; tabs[defaultTab].click(); @@ -346,9 +345,8 @@ export class SettingsManager { }); configureBtn.addEventListener("click", () => { openScanFiltersModal(this.plugin, filterType, (newValues) => { - this.plugin.settings.data.scanFilters[ - filterType - ].values = newValues; + this.plugin.settings.data.scanFilters[filterType].values = + newValues; this.plugin.saveSettings(); refreshTagList(); // Refresh the tag list after updating values }); @@ -400,9 +398,8 @@ export class SettingsManager { const newPolarity = Number( (e.target as HTMLSelectElement).value, ); - this.plugin.settings.data.scanFilters[ - filterType - ].polarity = newPolarity; + this.plugin.settings.data.scanFilters[filterType].polarity = + newPolarity; this.plugin.saveSettings(); }); }); @@ -1048,8 +1045,7 @@ export class SettingsManager { this.plugin.settings.data.tagColors.map( (tagColor) => ({ color: - this.plugin.settings.data - .globalSettings.tagColors[ + this.plugin.settings.data.tagColors[ tagColor.priority - 1 ]?.color || "#ff0000", }), @@ -1268,8 +1264,7 @@ export class SettingsManager { const renderCustomStatuses = () => { customStatusesContainer.empty(); // Clear existing rendered rows - const customStatuses = - this.plugin.settings.data.customStatuses; + const customStatuses = this.plugin.settings.data.customStatuses; if (!customStatuses || customStatuses.length === 0) { customStatusesContainer.createDiv({ @@ -1373,9 +1368,8 @@ export class SettingsManager { updatedStatus.availableAsCommand, type: updatedStatus.type, }; - this.plugin.settings.data!.customStatuses[ - index - ] = customStatus; + this.plugin.settings.data!.customStatuses[index] = + customStatus; await this.saveSettings(); renderCustomStatuses(); } @@ -1402,8 +1396,7 @@ export class SettingsManager { renderCustomStatuses(); const isTasksPluginEnabled = - this.plugin.settings.data.compatiblePlugins - .tasksPlugin; + this.plugin.settings.data.compatiblePlugins.tasksPlugin; // Add "Add New Status" button new Setting(contentEl) .addButton((btn) => @@ -1842,8 +1835,7 @@ export class SettingsManager { frontmatterItem !== null, ); - this.plugin.settings.data.frontmatterFormatting = - newOrder; + this.plugin.settings.data.frontmatterFormatting = newOrder; }, }); diff --git a/src/utils/algorithms/ColumnSegregator.ts b/src/utils/algorithms/ColumnSegregator.ts index dad87c97..434f5702 100644 --- a/src/utils/algorithms/ColumnSegregator.ts +++ b/src/utils/algorithms/ColumnSegregator.ts @@ -23,14 +23,12 @@ import { getAllTaskTags } from "../TaskItemUtils"; export const columnSegregator = ( settings: PluginDataJson, // setTasks: Dispatch>, - activeBoardIndex: number, + activeBoardData: Board, columnData: ColumnData, allTasks: taskJsonMerged | null, - onBoardDataChange?: (boardData: Board) => void + onBoardDataChange?: (boardData: Board) => void, ): taskItem[] => { - if (activeBoardIndex < 0 || !allTasks) return []; - - const boardConfigs = settings.data.boardConfigs; + if (!allTasks) return []; // Call the filter function based on the column's tag and properties let tasksToDisplay: taskItem[] = []; @@ -143,7 +141,7 @@ export const columnSegregator = ( const moment = _moment as unknown as typeof _moment.default; const diffDays = moment(taskUniversalDate).diff( moment(today), - "days" + "days", ); // console.log( @@ -165,7 +163,7 @@ export const columnSegregator = ( break; case colTypeNames.untagged: tasksToDisplay = pendingTasks.filter( - (task) => getAllTaskTags(task).length === 0 + (task) => getAllTaskTags(task).length === 0, ); break; case colTypeNames.namedTag: @@ -178,10 +176,10 @@ export const columnSegregator = ( const result = matchTagsWithWildcards( columnData?.coltag || "", - tag + tag, ); return result !== null; - }) + }), ); break; case colTypeNames.pathFiltered: @@ -208,7 +206,7 @@ export const columnSegregator = ( // Check if the task's file path contains the pattern return lowerCasePath.includes(pattern); } - } + }, ); return matchedPattern; }); @@ -221,23 +219,21 @@ export const columnSegregator = ( break; case colTypeNames.otherTags: // 1. Get the current board based on activeBoardIndex index - const currentBoard = boardConfigs.find( - (board: Board) => board.index === activeBoardIndex - ); + const currentBoard = activeBoardData; // 2. Collect all coltags from columns where colType is 'namedTag' const namedTags = currentBoard?.columns .filter( (col: ColumnData) => - col.colType === colTypeNames.namedTag && col.coltag + col.colType === colTypeNames.namedTag && col.coltag, ) .map((col: ColumnData) => - col.coltag?.toLowerCase().replace(`#`, "") + col.coltag?.toLowerCase().replace(`#`, ""), ) .filter( (tag): tag is string => - typeof tag === "string" && tag.length > 0 + typeof tag === "string" && tag.length > 0, ) || []; // 3. Now filter tasks @@ -270,13 +266,13 @@ export const columnSegregator = ( break; case colTypeNames.taskPriority: tasksToDisplay = pendingTasks.filter( - (task) => task.priority === columnData.taskPriority + (task) => task.priority === columnData.taskPriority, ); break; case colTypeNames.taskStatus: const allTasks = [...pendingTasks, ...completedTasks]; tasksToDisplay = allTasks.filter( - (task) => task.status === columnData.taskStatus + (task) => task.status === columnData.taskStatus, ); break; case colTypeNames.allPending: @@ -305,7 +301,7 @@ export const columnSegregator = ( // TODO : This code can be moved inside the ColumnSortingAlgorithm function. // If manualOrder is one of the sorting criteria, apply manual ordering using columnData.tasksIdManualOrder const hasManualOrder = columnData.sortCriteria.some( - (c) => c.criteria === "manualOrder" + (c) => c.criteria === "manualOrder", ); if (hasManualOrder) { // Ensure tasksIdManualOrder exists @@ -316,7 +312,7 @@ export const columnSegregator = ( // Add any new tasks (not present in manual order) to the TOP of the manual order array const currentIds = tasksToDisplay.map((t) => t.id); const missingIds = currentIds.filter( - (id) => !columnData.tasksIdManualOrder!.includes(id) + (id) => !columnData.tasksIdManualOrder!.includes(id), ); if (missingIds.length > 0) { // Prepend missing ids so newest appear on top @@ -327,7 +323,7 @@ export const columnSegregator = ( } let newTasksIdManualOrder = columnData.tasksIdManualOrder; - let currentBoardData = settings.data.boardConfigs[activeBoardIndex]; + let currentBoardData = activeBoardData; let didTasksIdManualOrderChange = false; // Build sorted list based on manual order @@ -340,7 +336,7 @@ export const columnSegregator = ( } else { newTasksIdManualOrder.splice( newTasksIdManualOrder.indexOf(id), - 1 + 1, ); didTasksIdManualOrderChange = true; } @@ -354,7 +350,7 @@ export const columnSegregator = ( "columnSegregator...\nNew manual order :", newTasksIdManualOrder, "\nOld manual order :", - columnData.tasksIdManualOrder + columnData.tasksIdManualOrder, ); if (onBoardDataChange && didTasksIdManualOrderChange) { onBoardDataChange(currentBoardData); @@ -366,7 +362,7 @@ export const columnSegregator = ( tasksToDisplay = columnSortingAlgorithm( settings.data.defaultStartTime, tasksToDisplay, - columnData.sortCriteria + columnData.sortCriteria, ); } } diff --git a/task-board-logs.log b/task-board-logs.log index 5da66e59..b0888e47 100644 --- a/task-board-logs.log +++ b/task-board-logs.log @@ -56,3 +56,39 @@ Context : Column.tsx/column-menu/configure-conlum-filters ```log Dont show popover. ``` + +------------- + +Timestamp : 2026-01-19T17:02:54.793Z +ID : 29 +Message : Error showing filter popover +Context : TaskBoardViewContent.tsx/handleFilterButtonClick + +#### Bug Content +```log +TypeError: Cannot read properties of undefined (reading 'boardFilter') +``` + +------------- + +Timestamp : 2026-01-19T17:34:31.126Z +ID : 90 +Message : The TaskBoardFileManager instance dont contain an currentBoardIndex number +Context : TaskBoardFileManager/saveCurrentBoard + +#### Bug Content +```log +ERROR : this.currentBoardIndex not found +``` + +------------- + +Timestamp : 2026-01-19T17:40:47.093Z +ID : 90 +Message : The TaskBoardFileManager instance dont contain an currentBoardIndex number +Context : TaskBoardFileManager/saveCurrentBoard + +#### Bug Content +```log +ERROR : this.currentBoardIndex not found +``` diff --git a/temp.data.json b/temp.data.json new file mode 100644 index 00000000..c9c0770c --- /dev/null +++ b/temp.data.json @@ -0,0 +1,1193 @@ +{ + "version": "1.8.7", + "data": { + "boardConfigs": [ + { + "name": "Path filtered", + "index": 0, + "columns": [ + { + "id": 3061361157, + "index": 1, + "colType": "pathFiltered", + "active": true, + "collapsed": false, + "name": "TaskNotes", + "filePaths": "TaskNotes/", + "sortCriteria": [ + { + "criteria": "manualOrder", + "order": "asc", + "priority": 1, + "uid": "ur7dguyt" + } + ], + "tasksIdManualOrder": [ + "367", + "372", + "398696308", + "373", + "370", + "3077781059", + "4184100420", + "3052793295", + "1506909345", + 274, + "4031700447", + "362" + ] + }, + { + "id": 1312742179, + "index": 2, + "colType": "pathFiltered", + "active": true, + "collapsed": false, + "name": "Indented Bug", + "filePaths": "Testing Indented Task Bug.md", + "filters": { + "rootCondition": "any", + "filterGroups": [] + } + }, + { + "id": 2025781698, + "index": 3, + "colType": "pathFiltered", + "active": true, + "collapsed": false, + "name": "QMD", + "filePaths": "A QMD file.qmd" + }, + { + "id": 1623496994, + "index": 4, + "colType": "completed", + "active": true, + "collapsed": false, + "name": "Completed", + "limit": 20 + } + ], + "hideEmptyColumns": false, + "boardFilter": { + "rootCondition": "any", + "filterGroups": [] + }, + "taskCount": { + "pending": 65, + "completed": 19 + } + }, + { + "columns": [ + { + "id": 1799377278, + "index": 1, + "colType": "undated", + "active": true, + "collapsed": false, + "name": "Backlog", + "datedBasedColumn": { + "from": 0, + "to": 0, + "dateType": "scheduledDate" + }, + "filters": { + "rootCondition": "any", + "filterGroups": [] + } + }, + { + "id": 3492, + "colType": "dated", + "active": true, + "collapsed": false, + "name": "Over Due", + "index": 2, + "datedBasedColumn": { + "dateType": "scheduledDate", + "from": -300, + "to": -1 + }, + "sortCriteria": [ + { + "criteria": "scheduledDate", + "order": "asc", + "priority": 1 + }, + { + "criteria": "time", + "order": "asc", + "priority": 2 + } + ], + "filters": { + "rootCondition": "any", + "filterGroups": [] + } + }, + { + "id": 3494, + "colType": "dated", + "active": true, + "collapsed": false, + "name": "Today", + "index": 3, + "datedBasedColumn": { + "dateType": "scheduledDate", + "from": 0, + "to": 0 + }, + "sortCriteria": [ + { + "criteria": "time", + "order": "asc", + "priority": 1 + } + ], + "filters": { + "rootCondition": "any", + "filterGroups": [] + }, + "minimized": true + }, + { + "id": 3493, + "colType": "dated", + "active": true, + "collapsed": false, + "name": "Tomorrow", + "index": 4, + "datedBasedColumn": { + "dateType": "scheduledDate", + "from": 1, + "to": 1 + } + }, + { + "id": 3495, + "colType": "dated", + "active": true, + "collapsed": false, + "name": "Future", + "index": 5, + "datedBasedColumn": { + "dateType": "scheduledDate", + "from": 2, + "to": 300 + } + }, + { + "id": 3497, + "colType": "completed", + "active": true, + "collapsed": false, + "limit": 20, + "name": "Completed, updated", + "index": 6, + "filters": { + "rootCondition": "any", + "filterGroups": [] + } + } + ], + "filters": [ + "#Test", + "#working", + "#new" + ], + "filterPolarity": "0", + "filterScope": "Both", + "name": "Time based workflow", + "index": 1, + "showColumnTags": true, + "showFilteredTags": false, + "hideEmptyColumns": false, + "boardFilter": { + "rootCondition": "any", + "filterGroups": [ + { + "id": "id-1763871967163-8a21jnuiq", + "groupCondition": "any", + "filters": [ + { + "id": "id-1763871967165-lct3l84v6", + "property": "tags", + "condition": "doesNotContain", + "value": "#task" + }, + { + "id": "id-1763871980225-nbzhu6dnv", + "property": "tags", + "condition": "doesNotContain", + "value": "#taskNote" + } + ] + } + ] + }, + "description": "Project to manage all the tasks related to plugin release.", + "taskCount": { + "pending": 4, + "completed": 1 + } + }, + { + "columns": [ + { + "id": 3187486162, + "index": 1, + "colType": "otherTags", + "active": true, + "collapsed": false, + "name": "Other tags", + "minimized": false + }, + { + "colType": "untagged", + "active": true, + "collapsed": false, + "name": "Backlogs", + "index": 2, + "id": 226119, + "sortCriterias": [], + "filters": { + "rootCondition": "any", + "filterGroups": [] + }, + "sortCriteria": [ + { + "criteria": "id", + "order": "desc", + "priority": 1, + "uid": "k6wjlc63" + } + ], + "minimized": false + }, + { + "id": 2485661779, + "index": 3, + "colType": "namedTag", + "active": false, + "collapsed": false, + "name": "#Task", + "coltag": "#Task", + "sortCriteria": [ + { + "criteria": "content", + "order": "asc", + "priority": 1, + "uid": "bmlpsogj" + } + ], + "tasksIdManualOrder": [ + "365", + 274, + "362" + ], + "minimized": false, + "filters": { + "rootCondition": "any", + "filterGroups": [] + } + }, + { + "colType": "namedTag", + "active": true, + "collapsed": false, + "name": "Can be implemented", + "index": 4, + "coltag": "#pending", + "id": 47626, + "filters": { + "rootCondition": "any", + "filterGroups": [] + }, + "minimized": false, + "sortCriteria": [ + { + "criteria": "manualOrder", + "order": "asc", + "priority": 1, + "uid": "285zohor" + } + ], + "tasksIdManualOrder": [ + "224", + "276", + "389", + "378", + "355", + "258", + "375" + ] + }, + { + "id": 3130768414, + "index": 5, + "colType": "namedTag", + "active": true, + "collapsed": false, + "name": "In Progress", + "coltag": "wip" + }, + { + "colType": "namedTag", + "active": true, + "collapsed": false, + "name": "Ready to publish", + "index": 6, + "coltag": "done", + "id": 328227, + "minimized": false + }, + { + "id": 1936828579, + "index": 7, + "colType": "namedTag", + "active": false, + "collapsed": false, + "name": "*/seeding/*", + "coltag": "*/seeding/*", + "minimized": false + }, + { + "id": 3193261849, + "index": 8, + "colType": "completed", + "active": true, + "collapsed": false, + "name": "Completed", + "limit": 20, + "filters": { + "rootCondition": "any", + "filterGroups": [] + }, + "minimized": false + } + ], + "filters": [ + "#Test", + "#working", + "*/seeding/*" + ], + "filterPolarity": "0", + "filterScope": "Both", + "name": "Tag based board", + "index": 2, + "showColumnTags": true, + "showFilteredTags": true, + "hideEmptyColumns": false, + "boardFilter": { + "rootCondition": "all", + "filterGroups": [] + }, + "filterConfig": { + "enableSavedFilters": true, + "savedConfigs": [ + { + "id": "filter-config-1760103432145-fevybeh8j", + "name": "Destinatio", + "description": "Filtering all tasks from \"Destinatio\" folder", + "filterState": { + "rootCondition": "any", + "filterGroups": [ + { + "id": "id-1760029960520-z2a1g0ka4", + "groupCondition": "all", + "filters": [ + { + "id": "id-1760029960521-t33fwt542", + "property": "filePath", + "condition": "startsWith", + "value": "Destinatio" + } + ] + } + ] + }, + "createdAt": "2025-10-10T13:37:12.145Z", + "updatedAt": "2025-10-10T13:37:12.145Z" + }, + { + "id": "filter-config-1764176875594-8gv5ee69p", + "name": "This", + "description": "If \"This\" is present in the content.", + "filterState": { + "rootCondition": "any", + "filterGroups": [ + { + "id": "id-1760365709344-buyyxwhhh", + "groupCondition": "all", + "filters": [ + { + "id": "id-1760365709345-8uyz54hm3", + "property": "content", + "condition": "contains", + "value": "This" + } + ] + } + ] + }, + "createdAt": "2025-11-26T17:07:55.594Z", + "updatedAt": "2025-11-26T17:07:55.594Z" + }, + { + "id": "filter-config-1764176933714-tm6ozdue7", + "name": "This and No", + "description": "Content contains this and no", + "filterState": { + "rootCondition": "any", + "filterGroups": [ + { + "id": "id-1760029960520-z2a1g0ka4", + "groupCondition": "all", + "filters": [ + { + "id": "id-1760029960521-t33fwt542", + "property": "content", + "condition": "contains", + "value": "This" + } + ] + }, + { + "id": "id-1764176913802-bwvk75kr4", + "groupCondition": "all", + "filters": [ + { + "id": "id-1764176913803-1ugczxozs", + "property": "content", + "condition": "contains", + "value": "No" + } + ] + } + ] + }, + "createdAt": "2025-11-26T17:08:53.714Z", + "updatedAt": "2025-11-26T17:08:53.714Z" + }, + { + "id": "filter-config-1764177013691-5uj7krjmm", + "name": "This and No, both", + "description": "Both", + "filterState": { + "rootCondition": "all", + "filterGroups": [ + { + "id": "id-1760029960520-z2a1g0ka4", + "groupCondition": "all", + "filters": [ + { + "id": "id-1760029960521-t33fwt542", + "property": "content", + "condition": "contains", + "value": "This" + } + ] + }, + { + "id": "id-1764176913802-bwvk75kr4", + "groupCondition": "all", + "filters": [ + { + "id": "id-1764176913803-1ugczxozs", + "property": "content", + "condition": "contains", + "value": "No" + } + ] + } + ] + }, + "createdAt": "2025-11-26T17:10:13.691Z", + "updatedAt": "2025-11-26T17:10:13.691Z" + } + ] + }, + "taskCount": { + "pending": 63, + "completed": 14 + }, + "swimlanes": { + "enabled": true, + "hideEmptySwimlanes": false, + "property": "tags", + "maxHeight": "500px", + "sortCriteria": "custom", + "customSortOrder": [ + { + "value": "first", + "index": 1 + }, + { + "value": "second", + "index": 2 + }, + { + "value": "third", + "index": 3 + } + ], + "groupAllRest": true, + "verticalHeaderUI": true, + "minimized": [] + } + }, + { + "columns": [ + { + "colType": "untagged", + "active": true, + "collapsed": false, + "name": "Backlogs", + "index": 1, + "id": 226119, + "sortCriterias": [], + "filters": { + "rootCondition": "any", + "filterGroups": [] + }, + "sortCriteria": [ + { + "criteria": "content", + "order": "desc", + "priority": 1, + "uid": "k6wjlc63" + } + ], + "minimized": false + }, + { + "id": 2485661779, + "index": 2, + "colType": "namedTag", + "active": false, + "collapsed": false, + "name": "#Task", + "coltag": "#Task", + "sortCriteria": [ + { + "criteria": "content", + "order": "asc", + "priority": 1, + "uid": "bmlpsogj" + } + ], + "tasksIdManualOrder": [ + "365", + 274, + "362" + ], + "minimized": false, + "filters": { + "rootCondition": "any", + "filterGroups": [] + } + }, + { + "id": 1936828579, + "index": 3, + "colType": "namedTag", + "active": false, + "collapsed": false, + "name": "*/seeding/*", + "coltag": "*/seeding/*" + }, + { + "colType": "namedTag", + "active": true, + "collapsed": false, + "name": "Can be implemented", + "index": 4, + "coltag": "#pending", + "id": 47626, + "filters": { + "rootCondition": "any", + "filterGroups": [] + }, + "minimized": false, + "sortCriteria": [ + { + "criteria": "manualOrder", + "order": "asc", + "priority": 1, + "uid": "pn3wvwal" + } + ], + "tasksIdManualOrder": [ + "375", + "389", + "224", + "355", + "276", + "258", + "378" + ] + }, + { + "colType": "namedTag", + "active": true, + "collapsed": false, + "name": "In Progress", + "index": 5, + "coltag": "working", + "id": 908753, + "filters": { + "rootCondition": "any", + "filterGroups": [] + }, + "minimized": false, + "workLimit": 5 + }, + { + "colType": "namedTag", + "active": true, + "collapsed": false, + "name": "In Review", + "index": 6, + "coltag": "Test", + "id": 396902, + "minimized": false, + "workLimit": 3 + }, + { + "colType": "namedTag", + "active": true, + "collapsed": false, + "name": "Ready to publish", + "index": 7, + "coltag": "done", + "id": 328227, + "minimized": false + }, + { + "id": 3193261849, + "index": 8, + "colType": "completed", + "active": true, + "collapsed": false, + "name": "Completed", + "limit": 20, + "filters": { + "rootCondition": "any", + "filterGroups": [] + }, + "minimized": false + } + ], + "filters": [ + "#Test", + "#working", + "*/seeding/*" + ], + "filterPolarity": "0", + "filterScope": "Both", + "name": "Tag based board (copy)", + "index": 3, + "showColumnTags": true, + "showFilteredTags": true, + "hideEmptyColumns": false, + "boardFilter": { + "rootCondition": "any", + "filterGroups": [] + }, + "taskCount": { + "pending": 60, + "completed": 15 + }, + "swimlanes": { + "enabled": false, + "showEmptySwimlanes": true, + "property": "tags", + "maxHeight": "500px", + "sortCriteria": "custom", + "customSortOrder": [ + { + "value": "first", + "index": 1 + }, + { + "value": "second", + "index": 2 + }, + { + "value": "third", + "index": 3 + } + ], + "groupAllRest": true, + "verticalHeaderUI": false + } + }, + { + "name": "Only column filters", + "index": 4, + "columns": [ + { + "id": 2050194674, + "index": 1, + "colType": "allPending", + "active": true, + "collapsed": false, + "name": "Backlogs", + "filters": { + "rootCondition": "any", + "filterGroups": [ + { + "id": "id-1768140055334-efs743ce1", + "groupCondition": "all", + "filters": [ + { + "id": "id-1768140055335-r98019pcb", + "property": "tags", + "condition": "isEmpty" + } + ] + } + ] + } + }, + { + "id": 850996788, + "index": 2, + "colType": "allPending", + "active": true, + "collapsed": false, + "name": "Important", + "filters": { + "rootCondition": "any", + "filterGroups": [ + { + "id": "id-1768140070366-ymlrpxfd5", + "groupCondition": "all", + "filters": [ + { + "id": "id-1768140070367-26fzfh3fi", + "property": "tags", + "condition": "contains", + "value": "#important" + } + ] + } + ] + } + }, + { + "id": 3370807172, + "index": 3, + "colType": "allPending", + "active": true, + "collapsed": false, + "name": "WIP", + "filters": { + "rootCondition": "any", + "filterGroups": [ + { + "id": "id-1768140095718-9u02uoxai", + "groupCondition": "all", + "filters": [ + { + "id": "id-1768140095719-sfcnddjpj", + "property": "tags", + "condition": "contains", + "value": "#wip" + } + ] + } + ] + } + }, + { + "id": 1957870296, + "index": 4, + "colType": "allPending", + "active": true, + "collapsed": false, + "name": "In Review", + "filters": { + "rootCondition": "any", + "filterGroups": [ + { + "id": "id-1768140118917-x50nyv0rm", + "groupCondition": "any", + "filters": [ + { + "id": "id-1768140118918-q6snn1uk7", + "property": "tags", + "condition": "contains", + "value": "#Test" + }, + { + "id": "id-1768140126093-wn5bwcpwz", + "property": "tags", + "condition": "contains", + "value": "#working" + } + ] + } + ] + }, + "workLimit": 3 + }, + { + "id": 4265650565, + "index": 5, + "colType": "completed", + "active": true, + "collapsed": false, + "name": "Completed", + "limit": 20 + } + ], + "hideEmptyColumns": false, + "showColumnTags": true, + "showFilteredTags": true, + "boardFilter": { + "rootCondition": "any", + "filterGroups": [] + }, + "swimlanes": { + "enabled": false, + "hideEmptySwimlanes": false, + "property": "tags", + "sortCriteria": "asc", + "minimized": [], + "maxHeight": "300px", + "verticalHeaderUI": false + }, + "taskCount": { + "pending": 63, + "completed": 14 + } + } + ], + "globalSettings": { + "lang": "zh-TW", + "scanFilters": { + "files": { + "polarity": 3, + "values": [ + "Testing Indented Task Bug.md", + "/\\b2025-\\d{2}-\\d{2}\\b/" + ] + }, + "folders": { + "polarity": 3, + "values": [ + "Task Board/1.7.0", + "/Notes/", + "Bible" + ] + }, + "tags": { + "polarity": 3, + "values": [ + "#CS", + "#placeholder/author", + "*/seeding/*" + ] + }, + "frontMatter": { + "polarity": 3, + "values": [ + "[\"created\": 2025-02-27]", + "[\"tags\": #TEST]", + "[\"background\": yellow]" + ] + } + }, + "firstDayOfWeek": "2", + "ignoreFileNameDates": false, + "taskCompletionFormat": "2", + "taskCompletionDateTimePattern": "YYYY-MM-DD/HH:mm", + "dailyNotesPluginComp": true, + "universalDateFormat": "YYYY-MM-DD", + "taskCompletionInLocalTime": true, + "taskCompletionShowUtcOffset": false, + "autoAddDue": true, + "scanVaultAtStartup": false, + "dayPlannerPlugin": false, + "realTimeScanner": true, + "columnWidth": "300px", + "showHeader": true, + "showFooter": true, + "showVerticalScroll": false, + "tagColors": [ + { + "name": "Bug", + "color": "rgba(131, 10, 18, 0.84)", + "priority": 1 + }, + { + "name": "feat", + "color": "rgba(115, 15, 151, 0.9490196078431372)", + "priority": 3 + }, + { + "name": "Test", + "color": "rgba(142, 157, 24, 1)", + "priority": 4 + }, + { + "name": "working", + "color": "rgba(22, 85, 17, 1)", + "priority": 5 + }, + { + "name": "New", + "color": "rgba(16, 50, 117, 1)", + "priority": 6 + }, + { + "name": "pending", + "color": "rgba(64, 20, 144, 1)", + "priority": 7 + }, + { + "name": "done", + "color": "rgba(14, 117, 84, 1)", + "priority": 7 + }, + { + "name": "*/seeding/*", + "color": "rgba(15, 121, 110, 1)", + "priority": 8 + }, + { + "name": "first", + "color": "rgba(13, 103, 37, 1)", + "priority": 9 + } + ], + "editButtonAction": "popUp", + "openOnStartup": false, + "customStatuses": [ + { + "symbol": " ", + "name": "Incomplete", + "nextStatusSymbol": "x", + "availableAsCommand": false, + "type": "TODO" + }, + { + "symbol": "X", + "name": "Complete", + "nextStatusSymbol": " ", + "availableAsCommand": false, + "type": "DONE" + }, + { + "symbol": "/", + "name": "In Progress", + "nextStatusSymbol": "x", + "availableAsCommand": true, + "type": "IN_PROGRESS" + }, + { + "symbol": "-", + "name": "Cancelled", + "nextStatusSymbol": " ", + "availableAsCommand": true, + "type": "CANCELLED" + }, + { + "symbol": ">", + "name": "Deferred", + "nextStatusSymbol": "x", + "availableAsCommand": false, + "type": "TODO" + }, + { + "symbol": "!", + "name": "Important", + "nextStatusSymbol": "x", + "availableAsCommand": false, + "type": "TODO" + }, + { + "symbol": "x", + "name": "Finished", + "nextStatusSymbol": " ", + "availableAsCommand": false, + "type": "DONE" + } + ], + "showTaskWithoutMetadata": false, + "tagColorsType": "text", + "compatiblePlugins": { + "dailyNotesPlugin": false, + "tasksPlugin": false, + "reminderPlugin": false, + "dayPlannerPlugin": true, + "quickAddPlugin": false + }, + "preDefinedNote": "Task_Board_Temp_Tasks.md", + "quickAddPluginDefaultChoice": "Task Board Temp Tasks", + "autoAddCreatedDate": true, + "autoAddUniversalDate": true, + "universalDate": "startDate", + "archivedTasksFilePath": "", + "showFileNameInCard": true, + "showFrontmatterTagsOnCards": true, + "tasksCacheFilePath": ".obsidian/plugins/task-board/tasks.json", + "notificationService": "obsiApp", + "frontmatterPropertyForReminder": "remind at", + "actions": [ + { + "enabled": true, + "trigger": "Complete", + "type": "move", + "targetColumn": "6" + } + ], + "cardSectionsVisibility": "hideBoth", + "hiddenTaskProperties": [], + "taskPropertyFormat": "3", + "taskNoteDefaultLocation": "", + "autoAddUniqueID": true, + "uniqueIdCounter": 586, + "experimentalFeatures": true, + "searchQuery": "", + "lastViewHistory": { + "viewedType": "kanban", + "boardIndex": 4, + "taskId": "", + "settingTab": 1 + }, + "taskNoteIdentifierTag": "task", + "doubleClickCardToEdit": "noteInTab", + "boundTaskCompletionToChildTasks": true, + "defaultStartTime": "23:59", + "archivedTBNotesFolderPath": "", + "frontmatterFormatting": [ + { + "index": 0, + "property": "ID", + "key": "id", + "taskItemKey": "id" + }, + { + "index": 1, + "property": "Title", + "key": "title", + "taskItemKey": "title" + }, + { + "index": 2, + "property": "Status", + "key": "status", + "taskItemKey": "status" + }, + { + "index": 3, + "property": "Priority", + "key": "priority", + "taskItemKey": "priority" + }, + { + "index": 4, + "property": "Tags", + "key": "tags", + "taskItemKey": "tags" + }, + { + "index": 5, + "property": "Time", + "key": "time", + "taskItemKey": "time" + }, + { + "index": 6, + "property": "Reminder", + "key": "reminder", + "taskItemKey": "reminder" + }, + { + "index": 7, + "property": "Created date", + "key": "created-date", + "taskItemKey": "createdDate" + }, + { + "index": 8, + "property": "Start date", + "key": "start-date", + "taskItemKey": "startDate" + }, + { + "index": 9, + "property": "Scheduled date", + "key": "scheduled", + "taskItemKey": "scheduledDate" + }, + { + "index": 10, + "property": "Due date", + "key": "due", + "taskItemKey": "due" + }, + { + "index": 11, + "property": "Depends on", + "key": "depends-on", + "taskItemKey": "dependsOn" + }, + { + "index": 12, + "property": "Cancelled date", + "key": "cancelled-date", + "taskItemKey": "cancelledDate" + }, + { + "index": 13, + "key": "completed-date", + "taskItemKey": "completionDate" + } + ], + "mapView": { + "background": "transparent", + "mapOrientation": "hor", + "optimizedRender": false, + "arrowDirection": "c2p", + "animatedEdges": false, + "scrollAction": "pan", + "showMinimap": true, + "renderVisibleNodes": true, + "edgeType": "default" + }, + "kanbanView": { + "lazyLoadingEnabled": true, + "initialTaskCount": 20, + "loadMoreCount": 10, + "scrollThresholdPercent": 80 + }, + "safeGuardFeature": true, + "visiblePropertiesList": [ + "id", + "priority", + "tags", + "time", + "reminder", + "createdDate", + "startDate", + "scheduledDate", + "dueDate", + "completionDate", + "cancelledDate", + "dependsOn", + "filePath", + "status", + "checkbox" + ], + "taskCardStyle": "emoji", + "autoAddCompletedDate": true, + "autoAddCancelledDate": true, + "scanMode": "manual" + } + } +} \ No newline at end of file From 15c5a9e727339178f865f129ba576886fd3cdf2e Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Tue, 20 Jan 2026 12:08:22 +0530 Subject: [PATCH 006/236] fix : the board switch tab is vanishing while swithching boards --- data.json | 5 ++- main.ts | 48 +++++++++++------------- src/components/KanbanView/LazyColumn.tsx | 2 +- src/components/TaskBoardViewContent.tsx | 11 +++--- src/managers/TaskBoardFileManager.ts | 22 ++++++++++- src/utils/taskNote/TaskNoteUtils.ts | 1 - src/views/TaskBoardView.tsx | 6 ++- 7 files changed, 55 insertions(+), 40 deletions(-) diff --git a/data.json b/data.json index 521af9a8..54550516 100644 --- a/data.json +++ b/data.json @@ -253,7 +253,7 @@ ], "lastViewHistory": { "viewedType": "kanban", - "boardIndex": 0, + "boardIndex": 1, "settingTab": 0, "taskId": "" }, @@ -274,6 +274,7 @@ "showMinimap": true, "renderVisibleNodes": false, "edgeType": "default" - } + }, + "searchQuery": "" } } \ No newline at end of file diff --git a/main.ts b/main.ts index 82028984..17694f9b 100644 --- a/main.ts +++ b/main.ts @@ -777,12 +777,6 @@ export default class TaskBoard extends Plugin { const totalFiles = this.deleteQueue.length; - // Show progress notice - this.currentProgressNotice = new Notice( - `Processing deleted files: 0/${totalFiles}`, - 0, - ); - let processed = 0; while (this.deleteQueue.length > 0) { const file = this.deleteQueue.shift()!; @@ -791,12 +785,18 @@ export default class TaskBoard extends Plugin { if ( fileTypeAllowedForScanning(this.plugin.settings.data, file) ) { + // Show progress notice + this.currentProgressNotice = new Notice( + `Processing deleted files: 0/${totalFiles}`, + 0, + ); + this.realTimeScanner.onFileDeleted(file); + + // Update progress notice + this.currentProgressNotice.messageEl.textContent = `Task Board : Processing deleted files: ${processed}/${totalFiles}`; } processed++; - - // Update progress notice - this.currentProgressNotice.messageEl.textContent = `Task Board : Processing deleted files: ${processed}/${totalFiles}`; } catch (error) { console.error( `Error processing deleted file ${file.path}:`, @@ -853,12 +853,6 @@ export default class TaskBoard extends Plugin { const totalFiles = this.createQueue.length; - // Show progress notice - this.currentProgressNotice = new Notice( - `Task Board : Processing created files: 0/${totalFiles}`, - 0, - ); - this.plugin.vaultScanner.refreshTasksFromFiles(this.createQueue, false); let processed = 0; @@ -866,18 +860,20 @@ export default class TaskBoard extends Plugin { const file = this.createQueue.shift()!; try { - // if ( - // fileTypeAllowedForScanning( - // this.plugin.settings.data, - // file - // ) - // ) { - // await this.realTimeScanner.processAllUpdatedFiles(file); - // } - processed++; + if ( + fileTypeAllowedForScanning(this.plugin.settings.data, file) + ) { + // Show progress notice + this.currentProgressNotice = new Notice( + `Task Board : Processing created files: 0/${totalFiles}`, + 0, + ); + // await this.realTimeScanner.processAllUpdatedFiles(file); - // Update progress notice - this.currentProgressNotice.messageEl.textContent = `Task Board : Processing created files: ${processed}/${totalFiles}`; + // Update progress notice + this.currentProgressNotice.messageEl.textContent = `Task Board : Processing created files: ${processed}/${totalFiles}`; + } + processed++; } catch (error) { console.error( `Error processing created file ${file.path}:`, diff --git a/src/components/KanbanView/LazyColumn.tsx b/src/components/KanbanView/LazyColumn.tsx index 8c2fd9bb..42e0a2ff 100644 --- a/src/components/KanbanView/LazyColumn.tsx +++ b/src/components/KanbanView/LazyColumn.tsx @@ -821,7 +821,7 @@ const LazyColumn: React.FC = ({ ); } // If insertIndex points to end (after last item) - if (insertIndex === localTasks.length) { + if (insertIndex === localTasks?.length) { elements.push(
Drop here
); diff --git a/src/components/TaskBoardViewContent.tsx b/src/components/TaskBoardViewContent.tsx index ce142587..8340a925 100644 --- a/src/components/TaskBoardViewContent.tsx +++ b/src/components/TaskBoardViewContent.tsx @@ -22,11 +22,11 @@ import { taskPropertiesNames, viewTypeNames } from "src/interfaces/Enums"; import { ScanVaultIcon, funnelIcon } from "src/interfaces/Icons"; import { bugReporterManagerInsatance } from "src/managers/BugReporter"; -const TaskBoardViewContent: React.FC<{ plugin: TaskBoard }> = ({ plugin }) => { +const TaskBoardViewContent: React.FC<{ plugin: TaskBoard, allBoards: Board[] }> = ({ plugin, allBoards }) => { // const [boards, setBoards] = useState(boardConfigs); const [activeBoardIndex, setActiveBoardIndex] = useState(plugin.settings.data.lastViewHistory.boardIndex ?? 0); const [currentBoardData, setCurrentBoardData] = useState(); - const [allBoardsData, setAllBoardsData] = useState(); + const [allBoardsData, setAllBoardsData] = useState(allBoards); const [allTasks, setAllTasks] = useState(); const [filteredTasks, setFilteredTasks] = useState(null); const [filteredTasksPerColumn, setFilteredTasksPerColumn] = useState([]); @@ -86,6 +86,7 @@ const TaskBoardViewContent: React.FC<{ plugin: TaskBoard }> = ({ plugin }) => { useEffect(() => { const fetchData = async () => { + console.log("TASK BOARD : Does this run while switching boards..."); try { const data = await plugin.taskBoardFileManager.loadBoard(activeBoardIndex); if (!data) throw "Board data not found."; @@ -97,8 +98,6 @@ const TaskBoardViewContent: React.FC<{ plugin: TaskBoard }> = ({ plugin }) => { setAllTasks(allTasks); setFreshInstall(false); } - - const allBoardsData = plugin.taskBoardFileManager.loadAllBoards(); } catch (error) { console.error( "Error loading tasks cache from disk\nIf this is appearing on a fresh install then no need to worry.\n", @@ -742,7 +741,7 @@ const TaskBoardViewContent: React.FC<{ plugin: TaskBoard }> = ({ plugin }) => { item.setTitle(t("open-board-configuration-modal")); item.setIcon("settings"); item.onClick(async () => { - openBoardConfigModal(plugin, allBoardsData!, activeBoardIndex, (updatedBoards, boardIndex) => { + openBoardConfigModal(plugin, allBoardsData, activeBoardIndex, (updatedBoards, boardIndex) => { // handleUpdateBoards(plugin, updatedBoards, setCurrentBoardData) if (activeBoardIndex === boardIndex) { setCurrentBoardData(updatedBoards[boardIndex]); @@ -903,7 +902,7 @@ const TaskBoardViewContent: React.FC<{ plugin: TaskBoard }> = ({ plugin }) => { className={`ConfigureBtn ${(isMobileView || Platform.isMobile) ? "taskBoardViewHeaderHideElements" : ""}`} aria-label={t("board-configure-button")} onClick={() => - openBoardConfigModal(plugin, allBoardsData!, activeBoardIndex, (updatedBoards, boardIndex) => { + openBoardConfigModal(plugin, allBoardsData, activeBoardIndex, (updatedBoards, boardIndex) => { // handleUpdateBoards(plugin, updatedBoards, setCurrentBoardData) if (activeBoardIndex === boardIndex) { setCurrentBoardData(updatedBoards[boardIndex]); diff --git a/src/managers/TaskBoardFileManager.ts b/src/managers/TaskBoardFileManager.ts index 43231cf6..ed715f1e 100644 --- a/src/managers/TaskBoardFileManager.ts +++ b/src/managers/TaskBoardFileManager.ts @@ -4,7 +4,7 @@ * This replaces the previous approach of storing all board data in data.json */ -import { App, TFile, Notice } from "obsidian"; +import { App, TFile, Notice, normalizePath } from "obsidian"; import { Board } from "src/interfaces/BoardConfigs"; import type TaskBoard from "main"; import { bugReporterManagerInsatance } from "./BugReporter"; @@ -80,7 +80,10 @@ export default class TaskBoardFileManager { // Convert board data to JSON string, then to Uint8Array for binary storage const jsonString = JSON.stringify(boardData); const uint8Array = new TextEncoder().encode(jsonString); - const arrayBuffer = uint8Array.buffer.slice(uint8Array.byteOffset, uint8Array.byteOffset + uint8Array.byteLength); + const arrayBuffer = uint8Array.buffer.slice( + uint8Array.byteOffset, + uint8Array.byteOffset + uint8Array.byteLength, + ); if (!fileExists) { // Create new file with binary data @@ -136,6 +139,21 @@ export default class TaskBoardFileManager { return false; } + const normalizedFilePath = normalizePath(filePath); + console.log( + "Original path :", + filePath, + "\nNormalized file path :", + normalizedFilePath, + ); + + const parts = normalizedFilePath.split("/"); + parts.pop(); + const folderPath = parts.join("/"); + if (!(await this.plugin.app.vault.adapter.exists(folderPath))) { + await this.plugin.app.vault.createFolder(folderPath); + } + return await this.saveBoardToFile(filePath, boardData); } catch (error) { console.error(`Error creating new board file ${filePath}:`, error); diff --git a/src/utils/taskNote/TaskNoteUtils.ts b/src/utils/taskNote/TaskNoteUtils.ts index cd566765..19fbcce1 100644 --- a/src/utils/taskNote/TaskNoteUtils.ts +++ b/src/utils/taskNote/TaskNoteUtils.ts @@ -14,7 +14,6 @@ import { PluginDataJson, } from "src/interfaces/GlobalSettings"; import { Notice, normalizePath } from "obsidian"; -import { bugReporter } from "src/services/OpenModals"; import { defaultTaskStatuses } from "src/interfaces/Enums"; import { bugReporterManagerInsatance } from "src/managers/BugReporter"; diff --git a/src/views/TaskBoardView.tsx b/src/views/TaskBoardView.tsx index 3162b5f2..1aadd1e6 100644 --- a/src/views/TaskBoardView.tsx +++ b/src/views/TaskBoardView.tsx @@ -62,7 +62,8 @@ export class TaskBoardView extends ItemView { if (mandatoryScanSignal) this.highlighgtScanvaultIcon(); // await this.loadBoards(); - this.renderBoard(); + const allBoardsData = this.plugin.taskBoardFileManager.loadAllBoards(); + this.renderBoard(allBoardsData); } @@ -92,12 +93,13 @@ export class TaskBoardView extends ItemView { // } // } - private renderBoard() { + private renderBoard(allBoardsData: Board[]) { this.root = createRoot(this.containerEl.children[1]); this.root.render( , , ); From 3b706c07d5eff6b1fe822b0c25f4755a72d3ae3d Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Tue, 20 Jan 2026 13:15:35 +0530 Subject: [PATCH 007/236] feat : seperate tabs for each board --- main.ts | 4 +- src/components/TaskBoardViewContent.tsx | 1 + src/interfaces/BoardConfigs.ts | 5 + src/managers/RealTimeScanner.ts | 28 +- src/managers/TaskBoardFileManager.ts | 351 ++++++++++++------------ src/views/TaskBoardView.tsx | 2 +- 6 files changed, 195 insertions(+), 196 deletions(-) diff --git a/main.ts b/main.ts index 17694f9b..2c334ccb 100644 --- a/main.ts +++ b/main.ts @@ -137,7 +137,7 @@ export default class TaskBoard extends Plugin { await this.vaultScanner.initializeTasksCache(); - await this.taskBoardFileManager.loadBoard(); + await this.taskBoardFileManager.loadAllBoards(); // Register events and commands only on Layout is ready this.app.workspace.onLayoutReady(() => { @@ -315,6 +315,8 @@ export default class TaskBoard extends Plugin { return this.view; }); + this.registerExtensions(["taskboard"], VIEW_TYPE_TASKBOARD); + // Register AddOrEditTask view (can be opened in tabs or popout windows) // this.registerView(VIEW_TYPE_ADD_OR_EDIT_TASK, (leaf) => { // console.log("Leaf returned by registerView :", leaf); diff --git a/src/components/TaskBoardViewContent.tsx b/src/components/TaskBoardViewContent.tsx index 8340a925..0043ff63 100644 --- a/src/components/TaskBoardViewContent.tsx +++ b/src/components/TaskBoardViewContent.tsx @@ -217,6 +217,7 @@ const TaskBoardViewContent: React.FC<{ plugin: TaskBoard, allBoards: Board[] }> plugin.processRenameQueue().then(() => console.log("Finished processing rename queue.")); setTimeout(() => { + eventEmitter.emit("REFRESH_BOARD"); console.log("Now will emit REFRESH_BOARD event..."); eventEmitter.emit("REFRESH_BOARD"); }, 100) diff --git a/src/interfaces/BoardConfigs.ts b/src/interfaces/BoardConfigs.ts index 1b41a279..3f06df7e 100644 --- a/src/interfaces/BoardConfigs.ts +++ b/src/interfaces/BoardConfigs.ts @@ -1,3 +1,4 @@ +import { generateRandomTempTaskId } from "src/utils/TaskItemUtils"; import { colTypeNames, defaultTaskStatuses } from "./Enums"; export interface columnSortingCriteria { @@ -127,6 +128,7 @@ export interface swimlaneConfigs { } export type Board = { + id: string; name: string; description?: string; index: number; @@ -226,6 +228,7 @@ export const DEFAULT_BOARDS: BoardConfigs = [ index: 6, }, ], + id: generateRandomTempTaskId(), name: "Time Based Workflow", index: 0, showColumnTags: false, @@ -292,6 +295,7 @@ export const DEFAULT_BOARDS: BoardConfigs = [ name: "Completed", }, ], + id: generateRandomTempTaskId(), name: "Tag Based Workflow", index: 1, showColumnTags: false, @@ -368,6 +372,7 @@ export const DEFAULT_BOARDS: BoardConfigs = [ index: 7, }, ], + id: generateRandomTempTaskId(), name: "Status Based Workflow", index: 2, showColumnTags: false, diff --git a/src/managers/RealTimeScanner.ts b/src/managers/RealTimeScanner.ts index 9a04ed9a..12c6d406 100644 --- a/src/managers/RealTimeScanner.ts +++ b/src/managers/RealTimeScanner.ts @@ -1,6 +1,6 @@ // /src/utils/RealTimeScanner.ts -import { App, TAbstractFile, TFile, TFolder } from "obsidian"; +import { App, Notice, TAbstractFile, TFile, TFolder } from "obsidian"; import type VaultScanner from "src/managers/VaultScanner"; import type TaskBoard from "main"; @@ -37,14 +37,14 @@ export class RealTimeScanner { try { localStorage.setItem( PENDING_SCAN_FILE_STACK, - JSON.stringify(this.taskBoardFileStack) + JSON.stringify(this.taskBoardFileStack), ); } catch (error) { bugReporterManagerInsatance.showNotice( 32, "Error saving file stack to localStorage.", String(error), - "RealTimeScanner.ts/saveStack" + "RealTimeScanner.ts/saveStack", ); } } @@ -57,7 +57,7 @@ export class RealTimeScanner { */ async processAllUpdatedFiles( currentFile?: TFile | string | undefined, - updatedTaskId?: string | undefined + updatedTaskId?: string | undefined, ) { // If a current file is provided, ensure it's included in the processing let newFile: TFile | null | undefined = null; @@ -86,7 +86,7 @@ export class RealTimeScanner { // Send all files for scanning and updating tasks result = await this.vaultScanner.refreshTasksFromFiles( files, - false + false, ); } @@ -98,6 +98,8 @@ export class RealTimeScanner { // Reset the editorModified flag after the scan. this.plugin.editorModified = false; + } else { + new Notice("Few files didnt got scanned..."); } setTimeout(() => { @@ -117,11 +119,11 @@ export class RealTimeScanner { if ( this.taskBoardFileStack.at(0) === undefined || !this.taskBoardFileStack.includes( - file instanceof TFile ? file.path : file + file instanceof TFile ? file.path : file, ) ) { this.taskBoardFileStack.push( - file instanceof TFile ? file.path : file + file instanceof TFile ? file.path : file, ); // Add the file to the stack this.saveStack(); // Save the updated stack } @@ -137,12 +139,11 @@ export class RealTimeScanner { onFileRenamed( file: TAbstractFile, oldPath: string, - archivedTaskNotesPath: string + archivedTaskNotesPath: string, ) { let foundFlag = false; // Find the oldPath inside the plugin.vaultScanner.tasksCache and replace it with the new file path. Please dont update it inside taskBoardFileStack. - const { Pending, Completed } = - this.plugin.vaultScanner.tasksCache; + const { Pending, Completed } = this.plugin.vaultScanner.tasksCache; [Pending, Completed].forEach((cache) => { if (cache && typeof cache === "object") { @@ -167,7 +168,7 @@ export class RealTimeScanner { } else if (file instanceof TFolder) { // Actually this is not at all needed as I am only running this function when a file is renamed. Also it was required because, it will anyways going to run of TFile, and if I run it for TFolder as well, it will run two files for the same file. If in case of child folders, it will too many times for the same file unnecessarily. const keysToUpdate = Object.keys(cache).filter((key) => - key.startsWith(oldPath + "/") + key.startsWith(oldPath + "/"), ); keysToUpdate.forEach((oldKey) => { const newKey = @@ -241,8 +242,7 @@ export class RealTimeScanner { } // Also remove the file from the tasks cache - const { Pending, Completed } = - this.plugin.vaultScanner.tasksCache; + const { Pending, Completed } = this.plugin.vaultScanner.tasksCache; [Pending, Completed].forEach((cache) => { if (cache && typeof cache === "object") { if (file instanceof TFile && cache.hasOwnProperty(file.path)) { @@ -251,7 +251,7 @@ export class RealTimeScanner { } else if (file instanceof TFolder) { // Actually this is not at all needed as I am only running this function when a file is deleted. Also it was required because, it will anyways going to run of TFile, and if I run it for TFolder as well, it will run two files for the same file. If in case of child folders, it will too many times for the same file unnecessarily. const keysToDelete = Object.keys(cache).filter((key) => - key.startsWith(file.path + "/") + key.startsWith(file.path + "/"), ); keysToDelete.forEach((key) => { delete cache[key]; diff --git a/src/managers/TaskBoardFileManager.ts b/src/managers/TaskBoardFileManager.ts index ed715f1e..d2556b0c 100644 --- a/src/managers/TaskBoardFileManager.ts +++ b/src/managers/TaskBoardFileManager.ts @@ -7,13 +7,11 @@ import { App, TFile, Notice, normalizePath } from "obsidian"; import { Board } from "src/interfaces/BoardConfigs"; import type TaskBoard from "main"; -import { bugReporterManagerInsatance } from "./BugReporter"; export default class TaskBoardFileManager { private app: App; private plugin: TaskBoard; private currentBoardIndex: number; - private currentBoardData: Board | null = null; private allBoardsData: Board[] | [] = []; constructor(plugin: TaskBoard) { @@ -38,20 +36,22 @@ export default class TaskBoardFileManager { } // Read the file - const file = this.app.vault.getAbstractFileByPath(filePath); - if (!file || !(file instanceof TFile)) { - console.warn(`Invalid TaskBoard file: ${filePath}`); - return null; - } + const file = await this.app.vault.adapter.readBinary(filePath); + const decodedData = new TextDecoder().decode(file); + console.log( + "Loaded file :", + file, + "\nDecoded data :\n", + decodedData, + ); - const fileContent = await this.app.vault.read(file); - if (!fileContent || fileContent.trim() === "") { + if (!decodedData) { console.warn(`TaskBoard file is empty: ${filePath}`); return null; } // Parse JSON content - const boardData: Board = JSON.parse(fileContent); + const boardData: Board = JSON.parse(decodedData); console.log( `Successfully loaded board from: ${filePath}`, boardData, @@ -63,6 +63,91 @@ export default class TaskBoardFileManager { } } + /** + * Loads all the boards data as per the file paths stored in the global settings. + * @returns All boards data as an array or an empty array if failed to load the boards. + */ + async loadAllBoards(): Promise { + try { + const boardFilesLocations = + this.plugin.settings.data.boardFilesLocation || []; + let allBoardsData: Board[] | [] = []; + + boardFilesLocations.forEach(async (boardFilePath: string) => { + if (!boardFilePath || boardFilePath.trim() === "") { + console.error( + `No board file path configured for index: ${this.currentBoardIndex}`, + ); + return []; + } + + // Load board from file + const boardData = await this.loadBoardFromFile(boardFilePath); + + if (boardData) { + // Cache the board data in memory + allBoardsData[boardData.index] = boardData; + console.log( + `Loaded and cached board "${boardData.name}" (index: ${boardData.index}) from: ${boardFilePath}`, + ); + + return boardData; + } else { + new Notice( + `Task Board : Error loading all boards data. Following board not found : ${boardFilePath}`, + ); + } + + return []; + }); + + this.allBoardsData = allBoardsData; + return allBoardsData; + } catch (error) { + console.error( + `Error loading board at index ${this.currentBoardIndex}:`, + error, + ); + return []; + } + } + + /** + * Loads the specific/current board data from the memory cache (allBoardsData) + * @param boardIndex - The index of the board to load + * @returns The board configuration object, or null if file cannot be loaded + */ + async loadBoard(boardIndex?: number): Promise { + try { + const currentBoardINdex = boardIndex ?? this.currentBoardIndex; + + // Validate board index + if ( + currentBoardINdex < 0 || + currentBoardINdex > this.allBoardsData.length - 1 + ) { + console.error( + `Invalid board index: ${currentBoardINdex}. Available boards: ${this.plugin.settings.data.boardFilesLocation.length}`, + ); + return null; + } + + const boardData = this.allBoardsData[currentBoardINdex]; + if (boardData) { + // Cache the board data in memory + this.currentBoardIndex = currentBoardINdex; + } + + return boardData; + } catch (error) { + console.error( + `Error loading board at index ${this.currentBoardIndex}:`, + error, + ); + return null; + } + } + /** * Save board configuration to a .taskboard file * @param filePath - The path to the .taskboard file @@ -107,6 +192,79 @@ export default class TaskBoardFileManager { } } + /** + * Save board configuration to disk by board index passed + * If no boardIndex has been passed, it will update currentBoard data + * Also updates the cached board data in memory + * @param updatedBoardData - The updated board configuration object + * @param boardIndex - (Optional) The index of the board to save + * @returns boolean - True if saved successfully, false otherwise + */ + async saveBoard( + updatedBoardData: Board, + boardIndex?: number, + ): Promise { + try { + const boardFilesLocation = + this.plugin.settings.data.boardFilesLocation || []; + + let boardIndexToUse = boardIndex; + if (!boardIndexToUse) { + boardIndexToUse = this.currentBoardIndex; + // if (!boardIndexToUse) { + // bugReporterManagerInsatance.showNotice( + // 90, + // "The TaskBoardFileManager instance dont contain an currentBoardIndex number", + // "ERROR : this.currentBoardIndex not found", + // "TaskBoardFileManager/saveCurrentBoard", + // ); + // return false; + // } + } + + // Validate board index + if ( + boardIndexToUse < 0 || + boardIndexToUse >= boardFilesLocation.length + ) { + console.error( + `Invalid board index: ${boardIndexToUse}. Available boards: ${boardFilesLocation.length}`, + ); + return false; + } + + const boardFilePath = boardFilesLocation[boardIndexToUse]; + if (!boardFilePath || boardFilePath.trim() === "") { + console.error( + `No board file path configured for index: ${boardIndexToUse}`, + ); + return false; + } + + // Save board to file + const success = await this.saveBoardToFile( + boardFilePath, + updatedBoardData, + ); + + if (success) { + this.allBoardsData[boardIndexToUse] = updatedBoardData; + + console.log( + `Saved board "${updatedBoardData.name}" (index: ${boardIndexToUse}) to: ${boardFilePath}`, + ); + } + + return success; + } catch (error) { + console.error( + `Error saving board at index ${boardIndex ?? this.currentBoardIndex}:`, + error, + ); + return false; + } + } + /** * Check if a .taskboard file exists * @param filePath - The path to the .taskboard file @@ -269,174 +427,8 @@ export default class TaskBoardFileManager { return createdCount; } - /** - * Load board configuration from disk by board index - * Reads the board file from boardFilesLocation based on the provided index - * Caches the board data in memory for quick access - * @param boardIndex - The index of the board to load - * @returns The board configuration object, or null if file cannot be loaded - */ - async loadBoard(boardIndex?: number): Promise { - try { - const boardFilesLocation = - this.plugin.settings.data.boardFilesLocation || []; - const currentBoardINdex = boardIndex ?? this.currentBoardIndex; - - // Validate board index - if ( - currentBoardINdex < 0 || - currentBoardINdex >= boardFilesLocation.length - ) { - console.error( - `Invalid board index: ${currentBoardINdex}. Available boards: ${boardFilesLocation.length}`, - ); - return null; - } - - const boardFilePath = boardFilesLocation[currentBoardINdex]; - if (!boardFilePath || boardFilePath.trim() === "") { - console.error( - `No board file path configured for index: ${currentBoardINdex}`, - ); - return null; - } - - // Load board from file - const boardData = await this.loadBoardFromFile(boardFilePath); - - if (boardData) { - // Cache the board data in memory - this.currentBoardData = boardData; - this.currentBoardIndex = currentBoardINdex; - console.log( - `Loaded and cached board "${boardData.name}" (index: ${currentBoardINdex}) from: ${boardFilePath}`, - ); - } - - return boardData; - } catch (error) { - console.error( - `Error loading board at index ${this.currentBoardIndex}:`, - error, - ); - return null; - } - } - - /** - * Save board configuration to disk by board index passed - * If no boardIndex has been passed, it will update currentBoard data - * Also updates the cached board data in memory - * @param updatedBoardData - The updated board configuration object - * @param boardIndex - (Optional) The index of the board to save - * @returns boolean - True if saved successfully, false otherwise - */ - async saveBoard( - updatedBoardData: Board, - boardIndex?: number, - ): Promise { - try { - const boardFilesLocation = - this.plugin.settings.data.boardFilesLocation || []; - - let boardIndexToUse = boardIndex; - if (!boardIndexToUse) { - boardIndexToUse = this.currentBoardIndex; - // if (!boardIndexToUse) { - // bugReporterManagerInsatance.showNotice( - // 90, - // "The TaskBoardFileManager instance dont contain an currentBoardIndex number", - // "ERROR : this.currentBoardIndex not found", - // "TaskBoardFileManager/saveCurrentBoard", - // ); - // return false; - // } - } - - // Validate board index - if ( - boardIndexToUse < 0 || - boardIndexToUse >= boardFilesLocation.length - ) { - console.error( - `Invalid board index: ${boardIndexToUse}. Available boards: ${boardFilesLocation.length}`, - ); - return false; - } - - const boardFilePath = boardFilesLocation[boardIndexToUse]; - if (!boardFilePath || boardFilePath.trim() === "") { - console.error( - `No board file path configured for index: ${boardIndexToUse}`, - ); - return false; - } - - // Save board to file - const success = await this.saveBoardToFile( - boardFilePath, - updatedBoardData, - ); - - if (success) { - // Update cached board data if this is the currently active board - if (boardIndexToUse === this.currentBoardIndex) { - this.currentBoardData = updatedBoardData; - } - - console.log( - `Saved board "${updatedBoardData.name}" (index: ${boardIndexToUse}) to: ${boardFilePath}`, - ); - } - - return success; - } catch (error) { - console.error( - `Error saving board at index ${boardIndex ?? this.currentBoardData}:`, - error, - ); - return false; - } - } - - loadAllBoards(): Board[] | [] { - try { - const boardFilesLocations = - this.plugin.settings.data.boardFilesLocation || []; - let allBoardsData: Board[] | [] = []; - - boardFilesLocations.forEach(async (boardFilePath: string) => { - if (!boardFilePath || boardFilePath.trim() === "") { - console.error( - `No board file path configured for index: ${this.currentBoardIndex}`, - ); - return []; - } - - // Load board from file - const boardData = await this.loadBoardFromFile(boardFilePath); - - if (boardData) { - // Cache the board data in memory - allBoardsData[boardData.index] = boardData; - console.log( - `Loaded and cached board "${boardData.name}" (index: ${boardData.index}) from: ${boardFilePath}`, - ); - } else { - new Notice( - `Task Board : Error loading all boards data. Following board not found : ${boardFilePath}`, - ); - } - }); - - return allBoardsData; - } catch (error) { - console.error( - `Error loading board at index ${this.currentBoardIndex}:`, - error, - ); - return []; - } + async getAllBoards(): Promise { + return this.allBoardsData; } /** @@ -445,7 +437,7 @@ export default class TaskBoardFileManager { * @returns The cached board data, or null if no board has been loaded */ async getCurrentBoardData(): Promise { - return this.currentBoardData; + return this.allBoardsData[this.currentBoardIndex]; } /** @@ -461,7 +453,6 @@ export default class TaskBoardFileManager { * Useful when switching between different boards or clearing state */ clearCurrentBoardCache(): void { - this.currentBoardData = null; this.allBoardsData = []; console.log("Cleared cached board data"); } diff --git a/src/views/TaskBoardView.tsx b/src/views/TaskBoardView.tsx index 1aadd1e6..12eea490 100644 --- a/src/views/TaskBoardView.tsx +++ b/src/views/TaskBoardView.tsx @@ -62,7 +62,7 @@ export class TaskBoardView extends ItemView { if (mandatoryScanSignal) this.highlighgtScanvaultIcon(); // await this.loadBoards(); - const allBoardsData = this.plugin.taskBoardFileManager.loadAllBoards(); + const allBoardsData = await this.plugin.taskBoardFileManager.getAllBoards(); this.renderBoard(allBoardsData); From 3a38b98589c1309ffd06d69245e04f25b57ea899 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Tue, 20 Jan 2026 13:15:50 +0530 Subject: [PATCH 008/236] test latest changes | Working with exceptions --- data.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data.json b/data.json index 54550516..05d8a324 100644 --- a/data.json +++ b/data.json @@ -252,8 +252,8 @@ "TaskBoard/Boards/Status Based Workflow.taskboard" ], "lastViewHistory": { - "viewedType": "kanban", - "boardIndex": 1, + "viewedType": "map", + "boardIndex": 2, "settingTab": 0, "taskId": "" }, From d4520f1c9ee4f3ddd0591dbe1b5f52507730206a Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Tue, 20 Jan 2026 22:57:31 +0530 Subject: [PATCH 009/236] draf : intercept the file click event to modify state data --- Chat.md | 120 ++++++++++++++++++++++++++++ data.json | 8 +- main.ts | 47 +++++++++++ src/utils/MarkdownFileOperations.ts | 1 - src/views/TaskBoardView.tsx | 21 ++++- task-board-logs.log | 69 ---------------- 6 files changed, 188 insertions(+), 78 deletions(-) create mode 100644 Chat.md diff --git a/Chat.md b/Chat.md new file mode 100644 index 00000000..763717e3 --- /dev/null +++ b/Chat.md @@ -0,0 +1,120 @@ +# Copilot Chat Conversation Export: Custom view for file clicks in + +**User:** @tu2-atmanand +**Thread URL:** https://github.com/copilot/c/9b862551-e33b-4adc-b4fa-d6d6956c67b9 + +## @tu2-atmanand + +I am trying to implement a functionality wherein, if I click on a particular type of file from the Obsidian's File Navigator, it should open my custom view. I can see that in this excalidraw plugin project, it has been done from the line 279 to 294. I have used exactly simlar code in my tu2-atmanand/task-board project, but the thing I am not able to understand is, how to find out on which file the user has clicked from the File Navigator. Because, right now in my project the view properly opens when I click on the file type I want to. But, it renders the default data, instead it was supposed to render the data from the file user has clicked to open the view. + +Can you please go through this excalidraw project and let me know how to exactly know on which file user has clicked and then load the data from that particular file and render inside the my custom view opened by Obsidian. + +## Copilot + +Short answer: when a file is clicked the file path is passed in the view state as state.state.file (Obsidian calls leaf.setViewState({ type: "markdown", state: { file: "" } })). The Excalidraw plugin intercepts that in its WorkspaceLeaf.setViewState monkey patch, swaps the view type to the custom view, and then the custom view receives the TFile (as this.file) and the file text (via setViewData / this.data). You should load the file contents from this.file (or from the state.file string) in your view's setViewData / onOpen, not rely only on constructor defaults. + +What Excalidraw does (how you should copy it) +- It intercepts workspace leaf setViewState and looks at state.type === "markdown" && state.state?.file (the clicked file path is state.state.file). +- If the file should open in Excalidraw, it replaces state.type with VIEW_TYPE_EXCALIDRAW and calls the original setViewState with the new state. (See registerMonkeyPatches -> WorkspaceLeaf.prototype.setViewState.) +- Because Obsidian will create the new view with the state that contains the file path, the view gets a TFile assigned to this.file and gets its contents delivered to setViewData. ExcalidrawView implements setViewData / loadDrawing which read this.file and this.data and render the scene. + +How to implement this in your plugin (concrete steps + minimal code) + +1) Intercept/force opening your custom view when a user clicks the file (optional) +- If you want clicks on files with extension .foo to open your view by default, use the same pattern as Excalidraw: around/monkey-patch WorkspaceLeaf.prototype.setViewState and when state.type === "markdown" && state.state?.file and fileShouldBeCustom(state.state.file) — replace state.type with YOUR_VIEW_TYPE then call next(newState,...). That ensures Obsidian creates your view and passes the file path along. + +Example (pseudo-code): +```ts +// inside onload() or after workspace ready +this.register( + around(WorkspaceLeaf.prototype, { + setViewState(next) { + return function(state: ViewState, ...rest: any[]) { + const markdownViewLoaded = state.type === "markdown" && state.state?.file; + if (markdownViewLoaded && fileShouldBeCustom(state.state.file)) { + const newState = { ...state, type: YOUR_VIEW_TYPE }; + return next.apply(this, [newState, ...rest]); + } + return next.apply(this, [state, ...rest]); + }; + } + }) +); +``` +Note: fileShouldBeCustom() is your function for matching extension/path. + +2) Make your view read the file passed in and render it +- Prefer extending TextFileView (or a FileView variant that receives a TFile). When Obsidian opens the view for a file, it will set this.file and call setViewData(data: string). Implement setViewData to store the incoming data and call your rendering logic. + +Minimal view code outline: +```ts +import { TextFileView, WorkspaceLeaf, TFile } from "obsidian"; + +export default class MyCustomView extends TextFileView { + constructor(leaf: WorkspaceLeaf, plugin: MyPlugin) { + super(leaf); + this.plugin = plugin; + } + + getViewType() { return "my-custom-view"; } + getDisplayText() { return this.file?.basename ?? "No file"; } + + // Obsidian calls this when the file content is provided/changed: + async setViewData(data: string, clear: boolean = false) { + // `this.file` is the TFile for the file the user clicked + if (!this.file) return; + this.data = data; // store it if you want, or read it fresh + // If you need the TFile object: + const tfile: TFile = this.file; + // Read direct from vault if needed: + // const content = await this.app.vault.read(tfile); + + // Now parse `data` (or content) and render inside your view: + this.renderFromFileData(data); + } + + async onOpen() { + // If for some reason setViewData wasn't called yet, you can get the file path: + const state = this.leaf.getViewState(); + const pathFromState = state?.state?.file; // string path + // or just rely on this.file and the setViewData call + } + + renderFromFileData(data: string) { + // your custom rendering + } +} +``` + +Why your view probably shows default data now +- You likely open your view but never consume the file passed by Obsidian (this.file / setViewData). Common mistakes: + - Creating your view that ignores this.file and uses a default initial state set in constructor. + - Not extending TextFileView or not implementing setViewData, so Obsidian does not provide the file text into your view lifecycle. + - Trying to read file path from the DOM or from active leaf wrongly; use this.file or state.state.file. + +Alternative if you don't want monkey patching +- If you only register your view and file extension (registerExtensions([...], MY_VIEW_TYPE)), clicking the file will open your view directly. You still must read the file as above. If your view isn't a TextFileView, in onOpen you can take the path string from leaf.getViewState()?.state?.file and then call this.app.vault.getAbstractFileByPath(path) and then this.app.vault.read(file) to get content. + +Example (non-TextFileView): +```ts +async onOpen() { + const path = this.leaf.getViewState()?.state?.file; + if (!path) return; + const file = this.app.vault.getAbstractFileByPath(path); + if (file && file instanceof TFile) { + const content = await this.app.vault.read(file); + this.renderFromFileData(content); + } +} +``` + +Summary (what to do next) +- Inspect your view class: + - If it extends TextFileView: implement/verify setViewData reads this.data or this.file and feeds that into your renderer. + - If it does not extend TextFileView: in onOpen read the path from this.leaf.getViewState().state.file and read via app.vault.read(TFile). +- If you want clicks to automatically open your view instead of the default markdown view, intercept WorkspaceLeaf.setViewState as Excalidraw does and replace the type with your view type (and include the file path in state so you still receive it). +- If you prefer, paste the minimal code of your view.onOpen / setViewData here and I can point out exactly what to change. + +If you want, I can: +- point to the exact lines in Excalidraw that show state.state.file being used (I already looked; the WorkspaceLeaf.setViewState monkey patch is where the file path is inspected), +- or review your tu2-atmanand/task-board view code and show the exact place where you should read this.file or the state file path. diff --git a/data.json b/data.json index 05d8a324..2f9930b5 100644 --- a/data.json +++ b/data.json @@ -139,7 +139,7 @@ "reminderPlugin": false, "quickAddPlugin": false }, - "preDefinedNote": "Meta/Task_Board/New_Tasks.md", + "preDefinedNote": "TEmp TEst/All Tasks.md", "taskNoteIdentifierTag": "taskNote", "taskNoteDefaultLocation": "Meta/Task_Board/Task_Notes", "quickAddPluginDefaultChoice": "", @@ -252,9 +252,9 @@ "TaskBoard/Boards/Status Based Workflow.taskboard" ], "lastViewHistory": { - "viewedType": "map", + "viewedType": "kanban", "boardIndex": 2, - "settingTab": 0, + "settingTab": 3, "taskId": "" }, "boundTaskCompletionToChildTasks": false, @@ -269,7 +269,7 @@ "mapOrientation": "hor", "optimizedRender": false, "arrowDirection": "c2p", - "animatedEdges": true, + "animatedEdges": false, "scrollAction": "zoom", "showMinimap": true, "renderVisibleNodes": false, diff --git a/main.ts b/main.ts index 2c334ccb..769f8898 100644 --- a/main.ts +++ b/main.ts @@ -311,12 +311,16 @@ export default class TaskBoard extends Plugin { registerTaskBoardView() { this.registerView(VIEW_TYPE_TASKBOARD, (leaf) => { + console.log("Leaf :", leaf); this.view = new TaskBoardView(this, leaf); return this.view; }); this.registerExtensions(["taskboard"], VIEW_TYPE_TASKBOARD); + // Monkey-patch WorkspaceLeaf.setViewState to intercept .taskboard file clicks + this.registerMonkeyPatchForTaskboardFiles(); + // Register AddOrEditTask view (can be opened in tabs or popout windows) // this.registerView(VIEW_TYPE_ADD_OR_EDIT_TASK, (leaf) => { // console.log("Leaf returned by registerView :", leaf); @@ -335,6 +339,49 @@ export default class TaskBoard extends Plugin { // }); } + /** + * Monkey-patch WorkspaceLeaf.setViewState to intercept .taskboard file clicks + * When a user clicks on a .taskboard file in the File Navigator, this intercepts + * the default markdown view and opens it in the TaskBoard custom view instead, + * while preserving the file path in the view state + */ + private registerMonkeyPatchForTaskboardFiles() { + const originalSetViewState = WorkspaceLeaf.prototype.setViewState; + + WorkspaceLeaf.prototype.setViewState = function ( + state: any, + eState?: any, + ) { + console.log("WorkspaceLeaf.setViewState called with state:", state); + // Check if this is a markdown view being opened for a .taskboard file + // const isMarkdownView = state.type === "markdown"; + const isTaskBoardView = state.type === VIEW_TYPE_TASKBOARD; + const filePath = state.state?.file as string | undefined; + const isTaskboardFile = filePath && filePath.endsWith(".taskboard"); + + if (isTaskBoardView && isTaskboardFile) { + // Replace the view type with our custom TaskBoard view + const newState = { + ...state, + type: VIEW_TYPE_TASKBOARD, + // Keep the file path in state so TaskBoardView can access it + state: { + ...state.state, + file: filePath, + }, + }; + console.log( + `TaskBoard: Intercepted .taskboard file click: ${filePath}\nNew state:`, + newState, + ); + return originalSetViewState.call(this, newState, eState); + } + + // For all other view types, proceed normally + return originalSetViewState.call(this, state, eState); + }; + } + registerEditorExtensions() { // TODO : The below editor extension will not going to be released in the upcoming version, will plan it for the next version. // Register task gutter extension diff --git a/src/utils/MarkdownFileOperations.ts b/src/utils/MarkdownFileOperations.ts index 492f1260..deb6b30e 100644 --- a/src/utils/MarkdownFileOperations.ts +++ b/src/utils/MarkdownFileOperations.ts @@ -2,7 +2,6 @@ import { TFile } from "obsidian"; import type TaskBoard from "main"; -import { bugReporter } from "src/services/OpenModals"; import { bugReporterManagerInsatance } from "src/managers/BugReporter"; /** diff --git a/src/views/TaskBoardView.tsx b/src/views/TaskBoardView.tsx index 12eea490..b113d980 100644 --- a/src/views/TaskBoardView.tsx +++ b/src/views/TaskBoardView.tsx @@ -61,11 +61,24 @@ export class TaskBoardView extends ItemView { if (mandatoryScanSignal) this.highlighgtScanvaultIcon(); - // await this.loadBoards(); - const allBoardsData = await this.plugin.taskBoardFileManager.getAllBoards(); - this.renderBoard(allBoardsData); - + // Check if a specific .taskboard file was clicked from File Navigator + const state = this.leaf.getViewState(); + console.log("TaskBoardView.tsx : state :", state); + const clickedFilePath = state?.state?.file as string | undefined; + + let allBoardsData: Board[]; + + if (clickedFilePath && typeof clickedFilePath === 'string' && clickedFilePath.endsWith('.taskboard')) { + // User clicked on a specific .taskboard file - load just that file + console.log(`TaskBoardView: Loading clicked file: ${clickedFilePath}`); + const specificBoardData = await this.plugin.taskBoardFileManager.loadBoardFromFile(clickedFilePath); + allBoardsData = specificBoardData ? [specificBoardData] : []; + } else { + // No specific file clicked - load all boards as usual + allBoardsData = await this.plugin.taskBoardFileManager.getAllBoards(); + } + this.renderBoard(allBoardsData); } async highlighgtScanvaultIcon() { diff --git a/task-board-logs.log b/task-board-logs.log index b0888e47..b9ce2899 100644 --- a/task-board-logs.log +++ b/task-board-logs.log @@ -23,72 +23,3 @@ ## Recent Bug Reports -Timestamp : 2026-01-15T16:47:07.456Z -ID : 4 -Message : Error showing filter popover -Context : Column.tsx/column-menu/configure-conlum-filters - -#### Bug Content -```log -Dont show popover. -``` - -------------- - -Timestamp : 2026-01-15T16:47:11.710Z -ID : 27 -Message : Error in Sortable onSort for child tasks -Context : AddOrEditTaskRC.tsx/childTasksListRef useEffect - -#### Bug Content -```log -Testing the new bug reporter. -``` - -------------- - -Timestamp : 2026-01-15T16:47:59.447Z -ID : 4 -Message : Error showing filter popover -Context : Column.tsx/column-menu/configure-conlum-filters - -#### Bug Content -```log -Dont show popover. -``` - -------------- - -Timestamp : 2026-01-19T17:02:54.793Z -ID : 29 -Message : Error showing filter popover -Context : TaskBoardViewContent.tsx/handleFilterButtonClick - -#### Bug Content -```log -TypeError: Cannot read properties of undefined (reading 'boardFilter') -``` - -------------- - -Timestamp : 2026-01-19T17:34:31.126Z -ID : 90 -Message : The TaskBoardFileManager instance dont contain an currentBoardIndex number -Context : TaskBoardFileManager/saveCurrentBoard - -#### Bug Content -```log -ERROR : this.currentBoardIndex not found -``` - -------------- - -Timestamp : 2026-01-19T17:40:47.093Z -ID : 90 -Message : The TaskBoardFileManager instance dont contain an currentBoardIndex number -Context : TaskBoardFileManager/saveCurrentBoard - -#### Bug Content -```log -ERROR : this.currentBoardIndex not found -``` From db30c3eea03bd718674736a5d1be2d1b4b11410a Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Tue, 20 Jan 2026 23:18:06 +0530 Subject: [PATCH 010/236] working intercept method | Need to pass the loaded board data as seperate param --- main.ts | 27 ++++++++++++++++++++------- src/interfaces/BoardConfigs.ts | 8 ++++---- src/views/TaskBoardView.tsx | 8 +++++--- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/main.ts b/main.ts index 769f8898..ae53f655 100644 --- a/main.ts +++ b/main.ts @@ -359,22 +359,35 @@ export default class TaskBoard extends Plugin { const filePath = state.state?.file as string | undefined; const isTaskboardFile = filePath && filePath.endsWith(".taskboard"); + console.log( + `Checking: isMarkdownView=${isTaskBoardView}, filePath=${filePath}, isTaskboardFile=${isTaskboardFile}`, + ); + if (isTaskBoardView && isTaskboardFile) { // Replace the view type with our custom TaskBoard view const newState = { ...state, type: VIEW_TYPE_TASKBOARD, - // Keep the file path in state so TaskBoardView can access it - state: { - ...state.state, - file: filePath, - }, }; console.log( - `TaskBoard: Intercepted .taskboard file click: ${filePath}\nNew state:`, + `TaskBoard: Intercepted .taskboard file click: ${filePath}`, + ); + + // Store the file path directly on the leaf instance for immediate access + (this as any).taskboardFilePath = filePath; + console.log(`TaskBoard: Stored file path on leaf: ${filePath}`); + + // Call the original setViewState with the new state + const result = originalSetViewState.call( + this, newState, + eState, ); - return originalSetViewState.call(this, newState, eState); + + // Also set ephemeral state for safety + this.setEphemeralState({ taskboardFilePath: filePath }); + + return result; } // For all other view types, proceed normally diff --git a/src/interfaces/BoardConfigs.ts b/src/interfaces/BoardConfigs.ts index 3f06df7e..553c2427 100644 --- a/src/interfaces/BoardConfigs.ts +++ b/src/interfaces/BoardConfigs.ts @@ -128,7 +128,7 @@ export interface swimlaneConfigs { } export type Board = { - id: string; + // id: string; name: string; description?: string; index: number; @@ -228,7 +228,7 @@ export const DEFAULT_BOARDS: BoardConfigs = [ index: 6, }, ], - id: generateRandomTempTaskId(), + // id: generateRandomTempTaskId(), name: "Time Based Workflow", index: 0, showColumnTags: false, @@ -295,7 +295,7 @@ export const DEFAULT_BOARDS: BoardConfigs = [ name: "Completed", }, ], - id: generateRandomTempTaskId(), + // id: generateRandomTempTaskId(), name: "Tag Based Workflow", index: 1, showColumnTags: false, @@ -372,7 +372,7 @@ export const DEFAULT_BOARDS: BoardConfigs = [ index: 7, }, ], - id: generateRandomTempTaskId(), + // id: generateRandomTempTaskId(), name: "Status Based Workflow", index: 2, showColumnTags: false, diff --git a/src/views/TaskBoardView.tsx b/src/views/TaskBoardView.tsx index b113d980..75136f09 100644 --- a/src/views/TaskBoardView.tsx +++ b/src/views/TaskBoardView.tsx @@ -62,9 +62,10 @@ export class TaskBoardView extends ItemView { if (mandatoryScanSignal) this.highlighgtScanvaultIcon(); // Check if a specific .taskboard file was clicked from File Navigator - const state = this.leaf.getViewState(); - console.log("TaskBoardView.tsx : state :", state); - const clickedFilePath = state?.state?.file as string | undefined; + // First check the leaf instance directly (set by monkey patch) + const clickedFilePath = (this.leaf as any).taskboardFilePath as string | undefined; + + console.log("TaskBoardView.tsx : clickedFilePath from leaf:", clickedFilePath); let allBoardsData: Board[]; @@ -75,6 +76,7 @@ export class TaskBoardView extends ItemView { allBoardsData = specificBoardData ? [specificBoardData] : []; } else { // No specific file clicked - load all boards as usual + console.log("TaskBoardView: No specific file, loading all boards"); allBoardsData = await this.plugin.taskBoardFileManager.getAllBoards(); } From 5de9ab930d6fea37878b3bad90addc7ceb155745 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Tue, 20 Jan 2026 23:40:57 +0530 Subject: [PATCH 011/236] fix : prototype cleanup for manal patching method --- main.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/main.ts b/main.ts index ae53f655..bb22fab4 100644 --- a/main.ts +++ b/main.ts @@ -393,6 +393,12 @@ export default class TaskBoard extends Plugin { // For all other view types, proceed normally return originalSetViewState.call(this, state, eState); }; + + // Register cleanup handler to restore the original prototype when plugin unloads + // This prevents memory leaks and ensures the patch is properly removed + this.register( + () => (WorkspaceLeaf.prototype.setViewState = originalSetViewState), + ); } registerEditorExtensions() { From a7020ab2140cce2f514734690e1420c840a21ea7 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Tue, 20 Jan 2026 23:48:43 +0530 Subject: [PATCH 012/236] use monkey-around for handling all patching stuff --- main.ts | 74 +++++++++++++++++++-------------------------------------- 1 file changed, 24 insertions(+), 50 deletions(-) diff --git a/main.ts b/main.ts index bb22fab4..4d132525 100644 --- a/main.ts +++ b/main.ts @@ -1,5 +1,6 @@ // main.ts +import { around } from "monkey-around"; import { App, Notice, @@ -346,59 +347,32 @@ export default class TaskBoard extends Plugin { * while preserving the file path in the view state */ private registerMonkeyPatchForTaskboardFiles() { - const originalSetViewState = WorkspaceLeaf.prototype.setViewState; - - WorkspaceLeaf.prototype.setViewState = function ( - state: any, - eState?: any, - ) { - console.log("WorkspaceLeaf.setViewState called with state:", state); - // Check if this is a markdown view being opened for a .taskboard file - // const isMarkdownView = state.type === "markdown"; - const isTaskBoardView = state.type === VIEW_TYPE_TASKBOARD; - const filePath = state.state?.file as string | undefined; - const isTaskboardFile = filePath && filePath.endsWith(".taskboard"); - - console.log( - `Checking: isMarkdownView=${isTaskBoardView}, filePath=${filePath}, isTaskboardFile=${isTaskboardFile}`, - ); - - if (isTaskBoardView && isTaskboardFile) { - // Replace the view type with our custom TaskBoard view - const newState = { - ...state, - type: VIEW_TYPE_TASKBOARD, - }; - console.log( - `TaskBoard: Intercepted .taskboard file click: ${filePath}`, - ); - - // Store the file path directly on the leaf instance for immediate access - (this as any).taskboardFilePath = filePath; - console.log(`TaskBoard: Stored file path on leaf: ${filePath}`); - - // Call the original setViewState with the new state - const result = originalSetViewState.call( - this, - newState, - eState, - ); - - // Also set ephemeral state for safety - this.setEphemeralState({ taskboardFilePath: filePath }); - - return result; - } + // Use monkey-around to safely patch WorkspaceLeaf.prototype.setViewState + // This allows multiple plugins to patch the same method without conflicts + const unregisterPatch = around(WorkspaceLeaf.prototype, { + setViewState: (next) => + function (this: WorkspaceLeaf, state: any, eState?: any) { + const isTaskBoardView = state.type === VIEW_TYPE_TASKBOARD; + const filePath = state.state?.file as string | undefined; + const isTaskboardFile = + filePath && filePath.endsWith(".taskboard"); + + if (isTaskBoardView && isTaskboardFile) { + // Store the file path directly on the leaf instance for immediate access + (this as any).taskboardFilePath = filePath; + + // Also set ephemeral state for safety + this.setEphemeralState({ taskboardFilePath: filePath }); + } - // For all other view types, proceed normally - return originalSetViewState.call(this, state, eState); - }; + // Call the next method in the chain (original or other patches) + return next.call(this, state, eState); + }, + }); - // Register cleanup handler to restore the original prototype when plugin unloads + // Register cleanup handler to unregister the patch when plugin unloads // This prevents memory leaks and ensures the patch is properly removed - this.register( - () => (WorkspaceLeaf.prototype.setViewState = originalSetViewState), - ); + this.register(unregisterPatch); } registerEditorExtensions() { From ca915636241d22d26cc86518f57cf8128f9202de Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Wed, 21 Jan 2026 22:10:18 +0530 Subject: [PATCH 013/236] fix : select board from file not opening --- Chat.md | 120 ---------- data.json | 21 +- main.ts | 28 ++- src/components/TaskBoardViewContent.tsx | 16 +- src/interfaces/BoardConfigs.ts | 12 +- src/interfaces/GlobalSettings.ts | 24 +- src/managers/TaskBoardFileManager.ts | 302 ++++++++++++++++++------ src/modals/BoardConfigModal.tsx | 41 ++-- src/views/TaskBoardView.tsx | 20 +- 9 files changed, 324 insertions(+), 260 deletions(-) delete mode 100644 Chat.md diff --git a/Chat.md b/Chat.md deleted file mode 100644 index 763717e3..00000000 --- a/Chat.md +++ /dev/null @@ -1,120 +0,0 @@ -# Copilot Chat Conversation Export: Custom view for file clicks in - -**User:** @tu2-atmanand -**Thread URL:** https://github.com/copilot/c/9b862551-e33b-4adc-b4fa-d6d6956c67b9 - -## @tu2-atmanand - -I am trying to implement a functionality wherein, if I click on a particular type of file from the Obsidian's File Navigator, it should open my custom view. I can see that in this excalidraw plugin project, it has been done from the line 279 to 294. I have used exactly simlar code in my tu2-atmanand/task-board project, but the thing I am not able to understand is, how to find out on which file the user has clicked from the File Navigator. Because, right now in my project the view properly opens when I click on the file type I want to. But, it renders the default data, instead it was supposed to render the data from the file user has clicked to open the view. - -Can you please go through this excalidraw project and let me know how to exactly know on which file user has clicked and then load the data from that particular file and render inside the my custom view opened by Obsidian. - -## Copilot - -Short answer: when a file is clicked the file path is passed in the view state as state.state.file (Obsidian calls leaf.setViewState({ type: "markdown", state: { file: "" } })). The Excalidraw plugin intercepts that in its WorkspaceLeaf.setViewState monkey patch, swaps the view type to the custom view, and then the custom view receives the TFile (as this.file) and the file text (via setViewData / this.data). You should load the file contents from this.file (or from the state.file string) in your view's setViewData / onOpen, not rely only on constructor defaults. - -What Excalidraw does (how you should copy it) -- It intercepts workspace leaf setViewState and looks at state.type === "markdown" && state.state?.file (the clicked file path is state.state.file). -- If the file should open in Excalidraw, it replaces state.type with VIEW_TYPE_EXCALIDRAW and calls the original setViewState with the new state. (See registerMonkeyPatches -> WorkspaceLeaf.prototype.setViewState.) -- Because Obsidian will create the new view with the state that contains the file path, the view gets a TFile assigned to this.file and gets its contents delivered to setViewData. ExcalidrawView implements setViewData / loadDrawing which read this.file and this.data and render the scene. - -How to implement this in your plugin (concrete steps + minimal code) - -1) Intercept/force opening your custom view when a user clicks the file (optional) -- If you want clicks on files with extension .foo to open your view by default, use the same pattern as Excalidraw: around/monkey-patch WorkspaceLeaf.prototype.setViewState and when state.type === "markdown" && state.state?.file and fileShouldBeCustom(state.state.file) — replace state.type with YOUR_VIEW_TYPE then call next(newState,...). That ensures Obsidian creates your view and passes the file path along. - -Example (pseudo-code): -```ts -// inside onload() or after workspace ready -this.register( - around(WorkspaceLeaf.prototype, { - setViewState(next) { - return function(state: ViewState, ...rest: any[]) { - const markdownViewLoaded = state.type === "markdown" && state.state?.file; - if (markdownViewLoaded && fileShouldBeCustom(state.state.file)) { - const newState = { ...state, type: YOUR_VIEW_TYPE }; - return next.apply(this, [newState, ...rest]); - } - return next.apply(this, [state, ...rest]); - }; - } - }) -); -``` -Note: fileShouldBeCustom() is your function for matching extension/path. - -2) Make your view read the file passed in and render it -- Prefer extending TextFileView (or a FileView variant that receives a TFile). When Obsidian opens the view for a file, it will set this.file and call setViewData(data: string). Implement setViewData to store the incoming data and call your rendering logic. - -Minimal view code outline: -```ts -import { TextFileView, WorkspaceLeaf, TFile } from "obsidian"; - -export default class MyCustomView extends TextFileView { - constructor(leaf: WorkspaceLeaf, plugin: MyPlugin) { - super(leaf); - this.plugin = plugin; - } - - getViewType() { return "my-custom-view"; } - getDisplayText() { return this.file?.basename ?? "No file"; } - - // Obsidian calls this when the file content is provided/changed: - async setViewData(data: string, clear: boolean = false) { - // `this.file` is the TFile for the file the user clicked - if (!this.file) return; - this.data = data; // store it if you want, or read it fresh - // If you need the TFile object: - const tfile: TFile = this.file; - // Read direct from vault if needed: - // const content = await this.app.vault.read(tfile); - - // Now parse `data` (or content) and render inside your view: - this.renderFromFileData(data); - } - - async onOpen() { - // If for some reason setViewData wasn't called yet, you can get the file path: - const state = this.leaf.getViewState(); - const pathFromState = state?.state?.file; // string path - // or just rely on this.file and the setViewData call - } - - renderFromFileData(data: string) { - // your custom rendering - } -} -``` - -Why your view probably shows default data now -- You likely open your view but never consume the file passed by Obsidian (this.file / setViewData). Common mistakes: - - Creating your view that ignores this.file and uses a default initial state set in constructor. - - Not extending TextFileView or not implementing setViewData, so Obsidian does not provide the file text into your view lifecycle. - - Trying to read file path from the DOM or from active leaf wrongly; use this.file or state.state.file. - -Alternative if you don't want monkey patching -- If you only register your view and file extension (registerExtensions([...], MY_VIEW_TYPE)), clicking the file will open your view directly. You still must read the file as above. If your view isn't a TextFileView, in onOpen you can take the path string from leaf.getViewState()?.state?.file and then call this.app.vault.getAbstractFileByPath(path) and then this.app.vault.read(file) to get content. - -Example (non-TextFileView): -```ts -async onOpen() { - const path = this.leaf.getViewState()?.state?.file; - if (!path) return; - const file = this.app.vault.getAbstractFileByPath(path); - if (file && file instanceof TFile) { - const content = await this.app.vault.read(file); - this.renderFromFileData(content); - } -} -``` - -Summary (what to do next) -- Inspect your view class: - - If it extends TextFileView: implement/verify setViewData reads this.data or this.file and feeds that into your renderer. - - If it does not extend TextFileView: in onOpen read the path from this.leaf.getViewState().state.file and read via app.vault.read(TFile). -- If you want clicks to automatically open your view instead of the default markdown view, intercept WorkspaceLeaf.setViewState as Excalidraw does and replace the type with your view type (and include the file path in state so you still receive it). -- If you prefer, paste the minimal code of your view.onOpen / setViewData here and I can point out exactly what to change. - -If you want, I can: -- point to the exact lines in Excalidraw that show state.state.file being used (I already looked; the WorkspaceLeaf.setViewState monkey patch is where the file path is inspected), -- or review your tu2-atmanand/task-board view code and show the exact place where you should read this.file or the state file path. diff --git a/data.json b/data.json index 2f9930b5..f7c7ab01 100644 --- a/data.json +++ b/data.json @@ -246,11 +246,6 @@ "uniqueIdCounter": 586, "experimentalFeatures": false, "safeGuardFeature": true, - "boardFilesLocation": [ - "TaskBoard/Boards/Time Based Workflow.taskboard", - "TaskBoard/Boards/Tag Based Workflow.taskboard", - "TaskBoard/Boards/Status Based Workflow.taskboard" - ], "lastViewHistory": { "viewedType": "kanban", "boardIndex": 2, @@ -275,6 +270,20 @@ "renderVisibleNodes": false, "edgeType": "default" }, - "searchQuery": "" + "searchQuery": "", + "taskBoardFilesRegistry": [ + { + "boardId": "3103563481", + "filePath": "TaskBoard/Boards/Time Based Workflow.taskboard" + }, + { + "boardId": "2957159294", + "filePath": "TaskBoard/Boards/Tag Based Workflow.taskboard" + }, + { + "boardId": "4271106430", + "filePath": "TaskBoard/Boards/Status Based Workflow.taskboard" + } + ] } } \ No newline at end of file diff --git a/main.ts b/main.ts index 4d132525..11a5e458 100644 --- a/main.ts +++ b/main.ts @@ -139,6 +139,10 @@ export default class TaskBoard extends Plugin { await this.vaultScanner.initializeTasksCache(); await this.taskBoardFileManager.loadAllBoards(); + console.log( + "TASK BOARD : Loaded following boards : ", + this.taskBoardFileManager.getAllBoards(), + ); // Register events and commands only on Layout is ready this.app.workspace.onLayoutReady(() => { @@ -783,9 +787,11 @@ export default class TaskBoard extends Plugin { // Hide progress notice after completion this.currentProgressNotice?.hide(); this.currentProgressNotice = null; - new Notice( - `✓ Task Board : Finished processing ${totalFiles} renamed file(s)`, - ); + if (processed > 0) { + new Notice( + `✓ Task Board : Finished processing ${totalFiles} renamed file(s)`, + ); + } } /** @@ -860,9 +866,11 @@ export default class TaskBoard extends Plugin { // Hide progress notice after completion this.currentProgressNotice?.hide(); this.currentProgressNotice = null; - new Notice( - `✓ Task Board : Finished processing ${totalFiles} deleted file(s)`, - ); + if (processed > 0) { + new Notice( + `✓ Task Board : Finished processing ${totalFiles} deleted file(s)`, + ); + } } /** @@ -934,9 +942,11 @@ export default class TaskBoard extends Plugin { // Hide progress notice after completion this.currentProgressNotice?.hide(); this.currentProgressNotice = null; - new Notice( - `✓ Task Board : Finished processing ${totalFiles} created file(s)`, - ); + if (processed > 0) { + new Notice( + `✓ Task Board : Finished processing ${totalFiles} created file(s)`, + ); + } } /** diff --git a/src/components/TaskBoardViewContent.tsx b/src/components/TaskBoardViewContent.tsx index 0043ff63..8bebc096 100644 --- a/src/components/TaskBoardViewContent.tsx +++ b/src/components/TaskBoardViewContent.tsx @@ -22,7 +22,7 @@ import { taskPropertiesNames, viewTypeNames } from "src/interfaces/Enums"; import { ScanVaultIcon, funnelIcon } from "src/interfaces/Icons"; import { bugReporterManagerInsatance } from "src/managers/BugReporter"; -const TaskBoardViewContent: React.FC<{ plugin: TaskBoard, allBoards: Board[] }> = ({ plugin, allBoards }) => { +const TaskBoardViewContent: React.FC<{ plugin: TaskBoard, allBoards: Board[], clickedFileBoard?: Board | null }> = ({ plugin, allBoards, clickedFileBoard }) => { // const [boards, setBoards] = useState(boardConfigs); const [activeBoardIndex, setActiveBoardIndex] = useState(plugin.settings.data.lastViewHistory.boardIndex ?? 0); const [currentBoardData, setCurrentBoardData] = useState(); @@ -88,10 +88,18 @@ const TaskBoardViewContent: React.FC<{ plugin: TaskBoard, allBoards: Board[] }> const fetchData = async () => { console.log("TASK BOARD : Does this run while switching boards..."); try { - const data = await plugin.taskBoardFileManager.loadBoard(activeBoardIndex); - if (!data) throw "Board data not found."; + if (clickedFileBoard) { + setCurrentBoardData(clickedFileBoard); - setCurrentBoardData(data); + // Get index of the new board from the registry based on the board id. + const indexOfNewBoard = plugin.taskBoardFileManager.getBoardIndexFromRegistry(clickedFileBoard.id);; + setActiveBoardIndex(indexOfNewBoard ?? plugin.settings.data.taskBoardFilesRegistry?.length); + } else { + const data = await plugin.taskBoardFileManager.loadBoardUsingIndex(activeBoardIndex); + if (!data) throw "Board data not found."; + + setCurrentBoardData(data); + } const allTasks = await loadTasksAndMerge(plugin, true); if (allTasks) { diff --git a/src/interfaces/BoardConfigs.ts b/src/interfaces/BoardConfigs.ts index 553c2427..fdb5e99a 100644 --- a/src/interfaces/BoardConfigs.ts +++ b/src/interfaces/BoardConfigs.ts @@ -128,10 +128,9 @@ export interface swimlaneConfigs { } export type Board = { - // id: string; + id: string; name: string; description?: string; - index: number; columns: ColumnData[]; hideEmptyColumns: boolean; showColumnTags: boolean; @@ -228,9 +227,8 @@ export const DEFAULT_BOARDS: BoardConfigs = [ index: 6, }, ], - // id: generateRandomTempTaskId(), + id: "3103563481", name: "Time Based Workflow", - index: 0, showColumnTags: false, showFilteredTags: true, hideEmptyColumns: false, @@ -295,9 +293,8 @@ export const DEFAULT_BOARDS: BoardConfigs = [ name: "Completed", }, ], - // id: generateRandomTempTaskId(), + id: "2957159294", name: "Tag Based Workflow", - index: 1, showColumnTags: false, showFilteredTags: true, hideEmptyColumns: false, @@ -372,9 +369,8 @@ export const DEFAULT_BOARDS: BoardConfigs = [ index: 7, }, ], - // id: generateRandomTempTaskId(), + id: "4271106430", name: "Status Based Workflow", - index: 2, showColumnTags: false, showFilteredTags: true, hideEmptyColumns: false, diff --git a/src/interfaces/GlobalSettings.ts b/src/interfaces/GlobalSettings.ts index b17d5050..9fffda59 100644 --- a/src/interfaces/GlobalSettings.ts +++ b/src/interfaces/GlobalSettings.ts @@ -67,6 +67,11 @@ export interface frontmatterFormatting { taskItemKey: string; } +export interface taskBoardFilesRegistryItem { + boardId: string; + filePath: string; +} + export interface globalSettingsData { openOnStartup: boolean; lang: string; @@ -122,7 +127,7 @@ export interface globalSettingsData { uniqueIdCounter: number; // Counter to generate unique IDs for tasks. This will keep track of the last used ID. experimentalFeatures: boolean; safeGuardFeature: boolean; - boardFilesLocation: string[]; + taskBoardFilesRegistry: taskBoardFilesRegistryItem[]; lastViewHistory: { viewedType: string; boardIndex: number; @@ -425,10 +430,19 @@ export const DEFAULT_SETTINGS: PluginDataJson = { uniqueIdCounter: 0, // Counter to generate unique IDs for tasks. This will keep track of the last used ID. --- IGNORE --- experimentalFeatures: false, safeGuardFeature: true, - boardFilesLocation: [ - `TaskBoard/Boards/${DEFAULT_BOARDS[0].name}.taskboard`, - `TaskBoard/Boards/${DEFAULT_BOARDS[1].name}.taskboard`, - `TaskBoard/Boards/${DEFAULT_BOARDS[2].name}.taskboard`, + taskBoardFilesRegistry: [ + { + boardId: DEFAULT_BOARDS[0].id, + filePath: `TaskBoard/Boards/${DEFAULT_BOARDS[0].name}.taskboard`, + }, + { + boardId: DEFAULT_BOARDS[1].id, + filePath: `TaskBoard/Boards/${DEFAULT_BOARDS[1].name}.taskboard`, + }, + { + boardId: DEFAULT_BOARDS[2].id, + filePath: `TaskBoard/Boards/${DEFAULT_BOARDS[2].name}.taskboard`, + }, ], lastViewHistory: { viewedType: "kanban", diff --git a/src/managers/TaskBoardFileManager.ts b/src/managers/TaskBoardFileManager.ts index d2556b0c..2fcee55d 100644 --- a/src/managers/TaskBoardFileManager.ts +++ b/src/managers/TaskBoardFileManager.ts @@ -7,6 +7,7 @@ import { App, TFile, Notice, normalizePath } from "obsidian"; import { Board } from "src/interfaces/BoardConfigs"; import type TaskBoard from "main"; +import { taskBoardFilesRegistryItem } from "src/interfaces/GlobalSettings"; export default class TaskBoardFileManager { private app: App; @@ -22,7 +23,7 @@ export default class TaskBoardFileManager { } /** - * Load board configuration from a .taskboard file + * Load board configuration from a .taskboard file from disk. * @param filePath - The path to the .taskboard file * @returns The board configuration object, or null if file doesn't exist or cannot be parsed */ @@ -64,87 +65,161 @@ export default class TaskBoardFileManager { } /** - * Loads all the boards data as per the file paths stored in the global settings. - * @returns All boards data as an array or an empty array if failed to load the boards. + * Loads the specific/current board data from the memory cache (allBoardsData), + * Based on the boardIndex passed + * @param boardIndex - The index of the board to load + * @returns The board configuration object, or null if file cannot be loaded */ - async loadAllBoards(): Promise { + async loadBoardUsingIndex(boardIndex: number): Promise { try { - const boardFilesLocations = - this.plugin.settings.data.boardFilesLocation || []; - let allBoardsData: Board[] | [] = []; + const boardIndexToUse = boardIndex ?? this.currentBoardIndex; + console.log("All boards data :", this.allBoardsData); - boardFilesLocations.forEach(async (boardFilePath: string) => { - if (!boardFilePath || boardFilePath.trim() === "") { - console.error( - `No board file path configured for index: ${this.currentBoardIndex}`, - ); - return []; - } - - // Load board from file - const boardData = await this.loadBoardFromFile(boardFilePath); - - if (boardData) { - // Cache the board data in memory - allBoardsData[boardData.index] = boardData; - console.log( - `Loaded and cached board "${boardData.name}" (index: ${boardData.index}) from: ${boardFilePath}`, - ); - - return boardData; - } else { - new Notice( - `Task Board : Error loading all boards data. Following board not found : ${boardFilePath}`, - ); - } + // Validate board index + if ( + boardIndexToUse < 0 || + boardIndexToUse > this.allBoardsData.length - 1 + ) { + console.error( + `Invalid board index: ${boardIndexToUse}. Available boards: ${this.plugin.settings.data.taskBoardFilesRegistry?.length}`, + ); + return null; + } - return []; - }); + const boardData = this.allBoardsData[boardIndexToUse]; + if (boardData) { + // Cache the board data in memory + this.currentBoardIndex = boardIndexToUse; + } - this.allBoardsData = allBoardsData; - return allBoardsData; + return boardData; } catch (error) { console.error( `Error loading board at index ${this.currentBoardIndex}:`, error, ); - return []; + return null; } } /** - * Loads the specific/current board data from the memory cache (allBoardsData) - * @param boardIndex - The index of the board to load + * First tries to see if the board is already cached in memory, + * If not, loads the board from the .taskboard file at the given path, + * and caches it in memory and updates the currentBoardIndex + * @param filePath - The path to the .taskboard file * @returns The board configuration object, or null if file cannot be loaded */ - async loadBoard(boardIndex?: number): Promise { + async loadBoardUsingPath(filePath: string): Promise { try { - const currentBoardINdex = boardIndex ?? this.currentBoardIndex; - - // Validate board index - if ( - currentBoardINdex < 0 || - currentBoardINdex > this.allBoardsData.length - 1 - ) { - console.error( - `Invalid board index: ${currentBoardINdex}. Available boards: ${this.plugin.settings.data.boardFilesLocation.length}`, - ); + if (!filePath || filePath.trim() === "") { + console.error(`No board file path provided to load the board`); return null; } - const boardData = this.allBoardsData[currentBoardINdex]; + // Check if board is already cached in memory based on id + const taskBoardFilesRegistry = + this.plugin.settings.data.taskBoardFilesRegistry || []; + const registryItem = taskBoardFilesRegistry.find( + (item) => item.filePath === filePath, + ); + if (registryItem) { + const cachedBoard = this.allBoardsData.find((board) => { + return board.id === registryItem.boardId; + }); + + if (cachedBoard) { + this.currentBoardIndex = + this.getBoardIndexFromRegistry(cachedBoard.id) ?? + this.allBoardsData.length; + console.log( + `Board "${cachedBoard.name}" (index: ${this.currentBoardIndex}) loaded from cache for file: ${filePath}`, + ); + return cachedBoard; + } + } + + // Load board from file (disk) since not found in cache + const boardData = await this.loadBoardFromFile(filePath); + if (boardData) { + const newIndex = this.allBoardsData.length; // Cache the board data in memory - this.currentBoardIndex = currentBoardINdex; + this.allBoardsData[newIndex] = boardData; + console.log( + `Loaded and cached board "${boardData.name}" (index: ${newIndex}) from: ${filePath}`, + ); + this.currentBoardIndex = newIndex; + + this.addNewBoardToRegistry(boardData.id, filePath); + + return boardData; + } else { + new Notice( + `Task Board : Error loading board data from file : ${filePath}`, + ); } - return boardData; + return null; + } catch (error) { + console.error(`Error loading board from file ${filePath}:`, error); + return null; + } + } + + /** + * Loads all the boards data from disk, + * as per the file paths stored in the global settings. + * @returns All boards data as an array or an empty array if failed to load the boards. + */ + async loadAllBoards(): Promise { + console.log("loadAllBoards : Starting to load all boards..."); + try { + const taskBoardFilesRegistry = + this.plugin.settings.data.taskBoardFilesRegistry || []; + let allBoardsData: Board[] | [] = []; + + taskBoardFilesRegistry.forEach( + async (taskBoardFileEntry: taskBoardFilesRegistryItem) => { + if ( + !taskBoardFileEntry.filePath || + taskBoardFileEntry.filePath.trim() === "" + ) { + console.error( + `No board file path configured for index: ${this.currentBoardIndex}`, + ); + return []; + } + + // Load board from file + const boardData = await this.loadBoardFromFile( + taskBoardFileEntry.filePath, + ); + + if (boardData) { + const boardIndex = + this.getBoardIndexFromRegistry(boardData.id) ?? + this.allBoardsData.length; + // Cache the board data in memory + allBoardsData[boardIndex] = boardData; + console.log( + `Loaded and cached board "${boardData.name}" (index: ${boardIndex}) from: ${taskBoardFileEntry.filePath}`, + ); + } else { + new Notice( + `Task Board : Error loading all boards data. Following board not found : ${taskBoardFileEntry.filePath}`, + ); + } + }, + ); + + this.allBoardsData = allBoardsData; + return allBoardsData; } catch (error) { console.error( `Error loading board at index ${this.currentBoardIndex}:`, error, ); - return null; + return []; } } @@ -205,8 +280,8 @@ export default class TaskBoardFileManager { boardIndex?: number, ): Promise { try { - const boardFilesLocation = - this.plugin.settings.data.boardFilesLocation || []; + const taskBoardFilesRegistry = + this.plugin.settings.data.taskBoardFilesRegistry || []; let boardIndexToUse = boardIndex; if (!boardIndexToUse) { @@ -225,16 +300,19 @@ export default class TaskBoardFileManager { // Validate board index if ( boardIndexToUse < 0 || - boardIndexToUse >= boardFilesLocation.length + boardIndexToUse >= taskBoardFilesRegistry.length ) { console.error( - `Invalid board index: ${boardIndexToUse}. Available boards: ${boardFilesLocation.length}`, + `Invalid board index: ${boardIndexToUse}. Available boards: ${taskBoardFilesRegistry.length}`, ); return false; } - const boardFilePath = boardFilesLocation[boardIndexToUse]; - if (!boardFilePath || boardFilePath.trim() === "") { + const taskBoardFileEntry = taskBoardFilesRegistry[boardIndexToUse]; + if ( + !taskBoardFileEntry.filePath || + taskBoardFileEntry.filePath.trim() === "" + ) { console.error( `No board file path configured for index: ${boardIndexToUse}`, ); @@ -243,7 +321,7 @@ export default class TaskBoardFileManager { // Save board to file const success = await this.saveBoardToFile( - boardFilePath, + taskBoardFileEntry.filePath, updatedBoardData, ); @@ -251,7 +329,7 @@ export default class TaskBoardFileManager { this.allBoardsData[boardIndexToUse] = updatedBoardData; console.log( - `Saved board "${updatedBoardData.name}" (index: ${boardIndexToUse}) to: ${boardFilePath}`, + `Saved board "${updatedBoardData.name}" (index: ${boardIndexToUse}) to: ${taskBoardFileEntry.filePath}`, ); } @@ -265,6 +343,33 @@ export default class TaskBoardFileManager { } } + /** + * Adds a new .taskboard file path to the registry in settings + * if it does not already exist + * @param filePath - The path to the .taskboard file + */ + async addNewBoardToRegistry( + boardId: string, + filePath: string, + ): Promise { + const taskBoardFilesRegistry = + this.plugin.settings.data.taskBoardFilesRegistry || []; + if ( + !taskBoardFilesRegistry.find((item) => item.filePath === filePath) + ) { + taskBoardFilesRegistry.push({ + filePath: filePath, + boardId: boardId, + }); + this.plugin.settings.data.taskBoardFilesRegistry = + taskBoardFilesRegistry; + await this.plugin.saveSettings(); + console.log(`Added new board file to registry: ${filePath}`); + } else { + console.log(`Board file already exists in registry: ${filePath}`); + } + } + /** * Check if a .taskboard file exists * @param filePath - The path to the .taskboard file @@ -372,16 +477,18 @@ export default class TaskBoardFileManager { * @returns Array of missing file paths */ async validateBoardFiles(): Promise { - const boardFilesLocation = - this.plugin.settings.data.boardFilesLocation || []; + const taskBoardFilesRegistry = + this.plugin.settings.data.taskBoardFilesRegistry || []; const missingFiles: string[] = []; - for (const filePath of boardFilesLocation) { - const exists = await this.boardFileExists(filePath); + for (const taskBoardFileEntry of taskBoardFilesRegistry) { + const exists = await this.boardFileExists( + taskBoardFileEntry.filePath, + ); if (!exists) { - missingFiles.push(filePath); + missingFiles.push(taskBoardFileEntry.filePath); console.warn( - `Expected board file not found: ${filePath}. It may have been moved or deleted.`, + `Expected board file not found: ${taskBoardFileEntry.filePath}. It may have been moved or deleted.`, ); } } @@ -398,27 +505,29 @@ export default class TaskBoardFileManager { async createMissingDefaultBoardFiles( defaultBoards: Board[], ): Promise { - const boardFilesLocation = - this.plugin.settings.data.boardFilesLocation || []; + const taskBoardFilesRegistry = + this.plugin.settings.data.taskBoardFilesRegistry || []; let createdCount = 0; - for (let i = 0; i < boardFilesLocation.length; i++) { - const filePath = boardFilesLocation[i]; - const exists = await this.boardFileExists(filePath); + for (let i = 0; i < taskBoardFilesRegistry.length; i++) { + const taskBoardFileEntry = taskBoardFilesRegistry[i]; + const exists = await this.boardFileExists( + taskBoardFileEntry.filePath, + ); if (!exists && i < defaultBoards.length) { const created = await this.createNewBoardFile( - filePath, + taskBoardFileEntry.filePath, defaultBoards[i], ); if (created) { createdCount++; console.log( - `Created default board file: ${filePath}`, + `Created default board file: ${taskBoardFileEntry.filePath}`, defaultBoards[i].name, ); new Notice( - `Created default board file: ${filePath} : ${defaultBoards[i].name}`, + `Created default board file: ${taskBoardFileEntry.filePath} : ${defaultBoards[i].name}`, ); } } @@ -427,13 +536,16 @@ export default class TaskBoardFileManager { return createdCount; } + /** + * Returns all the boards data cached in memory + * @returns Array of all board configurations + */ async getAllBoards(): Promise { return this.allBoardsData; } /** - * Get the currently cached board data - * Returns the board that was last loaded with loadBoard() + * Returns the board data that was last active/used by user. * @returns The cached board data, or null if no board has been loaded */ async getCurrentBoardData(): Promise { @@ -441,13 +553,47 @@ export default class TaskBoardFileManager { } /** - * Get the index of the currently cached board + * Get the index of the last used board as per the saved files registry * @returns The index of the currently loaded board, or null if no board has been loaded */ getCurrentBoardIndex(): number | null { return this.currentBoardIndex; } + /** + * Get the index of a board from the registry based on its boardId + * @param boardId - The ID of the board + * @returns The index of the board in the registry, or null if not found + */ + getBoardIndexFromRegistry(boardId: string): number | null { + const taskBoardFilesRegistry = + this.plugin.settings.data.taskBoardFilesRegistry || []; + const registryItem = taskBoardFilesRegistry.find( + (item) => item.boardId === boardId, + ); + if (registryItem) { + return taskBoardFilesRegistry.indexOf(registryItem); + } + return null; + } + + /** + * Get the file path of a board from the registry based on its boardId + * @param boardId - The ID of the board + * @returns The file path of the board, or null if not found + */ + getBoardFilepathFromRegistry(boardId: string): string | null { + const taskBoardFilesRegistry = + this.plugin.settings.data.taskBoardFilesRegistry || []; + const registryItem = taskBoardFilesRegistry.find( + (item) => item.boardId === boardId, + ); + if (registryItem) { + return registryItem.filePath; + } + return null; + } + /** * Clear the cached board data * Useful when switching between different boards or clearing state diff --git a/src/modals/BoardConfigModal.tsx b/src/modals/BoardConfigModal.tsx index 2b1027a3..ed645aab 100644 --- a/src/modals/BoardConfigModal.tsx +++ b/src/modals/BoardConfigModal.tsx @@ -21,6 +21,7 @@ import { columnTypeAndNameMapping, getPriorityOptionsForDropdown } from "src/int import { columnDataProp, AddColumnModal } from "./AddColumnModal"; import { SwimlanesConfigModal } from "./SwimlanesConfigModal"; import { bugReporterManagerInsatance } from "src/managers/BugReporter"; +import { generateRandomTempTaskId } from "src/utils/TaskItemUtils"; interface ConfigModalProps { plugin: TaskBoard; @@ -209,8 +210,8 @@ const ConfigModalContent: React.FC = ({ const handleAddNewBoard = async (oldBoards: Board[]) => { const newBoard: Board = { + id: generateRandomTempTaskId(), name: t("new-board"), - index: localBoards.length, columns: [], hideEmptyColumns: false, showColumnTags: true, @@ -270,24 +271,26 @@ const ConfigModalContent: React.FC = ({ app, mssg, onConfirm: () => { - if (selectedBoardIndex !== -1) { - const updatedBoards = [...localBoards]; - updatedBoards.splice(selectedBoardIndex, 1); - // Update indexes of boards below the deleted one - for (let i = selectedBoardIndex; i < updatedBoards.length; i++) { - updatedBoards[i].index = i; - } - setLocalBoards(updatedBoards); - setIsEdited(true); - if (updatedBoards.length === 0) { - handleAddNewBoard(updatedBoards); - setSelectedBoardIndex(0); - } else if (selectedBoardIndex !== 0) { - setSelectedBoardIndex(selectedBoardIndex - 1); - } - } else { - new Notice(t("no-board-selected-to-delete")); - } + new Notice('NOT IMPLEMENTED'); + + // if (selectedBoardIndex !== -1) { + // const updatedBoards = [...localBoards]; + // updatedBoards.splice(selectedBoardIndex, 1); + // // Update indexes of boards below the deleted one + // for (let i = selectedBoardIndex; i < updatedBoards.length; i++) { + // updatedBoards[i].index = i; + // } + // setLocalBoards(updatedBoards); + // setIsEdited(true); + // if (updatedBoards.length === 0) { + // handleAddNewBoard(updatedBoards); + // setSelectedBoardIndex(0); + // } else if (selectedBoardIndex !== 0) { + // setSelectedBoardIndex(selectedBoardIndex - 1); + // } + // } else { + // new Notice(t("no-board-selected-to-delete")); + // } }, onCancel: () => { // console.log("Board Deletion Operation Cancelled."); diff --git a/src/views/TaskBoardView.tsx b/src/views/TaskBoardView.tsx index 75136f09..001f33fa 100644 --- a/src/views/TaskBoardView.tsx +++ b/src/views/TaskBoardView.tsx @@ -61,26 +61,23 @@ export class TaskBoardView extends ItemView { if (mandatoryScanSignal) this.highlighgtScanvaultIcon(); + // All boards data should be cumpulsorily loaded. + let allBoardsData = await this.plugin.taskBoardFileManager.getAllBoards(); + // Check if a specific .taskboard file was clicked from File Navigator // First check the leaf instance directly (set by monkey patch) const clickedFilePath = (this.leaf as any).taskboardFilePath as string | undefined; console.log("TaskBoardView.tsx : clickedFilePath from leaf:", clickedFilePath); - let allBoardsData: Board[]; - + let clickedFileData: Board | null; if (clickedFilePath && typeof clickedFilePath === 'string' && clickedFilePath.endsWith('.taskboard')) { // User clicked on a specific .taskboard file - load just that file - console.log(`TaskBoardView: Loading clicked file: ${clickedFilePath}`); - const specificBoardData = await this.plugin.taskBoardFileManager.loadBoardFromFile(clickedFilePath); - allBoardsData = specificBoardData ? [specificBoardData] : []; + clickedFileData = await this.plugin.taskBoardFileManager.loadBoardUsingPath(clickedFilePath); + this.renderBoard(allBoardsData, clickedFileData); } else { - // No specific file clicked - load all boards as usual - console.log("TaskBoardView: No specific file, loading all boards"); - allBoardsData = await this.plugin.taskBoardFileManager.getAllBoards(); + this.renderBoard(allBoardsData); } - - this.renderBoard(allBoardsData); } async highlighgtScanvaultIcon() { @@ -108,13 +105,14 @@ export class TaskBoardView extends ItemView { // } // } - private renderBoard(allBoardsData: Board[]) { + private renderBoard(allBoardsData: Board[], clickedFileData?: Board | null) { this.root = createRoot(this.containerEl.children[1]); this.root.render( , , ); From fe3a29cd28b92b5c383478ac7aed6f4eac974187 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Thu, 22 Jan 2026 21:40:34 +0530 Subject: [PATCH 014/236] fix : the third default board index is 1 --- src/interfaces/GlobalSettings.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interfaces/GlobalSettings.ts b/src/interfaces/GlobalSettings.ts index 037897c6..90084608 100644 --- a/src/interfaces/GlobalSettings.ts +++ b/src/interfaces/GlobalSettings.ts @@ -381,7 +381,7 @@ export const DEFAULT_SETTINGS: PluginDataJson = { }, ], name: "Status Based Workflow", - index: 1, + index: 2, showColumnTags: false, showFilteredTags: true, hideEmptyColumns: false, From bab180cca0f20326a5fa6a445f65bae7607f931d Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Thu, 22 Jan 2026 21:41:27 +0530 Subject: [PATCH 015/236] fix : few other types of files are also getting scanned --- src/managers/VaultScanner.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/managers/VaultScanner.ts b/src/managers/VaultScanner.ts index 907bc70f..cdfffbe0 100644 --- a/src/managers/VaultScanner.ts +++ b/src/managers/VaultScanner.ts @@ -682,15 +682,17 @@ export function fileTypeAllowedForScanning( ): boolean { // console.log("Condition 1 :", notAllowedFileExtensionsRegEx.test(file.path), "\nCondition 2 :", file.path === // plugin.settings.data.globalSettings.archivedTasksFilePath, "\nCondition 3 :", , "\nCondition 4 :", ) - if (!globalSettings.archivedTBNotesFolderPath.trim()) return true; const filePath = file.path.toLocaleLowerCase(); if ( // notAllowedFileExtensionsRegEx.test(file.path) || allowedFileExtensionsRegEx.test(file.path) === false || - filePath.startsWith( - globalSettings.archivedTBNotesFolderPath.toLowerCase(), + ( + globalSettings.archivedTBNotesFolderPath.trim() !== "" && + filePath.startsWith( + globalSettings.archivedTBNotesFolderPath.toLowerCase(), + ) ) || filePath === globalSettings.archivedTasksFilePath.toLowerCase() ) { From e3dbfb9b47c59981066308e60c9d1a608975543c Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Thu, 22 Jan 2026 21:43:00 +0530 Subject: [PATCH 016/236] fix : a new folder should be created while changing the tasksCachePath --- src/services/OpenModals.ts | 2 +- src/utils/JsonFileOperations.ts | 58 ++++++++++++++++++---------- src/utils/TaskItemCacheOperations.ts | 1 - src/views/TaskBoardView.tsx | 2 +- 4 files changed, 39 insertions(+), 24 deletions(-) diff --git a/src/services/OpenModals.ts b/src/services/OpenModals.ts index 86e1a231..453b41f3 100644 --- a/src/services/OpenModals.ts +++ b/src/services/OpenModals.ts @@ -156,7 +156,7 @@ export const openAddNewTaskNoteModal = (app: App, plugin: TaskBoard) => { // Check if the directory exists, create if not const parts = newTask.filePath.split("/"); if (parts.length > 1) { - const dirPath = parts.slice(0, -1).join("/"); + const dirPath = parts.slice(0, -1).join("/").trim(); if (!(await plugin.app.vault.adapter.exists(dirPath))) { await plugin.app.vault.createFolder(dirPath); } diff --git a/src/utils/JsonFileOperations.ts b/src/utils/JsonFileOperations.ts index 1a771f35..27410f1e 100644 --- a/src/utils/JsonFileOperations.ts +++ b/src/utils/JsonFileOperations.ts @@ -10,6 +10,7 @@ import { Board } from "../interfaces/BoardConfigs"; import type TaskBoard from "main"; import { bugReporter } from "src/services/OpenModals"; import { bugReporterManagerInsatance } from "src/managers/BugReporter"; +import { App } from "obsidian"; // --------------- Operations with data.json --------------- @@ -24,7 +25,7 @@ export const loadGlobalSettings = async (plugin: TaskBoard) => { 68, "Failed to load global settings from data.json", String(error), - "JsonFileOperations.ts/loadGlobalSettings" + "JsonFileOperations.ts/loadGlobalSettings", ); return {}; } @@ -44,7 +45,7 @@ export const loadBoardsData = async (plugin: TaskBoard): Promise => { 69, "Failed to load board configurations from data.json", String(error), - "JsonFileOperations.ts/loadBoardsData" + "JsonFileOperations.ts/loadBoardsData", ); throw error; } @@ -53,7 +54,7 @@ export const loadBoardsData = async (plugin: TaskBoard): Promise => { // Function to save boards data to the JSON file export const saveBoardsData = async ( plugin: TaskBoard, - updatedBoards: Board[] + updatedBoards: Board[], ) => { try { // Fetch current settings @@ -69,7 +70,7 @@ export const saveBoardsData = async ( 70, "Failed to save board configurations to data.json", String(error), - "JsonFileOperations.ts/saveBoardsData" + "JsonFileOperations.ts/saveBoardsData", ); throw error; } @@ -79,7 +80,7 @@ export const saveBoardsData = async ( // load tasks from plugin.vaultScanner.tasksCache export const loadJsonCacheData = async ( - plugin: TaskBoard + plugin: TaskBoard, ): Promise => { try { return plugin.vaultScanner.tasksCache; @@ -88,7 +89,7 @@ export const loadJsonCacheData = async ( 71, "Failed to load tasks from tasks.json", String(error), - "JsonFileOperations.ts/loadJsonCacheData" + "JsonFileOperations.ts/loadJsonCacheData", ); return { VaultName: plugin.app.vault.getName(), @@ -101,7 +102,7 @@ export const loadJsonCacheData = async ( // load tasks from disk. export const loadJsonCacheDataFromDisk = async ( - plugin: TaskBoard + plugin: TaskBoard, ): Promise => { try { let path = `${plugin.app.vault.configDir}/plugins/task-board/tasks.json`; @@ -156,7 +157,7 @@ const writeFileWithRetry = async ( path: string, content: string, maxRetries: number = 3, - baseDelay: number = 1000 + baseDelay: number = 1000, ): Promise => { for (let attempt = 1; attempt <= maxRetries; attempt++) { try { @@ -182,7 +183,7 @@ const writeFileWithRetry = async ( // Wait with exponential backoff before retry const delay = baseDelay * Math.pow(2, attempt - 1); console.warn( - `Task Board: File write timeout (attempt ${attempt}/${maxRetries}), retrying in ${delay}ms...` + `Task Board: File write timeout (attempt ${attempt}/${maxRetries}), retrying in ${delay}ms...`, ); await new Promise((resolve) => setTimeout(resolve, delay)); } @@ -192,7 +193,7 @@ const writeFileWithRetry = async ( // Function to write tasks data to disk export const writeJsonCacheDataToDisk = async ( plugin: TaskBoard, - tasksData: jsonCacheData + tasksData: jsonCacheData, ): Promise => { try { let path = `${plugin.app.vault.configDir}/plugins/task-board/tasks.json`; @@ -205,7 +206,7 @@ export const writeJsonCacheDataToDisk = async ( await writeFileWithRetry( plugin, path, - JSON.stringify(tasksData, null, 4) + JSON.stringify(tasksData, null, 4), ); return true; @@ -225,7 +226,7 @@ export const writeJsonCacheDataToDisk = async ( 72, userMessage, errorMessage, - "JsonFileOperations.ts/writeJsonCacheDataFromDisk" + "JsonFileOperations.ts/writeJsonCacheDataFromDisk", ); } @@ -235,11 +236,11 @@ export const writeJsonCacheDataToDisk = async ( // Function to move the file from old path to new path export const moveTasksCacheFileToNewPath = ( - plugin: TaskBoard, + app: App, oldPath: string, - newPath: string + newPath: string, ) => { - return new Promise((resolve, reject) => { + return new Promise(async (resolve, reject) => { if ( oldPath === newPath || (newPath !== "" && newPath.endsWith(".json") === false) || @@ -249,11 +250,26 @@ export const moveTasksCacheFileToNewPath = ( return true; } + // Check if the directory exists, create if not + const parts = newPath.split("/"); + if (parts.length > 1) { + const dirPath = parts.slice(0, -1).join("/").trim(); + if (!(await app.vault.adapter.exists(dirPath))) { + await app.vault.createFolder(dirPath); + } + } + + console.log( + "moveTasksCacheFileToNewPath...\nOld path :", + oldPath, + "\nNew path :", + newPath, + ); if (newPath === "") - newPath = `${plugin.app.vault.configDir}/plugins/task-board/tasks.json`; + newPath = `${app.vault.configDir}/plugins/task-board/tasks.json`; if (oldPath === "") - oldPath = `${plugin.app.vault.configDir}/plugins/task-board/tasks.json`; - plugin.app.vault.adapter + oldPath = `${app.vault.configDir}/plugins/task-board/tasks.json`; + app.vault.adapter .rename(oldPath, newPath) // .then(() => { // // Update the tasksCacheFilePath in globalSettings @@ -268,7 +284,7 @@ export const moveTasksCacheFileToNewPath = ( 73, "Failed to move tasks.json file to new path", String(error), - "JsonFileOperations.ts/moveTasksCacheFileToNewPath" + "JsonFileOperations.ts/moveTasksCacheFileToNewPath", ); reject(error); return false; @@ -279,7 +295,7 @@ export const moveTasksCacheFileToNewPath = ( // Helper function to load tasks from tasks.json and merge them export const loadTasksAndMerge = async ( plugin: TaskBoard, - hardRefresh: boolean + hardRefresh: boolean, ): Promise => { try { let allTasks: jsonCacheData; @@ -316,7 +332,7 @@ export const loadTasksAndMerge = async ( const mergeTasks = (tasks: typeof allTasks.Pending) => Object.entries(tasks || {}).flatMap(([filePath, tasks]) => - tasks.map((task: taskItem) => ({ ...task, filePath })) + tasks.map((task: taskItem) => ({ ...task, filePath })), ); const allTasksMerged: taskJsonMerged = { diff --git a/src/utils/TaskItemCacheOperations.ts b/src/utils/TaskItemCacheOperations.ts index e1a23204..791d5887 100644 --- a/src/utils/TaskItemCacheOperations.ts +++ b/src/utils/TaskItemCacheOperations.ts @@ -10,7 +10,6 @@ import TaskBoard from "main"; import { jsonCacheData, taskItem } from "src/interfaces/TaskItem"; import { eventEmitter } from "src/services/EventEmitter"; -import { bugReporter } from "src/services/OpenModals"; import { loadJsonCacheDataFromDisk, writeJsonCacheDataToDisk, diff --git a/src/views/TaskBoardView.tsx b/src/views/TaskBoardView.tsx index e61293f5..cabb564b 100644 --- a/src/views/TaskBoardView.tsx +++ b/src/views/TaskBoardView.tsx @@ -10,7 +10,7 @@ import TaskBoardViewContent from "src/components/TaskBoardViewContent"; import type TaskBoard from "../../main"; import { PENDING_SCAN_FILE_STACK, VIEW_TYPE_TASKBOARD } from "src/interfaces/Constants"; import { loadBoardsData } from "src/utils/JsonFileOperations"; -import { bugReporter, openScanVaultModal } from "../services/OpenModals"; +import { openScanVaultModal } from "../services/OpenModals"; import { t } from "src/utils/lang/helper"; import { eventEmitter } from "src/services/EventEmitter"; import { bugReporterManagerInsatance } from "src/managers/BugReporter"; From 21c3109422cdcc110e101b8480d8933dcace4fac Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Thu, 22 Jan 2026 21:43:25 +0530 Subject: [PATCH 017/236] feat : re-designed tasksCachePath setting input to text input with buttons --- src/settings/SettingConstructUI.ts | 166 +++++++++++++++++++++-------- 1 file changed, 119 insertions(+), 47 deletions(-) diff --git a/src/settings/SettingConstructUI.ts b/src/settings/SettingConstructUI.ts index b6a599b1..3d3a874a 100644 --- a/src/settings/SettingConstructUI.ts +++ b/src/settings/SettingConstructUI.ts @@ -54,7 +54,7 @@ export class SettingsManager { win: Window; app: App; plugin: TaskBoard; - globalSettings: globalSettingsData | null = null; + globalSettings: globalSettingsData; allPickrs: Pickr[] = []; reloadNoticeAlreadyShown: boolean = false; @@ -62,6 +62,7 @@ export class SettingsManager { this.app = plugin.app; this.plugin = plugin; this.win = window; + this.globalSettings = this.plugin.settings.data.globalSettings; } private getPropertyDisplayName( @@ -269,9 +270,6 @@ export class SettingsManager { // this.contentEl.empty(); // Empty the contentEl to remove all child elements // } - // Reset global settings if necessary - this.globalSettings = null; - this.reloadNoticeAlreadyShown = false; //Destroy all Pickr instances @@ -451,6 +449,8 @@ export class SettingsManager { this.globalSettings!.scanMode = value as scanModeOptions; await this.saveSettings(); + + this.openReloadNoticeIfNeeded(); }), ); @@ -476,57 +476,129 @@ export class SettingsManager { ) .addToggle((toggle) => toggle.setValue(autoAddUniqueID).onChange(async (value) => { - this.globalSettings!.autoAddUniqueID = value; + this.globalSettings.autoAddUniqueID = value; await this.saveSettings(); this.openReloadNoticeIfNeeded(); }), ); - new Setting(contentEl) - .setClass("taskBoard-settings-wide-input") - .setName(t("tasks-cache-file-path")) - .setDesc( - createFragmentWithHTML( - t("tasks-cache-file-path-description") + - "
" + - t("tasks-cache-file-path-description-2"), - ), - ) - .addDropdown((dropdown) => { - const defaultPath = `${this.plugin.app.vault.configDir}/plugins/task-board/tasks.json`; - const suggestionContent = [ - defaultPath, - ...getFolderSuggestions(this.app).map((item) => - normalizePath(`${item}/task-board-cache.json`), + const tasksCachePathSettingsContainer = contentEl.createDiv({ + cls: "task-board-single-setting-container", + }); + const renderTasksCachePathSetting = () => { + tasksCachePathSettingsContainer.empty(); + + let newPath = this.globalSettings.tasksCacheFilePath; + new Setting(tasksCachePathSettingsContainer) + .setClass("taskBoard-settings-wide-input") + .setName(t("tasks-cache-file-path")) + .setDesc( + createFragmentWithHTML( + t("tasks-cache-file-path-description") + + "
" + + t("tasks-cache-file-path-description-2"), ), - ]; - // Add 'Default' option label for the default path - dropdown.addOption(defaultPath, `Default - ${defaultPath}`); - - // Add options to dropdown - suggestionContent.forEach((path) => { - dropdown.addOption(path, path); - }); + ) + .addText((text) => { + text.setValue(newPath).onChange((editedPath: string) => { + if (editedPath.endsWith(".json")) { + newPath = normalizePath(editedPath); + } else { + newPath = normalizePath( + editedPath + "/task-board-cache.json", + ); + } + }); - // Set current value - dropdown.setValue(tasksCacheFilePath); + // const inputEl = text.inputEl; + // const suggestionContent = getFolderSuggestions(this.app); + // const onSelectCallback = async (selectedPath: string) => { + // let newPath = ""; + // if (this.globalSettings) { + // if (selectedPath.endsWith(".json")) { + // this.globalSettings.tasksCacheFilePath = + // selectedPath; + // newPath = selectedPath; + // } else { + // newPath = normalizePath( + // selectedPath + "/task-board-cache.json", + // ); + // this.globalSettings.tasksCacheFilePath = + // newPath; + // } + // } + // renderTasksCachePathSetting(); + // await this.saveSettings(); + // }; + + // new MultiSuggest( + // inputEl, + // new Set(suggestionContent), + // onSelectCallback, + // this.app, + // ); + }) + .addButton((button) => + button.setButtonText(t("transfer")).onClick(async () => { + const result = await moveTasksCacheFileToNewPath( + this.app, + tasksCacheFilePath, + newPath, + ); + if (result) { + this.globalSettings.tasksCacheFilePath = newPath; + this.saveSettings(); + + renderTasksCachePathSetting(); + new Notice( + `Task Board cache file path changed to new location succussfully. New location : ${this.globalSettings.tasksCacheFilePath}`, + ); + } else { + new Notice( + "Task Board : Failed to change the path. Check logs for more info.", + ); + this.globalSettings.tasksCacheFilePath = `${this.app.vault.configDir}/plugins/task-board/tasks.json`; + this.saveSettings(); + + renderTasksCachePathSetting(); + } + }), + ) + .addButton((button) => + button.setButtonText(t("reset")).onClick(async () => { + newPath = `${this.app.vault.configDir}/plugins/task-board/tasks.json`; + + const result = await moveTasksCacheFileToNewPath( + this.app, + tasksCacheFilePath, + newPath, + ); - dropdown.onChange(async (selectedPath) => { - const result = await moveTasksCacheFileToNewPath( - this.plugin, - tasksCacheFilePath, - selectedPath, - ); + if (result) { + this.globalSettings.tasksCacheFilePath = newPath; + this.saveSettings(); + + renderTasksCachePathSetting(); + new Notice( + `Task Board cache file path reset was succussfully.`, + ); + } else { + new Notice( + "Task Board : Failed to change the path. Check logs for more info.", + ); + this.globalSettings.tasksCacheFilePath = `${this.app.vault.configDir}/plugins/task-board/tasks.json`; + this.saveSettings(); + + renderTasksCachePathSetting(); + } - if (this.globalSettings && result) { - this.globalSettings.tasksCacheFilePath = selectedPath; - await this.saveSettings(); + renderTasksCachePathSetting(); + }), + ); + }; - this.openReloadNoticeIfNeeded(); - } - }); - }); + renderTasksCachePathSetting(); // Setting to show/Hide the Header of the task card new Setting(contentEl) @@ -1510,7 +1582,7 @@ export class SettingsManager { // Use Obsidian's MarkdownUIRenderer to render markdown // @ts-ignore MarkdownUIRenderer.renderSubtaskText( - this.plugin.app, + this.app, markdown, markdownPreviewEl, "", @@ -1676,7 +1748,7 @@ export class SettingsManager { }); const inputEl = text.inputEl; - const suggestionContent = getFileSuggestions(this.plugin.app); + const suggestionContent = getFileSuggestions(this.app); const onSelectCallback = async (selectedPath: string) => { if (this.globalSettings) { this.globalSettings.archivedTasksFilePath = @@ -1781,7 +1853,7 @@ export class SettingsManager { const inputEl = text.inputEl; inputEl.placeholder = "e.g., TaskBoard/TaskNotes"; - const suggestionContent = getFolderSuggestions(this.plugin.app); + const suggestionContent = getFolderSuggestions(this.app); const onSelectCallback = async (selectedPath: string) => { if (this.globalSettings) { this.globalSettings.archivedTBNotesFolderPath = From d96eb9ad4c5d7db0028150ff0bc386b58b9a67ae Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Thu, 22 Jan 2026 21:43:54 +0530 Subject: [PATCH 018/236] fix : notices are shown when unsupported files are added/modified/deleted --- main.ts | 122 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 67 insertions(+), 55 deletions(-) diff --git a/main.ts b/main.ts index 458e923e..883eb402 100644 --- a/main.ts +++ b/main.ts @@ -661,8 +661,8 @@ export default class TaskBoard extends Plugin { /** * Add a file to the rename queue and schedule processing * @private - * @param {TAbstractFile} file - The file to add to the queue - * @param {string} oldPath - The old path of the file + * @param file - The file to add to the queue + * @param oldPath - The old path of the file */ private queueFileForRename(file: TAbstractFile, oldPath: string) { // Only queue TFile objects (not folders) that are allowed for scanning @@ -716,11 +716,11 @@ export default class TaskBoard extends Plugin { oldPath, archivedPath, ); - } - processed++; + processed++; - // Update progress notice - this.currentProgressNotice.messageEl.textContent = `Task Board : Processing renamed files: ${processed}/${totalFiles}`; + // Update progress notice + this.currentProgressNotice.messageEl.textContent = `Task Board : Processing renamed files: ${processed}/${totalFiles}`; + } } catch (error) { console.error( `Error processing renamed file ${file.path}:`, @@ -742,9 +742,11 @@ export default class TaskBoard extends Plugin { // Hide progress notice after completion this.currentProgressNotice?.hide(); this.currentProgressNotice = null; - new Notice( - `✓ Task Board : Finished processing ${totalFiles} renamed file(s)`, - ); + if (processed > 0) { + new Notice( + `✓ Task Board : Finished processing ${totalFiles} renamed file(s)`, + ); + } } /** @@ -796,11 +798,11 @@ export default class TaskBoard extends Plugin { ) ) { this.realTimeScanner.onFileDeleted(file); - } - processed++; + processed++; - // Update progress notice - this.currentProgressNotice.messageEl.textContent = `Task Board : Processing deleted files: ${processed}/${totalFiles}`; + // Update progress notice + this.currentProgressNotice.messageEl.textContent = `Task Board : Processing deleted files: ${processed}/${totalFiles}`; + } } catch (error) { console.error( `Error processing deleted file ${file.path}:`, @@ -822,9 +824,11 @@ export default class TaskBoard extends Plugin { // Hide progress notice after completion this.currentProgressNotice?.hide(); this.currentProgressNotice = null; - new Notice( - `✓ Task Board : Finished processing ${totalFiles} deleted file(s)`, - ); + if (processed > 0) { + new Notice( + `✓ Task Board : Finished processing ${totalFiles} deleted file(s)`, + ); + } } /** @@ -857,52 +861,55 @@ export default class TaskBoard extends Plugin { const totalFiles = this.createQueue.length; - // Show progress notice - this.currentProgressNotice = new Notice( - `Task Board : Processing created files: 0/${totalFiles}`, - 0, - ); - this.plugin.vaultScanner.refreshTasksFromFiles(this.createQueue, false); - let processed = 0; - while (this.createQueue.length > 0) { - const file = this.createQueue.shift()!; - - try { - // if ( - // fileTypeAllowedForScanning( - // this.plugin.settings.data.globalSettings, - // file - // ) - // ) { - // await this.realTimeScanner.processAllUpdatedFiles(file); - // } - processed++; + // Show progress notice only if the files are more than 10 + if (totalFiles > 10) { + this.currentProgressNotice = new Notice( + `Task Board : Processing created files: 0/${totalFiles}`, + 0, + ); + let processed = 0; + while (this.createQueue.length > 0) { + const file = this.createQueue.shift()!; + + try { + // if ( + // fileTypeAllowedForScanning( + // this.plugin.settings.data.globalSettings, + // file + // ) + // ) { + // await this.realTimeScanner.processAllUpdatedFiles(file); + // } + processed++; + + // Update progress notice + this.currentProgressNotice.messageEl.textContent = `Task Board : Processing created files: ${processed}/${totalFiles}`; + } catch (error) { + console.error( + `Error processing created file ${file.path}:`, + error, + ); + } - // Update progress notice - this.currentProgressNotice.messageEl.textContent = `Task Board : Processing created files: ${processed}/${totalFiles}`; - } catch (error) { - console.error( - `Error processing created file ${file.path}:`, - error, - ); + // Add delay between processing each file to prevent blocking UI + if (this.createQueue.length > 0) { + await new Promise((resolve) => + setTimeout(resolve, this.PROCESSING_INTERVAL), + ); + } } - // Add delay between processing each file to prevent blocking UI - if (this.createQueue.length > 0) { - await new Promise((resolve) => - setTimeout(resolve, this.PROCESSING_INTERVAL), + // Hide progress notice after completion + this.currentProgressNotice?.hide(); + this.currentProgressNotice = null; + if (processed > 0) { + new Notice( + `✓ Task Board : Finished processing ${totalFiles} created file(s)`, ); } } - - // Hide progress notice after completion - this.currentProgressNotice?.hide(); - this.currentProgressNotice = null; - new Notice( - `✓ Task Board : Finished processing ${totalFiles} created file(s)`, - ); } /** @@ -1301,7 +1308,12 @@ export default class TaskBoard extends Plugin { ); const deletedFilesList = [...deletedFiles]; - const changed_files = [...modifiedCreatedRenamedFiles]; + const changed_files = modifiedCreatedRenamedFiles.filter((file) => + fileTypeAllowedForScanning( + this.plugin.settings.data.globalSettings, + file, + ), + ); console.log( "Task Board : Fetching complete.\nModified files :", changed_files, From 6bea831170d37e440bf5e9d869e0336bf85e35a7 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Thu, 22 Jan 2026 21:44:13 +0530 Subject: [PATCH 019/236] new english strings and styles for the latest changes --- src/utils/lang/locale/en.ts | 4 +++- styles.css | 24 ++++++++++++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/utils/lang/locale/en.ts b/src/utils/lang/locale/en.ts index 70c8dbfb..27d787ea 100644 --- a/src/utils/lang/locale/en.ts +++ b/src/utils/lang/locale/en.ts @@ -537,7 +537,9 @@ const en: Lang = { "show-modified-files-message-on-startup-info": "If any of the vault files has been modified when Obsidian was inactive. These modified files will be auto-detected at the application start time, after a 10 second delay. This message will provide you to provide the statistics of which files has been modified. If you disable this setting, the modified files will be auto-scanned without needing your attention.", "properties": "Properties", "task-notes": "Task notes", - "inline-tasks": "Inline tasks" + "inline-tasks": "Inline tasks", + "transfer": "Transfer", + "reset": "Reset" }; export default en; diff --git a/styles.css b/styles.css index 19e2a9f4..3bf82196 100644 --- a/styles.css +++ b/styles.css @@ -2376,7 +2376,9 @@ button.clickable-icon.view-action.taskboardScanVaultBtn.highlight { } -/* /src/views/TaskBoardSettingConstructUI.ts */ +/*---------------------------------------------------- + /src/views/TaskBoardSettingConstructUI.ts + ----------------------------------------------------*/ /* .TaskBoardSettingTab { max-width: fit-content; @@ -2455,6 +2457,22 @@ button.clickable-icon.view-action.taskboardScanVaultBtn.highlight { min-width: auto; } +.setting-item.taskBoard-settings-wide-input .setting-item-control { + min-width: 80%; + + flex: 1 1 auto; + text-align: end; + display: flex !important; + justify-content: flex-end; + align-items: center; + gap: var(--size-4-2); + flex-wrap: wrap !important; +} + +.setting-item.taskBoard-settings-wide-input .setting-item-control input[type='text'] { + min-width: -webkit-fill-available !important; +} + /* Hidden Properties in Live Editor and Reading Mode */ /* .taskboard-hidden-property { display: none !important; @@ -2867,7 +2885,9 @@ li:hover .taskboard-hidden-property { } -/* /src/components/ScanVaultModal.tsx */ +/*------------------------------------------------------------ + /src/components/ScanVaultModal.tsx +------------------------------------------------------------*/ .scanVaultModalHome { min-width: 40vw; From 719c499b49ec3e68599622948b2cc10da6885591 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Thu, 22 Jan 2026 21:44:34 +0530 Subject: [PATCH 020/236] test the latest changes | working --- data.json | 4 +-- task-board-logs.log | 61 ++++++++++++++++++++------------------------- 2 files changed, 29 insertions(+), 36 deletions(-) diff --git a/data.json b/data.json index c9c0770c..7f7aa16a 100644 --- a/data.json +++ b/data.json @@ -861,7 +861,7 @@ "verticalHeaderUI": false }, "taskCount": { - "pending": 63, + "pending": 64, "completed": 14 } } @@ -1057,7 +1057,7 @@ "viewedType": "kanban", "boardIndex": 4, "taskId": "", - "settingTab": 1 + "settingTab": 4 }, "taskNoteIdentifierTag": "task", "doubleClickCardToEdit": "noteInTab", diff --git a/task-board-logs.log b/task-board-logs.log index 5da66e59..d497f42c 100644 --- a/task-board-logs.log +++ b/task-board-logs.log @@ -2,57 +2,50 @@ ## System Information -- **Obsidian version**: 1.10.3 -- **Installer version**: 1.10.3 -- **Operating system**: Windows 10 Home Single Language 10.0.19045 -- **Use [[Wikilinks]]**: true -- **Base color scheme**: light -- **Community theme**: none -- **Snippets enabled**: 2 -- **Plugins installed**: 51 -- **Plugins enabled**: - - Plugin Reloader v0.0.3 - - Advanced Debug Mode v1.9.1 - - Calendar v1.5.10 - - Task Board v1.8.7 - - Style Settings v1.0.9 - - BRAT v1.4.0 - - TaskNotes v4.2.0 - - Dataview v0.5.68 - - Excalidraw v2.18.3 - ## Recent Bug Reports -Timestamp : 2026-01-15T16:47:07.456Z -ID : 4 -Message : Error showing filter popover -Context : Column.tsx/column-menu/configure-conlum-filters +Timestamp : 2026-01-22T14:29:32.899Z +ID : 73 +Message : Failed to move tasks.json file to new path +Context : JsonFileOperations.ts/moveTasksCacheFileToNewPath + +#### Bug Content +```log +Error: ENOENT: no such file or directory, rename 'D:\Personal_Projects_Hub\IDE_Wise_Projects\Obsidian\TemplateToDevelopPlugin\Meta\Task_Board\Cache\task-board-cache.json' -> 'D:\Personal_Projects_Hub\IDE_Wise_Projects\Obsidian\TemplateToDevelopPlugin\Meta\Task_Board\task-board-cache.json' +``` + +------------- + +Timestamp : 2026-01-22T14:41:12.790Z +ID : 73 +Message : Failed to move tasks.json file to new path +Context : JsonFileOperations.ts/moveTasksCacheFileToNewPath #### Bug Content ```log -Dont show popover. +Error: Destination file already exists! ``` ------------- -Timestamp : 2026-01-15T16:47:11.710Z -ID : 27 -Message : Error in Sortable onSort for child tasks -Context : AddOrEditTaskRC.tsx/childTasksListRef useEffect +Timestamp : 2026-01-22T15:32:49.517Z +ID : 73 +Message : Failed to move tasks.json file to new path +Context : JsonFileOperations.ts/moveTasksCacheFileToNewPath #### Bug Content ```log -Testing the new bug reporter. +Error: ENOENT: no such file or directory, rename 'D:\Personal_Projects_Hub\IDE_Wise_Projects\Obsidian\TemplateToDevelopPlugin\Meta\task-board-cache.json' -> 'D:\Personal_Projects_Hub\IDE_Wise_Projects\Obsidian\TemplateToDevelopPlugin\Meta\Task_Board\task-board-cache.json' ``` ------------- -Timestamp : 2026-01-15T16:47:59.447Z -ID : 4 -Message : Error showing filter popover -Context : Column.tsx/column-menu/configure-conlum-filters +Timestamp : 2026-01-22T15:39:09.095Z +ID : 73 +Message : Failed to move tasks.json file to new path +Context : JsonFileOperations.ts/moveTasksCacheFileToNewPath #### Bug Content ```log -Dont show popover. +Error: ENOENT: no such file or directory, rename 'D:\Personal_Projects_Hub\IDE_Wise_Projects\Obsidian\TemplateToDevelopPlugin\Meta\Task_Board\task-board-cache.json' -> 'D:\Personal_Projects_Hub\IDE_Wise_Projects\Obsidian\TemplateToDevelopPlugin\Meta\Task_Board\Cache\task-board-cache.json' ``` From fab60290eef83464f61871975f99a8b7f833437c Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Sun, 25 Jan 2026 19:57:34 +0530 Subject: [PATCH 021/236] feat : implement date selector modal --- src/modals/date_picker/DatePickerComponent.ts | 67 +++++---- src/modals/date_picker/DatePickerModal.ts | 22 +-- src/services/OpenModals.ts | 132 ++++++++++-------- 3 files changed, 127 insertions(+), 94 deletions(-) diff --git a/src/modals/date_picker/DatePickerComponent.ts b/src/modals/date_picker/DatePickerComponent.ts index 8ae65dbf..ed5ec030 100644 --- a/src/modals/date_picker/DatePickerComponent.ts +++ b/src/modals/date_picker/DatePickerComponent.ts @@ -18,8 +18,9 @@ export interface DatePickerState { export class DatePickerComponent extends Component { private hostEl: HTMLElement; + private plugin: TaskBoard; private app: App; - private plugin?: TaskBoard; + private dateName: string | undefined = ""; private state: DatePickerState; private onDateChange?: (date: string) => void; private currentViewDate: moment.Moment; @@ -27,15 +28,16 @@ export class DatePickerComponent extends Component { constructor( hostEl: HTMLElement, - app: App, - plugin?: TaskBoard, + plugin: TaskBoard, + dateName: string | undefined, initialDate?: string, - dateMark: string = "📅" + dateMark: string = "📅", ) { super(); this.hostEl = hostEl; - this.app = app; this.plugin = plugin; + this.app = plugin.app; + this.dateName = dateName; this.state = { selectedDate: initialDate || null, dateMark: dateMark, @@ -63,11 +65,13 @@ export class DatePickerComponent extends Component { } setSelectedDate(date: string | null): void { + console.log("DatePickerComponent : Sumbitting following date :", date); this.state.selectedDate = date; this.updateSelectedDateDisplay(); - if (this.onDateChange && date) { - // Only pass the date string, let the caller handle formatting - this.onDateChange(date); + // Only pass the date string, let the caller handle formatting + if (this.onDateChange) { + if (date) this.onDateChange(date); + else this.onDateChange(""); } } @@ -75,7 +79,16 @@ export class DatePickerComponent extends Component { this.hostEl.empty(); this.hostEl.addClass("date-picker-root-container"); - const mainPanel = this.hostEl.createDiv({ + const datePickerContainer = this.hostEl.createDiv({ + cls: "date-picker-container", + }); + + const heading = datePickerContainer.createEl("h2", { + cls: "date-picker-heading", + text: "Change " + this.dateName + " Date", + }); + + const mainPanel = datePickerContainer.createDiv({ cls: "date-picker-main-panel", }); @@ -99,19 +112,19 @@ export class DatePickerComponent extends Component { // Add quick date options const quickOptions = [ - { amount: 0, unit: "days", label: t("Today") }, - { amount: 1, unit: "days", label: t("Tomorrow") }, - { amount: 2, unit: "days", label: t("In 2 days") }, - { amount: 3, unit: "days", label: t("In 3 days") }, - { amount: 5, unit: "days", label: t("In 5 days") }, - { amount: 1, unit: "weeks", label: t("In 1 week") }, - { amount: 10, unit: "days", label: t("In 10 days") }, - { amount: 2, unit: "weeks", label: t("In 2 weeks") }, - { amount: 1, unit: "months", label: t("In 1 month") }, - { amount: 2, unit: "months", label: t("In 2 months") }, - { amount: 3, unit: "months", label: t("In 3 months") }, - { amount: 6, unit: "months", label: t("In 6 months") }, - { amount: 1, unit: "years", label: t("In 1 year") }, + { amount: 0, unit: "days", label: t("today") }, + { amount: 1, unit: "days", label: t("tomorrow") }, + { amount: 2, unit: "days", label: t("in-2-days") }, + { amount: 3, unit: "days", label: t("in-3-days") }, + // { amount: 5, unit: "days", label: t("In 5 days") }, + { amount: 1, unit: "weeks", label: t("in-1-week") }, + // { amount: 10, unit: "days", label: t("In 10 days") }, + { amount: 2, unit: "weeks", label: t("in-2-weeks") }, + { amount: 1, unit: "months", label: t("in-1-month") }, + { amount: 2, unit: "months", label: t("in-2-months") }, + // { amount: 3, unit: "months", label: t("In 3 months") }, + { amount: 6, unit: "months", label: t("in-6-months") }, + { amount: 1, unit: "years", label: t("in-1-year") }, ]; quickOptions.forEach((option) => { @@ -126,7 +139,7 @@ export class DatePickerComponent extends Component { const date = this.moment().add( option.amount, - option.unit as moment.unitOfTime.DurationConstructor + option.unit as moment.unitOfTime.DurationConstructor, ); const formattedDate = date.format("YYYY-MM-DD"); @@ -153,7 +166,7 @@ export class DatePickerComponent extends Component { }); clearOption.createSpan({ - text: t("Clear Date"), + text: t("clear-date"), cls: "quick-option-label", }); @@ -175,7 +188,7 @@ export class DatePickerComponent extends Component { private renderCalendarHeader( container: HTMLElement, - currentDate: moment.Moment + currentDate: moment.Moment, ): void { const header = container.createDiv({ cls: "calendar-header", @@ -212,7 +225,7 @@ export class DatePickerComponent extends Component { private renderCalendarGrid( container: HTMLElement, - currentDate: moment.Moment + currentDate: moment.Moment, ): void { const grid = container.createDiv({ cls: "calendar-grid", @@ -297,7 +310,7 @@ export class DatePickerComponent extends Component { // Highlight selected calendar day this.hostEl.querySelectorAll(".calendar-day").forEach((el) => { const storedDate = (el as HTMLElement).getAttribute( - "data-date" + "data-date", ); if (storedDate && this.state.selectedDate === storedDate) { el.addClass("selected"); diff --git a/src/modals/date_picker/DatePickerModal.ts b/src/modals/date_picker/DatePickerModal.ts index eae43ae1..1a91bb9b 100644 --- a/src/modals/date_picker/DatePickerModal.ts +++ b/src/modals/date_picker/DatePickerModal.ts @@ -3,20 +3,22 @@ import { DatePickerComponent, DatePickerState } from "./DatePickerComponent"; import TaskBoard from "main"; export class DatePickerModal extends Modal { - public datePickerComponent!: DatePickerComponent; - public onDateSelected: ((date: string | null) => void) | null = null; - private plugin?: TaskBoard; + private plugin: TaskBoard; + private dateName?: string; private initialDate?: string; - private dateMark: string; + private dateMark!: string; + public datePickerComponent!: DatePickerComponent; + public onDateSelected: ((date: string) => void) | null = null; constructor( - app: App, - plugin?: TaskBoard, + plugin: TaskBoard, + dateName?: string, initialDate?: string, - dateMark: string = "📅" + dateMark: string = "📅", ) { - super(app); + super(plugin.app); this.plugin = plugin; + this.dateName = dateName; this.initialDate = initialDate; this.dateMark = dateMark; } @@ -27,10 +29,10 @@ export class DatePickerModal extends Modal { this.datePickerComponent = new DatePickerComponent( this.contentEl, - this.app, this.plugin, + this.dateName, this.initialDate, - this.dateMark + this.dateMark, ); this.datePickerComponent.onload(); diff --git a/src/services/OpenModals.ts b/src/services/OpenModals.ts index 453b41f3..8499e51d 100644 --- a/src/services/OpenModals.ts +++ b/src/services/OpenModals.ts @@ -27,13 +27,15 @@ import { ScanFilterModal } from "src/modals/ScanFilterModal"; import { ScanVaultModal } from "src/modals/ScanVaultModal"; import { TaskBoardActionsModal } from "src/modals/TaskBoardActionsModal"; import { bugReporterManagerInsatance } from "src/managers/BugReporter"; +import { DatePickerModal } from "src/modals/date_picker"; +import { updateTaskItemDate } from "src/utils/UserTaskEvents"; // Function to open the BoardConfigModal export const openBoardConfigModal = ( plugin: TaskBoard, boards: Board[], activeBoardIndex: number, - onSave: (updatedBoards: Board[]) => void + onSave: (updatedBoards: Board[]) => void, ) => { new BoardConfigureModal(plugin, boards, activeBoardIndex, onSave).open(); }; @@ -47,7 +49,7 @@ export const openAddNewTaskInCurrentFileModal = ( app: App, plugin: TaskBoard, activeFile: TFile, - cursorPosition?: { line: number; ch: number } | undefined + cursorPosition?: { line: number; ch: number } | undefined, ) => { const AddTaskModal = new AddOrEditTaskModal( plugin, @@ -55,9 +57,9 @@ export const openAddNewTaskInCurrentFileModal = ( addTaskInNote(plugin, newTask, true, cursorPosition).then( (newId) => { plugin.realTimeScanner.processAllUpdatedFiles( - newTask.filePath + newTask.filePath, ); - } + }, ); // DEPRECATED : See notes from //src/utils/TaskItemCacheOperations.ts file @@ -77,7 +79,7 @@ export const openAddNewTaskInCurrentFileModal = ( true, false, undefined, - activeFile.path + activeFile.path, ); AddTaskModal.open(); return true; @@ -86,10 +88,10 @@ export const openAddNewTaskInCurrentFileModal = ( export const openAddNewTaskModal = ( app: App, plugin: TaskBoard, - activeFile?: TFile + activeFile?: TFile, ) => { const preDefinedNoteFile = plugin.app.vault.getAbstractFileByPath( - plugin.settings.data.globalSettings.preDefinedNote + plugin.settings.data.globalSettings.preDefinedNote, ); const activeTFile = activeFile ? activeFile : preDefinedNoteFile; const AddTaskModal = new AddOrEditTaskModal( @@ -107,12 +109,12 @@ export const openAddNewTaskModal = ( quickAddPluginChoice, { value: completeTask + "\n", - } + }, ); } else { await addTaskInNote(plugin, newTask, false).then((newId) => { plugin.realTimeScanner.processAllUpdatedFiles( - newTask.filePath + newTask.filePath, ); }); } @@ -134,7 +136,7 @@ export const openAddNewTaskModal = ( undefined, activeTFile ? activeTFile.path - : plugin.settings.data.globalSettings.preDefinedNote + : plugin.settings.data.globalSettings.preDefinedNote, ); AddTaskModal.open(); }; @@ -145,7 +147,7 @@ export const openAddNewTaskNoteModal = (app: App, plugin: TaskBoard) => { async ( newTask: taskItem, quickAddPluginChoice: string, - noteContent: string | undefined + noteContent: string | undefined, ) => { if (!noteContent) { // console.warn("This code should not run..."); @@ -164,7 +166,7 @@ export const openAddNewTaskNoteModal = (app: App, plugin: TaskBoard) => { // Create or update the file const existingFile = plugin.app.vault.getFileByPath( - newTask.filePath + newTask.filePath, ); if (!existingFile) { await plugin.app.vault @@ -172,7 +174,7 @@ export const openAddNewTaskNoteModal = (app: App, plugin: TaskBoard) => { .then(() => { // This is required to rescan the updated file and refresh the board. plugin.realTimeScanner.onFileModified( - newTask.filePath + newTask.filePath, ); sleep(1000).then(() => { // TODO : Is 1 seconds really required ? @@ -183,17 +185,17 @@ export const openAddNewTaskNoteModal = (app: App, plugin: TaskBoard) => { new Notice( t("file-note-already-exists") + t( - "creating a new file with the following name :" + "creating a new file with the following name :", ) + ` Copy-${newTask.filePath}`, - 10000 + 10000, ); await plugin.app.vault .create(`Copy-${newTask.filePath}`, noteContent) .then(() => { // This is required to rescan the updated file and refresh the board. plugin.realTimeScanner.onFileModified( - `Copy-${newTask.filePath}` + `Copy-${newTask.filePath}`, ); sleep(1000).then(() => { plugin.realTimeScanner.processAllUpdatedFiles(); @@ -203,7 +205,7 @@ export const openAddNewTaskNoteModal = (app: App, plugin: TaskBoard) => { } catch (error) { console.error( "Error creating or updating task note:", - error + error, ); new Notice(t("error-creating-task-note"), 5000); return false; @@ -216,14 +218,14 @@ export const openAddNewTaskNoteModal = (app: App, plugin: TaskBoard) => { false, false, undefined, - "" + "", ); AddTaskModal.open(); }; export const openEditTaskModal = async ( plugin: TaskBoard, - existingTask: taskItem + existingTask: taskItem, ) => { const EditTaskModal = new AddOrEditTaskModal( plugin, @@ -240,9 +242,9 @@ export const openEditTaskModal = async ( (newId) => { plugin.realTimeScanner.processAllUpdatedFiles( updatedTask.filePath, - existingTask.id + existingTask.id, ); - } + }, ); // DEPRECATED : See notes from //src/utils/TaskItemCacheOperations.ts file @@ -259,21 +261,21 @@ export const openEditTaskModal = async ( false, true, existingTask, - existingTask.filePath + existingTask.filePath, ); EditTaskModal.open(); }; export const openEditTaskNoteModal = ( plugin: TaskBoard, - existingTask: taskItem + existingTask: taskItem, ) => { const EditTaskModal = new AddOrEditTaskModal( plugin, async ( updatedTask: taskItem, quickAddPluginChoice: string, - newTaskContent: string | undefined + newTaskContent: string | undefined, ) => { let eventData: UpdateTaskEventData = { taskID: existingTask.id, @@ -285,14 +287,14 @@ export const openEditTaskNoteModal = ( // Update frontmatter with task properties await updateFrontmatterInMarkdownFile( plugin, - updatedTask + updatedTask, ).then(() => { sleep(1000).then(() => { // TODO : Is 1 sec really required ? // This is required to rescan the updated file and refresh the board. plugin.realTimeScanner.processAllUpdatedFiles( updatedTask.filePath, - existingTask.id + existingTask.id, ); }); }); @@ -300,14 +302,14 @@ export const openEditTaskNoteModal = ( writeDataToVaultFile( plugin, updatedTask.filePath, - newTaskContent + newTaskContent, ).then(() => { sleep(1000).then(() => { // TODO : Is 1 sec really required ? // This is required to rescan the updated file and refresh the board. plugin.realTimeScanner.processAllUpdatedFiles( updatedTask.filePath, - existingTask.id + existingTask.id, ); }); }); @@ -323,7 +325,7 @@ export const openEditTaskNoteModal = ( 41, "Error updating task note", error as string, - "TaskNoteEventHandlers.ts/handleTaskNoteEdit" + "TaskNoteEventHandlers.ts/handleTaskNoteEdit", ); } }, @@ -331,7 +333,7 @@ export const openEditTaskNoteModal = ( false, true, existingTask, - existingTask.filePath + existingTask.filePath, ); EditTaskModal.open(); }; @@ -343,14 +345,14 @@ export const openEditTaskNoteModal = ( * @param message - The short message of the bug, shown to the user. * @param bugContent - The detailed information related to the bug. * @param context - The location where the bug was generated and some other context. - * + * * @deprecated v1.9.0 */ export const bugReporter = ( plugin: TaskBoard, message: string, bugContent: string, - context: string + context: string, ) => { // const leaves = plugin.app.workspace.getLeavesOfType(VIEW_TYPE_TASKBOARD); // if (leaves.length > 0) { @@ -397,7 +399,7 @@ export const bugReporter = ( plugin, message, bugContent, - context + context, ); bugReportModal.open(); el.hide(); @@ -412,7 +414,7 @@ export const bugReporter = ( }); }); }), - 0 + 0, ); bugReportNotice.messageEl.onClickEvent((e) => { @@ -458,7 +460,7 @@ export const openDiffContentCompareModal = ( cachedTaskContent: string, EditedTaskContent: string, taskContentFromFile: string, - onSelect: (which: "old" | "new") => void + onSelect: (which: "old" | "new") => void, ) => { const contentMismatchNotice = new Notice( createFragment((f) => { @@ -478,7 +480,7 @@ export const openDiffContentCompareModal = ( cachedTaskContent, EditedTaskContent, taskContentFromFile, - onSelect + onSelect, ); modal.open(); el.hide(); @@ -486,7 +488,7 @@ export const openDiffContentCompareModal = ( }); }); }), - 0 + 0, ); contentMismatchNotice.messageEl.onClickEvent((e) => { @@ -500,11 +502,11 @@ export const openDiffContentCompareModal = ( export const openTaskBoardActionsModal = ( plugin: TaskBoard, - activeBoardIndex: number + activeBoardIndex: number, ) => { const actionModal = new TaskBoardActionsModal( plugin, - plugin.settings.data.boardConfigs[activeBoardIndex].columns + plugin.settings.data.boardConfigs[activeBoardIndex].columns, ); actionModal.open(); }; @@ -512,7 +514,7 @@ export const openTaskBoardActionsModal = ( export const openScanFiltersModal = ( plugin: TaskBoard, filterType: "files" | "frontMatter" | "folders" | "tags", - onSave: (scanFilters: string[]) => void + onSave: (scanFilters: string[]) => void, ) => { new ScanFilterModal(plugin, filterType, async (newValues) => { onSave(newValues); @@ -572,7 +574,7 @@ export const openEditTaskView = async ( taskExists: boolean, task: taskItem, filePath: string, - location: "tab" | "split" | "window" = "tab" + location: "tab" | "split" | "window" = "tab", ): Promise => { const { workspace } = plugin.app; @@ -644,17 +646,17 @@ export const openEditTaskView = async ( async ( updatedTask, quickAddPluginChoice, - updatedNoteContent + updatedNoteContent, ) => { if (!isTaskNote) { // Update the task in the file and JSON updateTaskInFile( plugin, updatedTask, - task + task, ).then((newId) => { plugin.realTimeScanner.processAllUpdatedFiles( - updatedTask.filePath + updatedTask.filePath, ); }); } else { @@ -662,14 +664,14 @@ export const openEditTaskView = async ( // Update frontmatter with task properties await updateFrontmatterInMarkdownFile( plugin, - updatedTask + updatedTask, ).then(() => { // This is required to rescan the updated file and refresh the board. sleep(1000).then(() => { // TODO : Is 1 sec really required ? // This is required to rescan the updated file and refresh the board. plugin.realTimeScanner.processAllUpdatedFiles( - updatedTask.filePath + updatedTask.filePath, ); }); }); @@ -677,13 +679,13 @@ export const openEditTaskView = async ( writeDataToVaultFile( plugin, updatedTask.filePath, - updatedNoteContent + updatedNoteContent, ).then(() => { sleep(1000).then(() => { // TODO : Is 1 sec really required ? // This is required to rescan the updated file and refresh the board. plugin.realTimeScanner.processAllUpdatedFiles( - updatedTask.filePath + updatedTask.filePath, ); }); }); @@ -694,7 +696,7 @@ export const openEditTaskView = async ( activeNote, taskExists, task, - filePath + filePath, ); }); } catch { @@ -705,29 +707,29 @@ export const openEditTaskView = async ( async ( updatedTask, quickAddPluginChoice, - updatedNoteContent + updatedNoteContent, ) => { if (!isTaskNote) { // Update the task in the file and JSON updateTaskInFile(plugin, updatedTask, task).then( (newId) => { plugin.realTimeScanner.processAllUpdatedFiles( - updatedTask.filePath + updatedTask.filePath, ); - } + }, ); } else { if (!updatedNoteContent) { // Update frontmatter with task properties await updateFrontmatterInMarkdownFile( plugin, - updatedTask + updatedTask, ).then(() => { // This is required to rescan the updated file and refresh the board. sleep(1000).then(() => { // This is required to rescan the updated file and refresh the board. plugin.realTimeScanner.processAllUpdatedFiles( - updatedTask.filePath + updatedTask.filePath, ); }); }); @@ -735,13 +737,13 @@ export const openEditTaskView = async ( writeDataToVaultFile( plugin, updatedTask.filePath, - updatedNoteContent + updatedNoteContent, ).then(() => { sleep(1000).then(() => { // TODO : Is 1 sec really required ? // This is required to rescan the updated file and refresh the board. plugin.realTimeScanner.processAllUpdatedFiles( - updatedTask.filePath + updatedTask.filePath, ); }); }); @@ -752,7 +754,7 @@ export const openEditTaskView = async ( activeNote, taskExists, task, - filePath + filePath, ); // Replace the leaf's view with our configured view @@ -776,3 +778,19 @@ export const openEditTaskView = async ( return leaf; } }; + +export const openDateInputModal = async ( + plugin: TaskBoard, + dateName: string, + initialValue: string, + onSelect: (newDate: string) => void, +) => { + const datePicker = new DatePickerModal(plugin, dateName, initialValue); + datePicker.onDateSelected = async (date: string) => { + // newTask[dateType] = date; + // updateTaskItemDate(plugin, newTask, dateType, date); + onSelect(date); + }; + + datePicker.open(); +}; From 3e236b0c8d7bf836ed93667c5088d9348d91733c Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Sun, 25 Jan 2026 19:58:17 +0530 Subject: [PATCH 022/236] fix : trash files instead of deleting them completely --- src/utils/taskNote/TaskNoteUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/taskNote/TaskNoteUtils.ts b/src/utils/taskNote/TaskNoteUtils.ts index d5894a30..5ffefeaf 100644 --- a/src/utils/taskNote/TaskNoteUtils.ts +++ b/src/utils/taskNote/TaskNoteUtils.ts @@ -421,7 +421,7 @@ export async function deleteTaskNote( return; } - await plugin.app.vault.delete(file); + await plugin.app.vault.trash(file, true); new Notice(`Task note deleted: ${file.name}`); } catch (error) { console.error("Error deleting task note:", error); From d4ae7352d20b43aa0ad409b20f4ae814841da3a0 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Sun, 25 Jan 2026 19:59:50 +0530 Subject: [PATCH 023/236] code refactoring --- main.ts | 316 ++++++++++----------- src/interfaces/Mapping.ts | 6 +- src/managers/VaultScanner.ts | 9 +- src/utils/MarkdownFileOperations.ts | 14 +- src/utils/taskLine/TaskContentFormatter.ts | 14 +- 5 files changed, 182 insertions(+), 177 deletions(-) diff --git a/main.ts b/main.ts index 883eb402..c216a133 100644 --- a/main.ts +++ b/main.ts @@ -912,6 +912,164 @@ export default class TaskBoard extends Plugin { } } + async findModifiedFilesOnAppAbsense() { + if (this.vaultScanner.tasksCache.Modified_at) { + const LAST_UPDATED_TIME = Date.parse( + this.vaultScanner.tasksCache.Modified_at, + ); + console.log( + "Task Board : Fetching all modified files...\nLast modified time :", + LAST_UPDATED_TIME, + ); + let filesScannedCount = 0; + const modifiedCreatedRenamedFiles = this.app.vault + .getFiles() + .filter((file) => { + filesScannedCount++; + return ( + file.stat.mtime > LAST_UPDATED_TIME || + file.stat.ctime > LAST_UPDATED_TIME + ); + }); + + // Find deleted files by comparing cache with current vault files + const currentFilesPaths = new Set( + this.app.vault.getFiles().map((file) => file.path), + ); + const cachedFilesPaths = Object.keys( + this.vaultScanner.tasksCache.Pending || {}, + ).concat(Object.keys(this.vaultScanner.tasksCache.Completed || {})); + const deletedFiles = new Set( + cachedFilesPaths.filter( + (filePath) => !currentFilesPaths.has(filePath), + ), + ); + const deletedFilesList = [...deletedFiles]; + + const changed_files = modifiedCreatedRenamedFiles.filter((file) => + fileTypeAllowedForScanning( + this.plugin.settings.data.globalSettings, + file, + ), + ); + console.log( + "Task Board : Fetching complete.\nModified files :", + changed_files, + "\nDeleted files :", + deletedFilesList, + "\nFiles scanned :", + filesScannedCount, + ); + const totalFilesLength = + changed_files.length + deletedFilesList.length; + + if (totalFilesLength > 0) { + const modifiedFilesNotice = new Notice( + createFragment((f) => { + f.createDiv("bugReportNotice", (el) => { + el.createEl("p", { + text: `Task Board : ${totalFilesLength} files has been modified when Obsidian was inactive.`, + }); + el.createEl("button", { + text: t("show-me"), + cls: "reportBugButton", + onclick: () => { + // el.hide(); + + // Open a modal and show all these file names with their modified date-time in a nice UI. + const modifiedFilesModal = + new ModifiedFilesModal(this.app, { + modifiedFiles: changed_files, + deletedFiles: deletedFilesList, + }); + modifiedFilesModal.open(); + }, + }); + el.createEl("button", { + text: t("scan-them"), + cls: "ignoreBugButton", + onclick: async () => { + modifiedFilesNotice.hide(); + + // Show progress notice + this.currentProgressNotice = new Notice( + `Task Board : Processing modified files: 0/${totalFilesLength}`, + 0, + ); + + this.plugin.vaultScanner + .refreshTasksFromFiles( + changed_files, + false, + ) + .then(async () => { + console.log( + "Task Board : Will now going to update the deleted files cache...", + ); + if (deletedFilesList.length > 0) { + await this.plugin.vaultScanner.deleteCacheForFiles( + deletedFilesList, + ); + console.log( + "Task Board : Completed deleting cache of deleted files...", + ); + } + }); + + let modifiedFilesQueue = changed_files; + + let processed = 0; + while (modifiedFilesQueue.length > 0) { + const file = + modifiedFilesQueue.shift()!; + + try { + processed++; + + // Update progress notice + this.currentProgressNotice.messageEl.textContent = `Task Board : Processing created files: ${processed}/${totalFilesLength}`; + } catch (error) { + console.error( + `Error processing created file ${file.path}:`, + error, + ); + } + + // Add delay between processing each file to prevent blocking UI + if (modifiedFilesQueue.length > 0) { + await new Promise((resolve) => + setTimeout( + resolve, + this.PROCESSING_INTERVAL, + ), + ); + } + } + + // Hide progress notice after completion + this.currentProgressNotice?.hide(); + this.currentProgressNotice = null; + new Notice( + `✓ Task Board : Finished processing ${totalFilesLength} created file(s)`, + ); + }, + }); + }); + }), + 0, + ); + + modifiedFilesNotice.messageEl.onClickEvent((e) => { + if (e.target instanceof HTMLButtonElement) { + e.stopPropagation(); + e.preventDefault(); + e.stopImmediatePropagation(); + } + }); + } + } + } + /** * Registers all the events that TaskBoard needs to listen to */ @@ -1274,164 +1432,6 @@ export default class TaskBoard extends Plugin { // // this.saveSettings(); // } - async findModifiedFilesOnAppAbsense() { - if (this.vaultScanner.tasksCache.Modified_at) { - const LAST_UPDATED_TIME = Date.parse( - this.vaultScanner.tasksCache.Modified_at, - ); - console.log( - "Task Board : Fetching all modified files...\nLast modified time :", - LAST_UPDATED_TIME, - ); - let filesScannedCount = 0; - const modifiedCreatedRenamedFiles = this.app.vault - .getFiles() - .filter((file) => { - filesScannedCount++; - return ( - file.stat.mtime > LAST_UPDATED_TIME || - file.stat.ctime > LAST_UPDATED_TIME - ); - }); - - // Find deleted files by comparing cache with current vault files - const currentFilesPaths = new Set( - this.app.vault.getFiles().map((file) => file.path), - ); - const cachedFilesPaths = Object.keys( - this.vaultScanner.tasksCache.Pending || {}, - ).concat(Object.keys(this.vaultScanner.tasksCache.Completed || {})); - const deletedFiles = new Set( - cachedFilesPaths.filter( - (filePath) => !currentFilesPaths.has(filePath), - ), - ); - const deletedFilesList = [...deletedFiles]; - - const changed_files = modifiedCreatedRenamedFiles.filter((file) => - fileTypeAllowedForScanning( - this.plugin.settings.data.globalSettings, - file, - ), - ); - console.log( - "Task Board : Fetching complete.\nModified files :", - changed_files, - "\nDeleted files :", - deletedFilesList, - "\nFiles scanned :", - filesScannedCount, - ); - const totalFilesLength = - changed_files.length + deletedFilesList.length; - - if (totalFilesLength > 0) { - const modifiedFilesNotice = new Notice( - createFragment((f) => { - f.createDiv("bugReportNotice", (el) => { - el.createEl("p", { - text: `Task Board : ${totalFilesLength} files has been modified when Obsidian was inactive.`, - }); - el.createEl("button", { - text: t("show-me"), - cls: "reportBugButton", - onclick: () => { - // el.hide(); - - // Open a modal and show all these file names with their modified date-time in a nice UI. - const modifiedFilesModal = - new ModifiedFilesModal(this.app, { - modifiedFiles: changed_files, - deletedFiles: deletedFilesList, - }); - modifiedFilesModal.open(); - }, - }); - el.createEl("button", { - text: t("scan-them"), - cls: "ignoreBugButton", - onclick: async () => { - modifiedFilesNotice.hide(); - - // Show progress notice - this.currentProgressNotice = new Notice( - `Task Board : Processing modified files: 0/${totalFilesLength}`, - 0, - ); - - this.plugin.vaultScanner - .refreshTasksFromFiles( - changed_files, - false, - ) - .then(async () => { - console.log( - "Task Board : Will now going to update the deleted files cache...", - ); - if (deletedFilesList.length > 0) { - await this.plugin.vaultScanner.deleteCacheForFiles( - deletedFilesList, - ); - console.log( - "Task Board : Completed deleting cache of deleted files...", - ); - } - }); - - let modifiedFilesQueue = changed_files; - - let processed = 0; - while (modifiedFilesQueue.length > 0) { - const file = - modifiedFilesQueue.shift()!; - - try { - processed++; - - // Update progress notice - this.currentProgressNotice.messageEl.textContent = `Task Board : Processing created files: ${processed}/${totalFilesLength}`; - } catch (error) { - console.error( - `Error processing created file ${file.path}:`, - error, - ); - } - - // Add delay between processing each file to prevent blocking UI - if (modifiedFilesQueue.length > 0) { - await new Promise((resolve) => - setTimeout( - resolve, - this.PROCESSING_INTERVAL, - ), - ); - } - } - - // Hide progress notice after completion - this.currentProgressNotice?.hide(); - this.currentProgressNotice = null; - new Notice( - `✓ Task Board : Finished processing ${totalFilesLength} created file(s)`, - ); - }, - }); - }); - }), - 0, - ); - - modifiedFilesNotice.messageEl.onClickEvent((e) => { - if (e.target instanceof HTMLButtonElement) { - e.stopPropagation(); - e.preventDefault(); - e.stopImmediatePropagation(); - } - }); - } - } - } - private runOnPluginUpdate() { // Check if the plugin version has changed const currentVersion = newReleaseVersion; // Change this whenever you will going to release a new version. diff --git a/src/interfaces/Mapping.ts b/src/interfaces/Mapping.ts index cb90083a..6aa24d60 100644 --- a/src/interfaces/Mapping.ts +++ b/src/interfaces/Mapping.ts @@ -14,6 +14,7 @@ export const priorityEmojis: { [key: number]: string } = { export interface statusDropDownOption { value: string; + name: string; text: string; } @@ -41,10 +42,11 @@ export const getPriorityOptionsForDropdown = (): priorityDropDownOption[] => [ export const priorityOptions = getPriorityOptionsForDropdown(); export const getCustomStatusOptionsForDropdown = ( - statusConfigs: CustomStatus[] + statusConfigs: CustomStatus[], ): statusDropDownOption[] => { return statusConfigs.map(({ symbol, name }) => ({ value: symbol, + name: name, text: `${name} : [${symbol}]`, })); }; @@ -149,5 +151,5 @@ export const columnTypeAndNameMapping: { [key: string]: string } = { taskPriority: "Priority", pathFiltered: "Path filtered", completed: "Completed", - allPending: "All pending tasks" + allPending: "All pending tasks", }; diff --git a/src/managers/VaultScanner.ts b/src/managers/VaultScanner.ts index cdfffbe0..4a281a85 100644 --- a/src/managers/VaultScanner.ts +++ b/src/managers/VaultScanner.ts @@ -684,16 +684,15 @@ export function fileTypeAllowedForScanning( // plugin.settings.data.globalSettings.archivedTasksFilePath, "\nCondition 3 :", , "\nCondition 4 :", ) const filePath = file.path.toLocaleLowerCase(); + console.log("Filepath :", filePath); if ( // notAllowedFileExtensionsRegEx.test(file.path) || - allowedFileExtensionsRegEx.test(file.path) === false || - ( - globalSettings.archivedTBNotesFolderPath.trim() !== "" && + !allowedFileExtensionsRegEx.test(file.path) || + (globalSettings.archivedTBNotesFolderPath.trim() !== "" && filePath.startsWith( globalSettings.archivedTBNotesFolderPath.toLowerCase(), - ) - ) || + )) || filePath === globalSettings.archivedTasksFilePath.toLowerCase() ) { return false; diff --git a/src/utils/MarkdownFileOperations.ts b/src/utils/MarkdownFileOperations.ts index 492f1260..b747ee7e 100644 --- a/src/utils/MarkdownFileOperations.ts +++ b/src/utils/MarkdownFileOperations.ts @@ -13,7 +13,7 @@ import { bugReporterManagerInsatance } from "src/managers/BugReporter"; */ export const readDataOfVaultFile = async ( plugin: TaskBoard, - filePath: string + filePath: string, ): Promise => { try { const file = plugin.app.vault.getAbstractFileByPath(filePath); @@ -27,18 +27,18 @@ export const readDataOfVaultFile = async ( 75, "File not found in vault.", `File not found at path: ${filePath}`, - "MarkdownFileOperations.ts/readDataOfVaultFile" + "MarkdownFileOperations.ts/readDataOfVaultFile", ); throw `File not found at path: ${filePath}`; } } catch (error) { bugReporterManagerInsatance.showNotice( 76, - "Error reading data from vault files.", + `Error reading data from vault file. Couldnt able to read the following file : ${filePath}`, String(error), - "MarkdownFileOperations.ts/readDataOfVaultFile" + "MarkdownFileOperations.ts/readDataOfVaultFile", ); - throw error; + return ""; } }; @@ -52,7 +52,7 @@ export const readDataOfVaultFile = async ( export const writeDataToVaultFile = async ( plugin: TaskBoard, filePath: string, - newContent: string + newContent: string, ): Promise => { try { const file = plugin.app.vault.getAbstractFileByPath(filePath); @@ -70,7 +70,7 @@ export const writeDataToVaultFile = async ( 77, "Error writing to file in vault.", String(error), - "MarkdownFileOperations.ts/writeDataToVaultFile" + "MarkdownFileOperations.ts/writeDataToVaultFile", ); // throw error; } diff --git a/src/utils/taskLine/TaskContentFormatter.ts b/src/utils/taskLine/TaskContentFormatter.ts index fea51af9..8c02748b 100644 --- a/src/utils/taskLine/TaskContentFormatter.ts +++ b/src/utils/taskLine/TaskContentFormatter.ts @@ -1261,7 +1261,8 @@ export const sanitizeDependsOn = ( // For handleCheckboxChange /** - * Function to clean the task title by removing metadata. + * Function to remove only the properties which user has configured + * to be hidden using the hiddenTaskProperties setting. * @param plugin - The TaskBoard plugin instance. * @param task - The task item to clean. * @returns The cleaned task title without metadata. @@ -1289,10 +1290,10 @@ export const cleanTaskTitle = (plugin: TaskBoard, task: taskItem): string => { ) .trim(); - // If legacy showTaskWithoutMetadata is enabled, hide all properties (backward compatibility) - if (plugin.settings.data.globalSettings.showTaskWithoutMetadata) { - return cleanTaskTitleLegacy(task); - } + // // If legacy showTaskWithoutMetadata is enabled, hide all properties (backward compatibility) + // if (plugin.settings.data.globalSettings.showTaskWithoutMetadata) { + // return cleanTaskTitleLegacy(task); + // } // Hide only selected properties hiddenProperties.forEach((property) => { @@ -1441,6 +1442,9 @@ export const cleanTaskTitleLegacy = (task: taskItem): string => { ) .trim(); + // TODO : Support the legacy feature of adding all properties after the pipe symbol (|). + // If pipe symbol is present (` | `), then remove everything after the pipe symbol. + // Remove tags task.tags.forEach((tag) => { const tagRegex = new RegExp(`\\s*${tag}\\s*`, "g"); From e41c3b7a8b6b3b0c893ab4b7b9ae5806620538e6 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Sun, 25 Jan 2026 20:00:22 +0530 Subject: [PATCH 024/236] feat : Implement textInputModal for renaming files --- src/modals/TextInputModal.ts | 110 +++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 src/modals/TextInputModal.ts diff --git a/src/modals/TextInputModal.ts b/src/modals/TextInputModal.ts new file mode 100644 index 00000000..0d260c13 --- /dev/null +++ b/src/modals/TextInputModal.ts @@ -0,0 +1,110 @@ +import { App, Modal, Setting } from "obsidian"; + +export interface TextInputModalOptions { + title: string; + placeholder?: string; + initialValue?: string; + confirmText?: string; + cancelText?: string; +} + +/** + * Generic text input modal + */ +export class TextInputModal extends Modal { + private options: TextInputModalOptions; + private resolve!: (value: string | null) => void; + private inputEl!: HTMLInputElement; + + constructor(app: App, options: TextInputModalOptions) { + super(app); + this.options = { + confirmText: "Confirm", + cancelText: "Cancel", + ...options, + }; + } + + public show(): Promise { + return new Promise((resolve) => { + this.resolve = resolve; + this.open(); + }); + } + + onOpen() { + const { contentEl } = this; + contentEl.empty(); + + new Setting(contentEl).setName(this.options.title).setHeading(); + + new Setting(contentEl).addText((text) => { + this.inputEl = text.inputEl; + text.setPlaceholder(this.options.placeholder || "") + .setValue(this.options.initialValue || "") + .onChange(() => { + // Optional: real-time validation could go here + }); + + // Focus the input + setTimeout(() => { + this.inputEl.focus(); + this.inputEl.select(); + }, 100); + }); + + const buttonContainer = contentEl.createEl("div", { cls: "modal-button-container" }); + buttonContainer.style.display = "flex"; + buttonContainer.style.gap = "10px"; + buttonContainer.style.justifyContent = "flex-end"; + buttonContainer.style.marginTop = "20px"; + + const cancelButton = buttonContainer.createEl("button", { text: this.options.cancelText }); + cancelButton.addEventListener("click", () => { + this.resolve(null); + this.close(); + }); + + const confirmButton = buttonContainer.createEl("button", { + text: this.options.confirmText, + cls: "mod-cta", + }); + + confirmButton.addEventListener("click", () => { + const value = this.inputEl.value.trim(); + this.resolve(value || null); + this.close(); + }); + + // Handle Enter key + this.inputEl.addEventListener("keydown", (e) => { + if (e.key === "Enter") { + e.preventDefault(); + confirmButton.click(); + } else if (e.key === "Escape") { + e.preventDefault(); + cancelButton.click(); + } + }); + } + + onClose() { + const { contentEl } = this; + contentEl.empty(); + // Ensure promise is resolved even if modal is closed without selection + if (this.resolve) { + this.resolve(null); + } + } +} + +/** + * Utility function to show text input modal + */ +export async function showTextInputModal( + app: App, + options: TextInputModalOptions +): Promise { + const modal = new TextInputModal(app, options); + return modal.show(); +} From 76cadf89ab87ae5902ce2153bf23b76010d1fb38 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Sun, 25 Jan 2026 20:15:55 +0530 Subject: [PATCH 025/236] BREAKING : Stop scanning if fileContent is empty --- src/managers/VaultScanner.ts | 2 + src/modals/AddOrEditTaskModal.tsx | 5 +- src/utils/MarkdownFileOperations.ts | 4 +- src/utils/taskLine/TaskLineUtils.ts | 102 ++++++++++---------- src/utils/taskNote/TaskNoteEventHandlers.ts | 29 +++--- src/views/AddOrEditTaskView.tsx | 5 +- 6 files changed, 80 insertions(+), 67 deletions(-) diff --git a/src/managers/VaultScanner.ts b/src/managers/VaultScanner.ts index 4a281a85..6ae1bbff 100644 --- a/src/managers/VaultScanner.ts +++ b/src/managers/VaultScanner.ts @@ -101,6 +101,8 @@ export default class VaultScanner { this.plugin, fileNameWithPath, ); + if (fileContent == null) return "false"; + const lines = fileContent.split("\n"); const oldPendingFileCache = diff --git a/src/modals/AddOrEditTaskModal.tsx b/src/modals/AddOrEditTaskModal.tsx index 04da09c3..54cc77d4 100644 --- a/src/modals/AddOrEditTaskModal.tsx +++ b/src/modals/AddOrEditTaskModal.tsx @@ -70,7 +70,10 @@ export class AddOrEditTaskModal extends Modal { let noteContent: string = ""; if (this.isTaskNote) { if (this.filePath) { - noteContent = await readDataOfVaultFile(this.plugin, this.filePath); + const data = await readDataOfVaultFile(this.plugin, this.filePath); + + if (data == null) this.onClose(); + else noteContent = data; } else { noteContent = "---\ntitle: \n---\n"; diff --git a/src/utils/MarkdownFileOperations.ts b/src/utils/MarkdownFileOperations.ts index b747ee7e..e0a2fcaa 100644 --- a/src/utils/MarkdownFileOperations.ts +++ b/src/utils/MarkdownFileOperations.ts @@ -14,7 +14,7 @@ import { bugReporterManagerInsatance } from "src/managers/BugReporter"; export const readDataOfVaultFile = async ( plugin: TaskBoard, filePath: string, -): Promise => { +): Promise => { try { const file = plugin.app.vault.getAbstractFileByPath(filePath); if (file && file instanceof TFile) { @@ -38,7 +38,7 @@ export const readDataOfVaultFile = async ( String(error), "MarkdownFileOperations.ts/readDataOfVaultFile", ); - return ""; + return null; } }; diff --git a/src/utils/taskLine/TaskLineUtils.ts b/src/utils/taskLine/TaskLineUtils.ts index 9a5b29ed..6a802dc8 100644 --- a/src/utils/taskLine/TaskLineUtils.ts +++ b/src/utils/taskLine/TaskLineUtils.ts @@ -22,7 +22,7 @@ import { isTheContentDiffAreOnlySpaces_V2 } from "src/modals/DiffContentCompareM import { bugReporterManagerInsatance } from "src/managers/BugReporter"; /** - * This function will simpy check if the task title contains the recurring tag: + * This function will simpy check if the task title contains the recurring tag: * If the task title contains the recurring tag, it will return true. * If the task title does not contain the recurring tag, it will return false. * @param taskTitle - The title of the task to check. @@ -50,7 +50,7 @@ export const addTaskInNote = async ( plugin: TaskBoard, newTask: taskItem, editorActive: boolean, - cursorPosition?: { line: number; ch: number } | undefined + cursorPosition?: { line: number; ch: number } | undefined, ): Promise => { const filePath = allowedFileExtensionsRegEx.test(newTask.filePath) ? newTask.filePath @@ -60,7 +60,7 @@ export const addTaskInNote = async ( if (!(await plugin.fileExists(filePath))) { new Notice( `New note created since it does not exists : "${filePath}"`, - 5000 + 5000, ); // Create a new file if it doesn't exist await plugin.app.vault.create(filePath, ""); @@ -70,7 +70,7 @@ export const addTaskInNote = async ( let completeTask = await getFormattedTaskContent(newTask); const { formattedTaskContent, newId } = await addIdToTaskContent( plugin, - completeTask + completeTask, ); completeTask = formattedTaskContent; if (completeTask === "") @@ -78,6 +78,8 @@ export const addTaskInNote = async ( // Read the file content const fileContent = await readDataOfVaultFile(plugin, filePath); + if (fileContent == null) return; + let newContent = fileContent; if (editorActive) { @@ -106,7 +108,7 @@ export const addTaskInNote = async ( 47, "Error while adding the task in the file. Below error message might give more information on this issue. Report the issue if it needs developers attention.", String(error), - "TaskItemUtils.ts/addTaskInNote" + "TaskItemUtils.ts/addTaskInNote", ); } }; @@ -125,7 +127,7 @@ export const updateTaskInFile = async ( plugin: TaskBoard, updatedTask: taskItem, oldTask: taskItem, - forceAddId?: boolean + forceAddId?: boolean, ): Promise => { try { const oldTaskContent = await getFormattedTaskContent(oldTask); @@ -134,14 +136,14 @@ export const updateTaskInFile = async ( 48, "getSanitizedTaskContent returned empty string for old task", "getSanitizedTaskContent returned empty string", - "TaskItemUtils.ts/updateTaskInFile" + "TaskItemUtils.ts/updateTaskInFile", ); let updatedTaskContent = await getFormattedTaskContent(updatedTask); const { formattedTaskContent, newId } = await addIdToTaskContent( plugin, updatedTaskContent, - forceAddId + forceAddId, ); updatedTaskContent = formattedTaskContent; if (updatedTaskContent === "") @@ -149,14 +151,14 @@ export const updateTaskInFile = async ( 49, "getSanitizedTaskContent returned empty string for old task", "getSanitizedTaskContent returned empty string", - "TaskItemUtils.ts/updateTaskInFile" + "TaskItemUtils.ts/updateTaskInFile", ); const result = await replaceOldTaskWithNewTask( plugin, oldTask, oldTaskContent, - updatedTaskContent + updatedTaskContent, ); if (result) { @@ -221,7 +223,7 @@ export const updateTaskInFile = async ( 51, "Error while updating the task in the file. Below error message might give more information on this issue. Report the issue if it needs developers attention.", String(error), - "TaskItemUtils.ts/updateTaskInFile" + "TaskItemUtils.ts/updateTaskInFile", ); return undefined; } @@ -238,7 +240,7 @@ export const updateTaskInFile = async ( export const useTasksPluginToUpdateInFile = async ( plugin: TaskBoard, tasksPlugin: TasksPluginApi, - oldTask: taskItem + oldTask: taskItem, ): Promise => { try { // Prepare the updated task block @@ -249,13 +251,13 @@ export const useTasksPluginToUpdateInFile = async ( if (tasksPlugin.isTasksPluginEnabled()) { const { formattedTaskContent, newId } = await addIdToTaskContent( plugin, - oldTask.title + oldTask.title, ); const oldTaskTitleWithId = formattedTaskContent; const tasksPluginApiOutput = tasksPlugin.executeToggleTaskDoneCommand( oldTaskTitleWithId, - oldTask.filePath + oldTask.filePath, ); // if (!tasksPluginApiOutput) { @@ -285,7 +287,7 @@ export const useTasksPluginToUpdateInFile = async ( plugin, oldTask, completeOldTaskContent, - newContent + newContent, ); } else if ((twoTaskTitles.length = 1)) { const { formattedTaskContent, newId } = @@ -300,7 +302,7 @@ export const useTasksPluginToUpdateInFile = async ( plugin, oldTask, completeOldTaskContent, - newContent + newContent, ); } else if ((twoTaskTitles.length = 2)) { // if (twoTaskTitles[1].trim().startsWith("- [x]")) { @@ -318,7 +320,7 @@ export const useTasksPluginToUpdateInFile = async ( plugin, oldTask, completeOldTaskContent, - newContent + newContent, ); // } else if (twoTaskTitles[0].trim().startsWith("- [x]")) { // newContent = `${twoTaskTitles[0]}${ @@ -343,7 +345,7 @@ export const useTasksPluginToUpdateInFile = async ( 53, "Unexpected output from tasks plugin API. Since the task you are trying to update is a recurring task, Task Board cannot handle recurring tasks as of now and Tasks plugin didnt returned an expected output. Please report this issue so developers can enhance the integration.", `tasksPluginApiOutput: ${tasksPluginApiOutput}`, - "TaskItemUtils.ts/useTasksPluginToUpdateInFile" + "TaskItemUtils.ts/useTasksPluginToUpdateInFile", ); return; } @@ -369,7 +371,7 @@ export const useTasksPluginToUpdateInFile = async ( 54, "Tasks plugin is must for handling recurring tasks. Since the task you are trying to update is a recurring task and Task Board cannot handle recurring tasks as of now. Hence the plugin has not updated your content.", `Tasks plugin installed and enabled: ${tasksPlugin.isTasksPluginEnabled()}`, - "TaskItemUtils.ts/useTasksPluginToUpdateInFile" + "TaskItemUtils.ts/useTasksPluginToUpdateInFile", ); } } catch (error) { @@ -377,7 +379,7 @@ export const useTasksPluginToUpdateInFile = async ( 55, "Error while updating the recurring task in the file. Below error message might give more information on this issue. Report the issue if it needs developers attention.", String(error), - "TaskItemUtils.ts/useTasksPluginToUpdateInFile" + "TaskItemUtils.ts/useTasksPluginToUpdateInFile", ); throw error; } @@ -392,7 +394,7 @@ export const useTasksPluginToUpdateInFile = async ( */ export const deleteTaskFromFile = async ( plugin: TaskBoard, - task: taskItem + task: taskItem, ): Promise => { try { const oldTaskContent = await getFormattedTaskContent(task); @@ -401,14 +403,14 @@ export const deleteTaskFromFile = async ( 56, "getSanitizedTaskContent returned empty string for old task", "getSanitizedTaskContent returned empty string", - "TaskItemUtils.ts/updateTaskInFile" + "TaskItemUtils.ts/updateTaskInFile", ); await replaceOldTaskWithNewTask( plugin, task, oldTaskContent, - "" // Empty string indicates deletion + "", // Empty string indicates deletion ); // // Step 1: Read the file content @@ -466,7 +468,7 @@ export const deleteTaskFromFile = async ( 57, "Error deleting task from file. Below error message might give more information on this issue. Report the issue if it needs developers attention.", String(error), - "TaskItemUtils.ts/deleteTaskFromFile" + "TaskItemUtils.ts/deleteTaskFromFile", ); return false; } @@ -488,7 +490,7 @@ export const deleteTaskFromFile = async ( */ export const archiveTask = async ( plugin: TaskBoard, - task: taskItem + task: taskItem, ): Promise => { const archivedFilePath = plugin.settings.data.globalSettings.archivedTasksFilePath; @@ -503,7 +505,7 @@ export const archiveTask = async ( if (!(await plugin.fileExists(archivedFilePath))) { new Notice( `New Archived file created since it did not exist at path: "${archivedFilePath}"`, - 0 + 0, ); // Ensure all folders in the path exist before creating the file const lastSlash = archivedFilePath.lastIndexOf("/"); @@ -522,7 +524,7 @@ export const archiveTask = async ( // createFolder will create the single folder at currentPath try { await plugin.app.vault.createFolder( - currentPath + currentPath, ); } catch (error) { if (String(error).contains("already exists")) @@ -540,7 +542,7 @@ export const archiveTask = async ( 58, `A file exists where a folder is expected: ${currentPath}`, `Unexpected file at folder path: ${currentPath}`, - "TaskItemUtils.ts/archiveTask" + "TaskItemUtils.ts/archiveTask", ); } } @@ -553,7 +555,7 @@ export const archiveTask = async ( // Read the content of the file where archived tasks will be stored const archivedFileContent = await readDataOfVaultFile( plugin, - archivedFilePath + archivedFilePath, ); // Add the task to the top of the archived file content @@ -563,7 +565,7 @@ export const archiveTask = async ( await writeDataToVaultFile( plugin, archivedFilePath, - newArchivedContent + newArchivedContent, ); // Now delete the task from its original file @@ -578,7 +580,7 @@ export const archiveTask = async ( 59, "Error archiving task", error as string, - "TaskItemUtils.ts/archiveTask" + "TaskItemUtils.ts/archiveTask", ); } } else if (archivedFilePath === "") { @@ -588,7 +590,7 @@ export const archiveTask = async ( plugin, task, oldTaskContent, - `%%${oldTaskContent}%%` + `%%${oldTaskContent}%%`, ).then(() => { plugin.realTimeScanner.processAllUpdatedFiles(task.filePath); }); @@ -615,7 +617,7 @@ export const archiveTask = async ( 60, "Error archiving task in the same file. Either the task is not present in the file or the plugin is not able to find the correct match, because the task must have been edited in such a way that the title is not present in the file.", error as string, - "TaskItemUtils.ts/archiveTask" + "TaskItemUtils.ts/archiveTask", ); } } else { @@ -623,7 +625,7 @@ export const archiveTask = async ( 61, "Error archiving task. The below error message might help you to find the issue.", "Archived file path is not set in the plugin settings.", - "TaskItemUtils.ts/archiveTask" + "TaskItemUtils.ts/archiveTask", ); } }; @@ -644,7 +646,7 @@ export const replaceOldTaskWithNewTask = async ( plugin: TaskBoard, oldTask: taskItem, oldTaskContent: string, - newTaskContent: string + newTaskContent: string, ): Promise => { const filePath = allowedFileExtensionsRegEx.test(oldTask.filePath) ? oldTask.filePath @@ -662,6 +664,8 @@ export const replaceOldTaskWithNewTask = async ( try { // Step 1: Read the file content const fileContent = await readDataOfVaultFile(plugin, filePath); + if (fileContent == null) return false; + const lines = fileContent.split("\n"); const { startLine, startCharIndex, endLine, endCharIndex } = @@ -686,13 +690,13 @@ export const replaceOldTaskWithNewTask = async ( 62, `Task board couldnt able to find the task which you are trying to edit inside the file ${oldTask.filePath} at the line number : ${oldTask.taskLocation.startLine} . Looks like the file must have been edited in the absence of Task Board and the task location was misplaced. Please scan the file again using the file menu option and see if that fixes this issue.\n\nThis was actually a normal bug, but recently few users were facing this specific issue and the developers are uncertain about the exact cause of this issue. Hence will request to kindly report this issue to the developer and please metion the steps in detail which led to this issue to occur, since its not possible to find the exact cause of it by simply reading this report.`, `\n\nOldTask location :${JSON.stringify( - oldTask.taskLocation + oldTask.taskLocation, )}\n\nOldTask content inside task-board-cache :${ oldTask.title }\n\nAt present the content inside file at line number ${ oldTask.taskLocation.startLine } is: ${startLineText}`, - "TaskItemUtils.ts/replaceOldTaskWithNewTask" + "TaskItemUtils.ts/replaceOldTaskWithNewTask", ); return false; } @@ -705,7 +709,7 @@ export const replaceOldTaskWithNewTask = async ( taskLines[0] = taskLines[0].slice(startCharIndex); taskLines[taskLines.length - 1] = taskLines[taskLines.length - 1].slice( 0, - endCharIndex + endCharIndex, ); const oldTaskContentFromFile = taskLines.join("\n"); @@ -713,7 +717,7 @@ export const replaceOldTaskWithNewTask = async ( const joinFinalNoteContent = ( before: string, newTaskContent: string, - after: string + after: string, ) => { if (newTaskContent.trim() === "") { return `${before}${ @@ -727,8 +731,8 @@ export const replaceOldTaskWithNewTask = async ( ? after : `\n${after}` : newTaskContent.endsWith("\n") - ? "" - : `\n` + ? "" + : `\n` }`; }; @@ -743,13 +747,13 @@ export const replaceOldTaskWithNewTask = async ( const newContent = joinFinalNoteContent( before, newTaskContent, - after + after, ); await writeDataToVaultFile(plugin, filePath, newContent); } else if ( isTheContentDiffAreOnlySpaces_V2( oldTaskContent, - oldTaskContentFromFile + oldTaskContentFromFile, ) ) { // If the content is only spaces, we can safely replace it @@ -761,7 +765,7 @@ export const replaceOldTaskWithNewTask = async ( const newContent = joinFinalNoteContent( before, newTaskContent, - after + after, ); // Replace the old task block with the updated content await writeDataToVaultFile(plugin, filePath, newContent); @@ -783,17 +787,17 @@ export const replaceOldTaskWithNewTask = async ( const newContent = joinFinalNoteContent( before, newTaskContent, - after + after, ); // Replace the old task block with the updated content await writeDataToVaultFile( plugin, filePath, - newContent + newContent, ); } // If user chooses "new", do nothing - } + }, ); } else { // If safeguard feature has been disabled by the user. We will by-pass this check and instead of asking the user will directly go-ahead and replace the old content with the new content user has edited. @@ -805,7 +809,7 @@ export const replaceOldTaskWithNewTask = async ( const newContent = joinFinalNoteContent( before, newTaskContent, - after + after, ); // Replace the old task block with the updated content await writeDataToVaultFile(plugin, filePath, newContent); @@ -818,7 +822,7 @@ export const replaceOldTaskWithNewTask = async ( 63, "Error while updating the task in the file. Below error message might give more information on this issue. Report the issue if it needs developers attention.", String(error), - "TaskItemUtils.ts/replaceOldTaskWithNewTask" + "TaskItemUtils.ts/replaceOldTaskWithNewTask", ); return false; } diff --git a/src/utils/taskNote/TaskNoteEventHandlers.ts b/src/utils/taskNote/TaskNoteEventHandlers.ts index 2f9497f0..f5e4a5a1 100644 --- a/src/utils/taskNote/TaskNoteEventHandlers.ts +++ b/src/utils/taskNote/TaskNoteEventHandlers.ts @@ -23,14 +23,14 @@ import { statusTypeNames } from "src/interfaces/Enums"; */ export const handleTaskNoteStatusChange = async ( plugin: TaskBoard, - task: taskItem + task: taskItem, ) => { try { const newStatus = checkboxStateSwitcher(plugin, task.status); const globalSettings = plugin.settings.data.globalSettings; const moment = _moment as unknown as typeof _moment.default; const currentDateValue = moment().format( - globalSettings?.taskCompletionDateTimePattern + globalSettings?.taskCompletionDateTimePattern, ); console.log("handleTaskNoteStatusChange..\nnewStatus = ", newStatus); @@ -56,7 +56,7 @@ export const handleTaskNoteStatusChange = async ( } const newStatusName = getStatusNameFromStatusSymbol( newStatus.newSymbol, - plugin.settings.data.globalSettings + plugin.settings.data.globalSettings, ); // Update frontmatter with new status @@ -66,7 +66,7 @@ export const handleTaskNoteStatusChange = async ( // This is required to rescan the updated file and refresh the board. plugin.realTimeScanner.processAllUpdatedFiles( updatedTask.filePath, - task.legacyId + task.legacyId, ); }); }); @@ -87,7 +87,7 @@ export const handleTaskNoteStatusChange = async ( export const handleTaskNotePropertyUpdate = async ( plugin: TaskBoard, oldTask: taskItem, - updatedTask: taskItem + updatedTask: taskItem, ) => { try { // Update frontmatter with all updated properties @@ -96,7 +96,7 @@ export const handleTaskNotePropertyUpdate = async ( sleep(1000).then(() => { // This is required to rescan the updated file and refresh the board. plugin.realTimeScanner.processAllUpdatedFiles( - updatedTask.filePath + updatedTask.filePath, ); }); }); @@ -115,7 +115,7 @@ export const handleTaskNotePropertyUpdate = async ( */ export const handleTaskNoteDelete = async ( plugin: TaskBoard, - task: taskItem + task: taskItem, ) => { try { const file = plugin.app.vault.getFileByPath(task.filePath); @@ -135,8 +135,8 @@ export const handleTaskNoteDelete = async ( (tag: string) => tag.includes( plugin.settings.data.globalSettings - .taskNoteIdentifierTag - ) === false + .taskNoteIdentifierTag, + ) === false, ); // If no other tags remain, we could remove the tags property entirely @@ -160,7 +160,7 @@ export const handleTaskNoteDelete = async ( export const handleTaskNoteBodyChange = async ( plugin: TaskBoard, oldTask: taskItem, - updatedTask: taskItem + updatedTask: taskItem, ) => { try { const file = plugin.app.vault.getFileByPath(updatedTask.filePath); @@ -168,8 +168,9 @@ export const handleTaskNoteBodyChange = async ( const fileContent = await readDataOfVaultFile( plugin, - updatedTask.filePath + updatedTask.filePath, ); + if (fileContent == null) return; // Find the line representing the old task and the updated task // Find all lines between oldTask.body and updatedTask.body that are different @@ -200,21 +201,21 @@ export const handleTaskNoteBodyChange = async ( await writeDataToVaultFile( plugin, updatedTask.filePath, - updatedLines.join("\n") + updatedLines.join("\n"), ).then(() => { // This is required to rescan the updated file and refresh the board. sleep(1000).then(() => { // This is required to rescan the updated file and refresh the board. plugin.realTimeScanner.processAllUpdatedFiles( updatedTask.filePath, - oldTask.id + oldTask.id, ); }); }); } catch (error) { console.error( "TaskItemEventHandlers.ts : Error in handleTaskNoteBodyChange", - error + error, ); } }; diff --git a/src/views/AddOrEditTaskView.tsx b/src/views/AddOrEditTaskView.tsx index c465cab1..7e873706 100644 --- a/src/views/AddOrEditTaskView.tsx +++ b/src/views/AddOrEditTaskView.tsx @@ -75,7 +75,10 @@ export class AddOrEditTaskView extends ItemView { let noteContent: string = ""; if (this.isTaskNote) { if (this.filePath) { - noteContent = await readDataOfVaultFile(this.plugin, this.filePath); + const data = await readDataOfVaultFile(this.plugin, this.filePath); + + if (data == null) this.onClose(); + else noteContent = data; } else { noteContent = "---\ntitle: \n---\n"; From 63f73667edb098d2a83b308b196dcc766d84d8e7 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Sun, 25 Jan 2026 20:18:35 +0530 Subject: [PATCH 026/236] feat : complete all the task card context menu features --- src/components/KanbanView/TaskItem.tsx | 329 ++++++++++++----------- src/components/KanbanView/TaskItemV2.tsx | 312 ++++++++++----------- 2 files changed, 328 insertions(+), 313 deletions(-) diff --git a/src/components/KanbanView/TaskItem.tsx b/src/components/KanbanView/TaskItem.tsx index ed5f5e82..fe0db024 100644 --- a/src/components/KanbanView/TaskItem.tsx +++ b/src/components/KanbanView/TaskItem.tsx @@ -6,7 +6,7 @@ import { checkboxStateSwitcher, extractCheckboxSymbol, getObsidianIndentationSet import { handleCheckboxChange, handleDeleteTask, handleSubTasksChange } from 'src/utils/taskLine/TaskItemEventHandlers'; import { hookMarkdownLinkMouseEventHandlers, markdownButtonHoverPreviewEvent } from 'src/services/MarkdownHoverPreview'; -import { Component, Notice, Platform, Menu, TFile } from 'obsidian'; +import { Component, Notice, Platform, Menu, TFile, MenuItem } from 'obsidian'; import { MarkdownUIRenderer } from 'src/services/MarkdownUIRenderer'; import { cleanTaskTitleLegacy } from 'src/utils/taskLine/TaskContentFormatter'; import { updateRGBAOpacity } from 'src/utils/UIHelpers'; @@ -14,27 +14,22 @@ import { t } from 'src/utils/lang/helper'; import TaskBoard from 'main'; import { Board } from 'src/interfaces/BoardConfigs'; import { TaskRegularExpressions, TASKS_PLUGIN_DEFAULT_SYMBOLS } from 'src/regularExpressions/TasksPluginRegularExpr'; -import { getStatusNameFromStatusSymbol, isTaskNotePresentInTags } from 'src/utils/taskNote/TaskNoteUtils'; -import { allowedFileExtensionsRegEx } from 'src/regularExpressions/MiscelleneousRegExpr'; -import { bugReporter } from 'src/services/OpenModals'; +import { deleteTaskNote, getStatusNameFromStatusSymbol, isTaskNotePresentInTags } from 'src/utils/taskNote/TaskNoteUtils'; import { ChevronDown, EllipsisVertical, Grip } from 'lucide-react'; import { EditButtonMode, viewTypeNames, colTypeNames, taskPropertiesNames } from 'src/interfaces/Enums'; -import { getCustomStatusOptionsForDropdown, priorityEmojis } from 'src/interfaces/Mapping'; +import { getCustomStatusOptionsForDropdown, getPriorityOptionsForDropdown, priorityEmojis } from 'src/interfaces/Mapping'; import { taskItem, UpdateTaskEventData } from 'src/interfaces/TaskItem'; import { matchTagsWithWildcards, verifySubtasksAndChildtasksAreComplete } from 'src/utils/algorithms/ScanningFilterer'; import { handleTaskNoteStatusChange, handleTaskNoteBodyChange } from 'src/utils/taskNote/TaskNoteEventHandlers'; import { eventEmitter } from 'src/services/EventEmitter'; -import { RxDragHandleDots2 } from 'react-icons/rx'; -import { getUniversalDateEmoji, getUniversalDateFromTask, parseUniversalDate } from 'src/utils/DateTimeCalculations'; +import { getUniversalDateFromTask, parseUniversalDate } from 'src/utils/DateTimeCalculations'; import { getTaskFromId } from 'src/utils/TaskItemUtils'; import { handleEditTask, updateTaskItemStatus, updateTaskItemPriority, updateTaskItemDate, updateTaskItemReminder, updateTaskItemTags } from 'src/utils/UserTaskEvents'; import EditTagsModal from 'src/modals/EditTagsModal'; - -// Helper modal functions may be provided elsewhere; declare them for TypeScript -declare function showTextInputModal(app: any, options: { title?: string; placeholder?: string; initialValue?: string }): Promise; -declare function showConfirmationModal(app: any, options: any): Promise; import { dragDropTasksManagerInsatance, currentDragDataPayload } from 'src/managers/DragDropTasksManager'; import { bugReporterManagerInsatance } from 'src/managers/BugReporter'; +import { openDateInputModal } from 'src/services/OpenModals'; +import { showTextInputModal } from 'src/modals/TextInputModal'; export interface swimlaneDataProp { property: string; @@ -156,7 +151,7 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin el.innerHTML = ''; if (task.title === "") return; - const cleanedTitle = cleanTaskTitleLegacy(task); + const cleanedTitle = isTaskNote ? task.title : cleanTaskTitleLegacy(task); await MarkdownUIRenderer.renderTaskDisc( plugin.app, @@ -638,6 +633,9 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin const handleMenuButtonClicked = (event: React.MouseEvent) => { event.stopPropagation(); + + if (!globalSettings.experimentalFeatures) return; + const taskItemMenu = new Menu(); taskItemMenu.addItem((item) => { @@ -645,14 +643,15 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin item.setIsLabel(true); }); taskItemMenu.addItem((item) => { - item.setTitle(t("status")); item.setIcon("info"); + item.setTitle(t("status")); const statusMenu = item.setSubmenu() const customStatues = getCustomStatusOptionsForDropdown(plugin.settings.data.globalSettings.customStatuses); customStatues.forEach((status) => { statusMenu.addItem((item) => { - item.setTitle(status.text); + const itemDocFragment = MarkdownUIRenderer.renderSubtaskText(plugin.app, `- [${status.value}] ${status.name} (**[${status.value}]**)`, item.titleEl, '', null); + // item.setTitle(status.text); // item.setIcon("eye-off"); // TODO : In future map lucude-icons with the ITS theme emoji icons for custom statuses. item.onClick(() => { updateTaskItemStatus(plugin, task, status.value); @@ -661,6 +660,71 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin }) }); + // Priority submenu + taskItemMenu.addItem((item) => { + item.setIcon("flag"); + item.setTitle(t("priority")); + const priMenu = item.setSubmenu(); + const priorityOptions = getPriorityOptionsForDropdown(); + priorityOptions.forEach((p) => { + priMenu.addItem((it) => { + it.setTitle(p.text); + it.onClick(() => updateTaskItemPriority(plugin, task, p.value)); + }); + }); + }); + + // Tags editor modal - TODO : It doesnt make sense to build another modal specifically changing the tags, when the AddOrEditTaskModal can itself do this. + // taskItemMenu.addItem((item) => { + // item.setTitle(t("tags")); + // item.setIcon("tag"); + // item.onClick(() => { + // const modal = new EditTagsModal(plugin, task.tags || [], (newTags: string[]) => { + // updateTaskItemTags(plugin, task, task, newTags.map((tg) => (tg.startsWith('#') ? tg : `#${tg}`))); + // }); + // modal.open(); + // }); + // }); + + // Dates submenu + + taskItemMenu.addItem((it) => { + it.setIcon("calendar-plus") + it.setTitle(t("start-date")); + it.onClick(async () => { + openDateInputModal(plugin, t("start"), task.startDate, (newDate: string) => { + updateTaskItemDate(plugin, task, 'startDate', newDate); + }) + }); + }); + taskItemMenu.addItem((it) => { + it.setIcon("calendar-clock") + it.setTitle(t("scheduled-date")); + it.onClick(async () => { + openDateInputModal(plugin, t("scheduled"), task.scheduledDate, (newDate: string) => { + updateTaskItemDate(plugin, task, 'scheduledDate', newDate); + }) + }); + }); + taskItemMenu.addItem((it) => { + it.setIcon("calendar") + it.setTitle(t("due-date")); + it.onClick(async () => { + openDateInputModal(plugin, t("due"), task.due, (newDate: string) => { + updateTaskItemDate(plugin, task, 'due', newDate); + }) + }); + }); + + // Reminder item - open prompt for date/time + taskItemMenu.addItem((item) => { + item.setIcon("clock"); + item.setTitle(t("reminder")); + item.onClick(async () => { + // if (newReminder) updateTaskItemReminder(plugin, task, newReminder); + }); + }); + taskItemMenu.addSeparator(); taskItemMenu.addItem((item) => { @@ -668,90 +732,37 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin item.setIsLabel(true); }); taskItemMenu.addItem((item) => { + item.setIcon("copy"); item.setTitle(t("copy-task-title")); - item.setIcon("eye-off"); item.onClick(async () => { + try { + await navigator.clipboard.writeText(cleanTaskTitleLegacy(task)); + new Notice(t("copy-task-title-successful")); + } catch (error) { + new Notice(t("copy-task-title-unsuccessful")); + } }); + }); - // Priority submenu - taskItemMenu.addItem((item) => { - item.setTitle(t("priority")); - item.setIcon("flag"); - const priMenu = item.setSubmenu(); - const priorities = [ - { label: t("none"), value: 0 }, - { label: t("low"), value: 1 }, - { label: t("medium"), value: 2 }, - { label: t("high"), value: 3 }, - ]; - priorities.forEach((p) => { - priMenu.addItem((it) => { - it.setTitle(p.label); - it.onClick(() => updateTaskItemPriority(plugin, task, p.value)); - }); - }); - }); - - // Dates submenu - taskItemMenu.addItem((item) => { - item.setTitle(t("dates")); - item.setIcon("calendar"); - const dateMenu = item.setSubmenu(); - dateMenu.addItem((it) => { - it.setTitle(t("start-date")); - it.onClick(async () => { - const newDate = await showTextInputModal(plugin.app, { title: t("set-start-date"), placeholder: 'YYYY-MM-DD', initialValue: task.startDate || '' }); - if (newDate) updateTaskItemDate(plugin, task, 'startDate', newDate); - }); - }); - dateMenu.addItem((it) => { - it.setTitle(t("scheduled-date")); - it.onClick(async () => { - const newDate = await showTextInputModal(plugin.app, { title: t("set-scheduled-date"), placeholder: 'YYYY-MM-DD', initialValue: task.scheduledDate || '' }); - if (newDate) updateTaskItemDate(plugin, task, 'scheduledDate', newDate); - }); - }); - dateMenu.addItem((it) => { - it.setTitle(t("due-date")); - it.onClick(async () => { - const newDate = await showTextInputModal(plugin.app, { title: t("set-due-date"), placeholder: 'YYYY-MM-DD', initialValue: task.due || '' }); - if (newDate) updateTaskItemDate(plugin, task, 'due', newDate); - }); - }); - }); - - // Reminder item - open prompt for date/time - taskItemMenu.addItem((item) => { - item.setTitle(t("reminder")); - item.setIcon("clock"); - item.onClick(async () => { - const newReminder = await showTextInputModal(plugin.app, { title: t("set-reminder"), placeholder: 'YYYY-MM-DD HH:mm', initialValue: task.reminder || '' }); - if (newReminder) updateTaskItemReminder(plugin, task, newReminder); - }); - }); - - // Tags editor modal - taskItemMenu.addItem((item) => { - item.setTitle(t("tags")); - item.setIcon("tag"); - item.onClick(() => { - const modal = new EditTagsModal(plugin, task.tags || [], (newTags: string[]) => { - updateTaskItemTags(plugin, task, task, newTags.map((tg) => (tg.startsWith('#') ? tg : `#${tg}`))); - }); - modal.open(); - }); + taskItemMenu.addItem((item) => { + item.setIcon("file-input"); + item.setTitle(t("open-note")); + item.onClick(async () => { + handleEditTask(plugin, task, EditButtonMode.NoteInTab) }); }); taskItemMenu.addItem((item) => { - item.setTitle(t("open-note")); - item.setIcon("eye-off"); + item.setIcon("columns-2"); + item.setTitle(t("open-note-to-right")); item.onClick(async () => { + handleEditTask(plugin, task, EditButtonMode.NoteInSplit) }); }); + // Note actions submenu taskItemMenu.addItem((item) => { - item.setTitle(t("contextMenus.task.noteActions")); item.setIcon("file-text"); + item.setTitle(t("note-actions")); const submenu = (item as any).setSubmenu(); @@ -767,16 +778,16 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin } // Add common file actions (these will either supplement or replace the native menu) - submenu.addItem((subItem: any) => { - subItem.setTitle(t("contextMenus.task.rename")); + submenu.addItem((subItem: MenuItem) => { subItem.setIcon("pencil"); + subItem.setTitle(t("rename-note")); subItem.onClick(async () => { try { // Modal-based rename const currentName = file.basename; const newName = await showTextInputModal(plugin.app, { - title: t("contextMenus.task.renameTitle"), - placeholder: t("contextMenus.task.renamePlaceholder"), + title: t("rename-note"), + placeholder: t("rename-note-placeholder"), initialValue: currentName, }); @@ -794,15 +805,7 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin // Rename the file await plugin.app.vault.rename(file, newPath); - new Notice( - t("contextMenus.task.notices.renameSuccess") + finalName, - - ); - - // // Trigger update callback - // if (options.onUpdate) { - // options.onUpdate(); - // } + new Notice("contextMenus.task.notices.renameSuccess"); } } catch (error) { console.error("Error renaming file:", error); @@ -811,76 +814,78 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin }); }); - submenu.addItem((subItem: any) => { - subItem.setTitle(t("delete-note")); + submenu.addItem((subItem: MenuItem) => { subItem.setIcon("trash"); + subItem.setTitle(t("delete-note")); subItem.onClick(async () => { // Show confirmation and delete - const confirmed = await showConfirmationModal(plugin.app, { - title: t("contextMenus.task.deleteTitle"), - message: t("contextMenus.task.deleteMessage") + file.name, - confirmText: t("contextMenus.task.deleteConfirm"), - cancelText: t("common.cancel"), - isDestructive: true, - }); - if (confirmed) { - plugin.app.vault.trash(file, true); - } + // const confirmed = await showConfirmationModal(plugin.app, { + // title: t("contextMenus.task.deleteTitle"), + // message: t("contextMenus.task.deleteMessage") + file.name, + // confirmText: t("contextMenus.task.deleteConfirm"), + // cancelText: t("common.cancel"), + // isDestructive: true, + // }); + // if (confirmed) { + // } + + plugin.app.vault.trash(file, true); + // handleDeleteTask(plugin, task, true); }); }); - submenu.addSeparator(); - - submenu.addItem((subItem: any) => { - subItem.setTitle(t("contextMenus.task.copyPath")); - subItem.setIcon("copy"); - subItem.onClick(async () => { - try { - await navigator.clipboard.writeText(file.path); - new Notice(t("contextMenus.task.notices.copyPathSuccess")); - } catch (error) { - new Notice(t("contextMenus.task.notices.copyFailure")); - } - }); - }); - - submenu.addItem((subItem: any) => { - subItem.setTitle(t("contextMenus.task.copyUrl")); - subItem.setIcon("link"); - subItem.onClick(async () => { - try { - const url = `obsidian://open?vault=${encodeURIComponent(plugin.app.vault.getName())}&file=${encodeURIComponent(file.path)}`; - await navigator.clipboard.writeText(url); - new Notice(t("contextMenus.task.notices.copyUrlSuccess")); - } catch (error) { - new Notice(t("contextMenus.task.notices.copyFailure")); - } - }); - }); - - submenu.addSeparator(); - - submenu.addItem((subItem: any) => { - subItem.setTitle(t("contextMenus.task.showInExplorer")); - subItem.setIcon("folder-open"); - subItem.onClick(() => { - // Reveal file in file explorer - plugin.app.workspace - .getLeaf() - .setViewState({ - type: "file-explorer", - state: {}, - }) - .then(() => { - // Focus the file in the explorer - const fileExplorer = - plugin.app.workspace.getLeavesOfType("file-explorer")[0]; - if (fileExplorer?.view && "revealInFolder" in fileExplorer.view) { - (fileExplorer.view as any).revealInFolder(file); - } - }); - }); - }); + // submenu.addSeparator(); + + // submenu.addItem((subItem: any) => { + // subItem.setTitle(t("contextMenus.task.copyPath")); + // subItem.setIcon("copy"); + // subItem.onClick(async () => { + // try { + // await navigator.clipboard.writeText(file.path); + // new Notice(t("contextMenus.task.notices.copyPathSuccess")); + // } catch (error) { + // new Notice(t("contextMenus.task.notices.copyFailure")); + // } + // }); + // }); + + // submenu.addItem((subItem: any) => { + // subItem.setTitle(t("contextMenus.task.copyUrl")); + // subItem.setIcon("link"); + // subItem.onClick(async () => { + // try { + // const url = `obsidian://open?vault=${encodeURIComponent(plugin.app.vault.getName())}&file=${encodeURIComponent(file.path)}`; + // await navigator.clipboard.writeText(url); + // new Notice(t("contextMenus.task.notices.copyUrlSuccess")); + // } catch (error) { + // new Notice(t("contextMenus.task.notices.copyFailure")); + // } + // }); + // }); + + // submenu.addSeparator(); + + // submenu.addItem((subItem: any) => { + // subItem.setTitle(t("contextMenus.task.showInExplorer")); + // subItem.setIcon("folder-open"); + // subItem.onClick(() => { + // // Reveal file in file explorer + // plugin.app.workspace + // .getLeaf() + // .setViewState({ + // type: "file-explorer", + // state: {}, + // }) + // .then(() => { + // // Focus the file in the explorer + // const fileExplorer = + // plugin.app.workspace.getLeavesOfType("file-explorer")[0]; + // if (fileExplorer?.view && "revealInFolder" in fileExplorer.view) { + // (fileExplorer.view as any).revealInFolder(file); + // } + // }); + // }); + // }); } }); diff --git a/src/components/KanbanView/TaskItemV2.tsx b/src/components/KanbanView/TaskItemV2.tsx index e0a72c1d..331ae26e 100644 --- a/src/components/KanbanView/TaskItemV2.tsx +++ b/src/components/KanbanView/TaskItemV2.tsx @@ -156,7 +156,7 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act el.innerHTML = ''; if (task.title === "") return; - const cleanedTitle = cleanTaskTitleLegacy(task); + const cleanedTitle = isTaskNote ? task.title : cleanTaskTitleLegacy(task); await MarkdownUIRenderer.renderTaskDisc( plugin.app, @@ -638,6 +638,9 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act const handleMenuButtonClicked = (event: React.MouseEvent) => { event.stopPropagation(); + + if (!globalSettings.experimentalFeatures) return; + const taskItemMenu = new Menu(); taskItemMenu.addItem((item) => { @@ -645,14 +648,15 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act item.setIsLabel(true); }); taskItemMenu.addItem((item) => { - item.setTitle(t("status")); item.setIcon("info"); + item.setTitle(t("status")); const statusMenu = item.setSubmenu() const customStatues = getCustomStatusOptionsForDropdown(plugin.settings.data.globalSettings.customStatuses); customStatues.forEach((status) => { statusMenu.addItem((item) => { - item.setTitle(status.text); + const itemDocFragment = MarkdownUIRenderer.renderSubtaskText(plugin.app, `- [${status.value}] ${status.name} (**[${status.value}]**)`, item.titleEl, '', null); + // item.setTitle(status.text); // item.setIcon("eye-off"); // TODO : In future map lucude-icons with the ITS theme emoji icons for custom statuses. item.onClick(() => { updateTaskItemStatus(plugin, task, status.value); @@ -661,6 +665,71 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act }) }); + // Priority submenu + taskItemMenu.addItem((item) => { + item.setIcon("flag"); + item.setTitle(t("priority")); + const priMenu = item.setSubmenu(); + const priorityOptions = getPriorityOptionsForDropdown(); + priorityOptions.forEach((p) => { + priMenu.addItem((it) => { + it.setTitle(p.text); + it.onClick(() => updateTaskItemPriority(plugin, task, p.value)); + }); + }); + }); + + // Tags editor modal - TODO : It doesnt make sense to build another modal specifically changing the tags, when the AddOrEditTaskModal can itself do this. + // taskItemMenu.addItem((item) => { + // item.setTitle(t("tags")); + // item.setIcon("tag"); + // item.onClick(() => { + // const modal = new EditTagsModal(plugin, task.tags || [], (newTags: string[]) => { + // updateTaskItemTags(plugin, task, task, newTags.map((tg) => (tg.startsWith('#') ? tg : `#${tg}`))); + // }); + // modal.open(); + // }); + // }); + + // Dates submenu + + taskItemMenu.addItem((it) => { + it.setIcon("calendar-plus") + it.setTitle(t("start-date")); + it.onClick(async () => { + openDateInputModal(plugin, t("start"), task.startDate, (newDate: string) => { + updateTaskItemDate(plugin, task, 'startDate', newDate); + }) + }); + }); + taskItemMenu.addItem((it) => { + it.setIcon("calendar-clock") + it.setTitle(t("scheduled-date")); + it.onClick(async () => { + openDateInputModal(plugin, t("scheduled"), task.scheduledDate, (newDate: string) => { + updateTaskItemDate(plugin, task, 'scheduledDate', newDate); + }) + }); + }); + taskItemMenu.addItem((it) => { + it.setIcon("calendar") + it.setTitle(t("due-date")); + it.onClick(async () => { + openDateInputModal(plugin, t("due"), task.due, (newDate: string) => { + updateTaskItemDate(plugin, task, 'due', newDate); + }) + }); + }); + + // Reminder item - open prompt for date/time + taskItemMenu.addItem((item) => { + item.setIcon("clock"); + item.setTitle(t("reminder")); + item.onClick(async () => { + // if (newReminder) updateTaskItemReminder(plugin, task, newReminder); + }); + }); + taskItemMenu.addSeparator(); taskItemMenu.addItem((item) => { @@ -668,90 +737,37 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act item.setIsLabel(true); }); taskItemMenu.addItem((item) => { + item.setIcon("copy"); item.setTitle(t("copy-task-title")); - item.setIcon("eye-off"); item.onClick(async () => { + try { + await navigator.clipboard.writeText(cleanTaskTitleLegacy(task)); + new Notice(t("copy-task-title-successful")); + } catch (error) { + new Notice(t("copy-task-title-unsuccessful")); + } }); + }); - // Priority submenu - taskItemMenu.addItem((item) => { - item.setTitle(t("priority")); - item.setIcon("flag"); - const priMenu = item.setSubmenu(); - const priorities = [ - { label: t("none"), value: 0 }, - { label: t("low"), value: 1 }, - { label: t("medium"), value: 2 }, - { label: t("high"), value: 3 }, - ]; - priorities.forEach((p) => { - priMenu.addItem((it) => { - it.setTitle(p.label); - it.onClick(() => updateTaskItemPriority(plugin, task, p.value)); - }); - }); - }); - - // Dates submenu - taskItemMenu.addItem((item) => { - item.setTitle(t("dates")); - item.setIcon("calendar"); - const dateMenu = item.setSubmenu(); - dateMenu.addItem((it) => { - it.setTitle(t("start-date")); - it.onClick(async () => { - const newDate = await showTextInputModal(plugin.app, { title: t("set-start-date"), placeholder: 'YYYY-MM-DD', initialValue: task.startDate || '' }); - if (newDate) updateTaskItemDate(plugin, task, 'startDate', newDate); - }); - }); - dateMenu.addItem((it) => { - it.setTitle(t("scheduled-date")); - it.onClick(async () => { - const newDate = await showTextInputModal(plugin.app, { title: t("set-scheduled-date"), placeholder: 'YYYY-MM-DD', initialValue: task.scheduledDate || '' }); - if (newDate) updateTaskItemDate(plugin, task, 'scheduledDate', newDate); - }); - }); - dateMenu.addItem((it) => { - it.setTitle(t("due-date")); - it.onClick(async () => { - const newDate = await showTextInputModal(plugin.app, { title: t("set-due-date"), placeholder: 'YYYY-MM-DD', initialValue: task.due || '' }); - if (newDate) updateTaskItemDate(plugin, task, 'due', newDate); - }); - }); - }); - - // Reminder item - open prompt for date/time - taskItemMenu.addItem((item) => { - item.setTitle(t("reminder")); - item.setIcon("clock"); - item.onClick(async () => { - const newReminder = await showTextInputModal(plugin.app, { title: t("set-reminder"), placeholder: 'YYYY-MM-DD HH:mm', initialValue: task.reminder || '' }); - if (newReminder) updateTaskItemReminder(plugin, task, newReminder); - }); - }); - - // Tags editor modal - taskItemMenu.addItem((item) => { - item.setTitle(t("tags")); - item.setIcon("tag"); - item.onClick(() => { - const modal = new EditTagsModal(plugin, task.tags || [], (newTags: string[]) => { - updateTaskItemTags(plugin, task, task, newTags.map((tg) => (tg.startsWith('#') ? tg : `#${tg}`))); - }); - modal.open(); - }); + taskItemMenu.addItem((item) => { + item.setIcon("file-input"); + item.setTitle(t("open-note")); + item.onClick(async () => { + handleEditTask(plugin, task, EditButtonMode.NoteInTab) }); }); taskItemMenu.addItem((item) => { - item.setTitle(t("open-note")); - item.setIcon("eye-off"); + item.setIcon("columns-2"); + item.setTitle(t("open-note-to-right")); item.onClick(async () => { + handleEditTask(plugin, task, EditButtonMode.NoteInSplit) }); }); + // Note actions submenu taskItemMenu.addItem((item) => { - item.setTitle(t("contextMenus.task.noteActions")); item.setIcon("file-text"); + item.setTitle(t("note-actions")); const submenu = (item as any).setSubmenu(); @@ -767,16 +783,16 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act } // Add common file actions (these will either supplement or replace the native menu) - submenu.addItem((subItem: any) => { - subItem.setTitle(t("contextMenus.task.rename")); + submenu.addItem((subItem: MenuItem) => { subItem.setIcon("pencil"); + subItem.setTitle(t("rename-note")); subItem.onClick(async () => { try { // Modal-based rename const currentName = file.basename; const newName = await showTextInputModal(plugin.app, { - title: t("contextMenus.task.renameTitle"), - placeholder: t("contextMenus.task.renamePlaceholder"), + title: t("rename-note"), + placeholder: t("rename-note-placeholder"), initialValue: currentName, }); @@ -794,15 +810,7 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act // Rename the file await plugin.app.vault.rename(file, newPath); - new Notice( - t("contextMenus.task.notices.renameSuccess") + finalName, - - ); - - // // Trigger update callback - // if (options.onUpdate) { - // options.onUpdate(); - // } + new Notice("contextMenus.task.notices.renameSuccess"); } } catch (error) { console.error("Error renaming file:", error); @@ -811,76 +819,78 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act }); }); - submenu.addItem((subItem: any) => { - subItem.setTitle(t("delete-note")); + submenu.addItem((subItem: MenuItem) => { subItem.setIcon("trash"); + subItem.setTitle(t("delete-note")); subItem.onClick(async () => { // Show confirmation and delete - const confirmed = await showConfirmationModal(plugin.app, { - title: t("contextMenus.task.deleteTitle"), - message: t("contextMenus.task.deleteMessage") + file.name, - confirmText: t("contextMenus.task.deleteConfirm"), - cancelText: t("common.cancel"), - isDestructive: true, - }); - if (confirmed) { - plugin.app.vault.trash(file, true); - } + // const confirmed = await showConfirmationModal(plugin.app, { + // title: t("contextMenus.task.deleteTitle"), + // message: t("contextMenus.task.deleteMessage") + file.name, + // confirmText: t("contextMenus.task.deleteConfirm"), + // cancelText: t("common.cancel"), + // isDestructive: true, + // }); + // if (confirmed) { + // } + + plugin.app.vault.trash(file, true); + // handleDeleteTask(plugin, task, true); }); }); - submenu.addSeparator(); - - submenu.addItem((subItem: any) => { - subItem.setTitle(t("contextMenus.task.copyPath")); - subItem.setIcon("copy"); - subItem.onClick(async () => { - try { - await navigator.clipboard.writeText(file.path); - new Notice(t("contextMenus.task.notices.copyPathSuccess")); - } catch (error) { - new Notice(t("contextMenus.task.notices.copyFailure")); - } - }); - }); - - submenu.addItem((subItem: any) => { - subItem.setTitle(t("contextMenus.task.copyUrl")); - subItem.setIcon("link"); - subItem.onClick(async () => { - try { - const url = `obsidian://open?vault=${encodeURIComponent(plugin.app.vault.getName())}&file=${encodeURIComponent(file.path)}`; - await navigator.clipboard.writeText(url); - new Notice(t("contextMenus.task.notices.copyUrlSuccess")); - } catch (error) { - new Notice(t("contextMenus.task.notices.copyFailure")); - } - }); - }); - - submenu.addSeparator(); - - submenu.addItem((subItem: any) => { - subItem.setTitle(t("contextMenus.task.showInExplorer")); - subItem.setIcon("folder-open"); - subItem.onClick(() => { - // Reveal file in file explorer - plugin.app.workspace - .getLeaf() - .setViewState({ - type: "file-explorer", - state: {}, - }) - .then(() => { - // Focus the file in the explorer - const fileExplorer = - plugin.app.workspace.getLeavesOfType("file-explorer")[0]; - if (fileExplorer?.view && "revealInFolder" in fileExplorer.view) { - (fileExplorer.view as any).revealInFolder(file); - } - }); - }); - }); + // submenu.addSeparator(); + + // submenu.addItem((subItem: any) => { + // subItem.setTitle(t("contextMenus.task.copyPath")); + // subItem.setIcon("copy"); + // subItem.onClick(async () => { + // try { + // await navigator.clipboard.writeText(file.path); + // new Notice(t("contextMenus.task.notices.copyPathSuccess")); + // } catch (error) { + // new Notice(t("contextMenus.task.notices.copyFailure")); + // } + // }); + // }); + + // submenu.addItem((subItem: any) => { + // subItem.setTitle(t("contextMenus.task.copyUrl")); + // subItem.setIcon("link"); + // subItem.onClick(async () => { + // try { + // const url = `obsidian://open?vault=${encodeURIComponent(plugin.app.vault.getName())}&file=${encodeURIComponent(file.path)}`; + // await navigator.clipboard.writeText(url); + // new Notice(t("contextMenus.task.notices.copyUrlSuccess")); + // } catch (error) { + // new Notice(t("contextMenus.task.notices.copyFailure")); + // } + // }); + // }); + + // submenu.addSeparator(); + + // submenu.addItem((subItem: any) => { + // subItem.setTitle(t("contextMenus.task.showInExplorer")); + // subItem.setIcon("folder-open"); + // subItem.onClick(() => { + // // Reveal file in file explorer + // plugin.app.workspace + // .getLeaf() + // .setViewState({ + // type: "file-explorer", + // state: {}, + // }) + // .then(() => { + // // Focus the file in the explorer + // const fileExplorer = + // plugin.app.workspace.getLeavesOfType("file-explorer")[0]; + // if (fileExplorer?.view && "revealInFolder" in fileExplorer.view) { + // (fileExplorer.view as any).revealInFolder(file); + // } + // }); + // }); + // }); } }); From 3d193d8185aec0cab651663d71054ed6d808fb76 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Sun, 25 Jan 2026 20:18:59 +0530 Subject: [PATCH 027/236] few styling and english translation strings for latest changes --- src/utils/lang/locale/en.ts | 24 ++++++++++++++++++++++-- styles.css | 18 ++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/utils/lang/locale/en.ts b/src/utils/lang/locale/en.ts index 27d787ea..9f1eefb9 100644 --- a/src/utils/lang/locale/en.ts +++ b/src/utils/lang/locale/en.ts @@ -127,7 +127,7 @@ const en: Lang = { "task-board": "Task Board", "open-task-board": "Open task board", "open-task-board-in-new-window": "Open task board in new window", - "update-tasks-from-this-file": "Update tasks from this file", + "update-tasks-from-this-file": "Scan tasks from this file", "add-file-in-scan-filter": "Add file in scan filter", "add-folder-in-scan-filter": "Add folder in scan filter", "board-filters": "Board filters", @@ -539,7 +539,27 @@ const en: Lang = { "task-notes": "Task notes", "inline-tasks": "Inline tasks", "transfer": "Transfer", - "reset": "Reset" + "reset": "Reset", + "copy-task-title": "Copy task title", + "open-note": "Open note", + "note-actions": "Note actions", + "dates": "Dates", + "today": "Today", + "tomorrow": "Tomorrow", + "in-2-days": "In 2 days", + "in-3-days": "In 3 days", + "in-1-week": "In 1 week", + "in-2-weeks": "In 2 weeks", + "in-1-month": "In 1 month", + "in-2-months": "In 2 months", + "in-6-months": "In 6 months", + "in-1-year": "In 1 year", + "clear-date": "Clear date", + "rename-note": "Rename note", + "delete-note": "Delete note", + "open-note-to-right": "Open note to right", + "copy-task-title-successful": "Successfully copied the task title.", + "copy-task-title-unsuccessful": "There was an error while copying the task title." }; export default en; diff --git a/styles.css b/styles.css index 3bf82196..7c2b42c1 100644 --- a/styles.css +++ b/styles.css @@ -7554,3 +7554,21 @@ Date picker modal styles min-width: 20px; font-size: 16px; } + + +/* ----------------------------------------------------------- + MENU ITEMS +------------------------------------------------------------ */ + +.menu-item-title .contains-task-list { + margin-block: 4px; + padding-inline-start: 24px; +} + +.menu-item-title .contains-task-list>li>input { + vertical-align: middle; +} + +.menu-item-title .contains-task-list>li>input[type=checkbox]:checked::after { + padding-block-start: 4px; +} From 3050f4c13de7325653fed251b961a9b3639fe46b Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Sun, 25 Jan 2026 20:19:26 +0530 Subject: [PATCH 028/236] fix : missing imports --- src/components/KanbanView/TaskItemV2.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/KanbanView/TaskItemV2.tsx b/src/components/KanbanView/TaskItemV2.tsx index 331ae26e..15a54b73 100644 --- a/src/components/KanbanView/TaskItemV2.tsx +++ b/src/components/KanbanView/TaskItemV2.tsx @@ -6,7 +6,7 @@ import { checkboxStateSwitcher, extractCheckboxSymbol, getObsidianIndentationSet import { handleCheckboxChange, handleDeleteTask, handleSubTasksChange } from 'src/utils/taskLine/TaskItemEventHandlers'; import { hookMarkdownLinkMouseEventHandlers, markdownButtonHoverPreviewEvent } from 'src/services/MarkdownHoverPreview'; -import { Component, Notice, Platform, Menu, TFile } from 'obsidian'; +import { Component, Notice, Platform, Menu, TFile, MenuItem } from 'obsidian'; import { MarkdownUIRenderer } from 'src/services/MarkdownUIRenderer'; import { cleanTaskTitleLegacy } from 'src/utils/taskLine/TaskContentFormatter'; import { updateRGBAOpacity } from 'src/utils/UIHelpers'; @@ -16,10 +16,10 @@ import { Board } from 'src/interfaces/BoardConfigs'; import { TaskRegularExpressions, TASKS_PLUGIN_DEFAULT_SYMBOLS } from 'src/regularExpressions/TasksPluginRegularExpr'; import { getStatusNameFromStatusSymbol, isTaskNotePresentInTags } from 'src/utils/taskNote/TaskNoteUtils'; import { allowedFileExtensionsRegEx } from 'src/regularExpressions/MiscelleneousRegExpr'; -import { bugReporter } from 'src/services/OpenModals'; +import { bugReporter, openDateInputModal } from 'src/services/OpenModals'; import { ChevronDown, EllipsisVertical, Grip } from 'lucide-react'; import { EditButtonMode, viewTypeNames, colTypeNames, taskPropertiesNames } from 'src/interfaces/Enums'; -import { getCustomStatusOptionsForDropdown, priorityEmojis } from 'src/interfaces/Mapping'; +import { getCustomStatusOptionsForDropdown, getPriorityOptionsForDropdown, priorityEmojis } from 'src/interfaces/Mapping'; import { taskItem, UpdateTaskEventData } from 'src/interfaces/TaskItem'; import { matchTagsWithWildcards, verifySubtasksAndChildtasksAreComplete } from 'src/utils/algorithms/ScanningFilterer'; import { handleTaskNoteStatusChange, handleTaskNoteBodyChange } from 'src/utils/taskNote/TaskNoteEventHandlers'; From 3b686154fde23e8eaa545c921f60f86045a4a25f Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Sun, 25 Jan 2026 20:20:38 +0530 Subject: [PATCH 029/236] fix : few minor import fixes --- src/managers/DragDropTasksManager.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/managers/DragDropTasksManager.ts b/src/managers/DragDropTasksManager.ts index 66a59605..c8aae7dc 100644 --- a/src/managers/DragDropTasksManager.ts +++ b/src/managers/DragDropTasksManager.ts @@ -404,7 +404,7 @@ class DragDropTasksManager { UniversalDateOptions.dueDate; // Call the date input modal, to take new date from user. - const datePicker = new DatePickerModal(plugin.app, plugin); + const datePicker = new DatePickerModal(plugin); datePicker.onDateSelected = async (date: string | null) => { if (date) { // newTask[dateType] = date; @@ -648,7 +648,7 @@ class DragDropTasksManager { UniversalDateOptions.dueDate; // Call the date input modal, to take new date from user. - const datePicker = new DatePickerModal(plugin.app, plugin); + const datePicker = new DatePickerModal(plugin); datePicker.onDateSelected = async (date: string | null) => { if (date) { // newTask[dateType] = date; From 881df5061711206957396d75c74a220586804d4a Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Sun, 25 Jan 2026 20:21:03 +0530 Subject: [PATCH 030/236] test the latest changes | working --- data.json | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/data.json b/data.json index 7f7aa16a..75d49677 100644 --- a/data.json +++ b/data.json @@ -315,13 +315,13 @@ } ], "tasksIdManualOrder": [ - "224", - "276", - "389", "378", - "355", + "276", + "224", "258", - "375" + "355", + "375", + "389" ] }, { @@ -510,7 +510,7 @@ ] }, "taskCount": { - "pending": 63, + "pending": 64, "completed": 14 }, "swimlanes": { @@ -620,13 +620,13 @@ } ], "tasksIdManualOrder": [ - "375", - "389", - "224", - "355", + "378", "276", + "224", "258", - "378" + "355", + "375", + "389" ] }, { @@ -697,8 +697,8 @@ "filterGroups": [] }, "taskCount": { - "pending": 60, - "completed": 15 + "pending": 64, + "completed": 14 }, "swimlanes": { "enabled": false, @@ -1050,14 +1050,14 @@ "taskPropertyFormat": "3", "taskNoteDefaultLocation": "", "autoAddUniqueID": true, - "uniqueIdCounter": 586, + "uniqueIdCounter": 587, "experimentalFeatures": true, "searchQuery": "", "lastViewHistory": { "viewedType": "kanban", "boardIndex": 4, "taskId": "", - "settingTab": 4 + "settingTab": 7 }, "taskNoteIdentifierTag": "task", "doubleClickCardToEdit": "noteInTab", From 92850bd7fa5ba6d524c46e068e22c4667e3e4eae Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Mon, 26 Jan 2026 12:39:27 +0530 Subject: [PATCH 031/236] DEPRECATED : Column.tsx component --- src/components/KanbanView/Column.tsx | 809 --------------------------- 1 file changed, 809 deletions(-) delete mode 100644 src/components/KanbanView/Column.tsx diff --git a/src/components/KanbanView/Column.tsx b/src/components/KanbanView/Column.tsx deleted file mode 100644 index dd79a24d..00000000 --- a/src/components/KanbanView/Column.tsx +++ /dev/null @@ -1,809 +0,0 @@ -// /src/components/Column.tsx - -/** - * Column component - DEPRECATED - * This component has been now deprecated, since the LazyColum.tsx component is performing its intended functionality of lazy-loading without any issue in all the tested environments. - * Will still keep this file in the project and update it with the changes from LazyColumn.tsx, just to have a fallback backup ready. And for other testing. - * @kind component - * @deprecated - */ - -import React, { memo, useCallback, useEffect, useMemo, useState, useRef } from 'react'; - -import { CSSProperties } from 'react'; -import TaskItem, { swimlaneDataProp } from './TaskItem'; -import { t } from 'src/utils/lang/helper'; -import TaskBoard from 'main'; -import { Board, ColumnData, RootFilterState } from 'src/interfaces/BoardConfigs'; -import { taskItem } from 'src/interfaces/TaskItem'; -import { Menu, Platform } from 'obsidian'; -import { ViewTaskFilterPopover } from 'src/components/BoardFilters/ViewTaskFilterPopover'; -import { eventEmitter } from 'src/services/EventEmitter'; -import { ViewTaskFilterModal } from 'src/components/BoardFilters'; -import { ConfigureColumnSortingModal } from 'src/modals/ConfigureColumnSortingModal'; -import { matchTagsWithWildcards } from 'src/utils/algorithms/ScanningFilterer'; -import { isRootFilterStateEmpty } from 'src/utils/algorithms/BoardFilterer'; -import { dragDropTasksManagerInsatance } from 'src/managers/DragDropTasksManager'; -import { bugReporterManagerInsatance } from 'src/managers/BugReporter'; - -type CustomCSSProperties = CSSProperties & { - '--task-board-column-width': string; -}; - -export interface ColumnProps { - plugin: TaskBoard; - columnIndex: number; - activeBoardData: Board; - collapsed?: boolean; - columnData: ColumnData; - tasksForThisColumn: taskItem[]; - swimlaneData?: swimlaneDataProp; - hideColumnHeader?: boolean; - headerOnly?: boolean; -} - - -const Column: React.FC = ({ - plugin, - columnIndex, - activeBoardData, - columnData, - tasksForThisColumn, - swimlaneData, - hideColumnHeader = false, - headerOnly = false, -}) => { - if (activeBoardData?.hideEmptyColumns && (tasksForThisColumn === undefined || tasksForThisColumn.length === 0)) { - return null; // Don't render the column if it has no tasks and empty columns are hidden - } - const [isDragOver, setIsDragOver] = useState(false); - const [insertIndex, setInsertIndex] = useState(null); - const insertIndexRef = useRef(null); - const rafRef = useRef(null); - const tasksContainerRef = useRef(null); - - const scheduleSetInsertIndex = (pos: number | null) => { - if (insertIndexRef.current === pos) return; - if (rafRef.current) { - cancelAnimationFrame(rafRef.current); - rafRef.current = null; - } - rafRef.current = requestAnimationFrame(() => { - insertIndexRef.current = pos; - setInsertIndex(pos); - rafRef.current = null; - }); - }; - - // Local tasks state, initially set from external tasks - // const [tasks, setTasks] = useState(tasksForThisColumn); - // const tasks = useMemo(() => tasksForThisColumn, [tasksForThisColumn]); - // Local tasks state, initially set from external tasks - // console.log("Column.tsx : Data in tasks :", tasks); - const [localTasks, setLocalTasks] = useState(tasksForThisColumn); - // Detect external changes in tasksForThisColumn - useEffect(() => { - setLocalTasks(tasksForThisColumn); - }, [tasksForThisColumn]); - - // // Render tasks using the tasks passed from KanbanBoard - // useEffect(() => { - // if (allTasksExternal.Pending.length > 0 || allTasksExternal.Completed.length > 0) { - // columnSegregator(plugin, setTasks, activeBoardIndex, colType, columnData, allTasksExternal); - // } - // }, [colType, columnData, allTasksExternal]); - - const columnWidth = plugin.settings.data.globalSettings.columnWidth || '273px'; - // const activeBoardSettings = plugin.settings.data.boardConfigs[activeBoardIndex]; - - // Extra code to provide special data-types for theme support. - const tagColors = plugin.settings.data.globalSettings.tagColors; - const tagColorMap = new Map(tagColors.map((t) => [t.name, t])); - let tagData = tagColorMap.get(columnData?.coltag || ''); - if (!tagData) { - tagColorMap.forEach((tagColor, tagNameKey, mapValue) => { - const result = matchTagsWithWildcards(tagNameKey, columnData?.coltag || ''); - // console.log("Column.tsx : Matching tag result : ", { tagNameKey, columnTag: columnData?.coltag, result }); - // Return the first match found - if (result) tagData = tagColor; - }); - } - - // Determine whether an advanced filter is applied (used by header count UI) - const isAdvancedFilterApplied = !isRootFilterStateEmpty(columnData.filters); - - // If this column is requested to render header-only (used by swimlane top header), return just the header UI - if (headerOnly) { - return ( -
- {columnData.minimized ? ( -
openColumnMenu(evt)} aria-label={t("open-column-menu")}>{localTasks?.length ?? 0}
- ) : ( -
-
-
{columnData.name}
-
-
openColumnMenu(evt)} aria-label={t("open-column-menu")}> - {localTasks?.length ?? 0} -
-
- )} -
- ); - } - - async function handleMinimizeColumn() { - // const boardIndex = plugin.settings.data.boardConfigs.findIndex( - // (board: Board) => board.name === activeBoardData.name - // ); - const boardIndex = activeBoardData.index; - - if (boardIndex !== -1) { - // NOTE : This extra thing we need to do because, the columnData.index is stored starting with 1 and not 0. Hence, I we will need to subtract 1 from it. - // const columnIndex = plugin.settings.data.boardConfigs[boardIndex].columns.findIndex( - // (col: ColumnData) => col.id === columnData.id - // ); - const columnIndex = columnData.index - 1; - - if (columnIndex !== -1) { - // Set the minimized property to true - plugin.settings.data.boardConfigs[boardIndex].columns[columnIndex].minimized = !plugin.settings.data.boardConfigs[boardIndex].columns[columnIndex].minimized; - - // Save the settings - await plugin.saveSettings(); - - // Refresh the board view - eventEmitter.emit('REFRESH_BOARD'); - } - } - } - - /** - * Opens the column menu, which allows the user to sort and filter the tasks in the column, - * configure the column's sorting and filtering, and hide the column. - * - * @param {MouseEvent | React.MouseEvent} event - The event that triggered the menu - */ - function openColumnMenu(event: MouseEvent | React.MouseEvent) { - const columnMenu = new Menu(); - - columnMenu.addItem((item) => { - item.setTitle(t("sort-and-filter")); - item.setIsLabel(true); - }); - columnMenu.addItem((item) => { - item.setTitle(t("configure-column-sorting")); - item.setIcon("arrow-up-down"); - item.onClick(async () => { - // open sorting modal - const modal = new ConfigureColumnSortingModal( - plugin, - columnData, - (updatedColumnConfiguration: ColumnData) => { - // Update the column configuration in the board data - // const boardIndex = plugin.settings.data.boardConfigs.findIndex( - // (board: Board) => board.name === activeBoardData.name - // ); - const boardIndex = activeBoardData.index; - - if (boardIndex !== -1) { - // NOTE : This extra thing we need to do because, the columnData.index is stored starting with 1 and not 0. Hence, I we will need to subtract 1 from it. - // const columnIndex = plugin.settings.data.boardConfigs[boardIndex].columns.findIndex( - // (col: ColumnData) => col.id === columnData.id - // ); - const columnIndex = columnData.index - 1; - - if (columnIndex !== -1) { - // Update the column configuration - plugin.settings.data.boardConfigs[boardIndex].columns[columnIndex] = updatedColumnConfiguration; - - // Save the settings - plugin.saveSettings(); - - eventEmitter.emit('REFRESH_BOARD'); - } - } - }, - () => { - // onCancel callback - nothing to do - } - ); - modal.open(); - }); - }); - columnMenu.addItem((item) => { - item.setTitle(t("configure-column-filtering")); - item.setIcon("list-filter"); - item.onClick(async () => { - try { - // const boardIndex = plugin.settings.data.boardConfigs.findIndex( - // (board: Board) => board.name === activeBoardData.name - // ); - const boardIndex = activeBoardData.index; - // NOTE : This extra thing we need to do because, the columnData.index is stored starting with 1 and not 0. Hence, I we will need to subtract 1 from it. - // const columnIndex = plugin.settings.data.boardConfigs[boardIndex].columns.findIndex( - // (col: ColumnData) => col.id === columnData.id - // ); - const columnIndex = columnData.index - 1; - - if (Platform.isMobile || Platform.isMacOS) { - // If its a mobile platform, then we will open a modal instead of popover. - const filterModal = new ViewTaskFilterModal( - plugin, true, undefined, boardIndex, columnData.name, columnData.filters - ); - - // Set the close callback - mainly used for handling cancel actions - filterModal.filterCloseCallback = async (filterState) => { - if (filterState && boardIndex !== -1) { - if (columnIndex !== -1) { - // Update the column filters - plugin.settings.data.boardConfigs[boardIndex].columns[columnIndex].filters = filterState; - - // Save the settings - await plugin.saveSettings(); - - // Refresh the board view - eventEmitter.emit('REFRESH_BOARD'); - } - } - }; - - filterModal.open(); - } else { - // Get the position of the menu (approximate column position) - // Use CSS.escape to properly escape the selector value - const escapedTag = columnData.coltag ? CSS.escape(columnData.coltag) : ''; - const columnElement = document.querySelector(`[data-column-tag-name="${escapedTag}"]`) as HTMLElement; - const position = columnElement - ? { x: columnElement.getBoundingClientRect().left, y: columnElement.getBoundingClientRect().top + 40 } - : { x: 100, y: 100 }; // Fallback position - - // Create and show filter popover - // leafId is undefined for column filters (not tied to a specific leaf) - const popover = new ViewTaskFilterPopover( - plugin, - true, // forColumn is true - undefined, - boardIndex, - columnData.name, - columnData.filters - ); - - // Set up close callback to save filter state - popover.onClose = async (filterState?: RootFilterState) => { - if (filterState && boardIndex !== -1) { - if (columnIndex !== -1) { - // Update the column filters - plugin.settings.data.boardConfigs[boardIndex].columns[columnIndex].filters = filterState; - - // Save the settings - await plugin.saveSettings(); - - // Refresh the board view - eventEmitter.emit('REFRESH_BOARD'); - } - } - }; - - popover.showAtPosition(position); - } - } catch (error) { - bugReporterManagerInsatance.showNotice(1, "Error showing filter popover", String(error), "Column.tsx/column-menu/configure-conlum-filters"); - } - }); - }); - - columnMenu.addSeparator(); - - columnMenu.addItem((item) => { - item.setTitle(t("quick-actions")); - item.setIsLabel(true); - }); - columnMenu.addItem((item) => { - item.setTitle(t("hide-column")); - item.setIcon("eye-off"); - item.onClick(async () => { - // const boardIndex = plugin.settings.data.boardConfigs.findIndex( - // (board: Board) => board.name === activeBoardData.name - // ); - const boardIndex = activeBoardData.index; - - if (boardIndex !== -1) { - // NOTE : This extra thing we need to do because, the columnData.index is stored starting with 1 and not 0. Hence, I we will need to subtract 1 from it. - // const columnIndex = plugin.settings.data.boardConfigs[boardIndex].columns.findIndex( - // (col: ColumnData) => col.id === columnData.id - // ); - const columnIndex = columnData.index - 1; - - if (columnIndex !== -1) { - // Set the active property to false - plugin.settings.data.boardConfigs[boardIndex].columns[columnIndex].active = false; - - // Save the settings - await plugin.saveSettings(); - - // Refresh the board view - eventEmitter.emit('REFRESH_BOARD'); - } - } - }); - }); - - // Show minimize or maximize option based on current state - if (columnData.minimized) { - columnMenu.addItem((item) => { - item.setTitle(t("maximize-column")); - item.setIcon("panel-left-open"); - item.onClick(async () => { - await handleMinimizeColumn(); - }); - }); - } else { - columnMenu.addItem((item) => { - item.setTitle(t("minimize-column")); - item.setIcon("panel-left-close"); - item.onClick(async () => { - await handleMinimizeColumn(); - }); - }); - } - - // Use native event if available (React event has nativeEvent property) - columnMenu.showAtMouseEvent( - (event instanceof MouseEvent ? event : event.nativeEvent) - ); - } - - // ------------------------------------------------- - // ALL DRAG AND DROP RELATED FUNCTIONS - // ------------------------------------------------- - - /** - * Handles the drop event of a task in this column. - * Moves the task from its original position (dragIndex) to the new position (dropIndex). - * Updates the localTasks state and the columnData.tasksIdManualOrder if the column uses manualOrder. - * Clears the raf timer to prevent any pending raf calls. - * @param {React.DragEvent} e - The drag event. - * @param {number} dropIndex - The index at which to drop the task. - */ - const handleTaskDrop = async (e: React.DragEvent, dropIndex: number) => { - e.preventDefault(); - setIsDragOver(false); - setInsertIndex(null); - - const targetColumnContainer = tasksContainerRef.current; - if (!targetColumnContainer) { - return; - } - - // We are basically doing same thing from the handleDrop function below. - dragDropTasksManagerInsatance.handleDropEvent( - e.nativeEvent, - columnData, - targetColumnContainer, - swimlaneData - ); - - // Clear manager payload (drag finished) - dragDropTasksManagerInsatance.clearCurrentDragData(); - dragDropTasksManagerInsatance.clearDesiredDropIndex(); - - // const dragIndex = parseInt(e.dataTransfer.getData('text/plain')); - // if (isNaN(dragIndex) || dragIndex === dropIndex) return; - // const updated = [...localTasks]; - // const [moved] = updated.splice(dragIndex, 1); - // updated.splice(dropIndex, 0, moved); - // setLocalTasks(updated); - // // If this column uses manualOrder, update the columnData.tasksIdManualOrder to reflect new order - // const hasManualOrder = Array.isArray(columnData.sortCriteria) && columnData.sortCriteria.some((c) => c.criteria === 'manualOrder'); - // if (hasManualOrder) { - // columnData.tasksIdManualOrder = updated.map(t => t.id); - // } - - // clear any pending raf - if (rafRef.current) { - cancelAnimationFrame(rafRef.current); - rafRef.current = null; - } - }; - - const handleDrop = useCallback((e: React.DragEvent) => { - e.preventDefault(); - setIsDragOver(false); - - try { - // Get the data of the dragged task -- No need anymore, since its already stored in the dragdropmanager. - // const taskData = e.dataTransfer.getData('application/json'); - // if (taskData) { - // const { task, sourceColumnData } = JSON.parse(taskData); - - // // Ensure we have valid data - // if (!task || !sourceColumnData) return; - - // Get the target column container - const targetColumnContainer = (e.currentTarget) as HTMLDivElement; - - - // Try to locate the source container by stable column id first (works for all colTypes) -- No need to find this anymore, since I am not making use of sourceColumnContainer in dragdropmanager. - // let sourceColumnContainer: HTMLDivElement | null = null; - // if (sourceColumnData?.id) { - // try { - // const escapedId = CSS.escape(String(sourceColumnData.id)); - // sourceColumnContainer = document.querySelector(`.TaskBoardColumnsSection[data-column-id="${escapedId}"]`) as HTMLDivElement | null; - // } catch (err) { - // // fallback to tag-based lookup below - // } - // } - // if (!sourceColumnContainer) { - // // Fallback: find by tag name (legacy behavior) - // console.log("------------- I hope this fall-back mechanism is never running -------------"); - // const allColumnContainers = Array.from(document.querySelectorAll('.TaskBoardColumnsSection')) as HTMLDivElement[]; - // sourceColumnContainer = allColumnContainers.find(container => { - // const containerTag = container.getAttribute('data-column-tag-name'); - // return containerTag === sourceColumnData.coltag || sourceColumnData.coltag?.includes(containerTag || ''); - // }) || targetColumnContainer; - // } - - // we will allow cross-column drops now with target column having manualOrder sortCriteria. Disabling below code. - // const hasManualOrder = Array.isArray(columnData.sortCriteria) && columnData.sortCriteria.some((c) => c.criteria === 'manualOrder'); - // if (hasManualOrder && sourceColumnData.id !== columnData.id) { - // // Not allowed: ignore drop - // dragDropTasksManagerInsatance.clearCurrentDragData(); - // dragDropTasksManagerInsatance.clearDesiredDropIndex(); - // return; - // } - - // // Use the DragDropTasksManager to handle the drop - // try { - // const dragIdxStr = e.dataTransfer.getData('text/plain'); - // const dragIdx = dragIdxStr ? parseInt(dragIdxStr) : NaN; - // if (sourceColumnData.coltag === columnData.coltag && !isNaN(dragIdx) && insertIndexRef.current !== null) { - // // Reorder locally - // const updated = [...localTasks]; - // const [moved] = updated.splice(dragIdx, 1); - // updated.splice(insertIndexRef.current!, 0, moved); - // setLocalTasks(updated); - // setInsertIndex(null); - // insertIndexRef.current = null; - // // Update manual order if applicable - // const hasManualOrderLocal = Array.isArray(columnData.sortCriteria) && columnData.sortCriteria.some((c) => c.criteria === 'manualOrder'); - // if (hasManualOrderLocal) { - // columnData.tasksIdManualOrder = updated.map(t => t.id); - // } - // // Clear manager payload and skip default handling - // dragDropTasksManagerInsatance.clearCurrentDragData(); - // dragDropTasksManagerInsatance.clearDesiredDropIndex(); - // return; - // } - // } catch (err) { - // // ignore and continue to default handling - // } - - dragDropTasksManagerInsatance.handleDropEvent( - e.nativeEvent, - columnData, - targetColumnContainer, - swimlaneData - ); - - // Clear manager payload (drag finished) - dragDropTasksManagerInsatance.clearCurrentDragData(); - dragDropTasksManagerInsatance.clearDesiredDropIndex(); - // } - } catch (error) { - console.error('Error handling task drop:', error); - } - }, [columnData, plugin]); - - // This function will be only run when user will drag the taskItem on another taskItem. - // Compute insertion index based on mouse Y relative to task items inside the container. - const handleTaskItemDragOver = useCallback((e: React.DragEvent) => { - e.preventDefault(); - setIsDragOver(true); - try { - // Only compute insertion index for columns that use "manualOrder" as the sorting criteria. - const hasManualOrder = Array.isArray(columnData.sortCriteria) && columnData.sortCriteria.some((c) => c.criteria === 'manualOrder'); - if (!hasManualOrder) { - // Clear any visual placeholder and desired index - if (insertIndexRef.current !== null) { - scheduleSetInsertIndex(null); - } - dragDropTasksManagerInsatance.clearDesiredDropIndex(); - return; - } else { - // APPROACH 1 - COMPUTE INSERTION INDEX BASED ON MOUSE Y POSITION BY COMPARING WITH TASK ITEM BOUNDING RECTANGLES - // Else will proceed with finding the insertion index - // const container = e.currentTarget.parentElement as HTMLDivElement; - // const children = Array.from(container.querySelectorAll('.taskItemFadeIn')) as HTMLElement[]; - // let pos = children.length; // default to end - // const clientY = e.clientY; - // for (let i = 0; i < children.length; i++) { - // const child = children[i]; - // const rect = child.getBoundingClientRect(); - // const midpoint = rect.top + rect.height / 2; - // if (clientY < midpoint) { - // pos = i; - // break; - // } - // } - - // APPROACH 2 - DIRECTLY FETCH THE INDEX FROM THE DATA ATTRIBUTE OF THE HOVERED ELEMENT - let pos = 0 // Default to top of the column - const hoveredElement = e.currentTarget; - const draggedOverItemIndex = hoveredElement.getAttribute('data-taskitem-index'); - const draggedOverItemKey = hoveredElement.getAttribute('data-taskitem-id'); - const draggedItemKey = dragDropTasksManagerInsatance.getCurrentDragData()?.task.id; - // console.log('handleTaskItemDragOver... \ndataAttribute', draggedOverItemIndex, "\ndraggedItemIndex", draggedItemIndex); - if (draggedOverItemKey && draggedOverItemIndex && draggedOverItemKey !== draggedItemKey) { - const clientY = e.clientY; - const rect = hoveredElement.getBoundingClientRect(); - const midpoint = rect.top + rect.height / 2; - if (clientY < midpoint) { - pos = parseInt(draggedOverItemIndex, 10); - } else { - pos = parseInt(draggedOverItemIndex, 10) + 1; - } - - // Throttle updates via RAF - scheduleSetInsertIndex(pos); - // Store desired drop index in manager - dragDropTasksManagerInsatance.setDesiredDropIndex(pos); - } else { - // Clear any visual placeholder and desired index - if (insertIndexRef.current !== null) { - scheduleSetInsertIndex(null); - } - dragDropTasksManagerInsatance.clearDesiredDropIndex(); - } - - - // // Use the DragDropTasksManager to handle the drag over (this sets classes and dropEffect) - // dragDropTasksManagerInsatance.handleDragOver( - // e.nativeEvent, - // columnData, - // container - // ); - - const targetColumnContainer = tasksContainerRef.current as HTMLDivElement; - dragDropTasksManagerInsatance.handleCardDragOverEvent(e.nativeEvent as DragEvent, e.currentTarget as HTMLDivElement, targetColumnContainer, columnData); - } - } catch (error) { - console.error('Error computing insert index:', error); - } - }, [scheduleSetInsertIndex, columnData]); - - const handleDragOver = useCallback((e: React.DragEvent) => { - e.preventDefault(); - setIsDragOver(true); - try { - // // Try to read payload from the DataTransfer first - // let taskDataStr = ''; - // try { - // taskDataStr = e.dataTransfer.getData('application/json'); - // } catch (err) { - // // ignore - some environments restrict access - // } - - // let payload: any = null; - // if (taskDataStr) { - // try { payload = JSON.parse(taskDataStr); } catch { } - // } - - // // Fallback to manager-stored payload if dataTransfer is empty - // if (!payload) { - // payload = dragDropTasksManagerInsatance.getCurrentDragData(); - // } - - // if (!payload) return; - - // const { task, sourceColumnData } = payload; - // if (!task || !sourceColumnData) return; - - // Get the target column container - const targetColumnContainer = (e.currentTarget) as HTMLDivElement; - - // // Try id-based lookup first - // let sourceColumnContainer: HTMLDivElement | null = null; - // if (sourceColumnData?.id) { - // try { - // const escapedId = CSS.escape(String(sourceColumnData.id)); - // sourceColumnContainer = document.querySelector(`.TaskBoardColumnsSection[data-column-id="${escapedId}"]`) as HTMLDivElement | null; - // } catch (err) { - // // ignore and fall back to tag-based lookup - // } - // } - // if (!sourceColumnContainer) { - // const allColumnContainers = Array.from(document.querySelectorAll('.TaskBoardColumnsSection')) as HTMLDivElement[]; - // sourceColumnContainer = allColumnContainers.find(container => { - // const containerTag = container.getAttribute('data-column-tag-name'); - // return containerTag === sourceColumnData.coltag || sourceColumnData.coltag?.includes(containerTag || ''); - // }) || targetColumnContainer; - // } - - // Use the DragDropTasksManager to handle the drag over (this sets classes and dropEffect) - dragDropTasksManagerInsatance.handleColumnDragOverEvent( - e.nativeEvent, - columnData, - targetColumnContainer - ); - - // Below code is not required, since, I will call the dragDropTasksManagerInsatance.handleCardDragOverEvent from handleTaskItemDragOver. - // // If hovering over an actual card element, show card drop indicator - // try { - // const hovered = (e.target as HTMLElement).closest('.taskItem') as HTMLElement | null; - // if (hovered) { - // dragDropTasksManagerInsatance.handleCardDragOverEvent(e.nativeEvent as DragEvent, hovered); - // } - // } catch (err) { - // // ignore - // } - - // // Ensure cursor reflects allowed/not-allowed (best-effort fallback) - // const allowed = dragDropTasksManagerInsatance.isTaskDropAllowed(sourceColumnData, columnData); - // e.dataTransfer!.dropEffect = allowed ? 'move' : 'none'; - } catch (error) { - console.error('Error handling drag over:', error); - } - }, [columnData]); - - // Cleanup any pending RAF on unmount - useEffect(() => { - return () => { - if (rafRef.current) { - cancelAnimationFrame(rafRef.current); - rafRef.current = null; - } - }; - }, []); - - // Handle the dragleave event to remove the visual effect - const handleDragLeave = useCallback((e: React.DragEvent) => { - // Avoid flicker: if the drag event indicates the pointer is still within the container bounds, - // ignore this dragleave (this happens when moving between child elements). - try { - const container = e.currentTarget as HTMLElement; - const x = e.clientX; - const y = e.clientY; - if (typeof x === 'number' && typeof y === 'number') { - const rect = container.getBoundingClientRect(); - if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) { - // still inside container — ignore to prevent CSS flicker - return; - } - } - } catch (err) { - // ignore and continue cleanup - } - - setIsDragOver(false); - setInsertIndex(null); - dragDropTasksManagerInsatance.clearDesiredDropIndex(); - // Let manager clean up the dropindicator and column highlight - dragDropTasksManagerInsatance.handleDragLeaveEvent(e.currentTarget as HTMLDivElement); - }, []); - - // ------------------------------------------------- - // Render - // ------------------------------------------------- - - return ( -
- {columnData.minimized && !hideColumnHeader ? ( - // Minimized view - vertical bar with count and rotated text -
-
openColumnMenu(evt)} aria-label={t("open-column-menu")}> - {localTasks.length} -
-
{ - await handleMinimizeColumn(); - eventEmitter.emit('REFRESH_BOARD'); - }}>{columnData.name}
-
- ) : ( - // Normal view - <> - {!hideColumnHeader && ( -
-
-
{columnData.name}
-
-
openColumnMenu(evt)} aria-label={t("open-column-menu")}> - {localTasks?.length ?? 0} -
-
- )} -
{ handleDragOver(e); }} - onDragLeave={handleDragLeave} - onDrop={handleDrop} - onDragEnd={(e) => { setIsDragOver(false); setInsertIndex(null); dragDropTasksManagerInsatance.clearAllDragStyling(); }} - > - {columnData.minimized ? <> : ( - <> - {localTasks.length > 0 ? ( - (() => { - const elements: React.ReactNode[] = []; - for (let i = 0; i < localTasks.length; i++) { - // If insertIndex points to this position, render placeholder - if (insertIndex === i) { - elements.push( -
Drop here
- ); - } - const task = localTasks[i]; - elements.push( -
{ handleTaskItemDragOver(e); } - } - onDrop={e => handleTaskDrop(e, i)} - > - -
- ); - } - // If insertIndex points to end (after last item) - if (insertIndex === localTasks.length) { - elements.push( -
Drop here
- ); - } - return elements; - })() - ) : ( -

{t("no-tasks-available")}

- )} - - ) - } -
- - )} -
- ); - -}; - -// const MemoizedTaskItem = memo(TaskItem, (prevProps, nextProps) => { -// return ( -// prevProps.task.id === nextProps.task.id && // Immutable check -// prevProps.task.title === nextProps.task.title && -// prevProps.task.body === nextProps.task.body && -// prevProps.task.due === nextProps.task.due && -// prevProps.task.tags.join(",") === nextProps.task.tags.join(",") && -// prevProps.task.priority === nextProps.task.priority && -// prevProps.task.completed === nextProps.task.completed && -// prevProps.task.filePath === nextProps.task.filePath && -// prevProps.columnIndex === nextProps.columnIndex && -// prevProps.activeBoardSettings === nextProps.activeBoardSettings -// ); -// }); - -export default memo(Column); From 28fba32358ccd0115a43446bc33df98f34e95ae3 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Mon, 26 Jan 2026 12:40:23 +0530 Subject: [PATCH 032/236] feat : integrate the logger functionality and replace all `console.error` --- main.ts | 46 +-- src/components/AddOrEditTaskRC.tsx | 18 +- .../BoardFilters/FilterConfigModal.ts | 39 ++- .../BoardFilters/ViewTaskFilterModal.ts | 22 +- .../BoardFilters/ViewTaskFilterPopover.ts | 19 +- src/components/KanbanView/KanbanBoardView.tsx | 5 +- .../KanbanView/KanbanSwimlanesContainer.tsx | 12 +- src/components/KanbanView/LazyColumn.tsx | 18 +- src/components/KanbanView/TaskItem.tsx | 144 +++------- src/components/KanbanView/TaskItemV2.tsx | 152 +++------- src/components/MapView/MapView.tsx | 22 +- .../MapView/ResizableNodeSelected.tsx | 7 +- src/components/MapView/TasksImporterPanel.tsx | 7 +- src/components/TaskBoardViewContent.tsx | 7 +- src/managers/BugReporter.ts | 66 +++-- src/managers/DragDropTasksManager.ts | 270 +++++++++--------- src/managers/RealTimeScanner.ts | 30 +- src/managers/VaultScanner.ts | 25 +- src/modals/AddOrEditTaskModal.tsx | 4 +- src/modals/BugReporterModal.ts | 4 +- src/services/FileSystem.ts | 24 +- src/services/MarkdownUIRenderer.ts | 26 +- src/services/MultiSuggest.ts | 16 +- src/services/OpenModals.ts | 7 +- src/services/tasks-plugin/helpers.ts | 36 +-- src/settings/SettingSynchronizer.ts | 34 ++- src/taskboardAPIs.ts | 13 +- src/utils/CheckBoxUtils.ts | 33 ++- src/utils/DateTimeCalculations.ts | 19 +- src/utils/JsonFileOperations.ts | 6 +- src/utils/MarkdownFileOperations.ts | 8 +- src/utils/UIHelpers.ts | 36 +-- src/utils/lang/helper.ts | 17 +- src/utils/taskLine/TaskContentFormatter.ts | 105 +++---- src/utils/taskLine/TaskItemEventHandlers.ts | 33 +-- src/utils/taskNote/FrontmatterOperations.ts | 64 +++-- src/utils/taskNote/TaskNoteEventHandlers.ts | 35 ++- src/utils/taskNote/TaskNoteUtils.ts | 86 +++--- src/views/AddOrEditTaskView.tsx | 4 +- 39 files changed, 755 insertions(+), 764 deletions(-) diff --git a/main.ts b/main.ts index c216a133..fc471a8c 100644 --- a/main.ts +++ b/main.ts @@ -274,11 +274,19 @@ export default class TaskBoard extends Plugin { } async saveSettings(newSetting?: PluginDataJson) { - if (newSetting) { - this.settings = newSetting; - await this.saveData(newSetting); - } else { - await this.saveData(this.settings); + try { + if (newSetting) { + this.settings = newSetting; + await this.saveData(newSetting); + } else { + await this.saveData(this.settings); + } + } catch (err) { + bugReporterManagerInsatance.addToLogs( + 140, + String(err), + "main.ts/saveSettings", + ); } } @@ -722,9 +730,10 @@ export default class TaskBoard extends Plugin { this.currentProgressNotice.messageEl.textContent = `Task Board : Processing renamed files: ${processed}/${totalFiles}`; } } catch (error) { - console.error( - `Error processing renamed file ${file.path}:`, - error, + bugReporterManagerInsatance.addToLogs( + 162, + String(error), + "main.ts/processRenameQueue", ); } @@ -804,9 +813,10 @@ export default class TaskBoard extends Plugin { this.currentProgressNotice.messageEl.textContent = `Task Board : Processing deleted files: ${processed}/${totalFiles}`; } } catch (error) { - console.error( - `Error processing deleted file ${file.path}:`, - error, + bugReporterManagerInsatance.addToLogs( + 163, + String(error), + "main.ts/processDeleteQueue", ); } @@ -887,9 +897,10 @@ export default class TaskBoard extends Plugin { // Update progress notice this.currentProgressNotice.messageEl.textContent = `Task Board : Processing created files: ${processed}/${totalFiles}`; } catch (error) { - console.error( - `Error processing created file ${file.path}:`, - error, + bugReporterManagerInsatance.addToLogs( + 164, + String(error), + "main.ts/processCreateQueue", ); } @@ -1029,9 +1040,10 @@ export default class TaskBoard extends Plugin { // Update progress notice this.currentProgressNotice.messageEl.textContent = `Task Board : Processing created files: ${processed}/${totalFilesLength}`; } catch (error) { - console.error( - `Error processing created file ${file.path}:`, - error, + bugReporterManagerInsatance.addToLogs( + 165, + String(error), + "main.ts/findModifiedFilesOnAppAbsense", ); } diff --git a/src/components/AddOrEditTaskRC.tsx b/src/components/AddOrEditTaskRC.tsx index 1bde4df5..2349bb58 100644 --- a/src/components/AddOrEditTaskRC.tsx +++ b/src/components/AddOrEditTaskRC.tsx @@ -110,7 +110,11 @@ export const AddOrEditTaskRC: React.FC<{ text: `${customStatus.name} [${customStatus.symbol}]`, })); } else { - console.error("No custom statuses found."); + bugReporterManagerInsatance.addToLogs( + 129, + `customStatuses are empty in the settings.`, + "AddOrEditTaskRC.tsx", + ); } // ------------ Handle task property values changes ------------ @@ -1020,8 +1024,12 @@ export const AddOrEditTaskRC: React.FC<{ const validTasks = tasks.filter(Boolean) as taskItem[]; setChildTasks(validTasks); }) - .catch(err => { - console.error("Error fetching child tasks:", err); + .catch((err) => { + bugReporterManagerInsatance.addToLogs( + 130, + String(err), + "AddOrEditTaskRC.tsx/fetching child-tasks useEffect", + ); }); } }, []); @@ -1419,8 +1427,8 @@ export const AddOrEditTaskRC: React.FC<{ const tagName = tag.replace('#', ''); const customTagData = plugin.settings.data.globalSettings.tagColors.find(t => t.name === tagName); const tagColor = customTagData?.color; - const backgroundColor = tagColor ? updateRGBAOpacity(plugin, tagColor, 0.1) : `var(--tag-background)`; - const borderColor = tagColor ? updateRGBAOpacity(plugin, tagColor, 0.5) : `var(--tag-color-hover)`; + const backgroundColor = tagColor ? updateRGBAOpacity(tagColor, 0.1) : `var(--tag-background)`; + const borderColor = tagColor ? updateRGBAOpacity(tagColor, 0.5) : `var(--tag-color-hover)`; return (
void, - onLoad?: (config: SavedFilterConfig) => void + onLoad?: (config: SavedFilterConfig) => void, ) { super(app); this.plugin = plugin; @@ -124,7 +125,7 @@ export class FilterConfigModal extends Modal { .addDropdown((dropdown: DropdownComponent) => { dropdown.addOption( "", - t("select-a-saved-filter-configuration") + t("select-a-saved-filter-configuration"), ); savedConfigs.forEach((config) => { @@ -185,7 +186,7 @@ export class FilterConfigModal extends Modal { if (!board.filterConfig) return; const config = board.filterConfig.savedConfigs.find( - (c: SavedFilterConfig) => c.id === configId + (c: SavedFilterConfig) => c.id === configId, ); if (!config) return; @@ -198,14 +199,14 @@ export class FilterConfigModal extends Modal { detailsContainer.createEl("p", { text: `${t("created")}: ${new Date( - config.createdAt + config.createdAt, ).toLocaleString()}`, cls: "filter-config-meta", }); detailsContainer.createEl("p", { text: `${t("updated")}: ${new Date( - config.updatedAt + config.updatedAt, ).toLocaleString()}`, cls: "filter-config-meta", }); @@ -219,7 +220,7 @@ export class FilterConfigModal extends Modal { const groupCount = config.filterState.filterGroups.length; const totalFilters = config.filterState.filterGroups.reduce( (sum: number, group: FilterGroup) => sum + group.filters.length, - 0 + 0, ); filterSummary.createEl("p", { @@ -274,7 +275,11 @@ export class FilterConfigModal extends Modal { this.close(); } catch (error) { - console.error("Failed to save filter configuration:", error); + bugReporterManagerInsatance.addToLogs( + 111, + String(error), + "FilterConfigModal.ts/saveConfiguration", + ); new Notice(t("failed-to-save-filter-configuration")); } } @@ -290,7 +295,7 @@ export class FilterConfigModal extends Modal { if (!board.filterConfig) return; const config = board.filterConfig.savedConfigs.find( - (c: SavedFilterConfig) => c.id === configId + (c: SavedFilterConfig) => c.id === configId, ); if (!config) { @@ -305,7 +310,11 @@ export class FilterConfigModal extends Modal { this.close(); } catch (error) { - console.error("Failed to load filter configuration:", error); + bugReporterManagerInsatance.addToLogs( + 112, + String(error), + "FilterConfigModal.ts/loadConfiguration", + ); new Notice(t("failed-to-load-filter-configuration")); } } @@ -321,7 +330,7 @@ export class FilterConfigModal extends Modal { if (!board.filterConfig) return; const config = board.filterConfig.savedConfigs.find( - (c: SavedFilterConfig) => c.id === configId + (c: SavedFilterConfig) => c.id === configId, ); if (!config) { @@ -371,7 +380,7 @@ export class FilterConfigModal extends Modal { board.filterConfig.savedConfigs = board.filterConfig.savedConfigs.filter( - (c: SavedFilterConfig) => c.id !== configId + (c: SavedFilterConfig) => c.id !== configId, ); await this.plugin.saveSettings(); @@ -389,11 +398,15 @@ export class FilterConfigModal extends Modal { this.activeBoardIndex, undefined, this.onSave, - this.onLoad + this.onLoad, ); newModal.open(); } catch (error) { - console.error("Failed to delete filter configuration:", error); + bugReporterManagerInsatance.addToLogs( + 113, + String(error), + "FilterConfigModal.ts/deleteConfiguration", + ); new Notice(t("failed-to-delete-filter-configuration")); } } diff --git a/src/components/BoardFilters/ViewTaskFilterModal.ts b/src/components/BoardFilters/ViewTaskFilterModal.ts index 9b3358d7..1a4ff197 100644 --- a/src/components/BoardFilters/ViewTaskFilterModal.ts +++ b/src/components/BoardFilters/ViewTaskFilterModal.ts @@ -3,6 +3,7 @@ import type TaskBoard from "main"; import { t } from "src/utils/lang/helper"; import { RootFilterState } from "src/interfaces/BoardConfigs"; import { TaskFilterComponent } from "./ViewTaskFilter"; +import { bugReporterManagerInsatance } from "src/managers/BugReporter"; export class ViewTaskFilterModal extends Modal { private plugin: TaskBoard; @@ -20,7 +21,7 @@ export class ViewTaskFilterModal extends Modal { private leafId?: string, activeBoardIndex?: number, columnOrBoardName?: string, - initialFilterState?: RootFilterState + initialFilterState?: RootFilterState, ) { super(plugin.app); this.plugin = plugin; @@ -32,11 +33,11 @@ export class ViewTaskFilterModal extends Modal { if (forColumn) { this.setTitle( - t("column-filters-for") + " " + this.columnOrBoardName + t("column-filters-for") + " " + this.columnOrBoardName, ); } else { this.setTitle( - t("board-filters-for") + " " + this.columnOrBoardName + t("board-filters-for") + " " + this.columnOrBoardName, ); } } @@ -51,7 +52,7 @@ export class ViewTaskFilterModal extends Modal { this.app, this.leafId, this.activeBoardIndex, - this.initialFilterState + this.initialFilterState, ); // Ensure the component is properly loaded this.taskFilterComponent.onload(); @@ -66,9 +67,10 @@ export class ViewTaskFilterModal extends Modal { filterState = this.taskFilterComponent.getFilterState(); this.taskFilterComponent.onunload(); } catch (error) { - console.error( - "Failed to get filter state before modal close", - error + bugReporterManagerInsatance.addToLogs( + 114, + String(error), + "ViewTaskFilterModal.ts/onClose", ); } } @@ -79,7 +81,11 @@ export class ViewTaskFilterModal extends Modal { try { this.filterCloseCallback(filterState); } catch (error) { - console.error("Error in filter close callback", error); + bugReporterManagerInsatance.addToLogs( + 115, + String(error), + "ViewTaskFilterModal.ts/onClose", + ); } } } diff --git a/src/components/BoardFilters/ViewTaskFilterPopover.ts b/src/components/BoardFilters/ViewTaskFilterPopover.ts index 17edc0c7..e54a688d 100644 --- a/src/components/BoardFilters/ViewTaskFilterPopover.ts +++ b/src/components/BoardFilters/ViewTaskFilterPopover.ts @@ -7,6 +7,7 @@ import type TaskBoard from "main"; import { t } from "src/utils/lang/helper"; import { RootFilterState } from "src/interfaces/BoardConfigs"; import { TaskFilterComponent } from "./ViewTaskFilter"; +import { bugReporterManagerInsatance } from "src/managers/BugReporter"; export class ViewTaskFilterPopover extends Component @@ -31,7 +32,7 @@ export class ViewTaskFilterPopover private leafId?: string | undefined, activeBoardIndex?: number, columnOrBoardName?: string, - initialFilterState?: RootFilterState + initialFilterState?: RootFilterState, ) { super(); this.plugin = plugin; @@ -90,7 +91,7 @@ export class ViewTaskFilterPopover this.app, this.leafId, this.activeBoardIndex, - this.initialFilterState + this.initialFilterState, ); // Ensure the component is properly loaded this.taskFilterComponent.onload(); @@ -151,7 +152,7 @@ export class ViewTaskFilterPopover }, }, ], - } + }, ); } @@ -221,7 +222,11 @@ export class ViewTaskFilterPopover try { filterState = this.taskFilterComponent.getFilterState(); } catch (error) { - console.error("Failed to get filter state before close", error); + bugReporterManagerInsatance.addToLogs( + 116, + String(error), + "ViewTaskFilterPopover.ts/close", + ); } } @@ -245,7 +250,11 @@ export class ViewTaskFilterPopover try { this.onClose(filterState); } catch (error) { - console.error("Error in onClose callback", error); + bugReporterManagerInsatance.addToLogs( + 117, + String(error), + "ViewTaskFilterPopover.ts/close", + ); } } } diff --git a/src/components/KanbanView/KanbanBoardView.tsx b/src/components/KanbanView/KanbanBoardView.tsx index ff2f81ce..d9dfe12c 100644 --- a/src/components/KanbanView/KanbanBoardView.tsx +++ b/src/components/KanbanView/KanbanBoardView.tsx @@ -5,7 +5,6 @@ import React, { memo } from "react"; import { taskItem, taskJsonMerged } from "src/interfaces/TaskItem"; import { App } from "obsidian"; -import Column from "./Column"; import LazyColumn from "./LazyColumn"; import KanbanSwimlanesContainer from "./KanbanSwimlanesContainer"; import type TaskBoard from "main"; @@ -24,7 +23,7 @@ interface KanbanBoardProps { const KanbanBoard: React.FC = ({ plugin, board, allTasks, tasksPerColumn, loading, freshInstall }) => { // Check if lazy loading is enabled const lazyLoadingEnabled = plugin.settings.data.globalSettings.kanbanView?.lazyLoadingEnabled ?? false; - const ColumnComponent = lazyLoadingEnabled ? LazyColumn : Column; + const ColumnComponent = LazyColumn; // lazyLoadingEnabled ? LazyColumn : Column; return (
@@ -86,7 +85,7 @@ const MemoizedColumn = memo<{ activeBoardData: Board; columnData: any; tasksForThisColumn: taskItem[]; - Component: typeof Column | typeof LazyColumn; + Component: typeof LazyColumn; }>(({ Component, ...props }) => { return ; }, (prevProps, nextProps) => { diff --git a/src/components/KanbanView/KanbanSwimlanesContainer.tsx b/src/components/KanbanView/KanbanSwimlanesContainer.tsx index e521d2ce..0d955996 100644 --- a/src/components/KanbanView/KanbanSwimlanesContainer.tsx +++ b/src/components/KanbanView/KanbanSwimlanesContainer.tsx @@ -3,12 +3,12 @@ import React, { useMemo, memo } from 'react'; import { Board, ColumnData } from 'src/interfaces/BoardConfigs'; import { taskItem, taskJsonMerged } from 'src/interfaces/TaskItem'; -import Column from './Column'; import LazyColumn from './LazyColumn'; import type TaskBoard from 'main'; import { t } from 'src/utils/lang/helper'; import { ChevronDown, ChevronLast, ChevronLeft, ChevronRight } from 'lucide-react'; import { eventEmitter } from 'src/services/EventEmitter'; +import { bugReporterManagerInsatance } from 'src/managers/BugReporter'; interface KanbanSwimlanesContainerProps { plugin: TaskBoard; @@ -32,7 +32,7 @@ const KanbanSwimlanesContainer: React.FC = ({ tasksPerColumn, lazyLoadingEnabled, }) => { - const ColumnComponent = lazyLoadingEnabled ? LazyColumn : Column; + const ColumnComponent = LazyColumn; // lazyLoadingEnabled ? LazyColumn : Column; // Extract and organize swimlanes using tasksPerColumn (already segregated per active column) const { @@ -240,7 +240,11 @@ const KanbanSwimlanesContainer: React.FC = ({ await plugin.saveSettings(); eventEmitter.emit('REFRESH_BOARD'); } catch (err) { - console.error('Error toggling swimlane minimize:', err); + bugReporterManagerInsatance.addToLogs( + 121, + String(err), + "KanbanSwimlanesContainer.tsx/handleSwimlaneMinimize", + ); } } @@ -453,7 +457,7 @@ const MemoizedSwimlanColumn = memo<{ activeBoardData: Board; columnData: ColumnData; tasksForThisColumn: taskItem[]; - Component: typeof Column | typeof LazyColumn; + Component: typeof LazyColumn; hideColumnHeader?: boolean; swimlaneData?: { property: string, value: string }; headerOnly?: boolean; diff --git a/src/components/KanbanView/LazyColumn.tsx b/src/components/KanbanView/LazyColumn.tsx index 7ed7d1db..dfea321f 100644 --- a/src/components/KanbanView/LazyColumn.tsx +++ b/src/components/KanbanView/LazyColumn.tsx @@ -539,7 +539,11 @@ const LazyColumn: React.FC = ({ dragDropTasksManagerInsatance.clearDesiredDropIndex(); // } } catch (error) { - console.error('Error handling task drop:', error); + bugReporterManagerInsatance.addToLogs( + 118, + String(error), + "Column.tsx/handleDrop", + ); } }, [columnData, plugin]); @@ -616,7 +620,11 @@ const LazyColumn: React.FC = ({ dragDropTasksManagerInsatance.handleCardDragOverEvent(e.nativeEvent as DragEvent, e.currentTarget as HTMLDivElement, targetColumnContainer, columnData); } } catch (error) { - console.error('Error computing insert index:', error); + bugReporterManagerInsatance.addToLogs( + 119, + String(error), + "Column.tsx/handleTaskItemDragOVer", + ); } }, [scheduleSetInsertIndex, columnData]); @@ -690,7 +698,11 @@ const LazyColumn: React.FC = ({ // const allowed = dragDropTasksManagerInsatance.isTaskDropAllowed(sourceColumnData, columnData); // e.dataTransfer!.dropEffect = allowed ? 'move' : 'none'; } catch (error) { - console.error('Error handling drag over:', error); + bugReporterManagerInsatance.addToLogs( + 120, + String(error), + "Column.tsx/handleDragOver", + ); } }, [columnData]); diff --git a/src/components/KanbanView/TaskItem.tsx b/src/components/KanbanView/TaskItem.tsx index fe0db024..835fb660 100644 --- a/src/components/KanbanView/TaskItem.tsx +++ b/src/components/KanbanView/TaskItem.tsx @@ -168,7 +168,11 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin hookMarkdownLinkMouseEventHandlers(plugin.app, plugin, el, task.filePath, task.filePath); } catch (err) { - console.error('Error rendering task title:', err); + bugReporterManagerInsatance.addToLogs( + 122, + String(err), + "TaskItem.tsx/Main title rendering useEffect", + ); } })(); @@ -248,7 +252,11 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin break; } } catch (err) { - console.error('Error rendering subtask:', err); + bugReporterManagerInsatance.addToLogs( + 123, + String(err), + "TaskItem.tsx/Sub-tasks rendering useEffect", + ); } } })(); @@ -458,7 +466,7 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin }; if (highestPriorityTag && getOpacityValue(highestPriorityTag.color) > 0.2) { - return updateRGBAOpacity(plugin, highestPriorityTag.color, 0.2); + return updateRGBAOpacity(highestPriorityTag.color, 0.2); } return highestPriorityTag?.color; @@ -512,7 +520,11 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin handleCheckboxChange(plugin, task); } } catch (error) { - console.error("Error updating task:", error); + bugReporterManagerInsatance.addToLogs( + 124, + String(error), + "TaskItem.tsx/handleMainCheckBoxClick", + ); } // The component might be unmounted by the time this runs, but this is a safeguard. @@ -626,7 +638,6 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin event.ctrlKey = false; } } catch (error) { - console.error("Error opening child task modal:", error); bugReporterManagerInsatance.showNotice(6, "Error opening child task modal", String(error), "TaskItem.tsx/handleOpenChildTaskModal"); } } @@ -805,11 +816,15 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin // Rename the file await plugin.app.vault.rename(file, newPath); - new Notice("contextMenus.task.notices.renameSuccess"); + new Notice("File renamed successfully."); } } catch (error) { - console.error("Error renaming file:", error); - new Notice(t("contextMenus.task.notices.renameFailure")); + new Notice("There was an error while renaming the file."); + bugReporterManagerInsatance.addToLogs( + 125, + String(error), + "TaskItem.tsx/handleMenuButtonClicked/renaming", + ); } }); }); @@ -818,96 +833,15 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin subItem.setIcon("trash"); subItem.setTitle(t("delete-note")); subItem.onClick(async () => { - // Show confirmation and delete - // const confirmed = await showConfirmationModal(plugin.app, { - // title: t("contextMenus.task.deleteTitle"), - // message: t("contextMenus.task.deleteMessage") + file.name, - // confirmText: t("contextMenus.task.deleteConfirm"), - // cancelText: t("common.cancel"), - // isDestructive: true, - // }); - // if (confirmed) { - // } - - plugin.app.vault.trash(file, true); + plugin.app.vault.trash(file, true).then(() => { + new Notice("File deleted successfully. Moved to system trash."); + }) // handleDeleteTask(plugin, task, true); }); }); - - // submenu.addSeparator(); - - // submenu.addItem((subItem: any) => { - // subItem.setTitle(t("contextMenus.task.copyPath")); - // subItem.setIcon("copy"); - // subItem.onClick(async () => { - // try { - // await navigator.clipboard.writeText(file.path); - // new Notice(t("contextMenus.task.notices.copyPathSuccess")); - // } catch (error) { - // new Notice(t("contextMenus.task.notices.copyFailure")); - // } - // }); - // }); - - // submenu.addItem((subItem: any) => { - // subItem.setTitle(t("contextMenus.task.copyUrl")); - // subItem.setIcon("link"); - // subItem.onClick(async () => { - // try { - // const url = `obsidian://open?vault=${encodeURIComponent(plugin.app.vault.getName())}&file=${encodeURIComponent(file.path)}`; - // await navigator.clipboard.writeText(url); - // new Notice(t("contextMenus.task.notices.copyUrlSuccess")); - // } catch (error) { - // new Notice(t("contextMenus.task.notices.copyFailure")); - // } - // }); - // }); - - // submenu.addSeparator(); - - // submenu.addItem((subItem: any) => { - // subItem.setTitle(t("contextMenus.task.showInExplorer")); - // subItem.setIcon("folder-open"); - // subItem.onClick(() => { - // // Reveal file in file explorer - // plugin.app.workspace - // .getLeaf() - // .setViewState({ - // type: "file-explorer", - // state: {}, - // }) - // .then(() => { - // // Focus the file in the explorer - // const fileExplorer = - // plugin.app.workspace.getLeavesOfType("file-explorer")[0]; - // if (fileExplorer?.view && "revealInFolder" in fileExplorer.view) { - // (fileExplorer.view as any).revealInFolder(file); - // } - // }); - // }); - // }); } }); - // // Show minimize or maximize option based on current state - // if (columnData.minimized) { - // taskItemMenu.addItem((item) => { - // item.setTitle(t("maximize-column")); - // item.setIcon("panel-left-open"); - // item.onClick(async () => { - // await handleMinimizeColumn(); - // }); - // }); - // } else { - // taskItemMenu.addItem((item) => { - // item.setTitle(t("minimize-column")); - // item.setIcon("panel-left-close"); - // item.onClick(async () => { - // await handleMinimizeColumn(); - // }); - // }); - // } - // Use native event if available (React event has nativeEvent property) taskItemMenu.showAtMouseEvent( (event instanceof MouseEvent ? event : event.nativeEvent) @@ -919,7 +853,7 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin console.log("TaskItem : handleDragStart..."); if (!columnData) { e.preventDefault(); - console.warn('handleDragStart: columnData is undefined'); + bugReporterManagerInsatance.addToLogs(91, `Column data : undefined`, "TaskItem.tsx/handleDragStart"); return; } @@ -960,13 +894,15 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin // e.dataTransfer.setData('application/json', JSON.stringify({ task, sourceColumnData: columnData })); // e.dataTransfer.effectAllowed = 'move'; // } catch (ex) {/* ignore */ } - - console.error(err); + bugReporterManagerInsatance.addToLogs( + 126, + String(err), + "TaskItem.tsx/handleDragStart", + ); } }, [task, columnData]); const handleDragEnd = useCallback(() => { - console.log("TaskItem : handleDragEnd..."); setIsDragging(false); // Remove dim effect from this dragged task and clear manager state @@ -1001,8 +937,8 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin const tagName = tag.replace('#', ''); const customTag = plugin.settings.data.globalSettings.tagColorsType === "text" ? plugin.settings.data.globalSettings.tagColors.find(t => t.name === tagName) : undefined; const tagColor = customTag?.color || `var(--tag-color)`; - const backgroundColor = customTag ? updateRGBAOpacity(plugin, customTag.color, 0.1) : `var(--tag-background)`; // 10% opacity background - const borderColor = customTag ? updateRGBAOpacity(plugin, customTag.color, 0.5) : `var(--tag-color-hover)`; + const backgroundColor = customTag ? updateRGBAOpacity(customTag.color, 0.1) : `var(--tag-background)`; // 10% opacity background + const borderColor = customTag ? updateRGBAOpacity(customTag.color, 0.5) : `var(--tag-color-hover)`; // If columnIndex is defined, proceed to get the column if ( @@ -1059,8 +995,7 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin
); } catch (error) { - // bugReporterManagerInsatance.showNotice(7, "Error while rendering task header", error as string, "TaskItem.tsx/renderHeader"); - console.warn("TaskItem.tsx/renderHeader : Error while rendering task header", error); + bugReporterManagerInsatance.addToLogs(7, error as string, "TaskItem.tsx/renderHeader"); return null; } }; @@ -1159,8 +1094,7 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin ); } catch (error) { - // bugReporterManagerInsatance.showNotice(8, "Error while rendering sub-tasks", error as string, "TaskItem.tsx/renderSubTasks"); - console.warn("TaskItem.tsx/renderSubTasks : Error while rendering sub-tasks", error); + bugReporterManagerInsatance.addToLogs(8, error as string, "TaskItem.tsx/renderSubTasks"); return null; } }; @@ -1237,8 +1171,7 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin ); } catch (error) { - // bugReporterManagerInsatance.showNotice(9, "Error while rendering task footer", error as string, "TaskItem.tsx/renderFooter"); - console.warn("TaskItem.tsx/renderFooter : Error while rendering task footer", error); + bugReporterManagerInsatance.addToLogs(9, error as string, "TaskItem.tsx/renderFooter"); return null; } }; @@ -1300,8 +1233,7 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin return null; } } catch (error) { - // bugReporterManagerInsatance.showNotice(10, "Error while rendering child-tasks", error as string, "TaskItem.tsx/renderChildTasks"); - console.warn("TaskItem.tsx/renderChildTasks : Error while rendering child-tasks", error); + bugReporterManagerInsatance.addToLogs(10, error as string, "TaskItem.tsx/renderChildTasks"); return null; } }; diff --git a/src/components/KanbanView/TaskItemV2.tsx b/src/components/KanbanView/TaskItemV2.tsx index 15a54b73..0e116d69 100644 --- a/src/components/KanbanView/TaskItemV2.tsx +++ b/src/components/KanbanView/TaskItemV2.tsx @@ -173,7 +173,11 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act hookMarkdownLinkMouseEventHandlers(plugin.app, plugin, el, task.filePath, task.filePath); } catch (err) { - console.error('Error rendering task title:', err); + bugReporterManagerInsatance.addToLogs( + 122, + String(err), + "TaskItemV2.tsx/Main title rendering useEffect", + ); } })(); @@ -253,7 +257,11 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act break; } } catch (err) { - console.error('Error rendering subtask:', err); + bugReporterManagerInsatance.addToLogs( + 123, + String(err), + "TaskItemV2.tsx/Sub-tasks rendering useEffect", + ); } } })(); @@ -463,7 +471,7 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act }; if (highestPriorityTag && getOpacityValue(highestPriorityTag.color) > 0.2) { - return updateRGBAOpacity(plugin, highestPriorityTag.color, 0.2); + return updateRGBAOpacity(highestPriorityTag.color, 0.2); } return highestPriorityTag?.color; @@ -517,7 +525,11 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act handleCheckboxChange(plugin, task); } } catch (error) { - console.error("Error updating task:", error); + bugReporterManagerInsatance.addToLogs( + 124, + String(error), + "TaskItemV2.tsx/handleMainCheckBoxClick", + ); } // The component might be unmounted by the time this runs, but this is a safeguard. @@ -631,7 +643,6 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act event.ctrlKey = false; } } catch (error) { - console.error("Error opening child task modal:", error); bugReporterManagerInsatance.showNotice(12, "Error opening child task modal", String(error), "TaskItem.tsx/handleOpenChildTaskModal"); } } @@ -810,11 +821,15 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act // Rename the file await plugin.app.vault.rename(file, newPath); - new Notice("contextMenus.task.notices.renameSuccess"); + new Notice("File renamed successfully."); } } catch (error) { - console.error("Error renaming file:", error); - new Notice(t("contextMenus.task.notices.renameFailure")); + new Notice("There was an error while renaming the file."); + bugReporterManagerInsatance.addToLogs( + 125, + String(error), + "TaskItem.tsx/handleMenuButtonClicked/renaming", + ); } }); }); @@ -823,96 +838,15 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act subItem.setIcon("trash"); subItem.setTitle(t("delete-note")); subItem.onClick(async () => { - // Show confirmation and delete - // const confirmed = await showConfirmationModal(plugin.app, { - // title: t("contextMenus.task.deleteTitle"), - // message: t("contextMenus.task.deleteMessage") + file.name, - // confirmText: t("contextMenus.task.deleteConfirm"), - // cancelText: t("common.cancel"), - // isDestructive: true, - // }); - // if (confirmed) { - // } - - plugin.app.vault.trash(file, true); + plugin.app.vault.trash(file, true).then(() => { + new Notice("File deleted successfully. Moved to system trash."); + }) // handleDeleteTask(plugin, task, true); }); }); - - // submenu.addSeparator(); - - // submenu.addItem((subItem: any) => { - // subItem.setTitle(t("contextMenus.task.copyPath")); - // subItem.setIcon("copy"); - // subItem.onClick(async () => { - // try { - // await navigator.clipboard.writeText(file.path); - // new Notice(t("contextMenus.task.notices.copyPathSuccess")); - // } catch (error) { - // new Notice(t("contextMenus.task.notices.copyFailure")); - // } - // }); - // }); - - // submenu.addItem((subItem: any) => { - // subItem.setTitle(t("contextMenus.task.copyUrl")); - // subItem.setIcon("link"); - // subItem.onClick(async () => { - // try { - // const url = `obsidian://open?vault=${encodeURIComponent(plugin.app.vault.getName())}&file=${encodeURIComponent(file.path)}`; - // await navigator.clipboard.writeText(url); - // new Notice(t("contextMenus.task.notices.copyUrlSuccess")); - // } catch (error) { - // new Notice(t("contextMenus.task.notices.copyFailure")); - // } - // }); - // }); - - // submenu.addSeparator(); - - // submenu.addItem((subItem: any) => { - // subItem.setTitle(t("contextMenus.task.showInExplorer")); - // subItem.setIcon("folder-open"); - // subItem.onClick(() => { - // // Reveal file in file explorer - // plugin.app.workspace - // .getLeaf() - // .setViewState({ - // type: "file-explorer", - // state: {}, - // }) - // .then(() => { - // // Focus the file in the explorer - // const fileExplorer = - // plugin.app.workspace.getLeavesOfType("file-explorer")[0]; - // if (fileExplorer?.view && "revealInFolder" in fileExplorer.view) { - // (fileExplorer.view as any).revealInFolder(file); - // } - // }); - // }); - // }); } }); - // // Show minimize or maximize option based on current state - // if (columnData.minimized) { - // taskItemMenu.addItem((item) => { - // item.setTitle(t("maximize-column")); - // item.setIcon("panel-left-open"); - // item.onClick(async () => { - // await handleMinimizeColumn(); - // }); - // }); - // } else { - // taskItemMenu.addItem((item) => { - // item.setTitle(t("minimize-column")); - // item.setIcon("panel-left-close"); - // item.onClick(async () => { - // await handleMinimizeColumn(); - // }); - // }); - // } - // Use native event if available (React event has nativeEvent property) taskItemMenu.showAtMouseEvent( (event instanceof MouseEvent ? event : event.nativeEvent) @@ -924,7 +858,7 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act console.log("TaskItem : handleDragStart..."); if (!columnData) { e.preventDefault(); - console.warn('handleDragStart: columnData is undefined'); + bugReporterManagerInsatance.addToLogs(91, `Column data : undefined`, "TaskItem.tsx/handleDragStart"); return; } @@ -965,13 +899,15 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act // e.dataTransfer.setData('application/json', JSON.stringify({ task, sourceColumnData: columnData })); // e.dataTransfer.effectAllowed = 'move'; // } catch (ex) {/* ignore */ } - - console.error(err); + bugReporterManagerInsatance.addToLogs( + 126, + String(err), + "TaskItem.tsx/handleDragStart", + ); } }, [task, columnData]); const handleDragEnd = useCallback(() => { - console.log("TaskItem : handleDragEnd..."); setIsDragging(false); // Remove dim effect from this dragged task and clear manager state @@ -1008,9 +944,9 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act {task.tags.map((tag: string) => { const tagName = tag.replace('#', ''); const customTag = plugin.settings.data.globalSettings.tagColorsType === "text" ? plugin.settings.data.globalSettings.tagColors.find(t => t.name === tagName) : undefined; - const tagColor = customTag?.color || null; - // const backgroundColor = customTag ? updateRGBAOpacity(plugin, customTag.color, 0.1) : `var(--tag-background)`; // 10% opacity background - // const borderColor = customTag ? updateRGBAOpacity(plugin, customTag.color, 0.5) : `var(--tag-color-hover)`; + const tagColor = customTag?.color || `var(--tag-color)`; + const backgroundColor = customTag ? updateRGBAOpacity(customTag.color, 0.1) : `var(--tag-background)`; // 10% opacity background + const borderColor = customTag ? updateRGBAOpacity(customTag.color, 0.5) : `var(--tag-color-hover)`; // If columnIndex is defined, proceed to get the column if ( @@ -1029,11 +965,9 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act key={tagKey} className="taskItemTag" style={{ - color: `${tagColor ? 'white' : ''}`, - backgroundColor: `${tagColor ? tagColor : ''}`, - opacity: `${tagColor ? '0.8' : ''}` + color: tagColor, // border: `1px solid ${borderColor}`, - // backgroundColor: backgroundColor + backgroundColor: backgroundColor }} > {tag} @@ -1069,8 +1003,7 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act
); } catch (error) { - // bugReporterManagerInsatance.showNotice(13, "Error while rendering task header", error as string, "TaskItem.tsx/renderHeader"); - console.warn("TaskItem.tsx/renderHeader : Error while rendering task header", error); + bugReporterManagerInsatance.addToLogs(13, error as string, "TaskItemV2.tsx/renderHeader"); return null; } }; @@ -1170,8 +1103,7 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act ); } catch (error) { - // bugReporterManagerInsatance.showNotice(14, "Error while rendering sub-tasks", error as string, "TaskItem.tsx/renderSubTasks"); - console.warn("TaskItem.tsx/renderSubTasks : Error while rendering sub-tasks", error); + bugReporterManagerInsatance.addToLogs(14, error as string, "TaskItemV2.tsx/renderSubTasks"); return null; } }; @@ -1253,8 +1185,7 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act ); } catch (error) { - // bugReporterManagerInsatance.showNotice(15, "Error while rendering task footer", error as string, "TaskItem.tsx/renderFooter"); - console.warn("TaskItem.tsx/renderFooter : Error while rendering task footer", error); + bugReporterManagerInsatance.addToLogs(15, error as string, "TaskItemV2.tsx/renderFooter"); return null; } }; @@ -1316,8 +1247,7 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act return null; } } catch (error) { - // bugReporterManagerInsatance.showNotice(16, "Error while rendering child-tasks", error as string, "TaskItem.tsx/renderChildTasks"); - console.warn("TaskItem.tsx/renderChildTasks : Error while rendering child-tasks", error); + bugReporterManagerInsatance.addToLogs(16, error as string, "TaskItemV2.tsx/renderChildTasks"); return null; } }; diff --git a/src/components/MapView/MapView.tsx b/src/components/MapView/MapView.tsx index bc73a5ad..89e82030 100644 --- a/src/components/MapView/MapView.tsx +++ b/src/components/MapView/MapView.tsx @@ -116,7 +116,7 @@ const MapView: React.FC = ({ } } } catch (error) { - console.warn('Failed to load node positions from localStorage:', error); + bugReporterManagerInsatance.addToLogs(92, String(error), 'MapView.tsx/loadPositions'); allBoardPositions = {}; } @@ -127,7 +127,7 @@ const MapView: React.FC = ({ } return {}; } catch (error) { - console.warn('Failed to get positions for board', activeBoardIndex, ':', error); + bugReporterManagerInsatance.addToLogs(93, String(error), 'MapView.tsx/loadPositions'); return {}; } }; @@ -144,7 +144,7 @@ const MapView: React.FC = ({ } return {}; } catch (error) { - console.warn('Failed to load node sizes from localStorage:', error); + bugReporterManagerInsatance.addToLogs(94, String(error), 'MapView.tsx/loadNodeSizes'); return {}; } }; @@ -161,7 +161,7 @@ const MapView: React.FC = ({ } return { [activeBoardIndex]: { x: 10, y: 10, zoom: 1.5 } }; } catch (error) { - console.warn('Failed to load viewport from localStorage:', error); + bugReporterManagerInsatance.addToLogs(95, String(error), 'MapView.tsx/loadViewport'); return { [activeBoardIndex]: { x: 10, y: 10, zoom: 1.5 } }; } }; @@ -240,7 +240,8 @@ const MapView: React.FC = ({ return parsed; } } catch (e) { - console.warn('Error parsing columnWidth:', e); + bugReporterManagerInsatance.addToLogs(96, String(e), 'MapView.tsx/getDefaultWidth'); + } return 300; // Fallback default width }; @@ -253,7 +254,7 @@ const MapView: React.FC = ({ if (task.legacyId) { const id = task.legacyId; if (usedIds.has(id)) { - console.warn('Duplicate node id detected:', id, "\nTitle : ", task.title); + // console.warn('Duplicate node id detected:', id, "\nTitle : ", task.title); duplicateIds.add(id); return; // Skip duplicate } @@ -301,7 +302,7 @@ const MapView: React.FC = ({ if (duplicateIds.size > 0) { const stringOfListOfDuplicateIds = Array.from(duplicateIds).join(','); - // bugReporterManagerInsatance.showNotice(17, `Following duplicate IDs has been found for tasks : "${stringOfListOfDuplicateIds}" detected in Map View. This may cause unexpected behavior. Please consider changing the IDs of these tasks.`, "ERROR: Same id is present on two tasks", "MapView.tsx/initialNodes"); + bugReporterManagerInsatance.showNotice(17, `Following duplicate IDs has been found for tasks with IDs: "${stringOfListOfDuplicateIds}". This may cause unexpected behavior. Please consider changing the IDs of these tasks.`, "ERROR: Same id is present on two tasks", "MapView.tsx/initialNodes"); duplicateIds.clear(); } @@ -456,7 +457,7 @@ const MapView: React.FC = ({ } } } catch (error) { - console.warn('Failed to load existing positions:', error); + bugReporterManagerInsatance.addToLogs(97, String(error), 'MapView.tsx/handleNodePositionChange'); allBoardPositions = {}; } @@ -474,7 +475,7 @@ const MapView: React.FC = ({ try { localStorage.setItem(NODE_POSITIONS_STORAGE_KEY, JSON.stringify(allBoardPositions)); } catch (error) { - console.warn('Failed to save node positions:', error); + bugReporterManagerInsatance.addToLogs(98, String(error), 'MapView.tsx/handleNodePositionChange'); } }; @@ -645,7 +646,8 @@ const MapView: React.FC = ({ localStorage.setItem(VIEWPORT_STORAGE_KEY, JSON.stringify(parsed)); lastViewportSaveTime.current = now; } catch (error) { - console.warn('Failed to save viewport:', error); + bugReporterManagerInsatance.addToLogs(99, String(error), 'MapView.tsx/debouncedSetViewportStorage'); + } } }, 2000); diff --git a/src/components/MapView/ResizableNodeSelected.tsx b/src/components/MapView/ResizableNodeSelected.tsx index 61f2bbc8..89113c05 100644 --- a/src/components/MapView/ResizableNodeSelected.tsx +++ b/src/components/MapView/ResizableNodeSelected.tsx @@ -6,6 +6,7 @@ import type TaskBoard from 'main'; import { mapViewNodeMapOrientation } from 'src/interfaces/Enums'; import { CircleArrowDownIcon, CircleArrowRightIcon } from 'lucide-react'; import { t } from 'src/utils/lang/helper'; +import { bugReporterManagerInsatance } from 'src/managers/BugReporter'; interface dataProps extends React.ReactElement { props: { plugin: TaskBoard }; @@ -41,7 +42,11 @@ const ResizableNodeSelected: FC = ({ id, }; localStorage.setItem(NODE_SIZE_STORAGE_KEY, JSON.stringify(sizeData)); } catch (e) { - console.error('Failed to update node size in localStorage:', e); + bugReporterManagerInsatance.addToLogs( + 127, + String(e), + "ResizableNodeSelected.tsx/return()", + ); } }} /> diff --git a/src/components/MapView/TasksImporterPanel.tsx b/src/components/MapView/TasksImporterPanel.tsx index 4915df9b..2450f5b8 100644 --- a/src/components/MapView/TasksImporterPanel.tsx +++ b/src/components/MapView/TasksImporterPanel.tsx @@ -9,6 +9,7 @@ import { Board } from 'src/interfaces/BoardConfigs'; import { t } from 'src/utils/lang/helper'; import { eventEmitter } from 'src/services/EventEmitter'; import { applyIdToTaskItem } from 'src/utils/TaskItemUtils'; +import { bugReporterManagerInsatance } from 'src/managers/BugReporter'; interface TasksImporterPanelProps { plugin: TaskBoard; @@ -62,7 +63,11 @@ export const TasksImporterPanel: React.FC = ({ }) } } catch (error) { - console.error('Error importing task:', error); + bugReporterManagerInsatance.addToLogs( + 128, + String(error), + "TasksImporterPanel.tsx/handleImportTask", + ); } }; diff --git a/src/components/TaskBoardViewContent.tsx b/src/components/TaskBoardViewContent.tsx index d59642b0..cd8a1070 100644 --- a/src/components/TaskBoardViewContent.tsx +++ b/src/components/TaskBoardViewContent.tsx @@ -95,9 +95,10 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs setFreshInstall(false); } } catch (error) { - console.error( - "Error loading tasks cache from disk\nIf this is appearing on a fresh install then no need to worry.\n", - error + bugReporterManagerInsatance.addToLogs( + 131, + `No need to worry about this bug, if its appearing on the fresh install.\n${String(error)}`, + "TaskBoardViewContent.tsx/loading boards and tasks useEffect", ); setFreshInstall(true); } diff --git a/src/managers/BugReporter.ts b/src/managers/BugReporter.ts index d08c7e55..90723496 100644 --- a/src/managers/BugReporter.ts +++ b/src/managers/BugReporter.ts @@ -4,6 +4,7 @@ import { Notice, TFolder } from "obsidian"; import { BugReporterModal } from "src/modals/BugReporterModal"; import { fsPromises } from "src/services/FileSystem"; import { getObsidianDebugInfo } from "src/services/ObsidianDebugInfo"; +import { getCurrentLocalTimeString } from "src/utils/DateTimeCalculations"; /** * Interface for bug report entries @@ -28,7 +29,8 @@ class BugReporterManager { private plugin: TaskBoard | null = null; private alreadyShownBugsIDs: number[] = []; private LOG_FILE_PATH = ""; - private readonly MAX_RECENT_LOGS = 10; + private readonly MAX_RECENT_LOGS = 20; + private readonly MAX_USED_ID = 165; // This constant will not be used anywhere, its simply to keep track of the the recent ID used. private constructor() { // Private constructor to enforce singleton pattern @@ -58,13 +60,6 @@ class BugReporterManager { this.LOG_FILE_PATH = `${plugin.app.vault.configDir}/plugins/task-board/task-board-logs.log`; } - /** - * Get current timestamp in ISO format - */ - private getCurrentTimestamp(): string { - return new Date().toISOString(); - } - /** * Ensure the log file exists with proper structure */ @@ -80,13 +75,13 @@ class BugReporterManager { if (!existingContent) { // Create new log file with system info const systemInfo = await getObsidianDebugInfo( - this.plugin!.app + this.plugin!.app, ); const systemInfoText = this.formatSystemInfo(systemInfo); const initialContent = `# Task Board Logs\n\n## System Information\n\n${systemInfoText}\n\n## Recent Bug Reports\n\n`; await vault.adapter.write( this.LOG_FILE_PATH, - initialContent + initialContent, ); } } @@ -123,13 +118,13 @@ class BugReporterManager { // Find the "Recent Bug Reports" section const recentBugsSectionStart = content.indexOf( - "## Recent Bug Reports" + "## Recent Bug Reports", ); if (recentBugsSectionStart === -1) return []; // Extract content after "## Recent Bug Reports" const contentAfterHeader = content.substring( - recentBugsSectionStart + "## Recent Bug Reports".length + recentBugsSectionStart + "## Recent Bug Reports".length, ); // Split by the separator line @@ -163,7 +158,7 @@ class BugReporterManager { // Extract each field with more flexible matching const timestampMatch = entryText.match( - /Timestamp\s*:\s*(.+?)(?=\n|$)/ + /Timestamp\s*:\s*(.+?)(?=\n|$)/, ); if (timestampMatch) { entry.timestamp = timestampMatch[1].trim(); @@ -175,14 +170,14 @@ class BugReporterManager { } const messageMatch = entryText.match( - /Message\s*:\s*(.+?)(?=\nContext|$)/ + /Message\s*:\s*(.+?)(?=\nContext|$)/, ); if (messageMatch) { entry.message = messageMatch[1].trim(); } const contextMatch = entryText.match( - /Context\s*:\s*([\s\S]*?)(?=\n#### Bug Content|\n\n|$)/ + /Context\s*:\s*([\s\S]*?)(?=\n#### Bug Content|\n\n|$)/, ); if (contextMatch) { entry.context = contextMatch[1].trim(); @@ -234,7 +229,7 @@ ${entry.bugContent} id: number, message: string, bugContent: string, - context: string + context: string, ): Promise { try { const vault = this.plugin?.app.vault; @@ -247,7 +242,7 @@ ${entry.bugContent} // Create new bug report entry const newEntry: BugReportEntry = { - timestamp: this.getCurrentTimestamp(), + timestamp: getCurrentLocalTimeString(), id, message, context, @@ -268,7 +263,7 @@ ${entry.bugContent} // Read current file to extract system info const currentContent = await vault.adapter.read(this.LOG_FILE_PATH); const systemInfoMatch = currentContent.match( - /(## System Information\n\n[\s\S]*?)(?=## Recent Bug Reports)/ + /(## System Information\n\n[\s\S]*?)(?=## Recent Bug Reports)/, ); const systemInfoSection = systemInfoMatch ? systemInfoMatch[1].trim() @@ -290,7 +285,7 @@ ${entry.bugContent} id: number, message: string, bugContent: string, - context: string + context: string, ) => { // STEP 1 - Check if this type of bug, based on the id, is already visible to the user or not if (this.alreadyShownBugsIDs.includes(id)) { @@ -316,7 +311,7 @@ ${entry.bugContent} this.plugin!, message, bugContent, - context + context, ); bugReportModal.open(); el.hide(); @@ -331,7 +326,7 @@ ${entry.bugContent} }); }); }), - 0 + 0, ); bugReportNotice.messageEl.onClickEvent((e) => { @@ -346,6 +341,27 @@ ${entry.bugContent} this.appendBugReport(id, message, bugContent, context); }; + /** + * Appends a new bug report at the end of the log file. + */ + addToLogs = ( + id: number, + bugContent: string, + context: string, + ) => { + // STEP 1 - Check if this type of bug, based on the id, is already visible to the user or not + if (this.alreadyShownBugsIDs.includes(id)) { + // Bug already shown, don't show again + return; + } + + // STEP 2 - Store the ID to prevent showing the same error again + this.alreadyShownBugsIDs.push(id); + + // STEP 3 - Append the bug report to the task-board-logs.md file + this.appendBugReport(id, "", bugContent, context); + }; + async exportLogFile(): Promise { try { const vault = this.plugin?.app.vault; @@ -379,15 +395,15 @@ ${entry.bugContent} folderPath.endsWith("/") || folderPath.endsWith("\\") ? folderPath + exportFileName : folderPath + - (folderPath.includes("/") ? "/" : "\\") + - exportFileName; + (folderPath.includes("/") ? "/" : "\\") + + exportFileName; await fsPromises.writeFile(exportPath, data, "utf8"); new Notice(`Log file exported to ${exportPath}`); } else { // Web: use file save dialog let a = document.createElement("a"); a.href = URL.createObjectURL( - new Blob([data], { type: "application/json" }) + new Blob([data], { type: "application/json" }), ); a.download = exportFileName; document.body.appendChild(a); @@ -397,7 +413,7 @@ ${entry.bugContent} URL.revokeObjectURL(a.href); }, 1000); new Notice( - "Log file exported. Check the folder where you downloaded the file." + "Log file exported. Check the folder where you downloaded the file.", ); } } catch (err) { diff --git a/src/managers/DragDropTasksManager.ts b/src/managers/DragDropTasksManager.ts index c8aae7dc..ac7e49e2 100644 --- a/src/managers/DragDropTasksManager.ts +++ b/src/managers/DragDropTasksManager.ts @@ -14,7 +14,6 @@ import { import { eventEmitter } from "src/services/EventEmitter"; import { swimlaneDataProp } from "src/components/KanbanView/TaskItem"; import { - getStatusNameFromStatusSymbol, isTaskNotePresentInTags, updateFrontmatterInMarkdownFile, } from "src/utils/taskNote/TaskNoteUtils"; @@ -131,14 +130,14 @@ class DragDropTasksManager { sourceColumnData: ColumnData, targetColumnData: ColumnData, sourceColumnSwimlaneData: swimlaneDataProp | null | undefined, - targetColumnSwimlaneData: swimlaneDataProp | null | undefined + targetColumnSwimlaneData: swimlaneDataProp | null | undefined, ): Promise => { // This means, user either wants to change the order of the taskItems within the column or is changing the swimlanes. this.handleTasksOrderChange( this.plugin!, currentDragData, sourceColumnData, - this.desiredDropIndex + this.desiredDropIndex, ); if ( @@ -152,7 +151,7 @@ class DragDropTasksManager { newTask, sourceColumnSwimlaneData, targetColumnSwimlaneData, - plugin.settings.data.globalSettings + plugin.settings.data.globalSettings, ); eventEmitter.emit("UPDATE_TASK", { taskID: oldTask.id, @@ -161,7 +160,7 @@ class DragDropTasksManager { const isThisTaskNote = isTaskNotePresentInTags( plugin.settings.data.globalSettings.taskNoteIdentifierTag, - oldTask.tags + oldTask.tags, ); if (isThisTaskNote) { @@ -169,7 +168,7 @@ class DragDropTasksManager { sleep(1000).then(() => { plugin.realTimeScanner.processAllUpdatedFiles( oldTask.filePath, - oldTask.id + oldTask.id, ); }); }); @@ -177,13 +176,13 @@ class DragDropTasksManager { newTask.title = sanitizeTags( newTask.title, oldTask.tags, - newTask.tags + newTask.tags, ); console.log("Sanitized title after tag update:", newTask.title); updateTaskInFile(plugin, newTask, oldTask).then(() => { plugin.realTimeScanner.processAllUpdatedFiles( oldTask.filePath, - oldTask.id + oldTask.id, ); }); } @@ -210,14 +209,16 @@ class DragDropTasksManager { sourceColumn: ColumnData, targetColumn: ColumnData, sourceColumnSwimlaneData: swimlaneDataProp | null | undefined, - targetColumnSwimlaneData: swimlaneDataProp | null | undefined + targetColumnSwimlaneData: swimlaneDataProp | null | undefined, ): Promise => { if ( sourceColumn.coltag == undefined || targetColumn.coltag == undefined ) { - console.error( - "handleTaskMove_namedTag_to_namedTag: coltag undefined" + bugReporterManagerInsatance.addToLogs( + 132, + `coltag of either source or target column is undefined.\nSource=${sourceColumn.coltag}\nTarget=${targetColumn.coltag}`, + "DragDropTasksManager.ts/handleTaskMove_namedTag_to_namedTag", ); return; } @@ -233,7 +234,7 @@ class DragDropTasksManager { plugin, currentDragData, targetColumn, - this.desiredDropIndex + this.desiredDropIndex, ); // ----------------------------------------------- @@ -244,7 +245,7 @@ class DragDropTasksManager { newTask, sourceColumnSwimlaneData, targetColumnSwimlaneData, - plugin.settings.data.globalSettings + plugin.settings.data.globalSettings, ); console.log("newTask after swimlane change:", newTask); } @@ -258,7 +259,7 @@ class DragDropTasksManager { let newTags = newTask.tags.filter( (tag: string) => tag.replace("#", "").toLowerCase() !== - sourceTag.replace("#", "").toLowerCase() + sourceTag.replace("#", "").toLowerCase(), ); // Add the target column tag if it doesn't exist @@ -278,7 +279,7 @@ class DragDropTasksManager { console.log( "handleTaskMove_namedTag_to_namedTag...\nnewTask=", - newTask + newTask, ); // ----------------------------------------------- @@ -334,14 +335,13 @@ class DragDropTasksManager { sourceColumn: ColumnData, targetColumn: ColumnData, sourceColumnSwimlaneData: swimlaneDataProp | null | undefined, - targetColumnSwimlaneData: swimlaneDataProp | null | undefined + targetColumnSwimlaneData: swimlaneDataProp | null | undefined, ): Promise => { if (!currentDragData || !targetColumn.datedBasedColumn) { - console.error( - "No current drag data available for reordering : ", - JSON.stringify(currentDragData), - "\nOr the target column data : ", - JSON.stringify(targetColumn) + bugReporterManagerInsatance.addToLogs( + 133, + `No current drag data available for reordering.\ncurrentDragData=${JSON.stringify(currentDragData)}\ntargetColumn=${JSON.stringify(targetColumn)}`, + "DragDropTasksManager.ts/handleTaskMove_dated_to_dated", ); return; } @@ -355,7 +355,7 @@ class DragDropTasksManager { plugin, currentDragData, targetColumn, - this.desiredDropIndex + this.desiredDropIndex, ); if (sourceColumnSwimlaneData && targetColumnSwimlaneData) { @@ -363,7 +363,7 @@ class DragDropTasksManager { newTask, sourceColumnSwimlaneData, targetColumnSwimlaneData, - plugin.settings.data.globalSettings + plugin.settings.data.globalSettings, ); console.log("newTask after swimlane change:", newTask); } @@ -385,7 +385,7 @@ class DragDropTasksManager { const newDateValue = getAllDatesInRelativeRange( targetColumn.datedBasedColumn?.from, - targetColumn.datedBasedColumn?.to + targetColumn.datedBasedColumn?.to, )[0]; // newTask[dateType] = newDateValue; @@ -419,7 +419,7 @@ class DragDropTasksManager { 30, "The column configurations are currupted. Configurations are not valid for this operation. Kindly verify the column configuration in which you just dropped the task.", `Column configuration : ${JSON.stringify(targetColumn)}`, - "DragDropTasksManager.ts/handleTaskMove_dated_to_dated" + "DragDropTasksManager.ts/handleTaskMove_dated_to_dated", ); } }; @@ -439,19 +439,19 @@ class DragDropTasksManager { sourceColumn: ColumnData, targetColumn: ColumnData, sourceColumnSwimlaneData: swimlaneDataProp | null | undefined, - targetColumnSwimlaneData: swimlaneDataProp | null | undefined + targetColumnSwimlaneData: swimlaneDataProp | null | undefined, ): Promise => { if (!targetColumn.taskPriority) { - console.error( - "The priority value not found in the target column configuration : ", - JSON.stringify(targetColumn) + bugReporterManagerInsatance.addToLogs( + 134, + `The priority value not found in the target column configuration.\ntargetColumn=${JSON.stringify(targetColumn)}`, + "DragDropTasksManager.ts/handleTaskMove_priority_to_priority", ); return; } - const { updateTaskItemPriority } = await import( - "src/utils/UserTaskEvents" - ); + const { updateTaskItemPriority } = + await import("src/utils/UserTaskEvents"); const oldTask = currentDragData.task; let newTask = { ...oldTask } as taskItem; @@ -460,7 +460,7 @@ class DragDropTasksManager { plugin, currentDragData, targetColumn, - this.desiredDropIndex + this.desiredDropIndex, ); if (sourceColumnSwimlaneData && targetColumnSwimlaneData) { @@ -468,7 +468,7 @@ class DragDropTasksManager { newTask, sourceColumnSwimlaneData, targetColumnSwimlaneData, - plugin.settings.data.globalSettings + plugin.settings.data.globalSettings, ); console.log("newTask after swimlane change:", newTask); } @@ -494,19 +494,19 @@ class DragDropTasksManager { sourceColumn: ColumnData, targetColumn: ColumnData, sourceColumnSwimlaneData: swimlaneDataProp | null | undefined, - targetColumnSwimlaneData: swimlaneDataProp | null | undefined + targetColumnSwimlaneData: swimlaneDataProp | null | undefined, ): Promise => { if (!targetColumn.taskStatus) { - console.error( - "The status value not found in the target column configuration : ", - JSON.stringify(targetColumn) + bugReporterManagerInsatance.addToLogs( + 135, + `The status value not found in the target column configuration.\ntargetColumn=${JSON.stringify(targetColumn)}`, + "DragDropTasksManager.ts/handleTaskMove_status_to_status", ); return; } - const { updateTaskItemStatus } = await import( - "src/utils/UserTaskEvents" - ); + const { updateTaskItemStatus } = + await import("src/utils/UserTaskEvents"); const oldTask = currentDragData.task; let newTask = { ...oldTask } as taskItem; @@ -515,7 +515,7 @@ class DragDropTasksManager { plugin, currentDragData, targetColumn, - this.desiredDropIndex + this.desiredDropIndex, ); if (sourceColumnSwimlaneData && targetColumnSwimlaneData) { @@ -523,7 +523,7 @@ class DragDropTasksManager { newTask, sourceColumnSwimlaneData, targetColumnSwimlaneData, - plugin.settings.data.globalSettings + plugin.settings.data.globalSettings, ); console.log("newTask after swimlane change:", newTask); } @@ -537,7 +537,7 @@ class DragDropTasksManager { handleTaskMove_DONE_to_TODO = ( plugin: TaskBoard, - task: taskItem + task: taskItem, ): taskItem => { const newTitle = task.title; let newTask: taskItem = { @@ -549,14 +549,14 @@ class DragDropTasksManager { if ( !isTaskNotePresentInTags( plugin.settings.data.globalSettings.taskNoteIdentifierTag, - task.tags + task.tags, ) ) { newTask.title = sanitizeStatus( plugin.settings.data.globalSettings, task.title, " ", - statusTypeNames.TODO + statusTypeNames.TODO, ); } @@ -576,14 +576,13 @@ class DragDropTasksManager { sourceColumn: ColumnData, targetColumn: ColumnData, sourceColumnSwimlaneData: swimlaneDataProp | null | undefined, - targetColumnSwimlaneData: swimlaneDataProp | null | undefined + targetColumnSwimlaneData: swimlaneDataProp | null | undefined, ): Promise => { if (!currentDragData || !targetColumn.datedBasedColumn) { - console.error( - "No current drag data available for reordering : ", - JSON.stringify(currentDragData), - "\nOr the target column data : ", - JSON.stringify(targetColumn) + bugReporterManagerInsatance.addToLogs( + 136, + `No current drag data available for reordering.\ncurrentDragData=${JSON.stringify(currentDragData)}\ntargetColumn=${JSON.stringify(targetColumn)}`, + "DragDropTasksManager.ts/handleTaskMove_dated_to_dated", ); return; } @@ -597,7 +596,7 @@ class DragDropTasksManager { plugin, currentDragData, targetColumn, - this.desiredDropIndex + this.desiredDropIndex, ); if (sourceColumnSwimlaneData && targetColumnSwimlaneData) { @@ -605,7 +604,7 @@ class DragDropTasksManager { newTask, sourceColumnSwimlaneData, targetColumnSwimlaneData, - plugin.settings.data.globalSettings + plugin.settings.data.globalSettings, ); console.log("newTask after swimlane change:", newTask); } @@ -629,7 +628,7 @@ class DragDropTasksManager { const newDateValue = getAllDatesInRelativeRange( targetColumn.datedBasedColumn?.from, - targetColumn.datedBasedColumn?.to + targetColumn.datedBasedColumn?.to, )[0]; // newTask[dateType] = newDateValue; @@ -663,7 +662,7 @@ class DragDropTasksManager { 31, "The column configurations are currupted. Configurations are not valid for this operation. Kindly verify the column configuration in which you just dropped the task.", `Column configuration : ${JSON.stringify(targetColumn)}`, - "DragDropTasksManager.ts/handleTaskMove_dated_to_dated" + "DragDropTasksManager.ts/handleTaskMove_dated_to_dated", ); } }; @@ -681,11 +680,13 @@ class DragDropTasksManager { sourceColumn: ColumnData, targetColumn: ColumnData, sourceColumnSwimlaneData: swimlaneDataProp | null | undefined, - targetColumnSwimlaneData: swimlaneDataProp | null | undefined + targetColumnSwimlaneData: swimlaneDataProp | null | undefined, ): Promise => { if (!targetColumn?.coltag) { - console.error( - "handleTaskMove_to_namedTag: coltag undefined in the target column configs" + bugReporterManagerInsatance.addToLogs( + 137, + `coltag is undefined in the target column.\ntargetColumn=${JSON.stringify(targetColumn)}`, + "DragDropTasksManager.ts/handleTaskMove_to_namedTag", ); return; } @@ -698,7 +699,7 @@ class DragDropTasksManager { plugin, currentDragData, targetColumn, - this.desiredDropIndex + this.desiredDropIndex, ); // STEP 2 - Check if swimlanes are enabled and if user is moving from one swimlane to another. @@ -707,7 +708,7 @@ class DragDropTasksManager { newTask, sourceColumnSwimlaneData, targetColumnSwimlaneData, - plugin.settings.data.globalSettings + plugin.settings.data.globalSettings, ); console.log("newTask after swimlane change:", newTask); } @@ -746,19 +747,19 @@ class DragDropTasksManager { sourceColumn: ColumnData, targetColumn: ColumnData, sourceColumnSwimlaneData: swimlaneDataProp | null | undefined, - targetColumnSwimlaneData: swimlaneDataProp | null | undefined + targetColumnSwimlaneData: swimlaneDataProp | null | undefined, ): Promise => { if (!targetColumn.taskPriority) { - console.error( - "The priority value not found in the target column configuration : ", - JSON.stringify(targetColumn) + bugReporterManagerInsatance.addToLogs( + 138, + `The priority value not found in the target column configuration.\ntargetColumn=${JSON.stringify(targetColumn)}`, + "DragDropTasksManager.ts/handleTaskMove_to_priority", ); return; } - const { updateTaskItemPriority } = await import( - "src/utils/UserTaskEvents" - ); + const { updateTaskItemPriority } = + await import("src/utils/UserTaskEvents"); const oldTask = currentDragData.task; let newTask = { ...oldTask } as taskItem; @@ -767,7 +768,7 @@ class DragDropTasksManager { plugin, currentDragData, targetColumn, - this.desiredDropIndex + this.desiredDropIndex, ); if (sourceColumnSwimlaneData && targetColumnSwimlaneData) { @@ -775,7 +776,7 @@ class DragDropTasksManager { newTask, sourceColumnSwimlaneData, targetColumnSwimlaneData, - plugin.settings.data.globalSettings + plugin.settings.data.globalSettings, ); console.log("newTask after swimlane change:", newTask); } @@ -803,19 +804,19 @@ class DragDropTasksManager { sourceColumn: ColumnData, targetColumn: ColumnData, sourceColumnSwimlaneData: swimlaneDataProp | null | undefined, - targetColumnSwimlaneData: swimlaneDataProp | null | undefined + targetColumnSwimlaneData: swimlaneDataProp | null | undefined, ): Promise => { if (!targetColumn.taskStatus) { - console.error( - "The status value not found in the target column configuration : ", - JSON.stringify(targetColumn) + bugReporterManagerInsatance.addToLogs( + 139, + `The status value not found in the target column configuration.\ntargetColumn=${JSON.stringify(targetColumn)}`, + "DragDropTasksManager.ts/handleTaskMove_to_status", ); return; } - const { updateTaskItemStatus } = await import( - "src/utils/UserTaskEvents" - ); + const { updateTaskItemStatus } = + await import("src/utils/UserTaskEvents"); const oldTask = currentDragData.task; let newTask = { ...oldTask } as taskItem; @@ -824,7 +825,7 @@ class DragDropTasksManager { plugin, currentDragData, targetColumn, - this.desiredDropIndex + this.desiredDropIndex, ); if (sourceColumnSwimlaneData && targetColumnSwimlaneData) { @@ -832,7 +833,7 @@ class DragDropTasksManager { newTask, sourceColumnSwimlaneData, targetColumnSwimlaneData, - plugin.settings.data.globalSettings + plugin.settings.data.globalSettings, ); console.log("newTask after swimlane change:", newTask); } @@ -861,11 +862,10 @@ class DragDropTasksManager { sourceColumn: ColumnData, targetColumn: ColumnData, sourceColumnSwimlaneData: swimlaneDataProp | null | undefined, - targetColumnSwimlaneData: swimlaneDataProp | null | undefined + targetColumnSwimlaneData: swimlaneDataProp | null | undefined, ): Promise => { - const { updateTaskItemStatus } = await import( - "src/utils/UserTaskEvents" - ); + const { updateTaskItemStatus } = + await import("src/utils/UserTaskEvents"); const oldTask = currentDragData.task; let newTask = { ...oldTask } as taskItem; @@ -875,7 +875,7 @@ class DragDropTasksManager { plugin, currentDragData, targetColumn, - this.desiredDropIndex + this.desiredDropIndex, ); // STEP 2 - Check if swimlanes are enabled and if user is moving from one swimlane to another. @@ -884,14 +884,14 @@ class DragDropTasksManager { newTask, sourceColumnSwimlaneData, targetColumnSwimlaneData, - plugin.settings.data.globalSettings + plugin.settings.data.globalSettings, ); console.log("newTask after swimlane change:", newTask); } const newStatus = plugin.settings.data.globalSettings.customStatuses.find( - (status) => status.type === statusTypeNames.DONE + (status) => status.type === statusTypeNames.DONE, ); // FINALLY - Update the task in the note. @@ -909,7 +909,7 @@ class DragDropTasksManager { plugin: TaskBoard, currentDragData: currentDragDataPayload, targetColumnData: ColumnData, - desiredIndex: number | null + desiredIndex: number | null, ): void => { console.log( "handleTasksOrderChange called...\ncurrentDragData=", @@ -917,7 +917,7 @@ class DragDropTasksManager { "\ntargetColumnData=", targetColumnData, "\ndesiredIndex=", - desiredIndex + desiredIndex, ); if ( !( @@ -948,7 +948,7 @@ class DragDropTasksManager { targetColumnData.tasksIdManualOrder.splice( desiredIndex, 0, - task.id + task.id, ); } else { targetColumnData.tasksIdManualOrder.push(task.id); @@ -960,11 +960,7 @@ class DragDropTasksManager { ].columns[targetColumnData.index - 1] = targetColumnData; // Persist settings and refresh the board - try { - plugin.saveSettings(newSettings); - } catch (err) { - console.error("Error saving settings after task reorder:", err); - } + plugin.saveSettings(newSettings); }; /** @@ -978,7 +974,7 @@ class DragDropTasksManager { task: taskItem, sourceColumnSwimlaneData: swimlaneDataProp, targetColumnSwimlaneData: swimlaneDataProp, - globalSettings: globalSettingsData + globalSettings: globalSettingsData, ): Promise => { const property = sourceColumnSwimlaneData.property; const oldValue = sourceColumnSwimlaneData.value; @@ -994,14 +990,14 @@ class DragDropTasksManager { newTags = newTags.filter( (tag) => tag.replace("#", "").toLowerCase() !== - oldValue.replace("#", "").toLowerCase() + oldValue.replace("#", "").toLowerCase(), ); } // Add new tag of target swimlane if (newValue !== "All rest") newTags.push( - newValue.startsWith("#") ? newValue : `#${newValue}` + newValue.startsWith("#") ? newValue : `#${newValue}`, ); newTags = Array.from(new Set(newTags)); @@ -1010,7 +1006,7 @@ class DragDropTasksManager { globalSettings, property, oldTags, - newTags + newTags, ); } else { newTask = await updateTaskItemProperty( @@ -1018,7 +1014,7 @@ class DragDropTasksManager { globalSettings, property, oldValue, - newValue + newValue, ); } @@ -1046,7 +1042,7 @@ class DragDropTasksManager { */ isTaskDropAllowed( sourceColumnData: ColumnData, - targetColumnData: ColumnData + targetColumnData: ColumnData, ): boolean { // Since there are more positive rules then negative ones. // Hence this function will only mention the negative ones and return false. @@ -1158,12 +1154,12 @@ class DragDropTasksManager { // For column we can do this kind of heavy DOM traversing, // since there will be less columns, so querySelecting them all is not so big issue. const allColumnContainers = Array.from( - document.querySelectorAll(".TaskBoardColumnsSection") + document.querySelectorAll(".TaskBoardColumnsSection"), ) as HTMLDivElement[]; allColumnContainers.forEach((container) => { container.classList.remove( "drag-over-allowed", - "drag-over-not-allowed" + "drag-over-not-allowed", ); }); @@ -1200,7 +1196,7 @@ class DragDropTasksManager { e: DragEvent, draggedTaskItem: HTMLDivElement, currentDragData: currentDragDataPayload, - dragIndex: number + dragIndex: number, ): void { if (!e.dataTransfer) return; @@ -1254,7 +1250,7 @@ class DragDropTasksManager { e: DragEvent, cardEl: HTMLElement, columnContainerEl: HTMLDivElement, - ColumnData: ColumnData + ColumnData: ColumnData, ): void { if (!this.getCurrentDragData() || this.getCurrentDragData() === null) return; @@ -1279,7 +1275,7 @@ class DragDropTasksManager { const dropAllowed = this.handleColumnDragOverEvent( e, ColumnData, - columnContainerEl + columnContainerEl, ); if (!dropAllowed) return; @@ -1304,7 +1300,7 @@ class DragDropTasksManager { public handleColumnDragOverEvent( e: DragEvent, targetColumnData: ColumnData, - targetColumnContainer: HTMLDivElement + targetColumnContainer: HTMLDivElement, ): boolean { // console.log("DragDropTasksManager : handleDragOver called..."); e.preventDefault(); @@ -1313,14 +1309,18 @@ class DragDropTasksManager { ? this.currentDragData.sourceColumnData : null; if (!sourceColumnData) { - console.error("No source column data available for dragover."); + bugReporterManagerInsatance.addToLogs( + 141, + `No source column data available for dragover.\nSourceColumn=${JSON.stringify(sourceColumnData)}`, + "DragDropTasksManager.ts/handleColumnDragOverEvent", + ); return false; } // Check if drop is allowed const isDropAllowed = this.isTaskDropAllowed( sourceColumnData, - targetColumnData + targetColumnData, ); // console.log("isDropAllowed", isDropAllowed); @@ -1333,7 +1333,7 @@ class DragDropTasksManager { } else { console.log( "Task drop not allowed from column:", - sourceColumnData.name + sourceColumnData.name, ); // Apply CSS styling for not allowed drop targetColumnContainer.classList.add("drag-over-not-allowed"); @@ -1359,7 +1359,7 @@ class DragDropTasksManager { columnContainerEl.classList.remove( "drag-over-allowed", - "drag-over-not-allowed" + "drag-over-not-allowed", ); // Clear drag-over styling from all columns @@ -1393,22 +1393,28 @@ class DragDropTasksManager { e: DragEvent, targetColumnData: ColumnData, targetColumnContainer: HTMLDivElement, - targetColumnSwimlaneData: swimlaneDataProp | null | undefined + targetColumnSwimlaneData: swimlaneDataProp | null | undefined, ): void { console.log("DragDropTasksManager : handleDrop called..."); e.preventDefault(); // All checks before proceeding with the calculations... if (!this.currentDragData) { - console.error("No current drag data available for drop operation."); + bugReporterManagerInsatance.addToLogs( + 142, + `No current drag data available for drop operation.\currentDragData=${JSON.stringify(this.currentDragData)}`, + "DragDropTasksManager.ts/handleDropEvent", + ); return; } const sourceColumnData = this.currentDragData.sourceColumnData; const sourceColumnSwimlaneData = this.currentDragData.swimlaneData; if (!sourceColumnData) { - console.error( - "There was an error while capturing the source column data." + bugReporterManagerInsatance.addToLogs( + 143, + `There was an error while capturing the source column data.\sourceColumnData=${JSON.stringify(sourceColumnData)}`, + "DragDropTasksManager.ts/handleDropEvent", ); return; } @@ -1416,24 +1422,18 @@ class DragDropTasksManager { // Remove drag-over styling from target targetColumnContainer.classList.remove( "drag-over-allowed", - "drag-over-not-allowed" + "drag-over-not-allowed", ); // Check if drop is allowed const isDropAllowed = this.isTaskDropAllowed( sourceColumnData, - targetColumnData + targetColumnData, ); if (!isDropAllowed) { - console.warn( - "Task drop not allowed from column:", - sourceColumnData.name, - "to column:", - targetColumnData.name - ); new Notice( - `Task drop not allowed from column: ${sourceColumnData.name} to column: ${targetColumnData.name}` + `Task drop not allowed from column type: ${sourceColumnData.colType} to column type: ${targetColumnData.colType}`, ); return; } @@ -1457,7 +1457,7 @@ class DragDropTasksManager { sourceColumnData, targetColumnData, sourceColumnSwimlaneData, - targetColumnSwimlaneData + targetColumnSwimlaneData, ); return; } else if (targetColumnData.colType === colTypeNames.namedTag) { @@ -1467,7 +1467,7 @@ class DragDropTasksManager { sourceColumnData, targetColumnData, sourceColumnSwimlaneData, - targetColumnSwimlaneData + targetColumnSwimlaneData, ); } else if (targetColumnData.colType === colTypeNames.dated) { this.handleTaskMove_to_dated( @@ -1476,7 +1476,7 @@ class DragDropTasksManager { sourceColumnData, targetColumnData, sourceColumnSwimlaneData, - targetColumnSwimlaneData + targetColumnSwimlaneData, ); } else if (targetColumnData.colType === colTypeNames.taskPriority) { this.handleTaskMove_priority_to_priority( @@ -1485,7 +1485,7 @@ class DragDropTasksManager { sourceColumnData, targetColumnData, sourceColumnSwimlaneData, - targetColumnSwimlaneData + targetColumnSwimlaneData, ); } else if (targetColumnData.colType === colTypeNames.taskStatus) { this.handleTaskMove_status_to_status( @@ -1494,11 +1494,11 @@ class DragDropTasksManager { sourceColumnData, targetColumnData, sourceColumnSwimlaneData, - targetColumnSwimlaneData + targetColumnSwimlaneData, ); } else { new Notice( - "This operation is not possible in the current version. Please request this idea to the developer." + "This operation is not possible in the current version. Please request this idea to the developer.", ); } } else if (targetColumnData.colType === colTypeNames.completed) { @@ -1510,7 +1510,7 @@ class DragDropTasksManager { sourceColumnData, targetColumnData, sourceColumnSwimlaneData, - targetColumnSwimlaneData + targetColumnSwimlaneData, ); } else if (targetColumnData.colType === colTypeNames.dated) { // This means user is moving task to a dated column from any other type of column. @@ -1521,7 +1521,7 @@ class DragDropTasksManager { sourceColumnData, targetColumnData, sourceColumnSwimlaneData, - targetColumnSwimlaneData + targetColumnSwimlaneData, ); } else if (targetColumnData.colType === colTypeNames.namedTag) { // This means user is moving task to a namedTag column from any other type of column. @@ -1532,7 +1532,7 @@ class DragDropTasksManager { sourceColumnData, targetColumnData, sourceColumnSwimlaneData, - targetColumnSwimlaneData + targetColumnSwimlaneData, ); } else if (targetColumnData.colType === colTypeNames.taskPriority) { // This means user is moving task to a priority column from any other type of column. @@ -1543,7 +1543,7 @@ class DragDropTasksManager { sourceColumnData, targetColumnData, sourceColumnSwimlaneData, - targetColumnSwimlaneData + targetColumnSwimlaneData, ); } else if (targetColumnData.colType === colTypeNames.taskStatus) { // This means user is moving task to a status column from any other type of column. @@ -1554,11 +1554,11 @@ class DragDropTasksManager { sourceColumnData, targetColumnData, sourceColumnSwimlaneData, - targetColumnSwimlaneData + targetColumnSwimlaneData, ); } else { new Notice( - "This operation is not possible in the current version. Please request this idea to the developer." + "This operation is not possible in the current version. Please request this idea to the developer.", ); } } diff --git a/src/managers/RealTimeScanner.ts b/src/managers/RealTimeScanner.ts index 9a04ed9a..49b9ef9c 100644 --- a/src/managers/RealTimeScanner.ts +++ b/src/managers/RealTimeScanner.ts @@ -29,7 +29,11 @@ export class RealTimeScanner { } // this.startScanTimer(); } catch (error) { - console.error("Error loading file stack:", error); + bugReporterManagerInsatance.addToLogs( + 144, + String(error), + "RealTimeScanner.ts/initializeStack", + ); } } @@ -37,14 +41,14 @@ export class RealTimeScanner { try { localStorage.setItem( PENDING_SCAN_FILE_STACK, - JSON.stringify(this.taskBoardFileStack) + JSON.stringify(this.taskBoardFileStack), ); } catch (error) { bugReporterManagerInsatance.showNotice( 32, "Error saving file stack to localStorage.", String(error), - "RealTimeScanner.ts/saveStack" + "RealTimeScanner.ts/saveStack", ); } } @@ -57,7 +61,7 @@ export class RealTimeScanner { */ async processAllUpdatedFiles( currentFile?: TFile | string | undefined, - updatedTaskId?: string | undefined + updatedTaskId?: string | undefined, ) { // If a current file is provided, ensure it's included in the processing let newFile: TFile | null | undefined = null; @@ -86,7 +90,7 @@ export class RealTimeScanner { // Send all files for scanning and updating tasks result = await this.vaultScanner.refreshTasksFromFiles( files, - false + false, ); } @@ -117,11 +121,11 @@ export class RealTimeScanner { if ( this.taskBoardFileStack.at(0) === undefined || !this.taskBoardFileStack.includes( - file instanceof TFile ? file.path : file + file instanceof TFile ? file.path : file, ) ) { this.taskBoardFileStack.push( - file instanceof TFile ? file.path : file + file instanceof TFile ? file.path : file, ); // Add the file to the stack this.saveStack(); // Save the updated stack } @@ -137,12 +141,11 @@ export class RealTimeScanner { onFileRenamed( file: TAbstractFile, oldPath: string, - archivedTaskNotesPath: string + archivedTaskNotesPath: string, ) { let foundFlag = false; // Find the oldPath inside the plugin.vaultScanner.tasksCache and replace it with the new file path. Please dont update it inside taskBoardFileStack. - const { Pending, Completed } = - this.plugin.vaultScanner.tasksCache; + const { Pending, Completed } = this.plugin.vaultScanner.tasksCache; [Pending, Completed].forEach((cache) => { if (cache && typeof cache === "object") { @@ -167,7 +170,7 @@ export class RealTimeScanner { } else if (file instanceof TFolder) { // Actually this is not at all needed as I am only running this function when a file is renamed. Also it was required because, it will anyways going to run of TFile, and if I run it for TFolder as well, it will run two files for the same file. If in case of child folders, it will too many times for the same file unnecessarily. const keysToUpdate = Object.keys(cache).filter((key) => - key.startsWith(oldPath + "/") + key.startsWith(oldPath + "/"), ); keysToUpdate.forEach((oldKey) => { const newKey = @@ -241,8 +244,7 @@ export class RealTimeScanner { } // Also remove the file from the tasks cache - const { Pending, Completed } = - this.plugin.vaultScanner.tasksCache; + const { Pending, Completed } = this.plugin.vaultScanner.tasksCache; [Pending, Completed].forEach((cache) => { if (cache && typeof cache === "object") { if (file instanceof TFile && cache.hasOwnProperty(file.path)) { @@ -251,7 +253,7 @@ export class RealTimeScanner { } else if (file instanceof TFolder) { // Actually this is not at all needed as I am only running this function when a file is deleted. Also it was required because, it will anyways going to run of TFile, and if I run it for TFolder as well, it will run two files for the same file. If in case of child folders, it will too many times for the same file unnecessarily. const keysToDelete = Object.keys(cache).filter((key) => - key.startsWith(file.path + "/") + key.startsWith(file.path + "/"), ); keysToDelete.forEach((key) => { delete cache[key]; diff --git a/src/managers/VaultScanner.ts b/src/managers/VaultScanner.ts index 6ae1bbff..77d928ba 100644 --- a/src/managers/VaultScanner.ts +++ b/src/managers/VaultScanner.ts @@ -77,9 +77,10 @@ export default class VaultScanner { // Load existing tasks from JSON cache this.tasksCache = await loadJsonCacheDataFromDisk(this.plugin); } catch (error) { - console.error( - "Error loading tasks cache from disk\nIf this is appearing on a fresh install then no need to worry.\n", - error, + bugReporterManagerInsatance.addToLogs( + 145, + `No need to worry if this is appearing on the fresh install.\n${String(error)}`, + "RealTimeScanner.ts/initializeTasksCache", ); this.tasksCache = { VaultName: this.plugin?.app.vault.getName(), @@ -526,13 +527,12 @@ export default class VaultScanner { return "true"; } } catch (error) { - console.error( - "Error occurred while extracting tasks from file:", - file.path, - "\nERROR :", - error, + bugReporterManagerInsatance.addToLogs( + 146, + String(error), + "VaultScanner.ts/extractTasksFromFile", ); - return String(error); + return "false"; } } @@ -597,7 +597,6 @@ export default class VaultScanner { error as string, "VaultScanner.tsx/refreshTasksFromFiles", ); - console.error(error); return false; } } @@ -1204,7 +1203,11 @@ export async function compareFileCache( // This approach is optimal for most use cases as task arrays are typically small to medium sized return JSON.stringify(newCache) === JSON.stringify(oldCache); } catch (error) { - console.error("Error comparing file caches:", error); + bugReporterManagerInsatance.addToLogs( + 147, + String(error), + "VaultScanner.ts/compareFileCache", + ); // In case of error, assume they're different to trigger a refresh return false; } diff --git a/src/modals/AddOrEditTaskModal.tsx b/src/modals/AddOrEditTaskModal.tsx index 54cc77d4..9a4ba944 100644 --- a/src/modals/AddOrEditTaskModal.tsx +++ b/src/modals/AddOrEditTaskModal.tsx @@ -7,7 +7,7 @@ import TaskBoard from "main"; import { t } from "src/utils/lang/helper"; import { getFormattedTaskContent } from "src/utils/taskLine/TaskContentFormatter"; import { readDataOfVaultFile } from "src/utils/MarkdownFileOperations"; -import { getLocalDateTimeString } from "src/utils/DateTimeCalculations"; +import { getCurrentLocalTimeString } from "src/utils/DateTimeCalculations"; import { allowedFileExtensionsRegEx } from "src/regularExpressions/MiscelleneousRegExpr"; import { AddOrEditTaskRC } from "src/components/AddOrEditTaskRC"; import { taskItemEmpty } from "src/interfaces/Mapping"; @@ -78,7 +78,7 @@ export class AddOrEditTaskModal extends Modal { noteContent = "---\ntitle: \n---\n"; const defaultLocation = this.plugin.settings.data.globalSettings.taskNoteDefaultLocation || 'Meta/Task_Board/Task_Notes'; - const noteName = this.task.title || getLocalDateTimeString(); + const noteName = this.task.title || getCurrentLocalTimeString(); // Sanitize filename const sanitizedName = noteName.replace(/[<>:"/\\|?*]/g, '_'); this.filePath = normalizePath(`${defaultLocation}/${sanitizedName}.md`); diff --git a/src/modals/BugReporterModal.ts b/src/modals/BugReporterModal.ts index 28c9e2d2..f06e1cce 100644 --- a/src/modals/BugReporterModal.ts +++ b/src/modals/BugReporterModal.ts @@ -148,7 +148,7 @@ export class BugReporterModal extends Modal { ) .join("\n"); - const finalContentForHTMLDom = `

Developer message


${message.replaceAll( + const finalContentForHTMLDom = `

Message for user


${message.replaceAll( "\n", "
" )}

Error Message
${sanitizedErrorContent.replaceAll( @@ -156,7 +156,7 @@ export class BugReporterModal extends Modal { "
" )}


Context : ${context}

System Information
${systemInfoTextHTMLDom}
Any additional information and screenshots
`; - const finalContentForMarkdown = `# Bug Report\n\n## Developer message\n\n${message}\n\n## Error Message\n\n${sanitizedErrorContent}\n\n## Context\n${context}\n\n## System Information\n\n${systemInfoTextMarkdown}\n\n### Any additional information and screenshots`; + const finalContentForMarkdown = `# Bug Report\n\n## Message for user\n\n${message}\n\n## Error Message\n\n${sanitizedErrorContent}\n\n## Context\n${context}\n\n## System Information\n\n${systemInfoTextMarkdown}\n\n### Any additional information and screenshots`; return { finalContentForHTMLDom, finalContentForMarkdown }; } diff --git a/src/services/FileSystem.ts b/src/services/FileSystem.ts index 5294b30a..dfe90cb4 100644 --- a/src/services/FileSystem.ts +++ b/src/services/FileSystem.ts @@ -5,6 +5,7 @@ import type * as NodePath from "node:path"; import type * as NodeUrl from "node:url"; import type * as NodeZlib from "node:zlib"; import { Platform } from "obsidian"; +import { bugReporterManagerInsatance } from "src/managers/BugReporter"; // import { configureWebWorker } from './z-worker-inline'; // configureWebWorker(configure); @@ -60,11 +61,11 @@ export const zlib: typeof NodeZlib = Platform.isDesktopApp export function nodeBufferToArrayBuffer( buffer: Buffer, offset = 0, - length = buffer.byteLength + length = buffer.byteLength, ): ArrayBuffer | SharedArrayBuffer { return buffer.buffer.slice( buffer.byteOffset + offset, - buffer.byteOffset + offset + length + buffer.byteOffset + offset + length, ); } @@ -140,11 +141,11 @@ export class NodePickedFolder implements PickedFolder { for (let file of files) { if (file.isFile()) { results.push( - new NodePickedFile(path.join(filepath, file.name)) + new NodePickedFile(path.join(filepath, file.name)), ); } else if (file.isDirectory()) { results.push( - new NodePickedFolder(path.join(filepath, file.name)) + new NodePickedFolder(path.join(filepath, file.name)), ); } } @@ -185,7 +186,7 @@ export class WebPickedFile implements PickedFile { return new Promise((resolve, reject) => { let reader = new FileReader(); reader.addEventListener("load", () => - resolve(reader.result as string) + resolve(reader.result as string), ); reader.addEventListener("error", reject); reader.readAsText(this.file); @@ -200,7 +201,7 @@ export class WebPickedFile implements PickedFile { return new Promise((resolve, reject) => { let reader = new FileReader(); reader.addEventListener("load", () => - resolve(reader.result as ArrayBuffer) + resolve(reader.result as ArrayBuffer), ); reader.addEventListener("error", reject); reader.readAsArrayBuffer(this.file); @@ -218,7 +219,7 @@ export class WebPickedFile implements PickedFile { export async function getAllFiles( files: (PickedFolder | PickedFile)[], - filter?: (file: PickedFile) => boolean + filter?: (file: PickedFile) => boolean, ): Promise { let results: PickedFile[] = []; for (let file of files) { @@ -231,9 +232,10 @@ export async function getAllFiles( } } } catch (e) { - console.warn( - "FileSystem.ts/getAllFiles : There was an error while fetching all files : ", - e + bugReporterManagerInsatance.addToLogs( + 101, + String(e), + "FileSystem.ts/getAllFiles", ); } } @@ -252,7 +254,7 @@ export function parseFilePath(filepath: string): { } { let lastIndex = Math.max( filepath.lastIndexOf("/"), - filepath.lastIndexOf("\\") + filepath.lastIndexOf("\\"), ); let name = filepath; let parent = ""; diff --git a/src/services/MarkdownUIRenderer.ts b/src/services/MarkdownUIRenderer.ts index d2241f30..e5d99c35 100644 --- a/src/services/MarkdownUIRenderer.ts +++ b/src/services/MarkdownUIRenderer.ts @@ -1,13 +1,14 @@ // /src/services/MarkdownUIRenderer.ts import { App, Component, MarkdownRenderer } from "obsidian"; +import { bugReporterManagerInsatance } from "src/managers/BugReporter"; export type TextRenderer = ( app: App, text: string, element: HTMLSpanElement, path: string, - obsidianComponent: Component | null // null is allowed here only for tests + obsidianComponent: Component | null, // null is allowed here only for tests ) => Promise; /** @@ -24,7 +25,7 @@ export type TextRenderer = ( */ export function createAndAppendElement( tagName: K, - parentElement: HTMLElement + parentElement: HTMLElement, ): HTMLElementTagNameMap[K] { // Maintenance note: // We don't use the Obsidian convenience function li.createEl() here, because we don't have it available @@ -49,7 +50,7 @@ export class MarkdownUIRenderer { text: string, element: HTMLSpanElement, path: string, - obsidianComponent: Component | null + obsidianComponent: Component | null, ) { if (!obsidianComponent) { return; @@ -59,7 +60,7 @@ export class MarkdownUIRenderer { text, element, path, - obsidianComponent + obsidianComponent, ); } @@ -93,7 +94,7 @@ export class MarkdownUIRenderer { taskDescText: string, element: HTMLDivElement, path: string, - obsidianComponent: Component | null + obsidianComponent: Component | null, ) { if (!obsidianComponent) { return; @@ -104,7 +105,7 @@ export class MarkdownUIRenderer { taskDescText, element, path, - obsidianComponent + obsidianComponent, ); } @@ -113,7 +114,7 @@ export class MarkdownUIRenderer { subtaskText: string, el: HTMLElement, path: string, - taskItemComponent: Component | null + taskItemComponent: Component | null, ) { try { // console.log("renderSubtaskText : Received following text : ", subtaskText); @@ -127,13 +128,14 @@ export class MarkdownUIRenderer { subtaskText, el, path, - componentEl + componentEl, ); } catch (error) { - console.warn("Error rendering subtask text:", error, { - subtaskText, - path, - }); + bugReporterManagerInsatance.addToLogs( + 102, + String(error), + "MarkdownUIRenderer.ts/MarkdownUIRenderer.renderSubtaskText", + ); } } } diff --git a/src/services/MultiSuggest.ts b/src/services/MultiSuggest.ts index dbc1b415..06f782d0 100644 --- a/src/services/MultiSuggest.ts +++ b/src/services/MultiSuggest.ts @@ -4,6 +4,7 @@ import TaskBoard from "main"; import { AbstractInputSuggest, App, TFile, TFolder } from "obsidian"; import { CustomStatus } from "src/interfaces/GlobalSettings"; import { taskItem } from "src/interfaces/TaskItem"; +import { bugReporterManagerInsatance } from "src/managers/BugReporter"; import { allowedFileExtensionsRegEx } from "src/regularExpressions/MiscelleneousRegExpr"; export class MultiSuggest extends AbstractInputSuggest { @@ -13,7 +14,7 @@ export class MultiSuggest extends AbstractInputSuggest { private inputEl: HTMLInputElement, content: Set, private onSelectCb: (value: string) => void, - app: App + app: App, ) { super(app, inputEl); this.content = content; @@ -22,7 +23,7 @@ export class MultiSuggest extends AbstractInputSuggest { getSuggestions(inputStr: string): string[] { const lowerCaseInputStr = inputStr.toLocaleLowerCase(); return [...this.content].filter((content) => - content.toLocaleLowerCase().includes(lowerCaseInputStr) + content.toLocaleLowerCase().includes(lowerCaseInputStr), ); } @@ -65,7 +66,8 @@ export function getFileSuggestions(app: App): string[] { const files = app.vault .getAllLoadedFiles() .filter( - (f) => f instanceof TFile && allowedFileExtensionsRegEx.test(f.path) + (f) => + f instanceof TFile && allowedFileExtensionsRegEx.test(f.path), ) .map((f) => f.path); @@ -85,7 +87,7 @@ export function getTagSuggestions(app: App): string[] { export function getQuickAddPluginChoices( app: App, - quickAddPluginObj: any + quickAddPluginObj: any, ): string[] { try { if (!quickAddPluginObj) { @@ -100,7 +102,11 @@ export function getQuickAddPluginChoices( .filter((key) => choices[key].type === "Capture") .map((key) => choices[key].name); } catch (error) { - console.warn("Error fetching QuickAdd plugin choices:", error); + bugReporterManagerInsatance.addToLogs( + 103, + String(error), + "MultiSuggest.ts/getQuickAddPluginChoices", + ); return []; } } diff --git a/src/services/OpenModals.ts b/src/services/OpenModals.ts index 8499e51d..250458a0 100644 --- a/src/services/OpenModals.ts +++ b/src/services/OpenModals.ts @@ -203,9 +203,10 @@ export const openAddNewTaskNoteModal = (app: App, plugin: TaskBoard) => { }); } } catch (error) { - console.error( - "Error creating or updating task note:", - error, + bugReporterManagerInsatance.addToLogs( + 149, + String(error), + "OpenModals.ts/openAddNewTaskNoteModal/callback()", ); new Notice(t("error-creating-task-note"), 5000); return false; diff --git a/src/services/tasks-plugin/helpers.ts b/src/services/tasks-plugin/helpers.ts index 90f8602b..3e43c5d0 100644 --- a/src/services/tasks-plugin/helpers.ts +++ b/src/services/tasks-plugin/helpers.ts @@ -18,13 +18,17 @@ export async function isTasksPluginEnabled(plugin: TaskBoard) { const tasksPluginO = new TasksPluginApi(plugin); return tasksPluginO.isTasksPluginEnabled(); } catch (err) { - console.error("Error checking tasks plugin status:", err); + bugReporterManagerInsatance.addToLogs( + 148, + String(err), + "tasks-plugin/helpers.ts/isTasksPluginEnabled", + ); return false; } } export async function fetchTasksPluginCustomStatuses( - plugin: TaskBoard + plugin: TaskBoard, ): Promise { try { const tasksPluginO = new TasksPluginApi(plugin); @@ -32,12 +36,11 @@ export async function fetchTasksPluginCustomStatuses( "Tasks Plugin API:", tasksPluginO, "\nIs tasks plugin enabled?", - tasksPluginO.isTasksPluginEnabled() + tasksPluginO.isTasksPluginEnabled(), ); // if( plugin.app.plugins.getPlugin("obsidian-tasks-plugin")) { if (tasksPluginO.isTasksPluginEnabled()) { - plugin.settings.data.globalSettings.compatiblePlugins.tasksPlugin = - true; + plugin.settings.data.globalSettings.compatiblePlugins.tasksPlugin = true; // Define the path to the tasks plugin data.json file const path = `${plugin.app.vault.configDir}/plugins/obsidian-tasks-plugin/data.json`; @@ -56,10 +59,10 @@ export async function fetchTasksPluginCustomStatuses( const statusMap = new Map(); coreStatuses.forEach((status: CustomStatus) => - statusMap.set(status.symbol, status) + statusMap.set(status.symbol, status), ); customStatuses.forEach((status: CustomStatus) => - statusMap.set(status.symbol, status) + statusMap.set(status.symbol, status), ); const statuses: CustomStatus[] = Array.from(statusMap.values()); @@ -78,7 +81,7 @@ export async function fetchTasksPluginCustomStatuses( // Store it in the plugin settings if there is a difference if ( JSON.stringify( - plugin.settings.data.globalSettings.customStatuses + plugin.settings.data.globalSettings.customStatuses, ) !== JSON.stringify(statuses) ) { plugin.settings.data.globalSettings.customStatuses = statuses; @@ -86,9 +89,10 @@ export async function fetchTasksPluginCustomStatuses( } } } catch (error) { - console.warn( - "Error fetching custom statuses from tasks plugin:", - error + bugReporterManagerInsatance.addToLogs( + 100, + String(error), + "tasks-plugin/helper.ts/fetchTasksPluginCustomStatuses", ); return false; @@ -98,7 +102,7 @@ export async function fetchTasksPluginCustomStatuses( export async function openTasksPluginEditModal( plugin: TaskBoard, - oldTask: taskItem + oldTask: taskItem, ) { try { const tasksPlugin = new TasksPluginApi(plugin); @@ -109,7 +113,7 @@ export async function openTasksPluginEditModal( if (tasksPlugin.isTasksPluginEnabled()) { const tasksPluginApiOutput = await tasksPlugin.editTaskLineModal( - completeOldTaskContent + completeOldTaskContent, ); if (!tasksPluginApiOutput) { @@ -139,7 +143,7 @@ export async function openTasksPluginEditModal( plugin, oldTask, completeOldTaskContent, - newContent + newContent, ); } else if ((twoTaskTitles.length = 1)) { const { formattedTaskContent, newId } = @@ -154,7 +158,7 @@ export async function openTasksPluginEditModal( plugin, oldTask, completeOldTaskContent, - newContent + newContent, ); } else if ((twoTaskTitles.length = 2)) { newContent = `${twoTaskTitles[0]}${ @@ -171,7 +175,7 @@ export async function openTasksPluginEditModal( plugin, oldTask, completeOldTaskContent, - newContent + newContent, ); } else { bugReporterManagerInsatance.showNotice( diff --git a/src/settings/SettingSynchronizer.ts b/src/settings/SettingSynchronizer.ts index a015517f..d72f2330 100644 --- a/src/settings/SettingSynchronizer.ts +++ b/src/settings/SettingSynchronizer.ts @@ -9,6 +9,7 @@ import { t } from "src/utils/lang/helper"; import { Board, ColumnData } from "src/interfaces/BoardConfigs"; import { generateIdForFilters } from "src/components/BoardFilters/ViewTaskFilter"; import { colTypeNames } from "src/interfaces/Enums"; +import { bugReporterManagerInsatance } from "src/managers/BugReporter"; /** * Recursively migrates settings by adding missing fields from defaults to settings. @@ -30,14 +31,14 @@ export function migrateSettings(defaults: any, settings: any): PluginDataJson { ) { // This is a temporary migration applied since version 1.2.0. Can be removed, after around 6 months. settings[key] = Object.entries( - settings[key] as Record + settings[key] as Record, ).map( ([name, color], idx) => ({ name, color, priority: idx + 1, - } as any) + }) as any, ); } else if (key === "boardConfigs" && Array.isArray(settings[key])) { // This is a temporary solution to sync the boardConfigs. Will need to replace the range object with the new 'datedBasedColumn', which will have three values 'dateType', 'from' and 'to'. So, basically I want to copy `range.rangedata.from` value to `datedBasedColumn.from` and similarly for `range.rangedatato`. And for `datedBasedColumn.dateType`, put the value this.settings.data.globalSettings.universalDate @@ -86,7 +87,7 @@ export function migrateSettings(defaults: any, settings: any): PluginDataJson { property: "tags", condition: "contains", value: f, - }) + }), ), }, ], @@ -138,15 +139,15 @@ export async function exportConfigurations(plugin: TaskBoard): Promise { folderPath.endsWith("/") || folderPath.endsWith("\\") ? folderPath + exportFileName : folderPath + - (folderPath.includes("/") ? "/" : "\\") + - exportFileName; + (folderPath.includes("/") ? "/" : "\\") + + exportFileName; await fsPromises.writeFile(exportPath, fileContent, "utf8"); new Notice(`Settings exported to ${exportPath}`); } else { // Web: use file save dialog let a = document.createElement("a"); a.href = URL.createObjectURL( - new Blob([fileContent], { type: "application/json" }) + new Blob([fileContent], { type: "application/json" }), ); a.download = exportFileName; document.body.appendChild(a); @@ -156,12 +157,16 @@ export async function exportConfigurations(plugin: TaskBoard): Promise { URL.revokeObjectURL(a.href); }, 1000); new Notice( - "Settings exported. Check the folder where you downloaded the file." + "Settings exported. Check the folder where you downloaded the file.", ); } } catch (err) { new Notice("Failed to export settings."); - console.error(err); + bugReporterManagerInsatance.addToLogs( + 150, + String(err), + "SettingSynchronizer.ts/exportConfigurations", + ); } } @@ -170,7 +175,7 @@ export async function exportConfigurations(plugin: TaskBoard): Promise { * Preserves new fields in both files. */ export async function importConfigurations( - plugin: TaskBoard + plugin: TaskBoard, ): Promise { try { let importedContent: string | undefined = undefined; @@ -244,9 +249,10 @@ export async function importConfigurations( return true; } catch (err) { new Notice("Failed to import settings."); - console.error( - "SettingSynchronizer.ts/importConfigurations : Following error occured while importing settings : ", - err + bugReporterManagerInsatance.addToLogs( + 151, + String(err), + "SettingSynchronizer.ts/importConfigurations", ); return false; } @@ -257,7 +263,7 @@ export async function importConfigurations( * @param plugin - TaskBoard plugin instance */ export async function showReloadObsidianNotice( - plugin: TaskBoard + plugin: TaskBoard, ): Promise { const reloadObsidianNotice = new Notice( createFragment((f) => { @@ -282,7 +288,7 @@ export async function showReloadObsidianNotice( }); }); }), - 0 + 0, ); reloadObsidianNotice.messageEl.onClickEvent((e) => { diff --git a/src/taskboardAPIs.ts b/src/taskboardAPIs.ts index 906a1b7b..d9cf6d78 100644 --- a/src/taskboardAPIs.ts +++ b/src/taskboardAPIs.ts @@ -1,6 +1,7 @@ import TaskBoard from "main"; import { App } from "obsidian"; import { AddOrEditTaskModal } from "./modals/AddOrEditTaskModal"; +import { bugReporterManagerInsatance } from "./managers/BugReporter"; /** * TaskBoardApi provides external plugins with a public API to interact with Task Board functionality. @@ -68,7 +69,7 @@ export class TaskBoardApi { app, plugin, isTaskNote, - filePath + filePath, ); }, }; @@ -94,7 +95,7 @@ export class TaskBoardApi { app: App, plugin: TaskBoard, isTaskNote: boolean, - filePath?: string + filePath?: string, ): Promise { try { const AddTaskModal = new AddOrEditTaskModal( @@ -106,12 +107,16 @@ export class TaskBoardApi { true, false, undefined, - filePath + filePath, ); AddTaskModal.open(); return await AddTaskModal.waitForClose; } catch (error) { - console.error("Error opening add new task modal:", error); + bugReporterManagerInsatance.addToLogs( + 161, + String(error), + "taskBoardAPIs.ts/openAddNewTaskModal", + ); return String(error); } } diff --git a/src/utils/CheckBoxUtils.ts b/src/utils/CheckBoxUtils.ts index 5c6f42d6..0c70c38e 100644 --- a/src/utils/CheckBoxUtils.ts +++ b/src/utils/CheckBoxUtils.ts @@ -2,6 +2,7 @@ import type TaskBoard from "main"; import { Notice } from "obsidian"; import { statusTypeNames } from "src/interfaces/Enums"; import { CustomStatus, PluginDataJson } from "src/interfaces/GlobalSettings"; +import { bugReporterManagerInsatance } from "src/managers/BugReporter"; import { TaskRegularExpressions } from "src/regularExpressions/TasksPluginRegularExpr"; /** @@ -12,18 +13,18 @@ import { TaskRegularExpressions } from "src/regularExpressions/TasksPluginRegula */ export function checkboxStateSwitcher( plugin: TaskBoard, - symbol: string + symbol: string, ): { newSymbol: string; newSymbolType: string } { const { customStatuses } = plugin.settings.data.globalSettings; // Check if customStatuses is available and has entries if (customStatuses?.length > 0) { const oldStatus = customStatuses.find( - (status) => status.symbol === symbol + (status) => status.symbol === symbol, ); if (oldStatus) { const nextStatus = customStatuses.find( - (status) => status.symbol === oldStatus?.nextStatusSymbol + (status) => status.symbol === oldStatus?.nextStatusSymbol, ); if (nextStatus) { return { @@ -35,18 +36,18 @@ export function checkboxStateSwitcher( } new Notice( - "customStatuses is not available or empty. Please check your settings. Falling back to default behavior." + "customStatuses is not available or empty. Please check your settings. Falling back to default behavior.", ); // Default fallback behavior return symbol === "x" || symbol === "X" ? { newSymbol: " ", newSymbolType: statusTypeNames.TODO, - } + } : { newSymbol: "x", newSymbolType: statusTypeNames.DONE, - }; + }; } /** @@ -59,7 +60,7 @@ export function checkboxStateSwitcher( export function isTaskCompleted( titleOrSymbol: string, isTaskNote: boolean, - settings: PluginDataJson + settings: PluginDataJson, ): boolean { // console.log( // "isTaskCompleted...\ntitleOrSymbol :", @@ -170,9 +171,11 @@ export function getObsidianIndentationSetting(plugin: TaskBoard): string { return parsed?.useTab ? `\t` : " ".repeat(tabSize); }); return `\t`; // Default indentation while async read happens - } catch { - console.warn( - "CheckBoxUtils.ts : getObsidianIndentationSetting : There was an error reading vault config (app.json); using default indentation." + } catch (err) { + bugReporterManagerInsatance.addToLogs( + 108, + String(err), + "CheckBoxUtils.ts/getObsidianIndentationSetting", ); return `\t`; } @@ -185,7 +188,7 @@ export function getObsidianIndentationSetting(plugin: TaskBoard): string { * @returns A promise that resolves to the indentation string. */ export async function getObsidianIndentationSettingAsync( - plugin: TaskBoard + plugin: TaskBoard, ): Promise { try { if (plugin.app.vault.config) { @@ -203,9 +206,11 @@ export async function getObsidianIndentationSettingAsync( typeof parsed?.tabSize === "number" ? parsed.tabSize : 4; const useTab = !!parsed?.useTab; return useTab ? `\t` : " ".repeat(tabSize); - } catch { - console.warn( - "CheckBoxUtils.ts : getObsidianIndentationSetting : There was an error reading vault config (app.json); using default indentation." + } catch (err) { + bugReporterManagerInsatance.addToLogs( + 109, + String(err), + "CheckBoxUtils.ts/getObsidianIndentationSettingAsync", ); return `\t`; } diff --git a/src/utils/DateTimeCalculations.ts b/src/utils/DateTimeCalculations.ts index aa5614d0..e4d341b5 100644 --- a/src/utils/DateTimeCalculations.ts +++ b/src/utils/DateTimeCalculations.ts @@ -2,26 +2,11 @@ import TaskBoard from "main"; import { UniversalDateOptions } from "src/interfaces/Enums"; import { taskItem } from "src/interfaces/TaskItem"; -/** - * Returns local time in ISO-like format (YYYY-MM-DDTHH:MM) without milliseconds or timezone - * @returns Current local time in ISO format string (YYYY-MM-DDTHH:MM) - * - * @todo - Update this function and the below one and their names. And take input as the format from the setting and return the value as per the format. - */ -export const getLocalDateTimeString = (): string => { - const now = new Date(); - const year = now.getFullYear(); - const month = String(now.getMonth() + 1).padStart(2, "0"); - const day = String(now.getDate()).padStart(2, "0"); - const hours = String(now.getHours()).padStart(2, "0"); - const minutes = String(now.getMinutes()).padStart(2, "0"); - - return `${year}-${month}-${day}T${hours}:${minutes}`; -}; - /** * Returns local time in ISO-like format (YYYY-MM-DDTHH:MM:SS) without milliseconds or timezone * @returns Current local time in ISO format string (YYYY-MM-DDTHH:MM:SS) + * + * @todo - Update this function to take input as the format from the setting and return the value as per the format. */ export const getCurrentLocalTimeString = (): string => { const now = new Date(); diff --git a/src/utils/JsonFileOperations.ts b/src/utils/JsonFileOperations.ts index 27410f1e..b8509973 100644 --- a/src/utils/JsonFileOperations.ts +++ b/src/utils/JsonFileOperations.ts @@ -118,7 +118,11 @@ export const loadJsonCacheDataFromDisk = async ( // }; return cacheData; } catch (error) { - console.error("Error reading tasks.json from disk:", error); // This error will be shown for a fresh install hence dont use the bugReporter here. + bugReporterManagerInsatance.addToLogs( + 159, + `No need to worry if this is shown on a fresh install.\n${String(error)}`, + "TaskNoteUtils.ts/updateFrontmatterInMarkdownFile", + ); throw error; } }; diff --git a/src/utils/MarkdownFileOperations.ts b/src/utils/MarkdownFileOperations.ts index e0a2fcaa..4b311702 100644 --- a/src/utils/MarkdownFileOperations.ts +++ b/src/utils/MarkdownFileOperations.ts @@ -61,14 +61,18 @@ export const writeDataToVaultFile = async ( // plugin.fileUpdatedUsingModal = file.path; } else { // new Notice(`${t("file-not-found-at-path")} ${filePath}`); - console.error(`File not found at path: ${filePath}`); + bugReporterManagerInsatance.addToLogs( + 160, + `File not found at path.\nPath: ${filePath}`, + "MarkdownFileOperations.ts/writeDataToVaultFile", + ); throw `File not found at path: ${filePath}`; } return; } catch (error) { bugReporterManagerInsatance.showNotice( 77, - "Error writing to file in vault.", + `Error writing to file in vault. Make sure the following file exists : ${filePath}`, String(error), "MarkdownFileOperations.ts/writeDataToVaultFile", ); diff --git a/src/utils/UIHelpers.ts b/src/utils/UIHelpers.ts index 4525b406..03ff10f3 100644 --- a/src/utils/UIHelpers.ts +++ b/src/utils/UIHelpers.ts @@ -1,5 +1,6 @@ import type TaskBoard from "main"; import { sanitizeHTMLToDom } from "obsidian"; +import { bugReporterManagerInsatance } from "src/managers/BugReporter"; /** * Convert a hex color string to an rgba() CSS string. @@ -62,39 +63,24 @@ export function colorTo20PercentOpacity(color: string): string { * @param newOpacity - New opacity value between 0 and 1 * @returns RGBA color string with updated opacity */ -export function updateRGBAOpacity( - plugin: TaskBoard, - rgba: string, - newOpacity: number -): string { +export function updateRGBAOpacity(rgba: string, newOpacity: number): string { + let newRGBA = rgba; if (rgba.startsWith("#")) { - rgba = hexToRgba(rgba, newOpacity); - - const regex = /^rgba\((\d+),\s*(\d+),\s*(\d+),\s*[\d.]+\)$/; - const match = rgba.match(regex); - - if (match) { - return `rgba(${match[1]}, ${match[2]}, ${match[3]}, ${newOpacity})`; - } else { - // bugReporterManagerInsatance.showNotice( - // 84, - // "Invalid RGBA color string", - // `Invalid RGBA color string: ${rgba}`, - // "updateRGBAOpacity function" - // ); - console.warn(`Invalid RGBA color string: ${rgba}`); - return rgba; - } + newRGBA = hexToRgba(rgba, newOpacity); } const regex = /^rgba\((\d+),\s*(\d+),\s*(\d+),\s*[\d.]+\)$/; - const match = rgba.match(regex); + const match = newRGBA.match(regex); if (match) { return `rgba(${match[1]}, ${match[2]}, ${match[3]}, ${newOpacity})`; } else { - console.warn(`Invalid RGBA color string: ${rgba}`); - return rgba; + bugReporterManagerInsatance.addToLogs( + 110, + `rgba match : ${newRGBA}`, + "UIHelpers.ts/updateRGBAOpacity", + ); + return newRGBA; } } diff --git a/src/utils/lang/helper.ts b/src/utils/lang/helper.ts index 94e1be49..5e21f57b 100644 --- a/src/utils/lang/helper.ts +++ b/src/utils/lang/helper.ts @@ -41,7 +41,7 @@ export const loadTranslationsOnStartup = async (plugin: TaskBoard) => { try { const pluginFolder = `${plugin.app.vault.configDir}/plugins/task-board/`; const filePath = normalizePath( - `${pluginFolder}/locales/${lang}.json` + `${pluginFolder}/locales/${lang}.json`, ); const file = await plugin.app.vault.adapter.read(filePath); const parsed = JSON.parse(file); @@ -49,9 +49,10 @@ export const loadTranslationsOnStartup = async (plugin: TaskBoard) => { // Add the loaded translations to i18next i18next.addResourceBundle(lang, "translation", parsed, true, true); } catch (err) { - console.warn( - `Could not load language file for '${lang}', falling back to English.`, - err + bugReporterManagerInsatance.addToLogs( + 104, + String(err), + "lang/helper.ts/loadTranslationsOnStartup", ); } } @@ -95,7 +96,7 @@ export function tSync(key: string): string { // Download and apply a new language file export async function downloadAndApplyLanguageFile( - plugin: TaskBoard + plugin: TaskBoard, ): Promise { const lang = getLanguage(); @@ -112,7 +113,7 @@ export async function downloadAndApplyLanguageFile( let progressNotice = new Notice( `Downloading '${lang}' language file...`, - 0 + 0, ); try { @@ -135,7 +136,7 @@ export async function downloadAndApplyLanguageFile( progressNotice.hide(); new Notice( `Language file '${lang}.json' downloaded successfully!\nPlease reload Obsidian to apply changes.`, - 0 + 0, ); return true; @@ -145,7 +146,7 @@ export async function downloadAndApplyLanguageFile( 44, `You have selected the following language for Obsidian application : ${langCodes[lang]} - ${lang}.\nBased on the error message below, either your internet is OFF or the language translation file is not present at the following link : https://github.com/tu2-atmanand/Task-Board/main/src/utils/lang/locale/. \nIt would be really helpful if you can contribute for your native language translation by visiting the following link : https://tu2-atmanand.github.io/task-board-docs/docs/Advanced/Contribution_For_Languages/`, err as string, - "helper.ts/downloadAndApplyLanguageFile" + "helper.ts/downloadAndApplyLanguageFile", ); return false; diff --git a/src/utils/taskLine/TaskContentFormatter.ts b/src/utils/taskLine/TaskContentFormatter.ts index 8c02748b..17d107de 100644 --- a/src/utils/taskLine/TaskContentFormatter.ts +++ b/src/utils/taskLine/TaskContentFormatter.ts @@ -24,6 +24,7 @@ import { taskItem } from "src/interfaces/TaskItem"; import { cursorLocation } from "src/interfaces/TaskItem"; import { generateTaskId } from "../TaskItemUtils"; import { moment as _moment } from "obsidian"; +import { bugReporterManagerInsatance } from "src/managers/BugReporter"; /** * Function to get the formatted task content. The content will look similar to how it goes into your notes. @@ -31,7 +32,7 @@ import { moment as _moment } from "obsidian"; * @returns The formatted task content as a string. */ export const getFormattedTaskContent = async ( - task: taskItem + task: taskItem, ): Promise => { if (!task || !task.title) { return ""; @@ -74,7 +75,7 @@ export const getFormattedTaskContent = async ( export const addIdToTaskContent = async ( Plugin: TaskBoard, formattedTaskContent: string, - forcefullyAddId?: boolean + forcefullyAddId?: boolean, ): Promise<{ formattedTaskContent: string; newId: string | undefined }> => { const taskId = extractTaskId(formattedTaskContent); let newId = undefined; @@ -89,28 +90,28 @@ export const addIdToTaskContent = async ( case taskPropertyFormatOptions.default: formattedTaskContent = formattedTaskContent.replace( /^(.*?)(\n|$)/, - `$1 🆔 ${newId} $2` + `$1 🆔 ${newId} $2`, ); break; case taskPropertyFormatOptions.dataviewPlugin: formattedTaskContent = formattedTaskContent.replace( /^(.*?)(\n|$)/, - `$1 [id:: ${newId}] $2` + `$1 [id:: ${newId}] $2`, ); break; case taskPropertyFormatOptions.obsidianNative: formattedTaskContent = formattedTaskContent.replace( /^(.*?)(\n|$)/, - `$1 @id(${newId}) $2` + `$1 @id(${newId}) $2`, ); break; default: formattedTaskContent = formattedTaskContent.replace( /^(.*?)(\n|$)/, - `$1 🆔 ${newId} $2` + `$1 🆔 ${newId} $2`, ); break; } @@ -167,7 +168,7 @@ export const getFormattedTaskContentSync = (task: taskItem): string => { */ export const getSanitizedTaskContent = ( plugin: TaskBoard, - updatedTask: taskItem + updatedTask: taskItem, ): string => { // if (updatedTask.title === "") { // return ""; @@ -183,7 +184,7 @@ export const getSanitizedTaskContent = ( updatedTitle = sanitizePriority( globalSettings, updatedTitle, - updatedTask.priority + updatedTask.priority, ); updatedTitle = sanitizeTime(globalSettings, updatedTitle, updatedTask.time); @@ -191,49 +192,49 @@ export const getSanitizedTaskContent = ( updatedTitle = sanitizeCreatedDate( globalSettings, updatedTitle, - updatedTask.createdDate + updatedTask.createdDate, ); updatedTitle = sanitizeStartDate( globalSettings, updatedTitle, - updatedTask.startDate + updatedTask.startDate, ); updatedTitle = sanitizeScheduledDate( globalSettings, updatedTitle, - updatedTask.scheduledDate + updatedTask.scheduledDate, ); updatedTitle = sanitizeDueDate( globalSettings, updatedTitle, - updatedTask.due + updatedTask.due, ); updatedTitle = sanitizeTags( updatedTitle, updatedTask.tags, - updatedTask.tags || [] + updatedTask.tags || [], ); updatedTitle = sanitizeReminder( globalSettings, updatedTitle, - updatedTask?.reminder || "" + updatedTask?.reminder || "", ); updatedTitle = sanitizeCompletionDate( globalSettings, updatedTitle, - updatedTask.completion || "" + updatedTask.completion || "", ); updatedTitle = sanitizeCancelledDate( globalSettings, updatedTitle, - updatedTask.cancelledDate || "" + updatedTask.cancelledDate || "", ); // Build the formatted string for the main task @@ -270,13 +271,15 @@ export const sanitizeStatus = ( globalSettings: globalSettingsData, oldTitle: string, newStatusSymbol: string, - newStatusType: string + newStatusType: string, ): string => { const oldStatusValuematch = oldTitle.match(/\[(.)\]/); // Extract the symbol inside [ ] let newTitle = oldTitle; if (!oldStatusValuematch || oldStatusValuematch.length < 2) { - console.warn( - "There was an issue while extracting the old status value from the old title. Old title value has been returned as it is." + bugReporterManagerInsatance.addToLogs( + 106, + `Status symbol not found in the following oldTtitle : ${oldTitle}`, + "TaskContentFormatter.ts/sanitizeStatus", ); return oldTitle; } @@ -286,22 +289,22 @@ export const sanitizeStatus = ( if (newStatusType === statusTypeNames.DONE) { const moment = _moment as unknown as typeof _moment.default; const currentDateValue = moment().format( - globalSettings?.taskCompletionDateTimePattern + globalSettings?.taskCompletionDateTimePattern, ); newTitle = sanitizeCompletionDate( globalSettings, newTitle, - currentDateValue + currentDateValue, ); } else if (newStatusType === statusTypeNames.CANCELLED) { const moment = _moment as unknown as typeof _moment.default; const currentDateValue = moment().format( - globalSettings?.taskCompletionDateTimePattern + globalSettings?.taskCompletionDateTimePattern, ); newTitle = sanitizeCancelledDate( globalSettings, newTitle, - currentDateValue + currentDateValue, ); } else { newTitle = sanitizeCancelledDate(globalSettings, newTitle, ""); @@ -323,7 +326,7 @@ export const sanitizeCreatedDate = ( globalSettings: globalSettingsData, title: string, createdDate: string, - cursorLocation?: cursorLocation + cursorLocation?: cursorLocation, ): string => { const createdDateRegex = /➕\s*(\d{4}-\d{2}-\d{2}|\d{2}-\d{2}-\d{4})|\[created::\s*?\d{4}-\d{2}-\d{2}\]|@created\(\d{4}-\d{2}-\d{2}\)/; @@ -391,7 +394,7 @@ export const sanitizeStartDate = ( globalSettings: globalSettingsData, title: string, startDate: string, - cursorLocation?: cursorLocation + cursorLocation?: cursorLocation, ): string => { const startDateRegex = /🛫\s*(\d{4}-\d{2}-\d{2}|\d{2}-\d{2}-\d{4})|\[start::\s*?\d{4}-\d{2}-\d{2}\]|@start\(\d{4}-\d{2}-\d{2}\)/; @@ -455,7 +458,7 @@ export const sanitizeScheduledDate = ( globalSettings: globalSettingsData, title: string, scheduledDate: string, - cursorLocation?: cursorLocation + cursorLocation?: cursorLocation, ): string => { const scheduledDateRegex = /⏳\s*(\d{4}-\d{2}-\d{2}|\d{2}-\d{2}-\d{4})|\[scheduled::\s*?\d{4}-\d{2}-\d{2}\]|@scheduled\(\d{4}-\d{2}-\d{2}\)/; @@ -525,7 +528,7 @@ export const sanitizeDueDate = ( globalSettings: globalSettingsData, title: string, dueDate: string, - cursorLocation?: cursorLocation + cursorLocation?: cursorLocation, ): string => { const dueDateRegex = /📅\s*(\d{4}-\d{2}-\d{2}|\d{2}-\d{2}-\d{4})|\[due::\s*?\d{4}-\d{2}-\d{2}\]|@due\(\d{4}-\d{2}-\d{2}\)/; @@ -590,7 +593,7 @@ export const sanitizeCompletionDate = ( globalSettings: globalSettingsData, title: string, completionDate: string, - cursorLocation?: cursorLocation + cursorLocation?: cursorLocation, ): string => { const completionDateRegex = /\[completion::[^\]]+\]|\@completion\(.*?\)|✅\s*.*?(?=\s|$)/; @@ -657,7 +660,7 @@ export const sanitizeCancelledDate = ( globalSettings: globalSettingsData, title: string, cancelledDate: string, - cursorLocation?: cursorLocation + cursorLocation?: cursorLocation, ): string => { const cancellationDateRegex = /❌\s*(\d{4}-\d{2}-\d{2}|\d{2}-\d{2}-\d{4})|\[cancelled::\s*?\d{4}-\d{2}-\d{2}\]|@cancelled\(\d{4}-\d{2}-\d{2}\)/; @@ -724,7 +727,7 @@ export const sanitizeTime = ( globalSettings: globalSettingsData, title: string, newTime: string, - cursorLocation?: cursorLocation + cursorLocation?: cursorLocation, ): string => { const timeAtStartRegex = /]\s*(\d{2}:\d{2}\s*-\s*\d{2}:\d{2})/; const timeFormatsRegex = @@ -822,7 +825,7 @@ export const sanitizePriority = ( globalSettings: globalSettingsData, title: string, newPriority: number, - cursorLocation?: cursorLocation + cursorLocation?: cursorLocation, ): string => { // // Create a regex pattern to match any priority emoji // const emojiPattern = new RegExp( @@ -910,7 +913,7 @@ export const sanitizePriority = ( } let match = title.match( - new RegExp(`\\[priority::\\s*${extractedPriorityMatch}\\s*\\]`) + new RegExp(`\\[priority::\\s*${extractedPriorityMatch}\\s*\\]`), ); if (match) { return newPriority > 0 @@ -919,7 +922,7 @@ export const sanitizePriority = ( } match = title.match( - new RegExp(`@priority\\(\\s*${extractedPriorityMatch}\\s*\\)`) + new RegExp(`@priority\\(\\s*${extractedPriorityMatch}\\s*\\)`), ); if (match) { return newPriority > 0 @@ -933,7 +936,7 @@ export const sanitizePriority = ( } else { return title.replace( priorityEmojis[extractedPriorityMatch], - priorityEmojis[newPriority] + priorityEmojis[newPriority], ); } }; @@ -950,7 +953,7 @@ export const sanitizeTags = ( title: string, oldTagsList: string[], newTagsList: string[], - cursorLocation?: cursorLocation + cursorLocation?: cursorLocation, ): string => { console.log( "sanitizeTags...\ntitle: ", @@ -960,7 +963,7 @@ export const sanitizeTags = ( "\nnewTagsList: ", newTagsList, "\ncursorLocation: ", - cursorLocation + cursorLocation, ); // Remove and tags before processing let updatedTitle = title; @@ -969,7 +972,7 @@ export const sanitizeTags = ( // Regex to extract tags from title const tagsRegex = /\s+#([^\s!@#$%^&*()+=;:'"?<>{}[\]-]+)(?=\s|$)/g; const extractedTags = (tempTitle.match(tagsRegex) || []).map((t) => - t.trim() + t.trim(), ); // const oldTagSet = new Set(oldTagsList.map((t) => t.trim())); @@ -1034,7 +1037,7 @@ export const sanitizeReminder = ( globalSettings: globalSettingsData, title: string, newReminder: string, - cursorLocation?: cursorLocation + cursorLocation?: cursorLocation, ): string => { const formatReminder = (reminder: string) => { const date = new Date(reminder); @@ -1111,7 +1114,7 @@ export const sanitizeDependsOn = ( globalSettings: globalSettingsData, title: string, dependesOnIds: string[], - cursorLocation?: cursorLocation + cursorLocation?: cursorLocation, ): string => { const extractedDependsOnMatch = extractDependsOn(title); @@ -1261,7 +1264,7 @@ export const sanitizeDependsOn = ( // For handleCheckboxChange /** - * Function to remove only the properties which user has configured + * Function to remove only the properties which user has configured * to be hidden using the hiddenTaskProperties setting. * @param plugin - The TaskBoard plugin instance. * @param task - The task item to clean. @@ -1286,7 +1289,7 @@ export const cleanTaskTitle = (plugin: TaskBoard, task: taskItem): string => { cleanedTitle = cleanedTitle .replace( new RegExp(TaskRegularExpressions.indentationAndCheckboxRegex, "u"), - "" + "", ) .trim(); @@ -1370,7 +1373,7 @@ export const cleanTaskTitle = (plugin: TaskBoard, task: taskItem): string => { // Remove priority in various formats if (task.priority > 0) { let match = cleanedTitle.match( - /\[priority::\s*(\d{1,2})\]/ + /\[priority::\s*(\d{1,2})\]/, ); if (match) { cleanedTitle = cleanedTitle.replace(match[0], ""); @@ -1389,7 +1392,7 @@ export const cleanTaskTitle = (plugin: TaskBoard, task: taskItem): string => { `(${Object.values(priorityEmojis) .map((emoji) => `\\s*${emoji}\\s*`) .join("|")})`, - "g" + "g", ); // Replace the first valid priority emoji found @@ -1399,7 +1402,7 @@ export const cleanTaskTitle = (plugin: TaskBoard, task: taskItem): string => { return match.trim() === priorityIcon ? " " : match; - } + }, ); } } @@ -1438,12 +1441,12 @@ export const cleanTaskTitleLegacy = (task: taskItem): string => { cleanedTitle = cleanedTitle .replace( new RegExp(TaskRegularExpressions.indentationAndCheckboxRegex, "u"), - "" + "", ) .trim(); // TODO : Support the legacy feature of adding all properties after the pipe symbol (|). - // If pipe symbol is present (` | `), then remove everything after the pipe symbol. + // If pipe symbol is present (` | `), then remove everything after the pipe symbol. // Remove tags task.tags.forEach((tag) => { @@ -1458,7 +1461,7 @@ export const cleanTaskTitleLegacy = (task: taskItem): string => { if (task.legacyId) { const combinedIdRegex = new RegExp( `(?:${TASKS_PLUGIN_DEFAULT_SYMBOLS.TaskFormatRegularExpressions.idRegex.source})|(?:${DATAVIEW_PLUGIN_DEFAULT_SYMBOLS.TaskFormatRegularExpr.idRegex.source})`, - "g" // add the 'g' flag if you want to match all occurrences + "g", // add the 'g' flag if you want to match all occurrences ); const idMatch = cleanedTitle.match(combinedIdRegex); if (idMatch) { @@ -1538,7 +1541,7 @@ export const cleanTaskTitleLegacy = (task: taskItem): string => { `(${Object.values(priorityEmojis) .map((emoji) => `\\s*${emoji}\\s*`) .join("|")})`, - "g" + "g", ); // Replace the first valid priority emoji found @@ -1552,7 +1555,7 @@ export const cleanTaskTitleLegacy = (task: taskItem): string => { if (task.dependsOn && task.dependsOn.length > 0) { const combinedDependsOnRegex = new RegExp( `(?:${TASKS_PLUGIN_DEFAULT_SYMBOLS.TaskFormatRegularExpressions.dependsOnRegex.source})|(?:${DATAVIEW_PLUGIN_DEFAULT_SYMBOLS.TaskFormatRegularExpr.dependsOnRegex.source})`, - "g" // add the 'g' flag if you want to match all occurrences + "g", // add the 'g' flag if you want to match all occurrences ); const match = cleanedTitle.match(combinedDependsOnRegex); if (match) { @@ -1575,14 +1578,14 @@ export const cleanTaskTitleLegacy = (task: taskItem): string => { .replace( TASKS_PLUGIN_DEFAULT_SYMBOLS.TaskFormatRegularExpressions .recurrenceRegex, - "" + "", ) .trim(); cleanedTitle = cleanedTitle .replace( TASKS_PLUGIN_DEFAULT_SYMBOLS.TaskFormatRegularExpressions .onCompletionRegex, - "" + "", ) .trim(); diff --git a/src/utils/taskLine/TaskItemEventHandlers.ts b/src/utils/taskLine/TaskItemEventHandlers.ts index 4fc8135d..e706dbf0 100644 --- a/src/utils/taskLine/TaskItemEventHandlers.ts +++ b/src/utils/taskLine/TaskItemEventHandlers.ts @@ -42,7 +42,7 @@ export const handleCheckboxChange = (plugin: TaskBoard, task: taskItem) => { plugin.settings.data.globalSettings, task.title, newStatus.newSymbol, - newStatus.newSymbolType + newStatus.newSymbolType, ); const taskWithUpdatedStatus = { ...task, @@ -54,12 +54,12 @@ export const handleCheckboxChange = (plugin: TaskBoard, task: taskItem) => { (newId) => { plugin.realTimeScanner.processAllUpdatedFiles( task.filePath, - task.legacyId + task.legacyId, ); // DEPRECATED : See notes from //src/utils/TaskItemCacheOperations.ts file // moveFromCompletedToPending(plugin, taskWithUpdatedStatus); - } + }, ); // if (isTaskCompleted(`- [${task.status}]`, false, plugin.settings)) { @@ -130,7 +130,7 @@ export const handleCheckboxChange = (plugin: TaskBoard, task: taskItem) => { 45, "Tasks plugin is must for handling recurring tasks. Since the task you are trying to update is a recurring task and Task Board cannot handle recurring tasks as of now. Hence the plugin has not updated your content.", `Tasks plugin installed and enabled: ${tasksPlugin.isTasksPluginEnabled()}`, - "TaskItemUtils.ts/useTasksPluginToUpdateInFile" + "TaskItemUtils.ts/useTasksPluginToUpdateInFile", ); // useTasksPluginToUpdateInFile(plugin, tasksPlugin, task) @@ -154,23 +154,18 @@ export const handleCheckboxChange = (plugin: TaskBoard, task: taskItem) => { .then(() => { plugin.realTimeScanner.processAllUpdatedFiles( task.filePath, - task.legacyId + task.legacyId, ); // NOTE : This is not necessary any more as I am scanning the file after it has been updated. // // Move from Pending to Completed // moveFromPendingToCompleted(plugin, taskWithUpdatedStatus); }) - .catch((error) => { - // bugReporterManagerInsatance.showNotice( - // 46, - // "Error updating recurring task in file", - // error as string, - // "TaskItemEventHandlers.ts/handleCheckboxChange" - // ); - console.error( - "TaskItemEventHandlers.ts : Error updating recurring task in file", - error + .catch((err) => { + bugReporterManagerInsatance.addToLogs( + 152, + String(err), + "TaskItemEventHandlers.ts/handleCheckboxChange", ); }); } @@ -186,7 +181,7 @@ export const handleCheckboxChange = (plugin: TaskBoard, task: taskItem) => { export const handleSubTasksChange = ( plugin: TaskBoard, oldTask: taskItem, - updatedTask: taskItem + updatedTask: taskItem, ) => { // DEPRECATED : See notes from //src/utils/TaskItemCacheOperations.ts file // updateTaskInJson(plugin, updatedTask); @@ -194,7 +189,7 @@ export const handleSubTasksChange = ( updateTaskInFile(plugin, updatedTask, oldTask).then((newId) => { plugin.realTimeScanner.processAllUpdatedFiles( updatedTask.filePath, - oldTask.id + oldTask.id, ); }); }; @@ -208,7 +203,7 @@ export const handleSubTasksChange = ( export const handleDeleteTask = ( plugin: TaskBoard, task: taskItem, - isTaskNote: boolean + isTaskNote: boolean, ) => { const mssg = t("confirm-task-delete-description"); const app = plugin.app; @@ -221,7 +216,7 @@ export const handleDeleteTask = ( } else { deleteTaskFromFile(plugin, task).then(() => { plugin.realTimeScanner.processAllUpdatedFiles( - task.filePath + task.filePath, ); }); diff --git a/src/utils/taskNote/FrontmatterOperations.ts b/src/utils/taskNote/FrontmatterOperations.ts index 5679ed14..95ea56de 100644 --- a/src/utils/taskNote/FrontmatterOperations.ts +++ b/src/utils/taskNote/FrontmatterOperations.ts @@ -11,6 +11,7 @@ import { generateTaskId } from "../TaskItemUtils"; import { statusTypeNames } from "src/interfaces/Enums"; import { moment as _moment } from "obsidian"; import { getCurrentLocalTimeString } from "../DateTimeCalculations"; +import { bugReporterManagerInsatance } from "src/managers/BugReporter"; /** * Extract frontmatter from file content @@ -20,7 +21,7 @@ import { getCurrentLocalTimeString } from "../DateTimeCalculations"; */ export function extractFrontmatterFromFile( plugin: TaskBoard, - file: TFile + file: TFile, ): customFrontmatterCache | undefined { // Method 1 - Find the frontmatter using delimiters // // Check if the file starts with frontmatter delimiter @@ -72,7 +73,7 @@ export function extractFrontmatterFromFile( */ export function extractFrontmatterFromContent( plugin: TaskBoard, - fileContent: string + fileContent: string, ): customFrontmatterCache | undefined { // Method 1 - Find the frontmatter using delimiters // Check if the content starts with frontmatter delimiter @@ -94,9 +95,10 @@ export function extractFrontmatterFromContent( const frontmatter = parseYaml(yamlContent) as customFrontmatterCache; return frontmatter; } catch (error) { - console.warn( - "FrontmatterOperations.ts/extractFrontmatterFromContent : Failed to parse frontmatter:", - error + bugReporterManagerInsatance.addToLogs( + 107, + String(error), + "TaskContentFormatter.ts/sanitizeStatus", ); return undefined; } @@ -108,7 +110,7 @@ export function extractFrontmatterFromContent( * @returns string[] - Array of tags */ export function extractFrontmatterTags( - frontmatter: Partial | undefined + frontmatter: Partial | undefined, ): string[] { if (!frontmatter) { return []; @@ -150,18 +152,18 @@ export function extractFrontmatterTags( function orderFrontmatterProperties( frontmatterObj: Partial, frontmatterFormatting: frontmatterFormatting[], - existingFrontmatter?: customFrontmatterCache + existingFrontmatter?: customFrontmatterCache, ): Partial { const orderedFrontmatter: Partial = {}; // Create a set of all custom frontmatter keys for quick lookup const customKeys = new Set( - frontmatterFormatting.map((format) => format.key) + frontmatterFormatting.map((format) => format.key), ); // Sort frontmatter formatting by index and add properties in order const sortedFormatting = [...frontmatterFormatting].sort( - (a, b) => a.index - b.index + (a, b) => a.index - b.index, ); for (const format of sortedFormatting) { @@ -202,7 +204,7 @@ function orderFrontmatterProperties( export function createFrontmatterFromTask( plugin: TaskBoard, task: taskItem, - frontmatterFormatting: frontmatterFormatting[] + frontmatterFormatting: frontmatterFormatting[], ): string { const frontmatterObj: Partial = {}; @@ -211,15 +213,15 @@ export function createFrontmatterFromTask( frontmatterObj[getCustomFrontmatterKey("status", frontmatterFormatting)] = getStatusNameFromStatusSymbol( task?.status, - plugin.settings.data.globalSettings + plugin.settings.data.globalSettings, ) || "pending"; frontmatterObj[getCustomFrontmatterKey("tags", frontmatterFormatting)] = [ plugin.settings.data.globalSettings.taskNoteIdentifierTag, ...(task?.tags?.filter( (tag) => tag.includes( - plugin.settings.data.globalSettings.taskNoteIdentifierTag - ) === false + plugin.settings.data.globalSettings.taskNoteIdentifierTag, + ) === false, ) ?? []), ]; @@ -270,7 +272,7 @@ export function createFrontmatterFromTask( // Order the frontmatter properties based on index values const orderedFrontmatter = orderFrontmatterProperties( frontmatterObj, - frontmatterFormatting + frontmatterFormatting, ); return createYamlFromObject(orderedFrontmatter); @@ -288,7 +290,7 @@ export function updateFrontmatterProperties( plugin: TaskBoard, existingFrontmatter: customFrontmatterCache | undefined, task: taskItem, - forceId?: boolean + forceId?: boolean, ): Partial { const frontmatterFormatting: frontmatterFormatting[] = plugin.settings.data.globalSettings.frontmatterFormatting; @@ -357,13 +359,13 @@ export function updateFrontmatterProperties( const hasIdentifierTag = finalTags.some( (tag) => - tag.replace("#", "").toLowerCase() === identifierTag.toLowerCase() + tag.replace("#", "").toLowerCase() === identifierTag.toLowerCase(), ); finalTags = hasIdentifierTag ? finalTags : [...finalTags, identifierTag]; // Remove duplicates and empty entries tempUpdates[tagsKey] = Array.from( - new Set(finalTags.map((t) => String(t).trim()).filter(Boolean)) + new Set(finalTags.map((t) => String(t).trim()).filter(Boolean)), ); // Update or add unique ID @@ -383,7 +385,7 @@ export function updateFrontmatterProperties( if (task.status) { const statusName = getStatusNameFromStatusSymbol( task.status, - plugin.settings.data.globalSettings + plugin.settings.data.globalSettings, ); tempUpdates[statusKey] = statusName ?? `"${task.status}"`; } @@ -402,7 +404,7 @@ export function updateFrontmatterProperties( // Update date properties const createdDateKey = getCustomFrontmatterKey( "createdDate", - frontmatterFormatting + frontmatterFormatting, ); if (task.createdDate) { tempUpdates[createdDateKey] = task.createdDate; @@ -413,7 +415,7 @@ export function updateFrontmatterProperties( const startDateKey = getCustomFrontmatterKey( "startDate", - frontmatterFormatting + frontmatterFormatting, ); if (task.startDate) { tempUpdates[startDateKey] = task.startDate; @@ -424,7 +426,7 @@ export function updateFrontmatterProperties( const scheduledDateKey = getCustomFrontmatterKey( "scheduledDate", - frontmatterFormatting + frontmatterFormatting, ); if (task.scheduledDate) { tempUpdates[scheduledDateKey] = task.scheduledDate; @@ -443,13 +445,13 @@ export function updateFrontmatterProperties( const statusConfig = plugin.settings.data.globalSettings.customStatuses.find( - (status) => status.symbol === task.status + (status) => status.symbol === task.status, ); const statusType = statusConfig ? statusConfig.type : undefined; const cancelledDateKey = getCustomFrontmatterKey( "cancelledDate", - frontmatterFormatting + frontmatterFormatting, ); if (statusType === statusTypeNames.CANCELLED) { if (task.cancelledDate) { @@ -458,7 +460,7 @@ export function updateFrontmatterProperties( const globalSettings = plugin.settings.data.globalSettings; const moment = _moment as unknown as typeof _moment.default; const currentDateValue = moment().format( - globalSettings?.taskCompletionDateTimePattern + globalSettings?.taskCompletionDateTimePattern, ); tempUpdates[cancelledDateKey] = currentDateValue; @@ -470,7 +472,7 @@ export function updateFrontmatterProperties( const completionKey = getCustomFrontmatterKey( "completion", - frontmatterFormatting + frontmatterFormatting, ); if (statusType === statusTypeNames.DONE) { if (task.cancelledDate) { @@ -479,7 +481,7 @@ export function updateFrontmatterProperties( const globalSettings = plugin.settings.data.globalSettings; const moment = _moment as unknown as typeof _moment.default; const currentDateValue = moment().format( - globalSettings?.taskCompletionDateTimePattern + globalSettings?.taskCompletionDateTimePattern, ); tempUpdates[completionKey] = currentDateValue; @@ -491,7 +493,7 @@ export function updateFrontmatterProperties( const priorityKey = getCustomFrontmatterKey( "priority", - frontmatterFormatting + frontmatterFormatting, ); if (task.priority && task.priority > 0) { tempUpdates[priorityKey] = @@ -503,7 +505,7 @@ export function updateFrontmatterProperties( const reminderKey = getCustomFrontmatterKey( "reminder", - frontmatterFormatting + frontmatterFormatting, ); if (task?.reminder) { tempUpdates[reminderKey] = task.reminder; @@ -514,7 +516,7 @@ export function updateFrontmatterProperties( const dependsOnKey = getCustomFrontmatterKey( "dependsOn", - frontmatterFormatting + frontmatterFormatting, ); if (task?.dependsOn && task.dependsOn.length > 0) { tempUpdates[dependsOnKey] = task.dependsOn; @@ -527,7 +529,7 @@ export function updateFrontmatterProperties( const orderedFrontmatter = orderFrontmatterProperties( tempUpdates, frontmatterFormatting, - oldFrontmatter + oldFrontmatter, ); return orderedFrontmatter; @@ -539,7 +541,7 @@ export function updateFrontmatterProperties( * @returns string - YAML string */ export function createYamlFromObject( - obj: Partial + obj: Partial, ): string { // METHOD 1 - Using Obsidian's API const YAMLstringUsingAPI = stringifyYaml(obj); diff --git a/src/utils/taskNote/TaskNoteEventHandlers.ts b/src/utils/taskNote/TaskNoteEventHandlers.ts index f5e4a5a1..e6db227d 100644 --- a/src/utils/taskNote/TaskNoteEventHandlers.ts +++ b/src/utils/taskNote/TaskNoteEventHandlers.ts @@ -14,6 +14,7 @@ import { } from "../MarkdownFileOperations"; import { moment as _moment } from "obsidian"; import { statusTypeNames } from "src/interfaces/Enums"; +import { bugReporterManagerInsatance } from "src/managers/BugReporter"; /** * Handle task note status change (checkbox change) @@ -73,8 +74,12 @@ export const handleTaskNoteStatusChange = async ( new Notice(`Task note status updated to ${newStatusName}`); } catch (error) { - console.error("Error updating task note status:", error); - new Notice("Error updating task note status: " + String(error)); + bugReporterManagerInsatance.showNotice( + 153, + "There was an error while updating the status.", + String(error), + "TaskNoteEventHandlers.ts/handleTaskNoteStatusChange", + ); } }; @@ -103,8 +108,12 @@ export const handleTaskNotePropertyUpdate = async ( new Notice("Task note properties updated"); } catch (error) { - console.error("Error updating task note properties:", error); - new Notice("Error updating task note properties: " + String(error)); + bugReporterManagerInsatance.showNotice( + 154, + "There was an error while updating the task note.", + String(error), + "TaskNoteEventHandlers.ts/handleTaskNotePropertyUpdate", + ); } }; @@ -113,7 +122,7 @@ export const handleTaskNotePropertyUpdate = async ( * @param plugin - TaskBoard plugin instance * @param task - Task note to delete */ -export const handleTaskNoteDelete = async ( +export const handleTaskNote2NormalNote = async ( plugin: TaskBoard, task: taskItem, ) => { @@ -146,8 +155,12 @@ export const handleTaskNoteDelete = async ( new Notice("Task note converted back to regular note"); } } catch (error) { - console.error("Error deleting task note:", error); - new Notice("Error deleting task note: " + String(error)); + bugReporterManagerInsatance.showNotice( + 155, + "There was an error while converting the note back to normal note.", + String(error), + "TaskNoteEventHandlers.ts/handleTaskNote2NormalNote", + ); } }; @@ -213,9 +226,11 @@ export const handleTaskNoteBodyChange = async ( }); }); } catch (error) { - console.error( - "TaskItemEventHandlers.ts : Error in handleTaskNoteBodyChange", - error, + bugReporterManagerInsatance.showNotice( + 156, + "There was an error while updating the body of the task note. Below error will provide some more information.", + String(error), + "TaskNoteEventHandlers.ts/handleTaskNoteBodyChange", ); } }; diff --git a/src/utils/taskNote/TaskNoteUtils.ts b/src/utils/taskNote/TaskNoteUtils.ts index 5ffefeaf..471f5d79 100644 --- a/src/utils/taskNote/TaskNoteUtils.ts +++ b/src/utils/taskNote/TaskNoteUtils.ts @@ -25,7 +25,7 @@ import { bugReporterManagerInsatance } from "src/managers/BugReporter"; */ export function isTaskNotePresentInFrontmatter( taskNoteIdentifierTag: string, - frontmatter: Partial | undefined + frontmatter: Partial | undefined, ): boolean { if (!frontmatter || !frontmatter.tags) { return false; @@ -50,12 +50,12 @@ export function isTaskNotePresentInFrontmatter( */ export function isTaskNotePresentInTags( taskNoteIdentifierTag: string, - tags: string[] + tags: string[], ): boolean { return tags ? tags.some((tag) => - tag.toLowerCase().includes(taskNoteIdentifierTag.toLowerCase()) - ) + tag.toLowerCase().includes(taskNoteIdentifierTag.toLowerCase()), + ) : false; } @@ -68,7 +68,7 @@ export function isTaskNotePresentInTags( export function extractTaskNoteProperties( frontmatter: Partial | undefined, filePath: string, - settings: PluginDataJson + settings: PluginDataJson, ): Partial { if (!frontmatter) { return {}; @@ -86,20 +86,20 @@ export function extractTaskNoteProperties( getCustomFrontmatterKey("title", frontmatterFormatting) ] || "", tags: Array.isArray( - frontmatter[getCustomFrontmatterKey("tags", frontmatterFormatting)] + frontmatter[getCustomFrontmatterKey("tags", frontmatterFormatting)], ) ? frontmatter[ getCustomFrontmatterKey("tags", frontmatterFormatting) - ] + ] : typeof frontmatter[ - getCustomFrontmatterKey("tags", frontmatterFormatting) - ] === "string" - ? frontmatter[ - getCustomFrontmatterKey("tags", frontmatterFormatting) - ] - .split(",") - .map((tag: string) => tag.trim()) - : [], + getCustomFrontmatterKey("tags", frontmatterFormatting) + ] === "string" + ? frontmatter[ + getCustomFrontmatterKey("tags", frontmatterFormatting) + ] + .split(",") + .map((tag: string) => tag.trim()) + : [], time: frontmatter[ getCustomFrontmatterKey("time", frontmatterFormatting) @@ -131,13 +131,13 @@ export function extractTaskNoteProperties( priority: mapPriorityNameFromFrontmatter( frontmatter[ getCustomFrontmatterKey("priority", frontmatterFormatting) - ] + ], ), status: getStatusSymbolFromStatusName( frontmatter[ getCustomFrontmatterKey("status", frontmatterFormatting) ], - settings + settings, ), dependsOn: frontmatter[ @@ -197,11 +197,11 @@ export function getPriorityNameForTaskNote(priority: number): string { export function getCustomFrontmatterKey( taskItemKey: string, - frontmatterFormatting: frontmatterFormatting[] + frontmatterFormatting: frontmatterFormatting[], ): string { // Find the custom mapping for this task item key const customMapping = frontmatterFormatting.find( - (mapping) => mapping.taskItemKey === taskItemKey + (mapping) => mapping.taskItemKey === taskItemKey, ); // Return custom frontmatter key if found, otherwise return the original key @@ -216,7 +216,7 @@ export function getCustomFrontmatterKey( */ export function getStatusSymbolFromStatusName( statusName: string | undefined, - settings: PluginDataJson + settings: PluginDataJson, ): string { if (!statusName) return " "; @@ -248,7 +248,7 @@ export function getStatusSymbolFromStatusName( */ export function getStatusNameFromStatusSymbol( statusSymbol: string | undefined, - globalSettings: globalSettingsData + globalSettings: globalSettingsData, ): string { if (!statusSymbol) return "pending"; @@ -286,7 +286,7 @@ export function getStatusNameFromStatusSymbol( export function formatTaskNoteContent( plugin: TaskBoard, updatedTask: taskItem, - oldNoteContent: string + oldNoteContent: string, ): { newContent: string; newFrontmatter: string; @@ -295,14 +295,14 @@ export function formatTaskNoteContent( try { const existingFrontmatter = extractFrontmatterFromContent( plugin, - oldNoteContent + oldNoteContent, ); // Update frontmatter properties based on updatedTask const updatedFrontmatter = updateFrontmatterProperties( plugin, existingFrontmatter, - updatedTask + updatedTask, ); const newFrontmatter = createYamlFromObject(updatedFrontmatter); @@ -315,7 +315,11 @@ export function formatTaskNoteContent( }`; // I hope the content returned from the stringifyYaml API will always have a newline at the end. return { newContent, newFrontmatter, contentWithoutFrontmatter }; } catch (error) { - console.error("Error updating task note frontmatter:", error); + bugReporterManagerInsatance.addToLogs( + 157, + String(error), + "TaskNoteUtils.ts/formatTaskNoteContent", + ); return { newContent: "", newFrontmatter: "", @@ -334,7 +338,7 @@ export function formatTaskNoteContent( export async function updateFrontmatterInMarkdownFile( plugin: TaskBoard, task: taskItem, - forceId?: boolean + forceId?: boolean, ): Promise { try { const file = plugin.app.vault.getFileByPath(task.filePath); @@ -348,13 +352,13 @@ export async function updateFrontmatterInMarkdownFile( plugin, existing, task, - forceId + forceId, ); console.log( "updateFrontmatterInMarkdownFile...\nUpdated frontmatter", updated, "\nold frontmatter", - existing + existing, ); for (const key of Object.keys(updated)) { existing[key] = updated[key]; @@ -395,7 +399,11 @@ export async function updateFrontmatterInMarkdownFile( // await writeDataToVaultFile(plugin, task.filePath, newContent); } catch (error) { - console.error("Error updating task note frontmatter:", error); + bugReporterManagerInsatance.addToLogs( + 158, + String(error), + "TaskNoteUtils.ts/updateFrontmatterInMarkdownFile", + ); throw error; } } @@ -407,7 +415,7 @@ export async function updateFrontmatterInMarkdownFile( */ export async function deleteTaskNote( plugin: TaskBoard, - filePath: string + filePath: string, ): Promise { try { const file = plugin.app.vault.getFileByPath(filePath); @@ -416,7 +424,7 @@ export async function deleteTaskNote( 64, "There was an issue while deleting the task note.", `File not found at path: ${filePath}`, - "deleteTaskNote" + "deleteTaskNote", ); return; } @@ -424,12 +432,11 @@ export async function deleteTaskNote( await plugin.app.vault.trash(file, true); new Notice(`Task note deleted: ${file.name}`); } catch (error) { - console.error("Error deleting task note:", error); bugReporterManagerInsatance.showNotice( 65, "There was an issue while deleting the task note.", String(error), - "deleteTaskNote" + "deleteTaskNote", ); throw error; } @@ -442,7 +449,7 @@ export async function deleteTaskNote( */ export async function archiveTaskNote( plugin: TaskBoard, - filePath: string + filePath: string, ): Promise { try { const file = plugin.app.vault.getFileByPath(filePath); @@ -451,7 +458,7 @@ export async function archiveTaskNote( 66, "There was an issue while archiving the task note.", `File not found at path: ${filePath}`, - "archiveTaskNote" + "archiveTaskNote", ); return; } @@ -475,7 +482,7 @@ export async function archiveTaskNote( // Construct the new file path const newFilePath = normalizePath( - `${normalizedArchivePath}/${file.name}` + `${normalizedArchivePath}/${file.name}`, ); // Check if a file with the same name already exists in the archive folder @@ -485,11 +492,11 @@ export async function archiveTaskNote( const nameWithoutExt = file.basename; const ext = file.extension; const uniqueFilePath = normalizePath( - `${normalizedArchivePath}/${nameWithoutExt}-${timestamp}.${ext}` + `${normalizedArchivePath}/${nameWithoutExt}-${timestamp}.${ext}`, ); await plugin.app.vault.rename(file, uniqueFilePath); new Notice( - `Task note archived as: ${nameWithoutExt}-${timestamp}.${ext}` + `Task note archived as: ${nameWithoutExt}-${timestamp}.${ext}`, ); } else { await plugin.app.vault.rename(file, newFilePath); @@ -502,12 +509,11 @@ export async function archiveTaskNote( if (plugin.vaultScanner.tasksCache.Completed[filePath]) delete plugin.vaultScanner.tasksCache.Completed[filePath]; } catch (error) { - console.error("Error archiving task note:", error); bugReporterManagerInsatance.showNotice( 67, "There was an issue while archiving the task note.", String(error), - "archiveTaskNote" + "archiveTaskNote", ); // throw error; } diff --git a/src/views/AddOrEditTaskView.tsx b/src/views/AddOrEditTaskView.tsx index 7e873706..0e370ca3 100644 --- a/src/views/AddOrEditTaskView.tsx +++ b/src/views/AddOrEditTaskView.tsx @@ -8,7 +8,7 @@ import { t } from "src/utils/lang/helper"; import { AddOrEditTaskRC } from "src/components/AddOrEditTaskRC"; import { getFormattedTaskContent } from "src/utils/taskLine/TaskContentFormatter"; import { readDataOfVaultFile } from "src/utils/MarkdownFileOperations"; -import { getLocalDateTimeString } from "src/utils/DateTimeCalculations"; +import { getCurrentLocalTimeString } from "src/utils/DateTimeCalculations"; import { allowedFileExtensionsRegEx } from "src/regularExpressions/MiscelleneousRegExpr"; import { taskItemEmpty } from "src/interfaces/Mapping"; import { taskItem } from "src/interfaces/TaskItem"; @@ -83,7 +83,7 @@ export class AddOrEditTaskView extends ItemView { noteContent = "---\ntitle: \n---\n"; const defaultLocation = this.plugin.settings.data.globalSettings.taskNoteDefaultLocation || 'Meta/Task_Board/Task_Notes'; - const noteName = this.task.title || getLocalDateTimeString(); + const noteName = this.task.title || getCurrentLocalTimeString(); // Sanitize filename const sanitizedName = noteName.replace(/[<>:"/\\|?*]/g, '_'); this.filePath = normalizePath(`${defaultLocation}/${sanitizedName}.md`); From d85fbc444c7b51cb30ad839ae2d7dd622dfec648 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Mon, 26 Jan 2026 12:42:20 +0530 Subject: [PATCH 033/236] test the latest changes | working --- data.json | 7 ++++--- task-board-logs.log | 12 ++++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/data.json b/data.json index 75d49677..99446f04 100644 --- a/data.json +++ b/data.json @@ -1050,12 +1050,12 @@ "taskPropertyFormat": "3", "taskNoteDefaultLocation": "", "autoAddUniqueID": true, - "uniqueIdCounter": 587, + "uniqueIdCounter": 588, "experimentalFeatures": true, "searchQuery": "", "lastViewHistory": { "viewedType": "kanban", - "boardIndex": 4, + "boardIndex": 2, "taskId": "", "settingTab": 7 }, @@ -1182,7 +1182,8 @@ "dependsOn", "filePath", "status", - "checkbox" + "checkbox", + "subTasks" ], "taskCardStyle": "emoji", "autoAddCompletedDate": true, diff --git a/task-board-logs.log b/task-board-logs.log index d497f42c..7b45e334 100644 --- a/task-board-logs.log +++ b/task-board-logs.log @@ -49,3 +49,15 @@ Context : JsonFileOperations.ts/moveTasksCacheFileToNewPath ```log Error: ENOENT: no such file or directory, rename 'D:\Personal_Projects_Hub\IDE_Wise_Projects\Obsidian\TemplateToDevelopPlugin\Meta\Task_Board\task-board-cache.json' -> 'D:\Personal_Projects_Hub\IDE_Wise_Projects\Obsidian\TemplateToDevelopPlugin\Meta\Task_Board\Cache\task-board-cache.json' ``` + +------------- + +Timestamp : 2026-01-26T06:52:46.386Z +ID : 17 +Message : Following duplicate IDs has been found for tasks with IDs: "390". This may cause unexpected behavior. Please consider changing the IDs of these tasks. +Context : MapView.tsx/initialNodes + +#### Bug Content +```log +ERROR: Same id is present on two tasks +``` From cfc6b5d5ccbfa1d35dfceb8a642c7c43c941a063 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Tue, 27 Jan 2026 11:05:35 +0530 Subject: [PATCH 034/236] Deprecated : the setting to switch between lazyColumn and normal column --- src/settings/SettingConstructUI.ts | 51 +++++++++++++++--------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/src/settings/SettingConstructUI.ts b/src/settings/SettingConstructUI.ts index 3d3a874a..1c4ec4e2 100644 --- a/src/settings/SettingConstructUI.ts +++ b/src/settings/SettingConstructUI.ts @@ -953,31 +953,32 @@ export class SettingsManager { ); // Lazy loading settings for Kanban view - new Setting(contentEl) - .setName("Enable lazy loading for Kanban view") - .setDesc( - "When enabled, only a limited number of task cards are initially rendered per column. More tasks load automatically as you scroll within each column. This significantly improves performance for boards with thousands of tasks. This setting is just to provide a backward compatibility in case user faces any issue with rendering. If there are no issues faced with this approach, this setting will be removed in the future releases and this technique will be used a default behavior for rendering the columns.", - ) - .addToggle((toggle) => - toggle - .setValue( - this.globalSettings!.kanbanView?.lazyLoadingEnabled ?? - false, - ) - .onChange(async (value) => { - if (!this.globalSettings!.kanbanView) { - this.globalSettings!.kanbanView = { - lazyLoadingEnabled: false, - initialTaskCount: 20, - loadMoreCount: 10, - scrollThresholdPercent: 80, - }; - } - this.globalSettings!.kanbanView.lazyLoadingEnabled = - value; - await this.saveSettings(); - }), - ); + // @deprecated - v1.9.0 + // new Setting(contentEl) + // .setName("Enable lazy loading for Kanban view") + // .setDesc( + // "When enabled, only a limited number of task cards are initially rendered per column. More tasks load automatically as you scroll within each column. This significantly improves performance for boards with thousands of tasks. This setting is just to provide a backward compatibility in case user faces any issue with rendering. If there are no issues faced with this approach, this setting will be removed in the future releases and this technique will be used a default behavior for rendering the columns.", + // ) + // .addToggle((toggle) => + // toggle + // .setValue( + // this.globalSettings!.kanbanView?.lazyLoadingEnabled ?? + // false, + // ) + // .onChange(async (value) => { + // if (!this.globalSettings!.kanbanView) { + // this.globalSettings!.kanbanView = { + // lazyLoadingEnabled: false, + // initialTaskCount: 20, + // loadMoreCount: 10, + // scrollThresholdPercent: 80, + // }; + // } + // this.globalSettings!.kanbanView.lazyLoadingEnabled = + // value; + // await this.saveSettings(); + // }), + // ); new Setting(contentEl).setName(t("tag-colors")).setHeading(); From 98df0f19d2c734358383977adee0e78e1bdca630 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Tue, 27 Jan 2026 11:06:22 +0530 Subject: [PATCH 035/236] feat : add icon in setting and small UI improvement --- src/settings/SettingConstructUI.ts | 41 ++++++++++++++++++----------- src/settings/TaskBoardSettingTab.ts | 2 ++ 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/src/settings/SettingConstructUI.ts b/src/settings/SettingConstructUI.ts index 1c4ec4e2..e233383b 100644 --- a/src/settings/SettingConstructUI.ts +++ b/src/settings/SettingConstructUI.ts @@ -1,6 +1,13 @@ // /src/views/TaskBoardSettingConstructUI.ts -import { App, Notice, Setting, normalizePath, setIcon } from "obsidian"; +import { + App, + Notice, + Setting, + SettingGroup, + normalizePath, + setIcon, +} from "obsidian"; import { buyMeCoffeeSVGIcon, kofiSVGIcon } from "src/interfaces/Icons"; import Pickr from "@simonwep/pickr"; import Sortable from "sortablejs"; @@ -1225,21 +1232,23 @@ export class SettingsManager { renderTagColors(); // Add "Add New Tag Color" button - new Setting(contentEl).addButton((btn) => - btn - .setButtonText(t("add-tag-color")) - .setCta() - .onClick(async () => { - const newTag = { - name: "", - color: "rgba(255, 0, 0, 1)", - priority: this.globalSettings!.tagColors.length + 1, - }; - this.globalSettings!.tagColors.push(newTag); - await this.saveSettings(); - renderTagColors(); - }), - ); + new Setting(contentEl) + .addButton((btn) => + btn + .setButtonText(t("add-tag-color")) + .setCta() + .onClick(async () => { + const newTag = { + name: "", + color: "rgba(255, 0, 0, 1)", + priority: this.globalSettings!.tagColors.length + 1, + }; + this.globalSettings!.tagColors.push(newTag); + await this.saveSettings(); + renderTagColors(); + }), + ) + .setClass("task-board-settingtab-add-tag-color-btn"); // new Setting(contentEl) // .setName(t("live-editor-and-reading-mode")) diff --git a/src/settings/TaskBoardSettingTab.ts b/src/settings/TaskBoardSettingTab.ts index cc6c1d5d..c3a09251 100644 --- a/src/settings/TaskBoardSettingTab.ts +++ b/src/settings/TaskBoardSettingTab.ts @@ -6,11 +6,13 @@ import { SettingsManager } from "src/settings/SettingConstructUI"; import type TaskBoard from "../../main"; import { globalSettingsData } from "src/interfaces/GlobalSettings"; import { t } from "src/utils/lang/helper"; +import { TaskBoardIcon } from "src/interfaces/Icons"; export class TaskBoardSettingTab extends PluginSettingTab { plugin: TaskBoard; settingsManager: SettingsManager; globalSettings: globalSettingsData | null = null; + icon: string = TaskBoardIcon; constructor(app: App, plugin: TaskBoard) { super(app, plugin); From 2c6b5ffc77991e55683807a05873331e12e4044b Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Tue, 27 Jan 2026 11:07:06 +0530 Subject: [PATCH 036/236] BREAKING : update node packages --- package-lock.json | 654 ++++++++++++++++++++++++++-------------------- 1 file changed, 365 insertions(+), 289 deletions(-) diff --git a/package-lock.json b/package-lock.json index 62b4674c..3f49bff0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -55,9 +55,9 @@ } }, "node_modules/@antfu/utils": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-8.1.1.tgz", - "integrity": "sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-9.2.0.tgz", + "integrity": "sha512-Oq1d9BGZakE/FyoEtcNeSwM7MpDO2vUBi11RWBZXf75zPsbUVWmUs03EqkRFrcgbXyKTas0BdZWC1wcuSoqSAw==", "dev": true, "license": "MIT", "funding": { @@ -65,9 +65,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.27.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz", - "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", + "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -102,6 +102,13 @@ "lodash-es": "4.17.21" } }, + "node_modules/@chevrotain/cst-dts-gen/node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "dev": true, + "license": "MIT" + }, "node_modules/@chevrotain/gast": { "version": "11.0.3", "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-11.0.3.tgz", @@ -113,6 +120,13 @@ "lodash-es": "4.17.21" } }, + "node_modules/@chevrotain/gast/node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "dev": true, + "license": "MIT" + }, "node_modules/@chevrotain/regexp-to-ast": { "version": "11.0.3", "resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-11.0.3.tgz", @@ -135,9 +149,9 @@ "license": "Apache-2.0" }, "node_modules/@codemirror/autocomplete": { - "version": "6.18.6", - "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.6.tgz", - "integrity": "sha512-PHHBXFomUs5DF+9tCOM/UoW6XQ4R44lLNNhRaW9PKPTU0D7lIjRg3ElxaJnTwsl/oHiR93WSXDBrekhoUGCPtg==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.0.tgz", + "integrity": "sha512-bOwvTOIJcG5FVo5gUUupiwYh8MioPLQ4UcqbcRf7UQ98X90tCa9E1kZ3Z7tqwpZxYyOvh1YTYbmZE9RTfTp5hg==", "dev": true, "license": "MIT", "dependencies": { @@ -148,9 +162,9 @@ } }, "node_modules/@codemirror/commands": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.8.1.tgz", - "integrity": "sha512-KlGVYufHMQzxbdQONiLyGQDUW0itrLZwq3CcY7xpv9ZLRHqzkBSoteocBHtMCoY7/Ci4xhzSrToIeLg7FxHuaw==", + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.10.1.tgz", + "integrity": "sha512-uWDWFypNdQmz2y1LaNJzK7fL7TYKLeUAU0npEC685OKTF3KcQ2Vu3klIM78D7I6wGhktme0lh3CuQLv0ZCrD9Q==", "dev": true, "license": "MIT", "dependencies": { @@ -162,7 +176,7 @@ }, "node_modules/@codemirror/language": { "version": "6.11.2", - "resolved": "git+ssh://git@github.com/lishid/cm-language.git#a9c3c7efe17dd1d24395ee2a179fe12dd6ed1e76", + "resolved": "git+ssh://git@github.com/lishid/cm-language.git#2d416d7835867d1b1e8d0e726b147fc1135c9f92", "dev": true, "license": "MIT", "dependencies": { @@ -175,9 +189,9 @@ } }, "node_modules/@codemirror/lint": { - "version": "6.8.5", - "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.5.tgz", - "integrity": "sha512-s3n3KisH7dx3vsoeGMxsbRAgKe4O1vbrnKBClm99PU0fWxmxsx5rR2PfqQgIt+2MMJBHbiJ5rfIdLYfB9NNvsA==", + "version": "6.9.2", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.9.2.tgz", + "integrity": "sha512-sv3DylBiIyi+xKwRCJAAsBZZZWo82shJ/RTMymLabAdtbkV5cSKwWDeCgtUq3v8flTaXS2y1kKkICuRYtUswyQ==", "dev": true, "license": "MIT", "dependencies": { @@ -187,14 +201,14 @@ } }, "node_modules/@codemirror/search": { - "version": "6.5.11", - "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.11.tgz", - "integrity": "sha512-KmWepDE6jUdL6n8cAAqIpRmLPBZ5ZKnicE8oGU/s3QrAVID+0VhLFrzUucVKHG5035/BSykhExDL/Xm7dHthiA==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.6.0.tgz", + "integrity": "sha512-koFuNXcDvyyotWcgOnZGmY7LZqEOXZaaxD/j6n18TCLx2/9HieZJ5H6hs1g8FiRxBD0DNfs0nXn17g872RmYdw==", "dev": true, "license": "MIT", "dependencies": { "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", + "@codemirror/view": "^6.37.0", "crelt": "^1.0.5" } }, @@ -209,9 +223,9 @@ } }, "node_modules/@codemirror/view": { - "version": "6.38.1", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.1.tgz", - "integrity": "sha512-RmTOkE7hRU3OVREqFVITWHz6ocgBjv08GoePscAakgVQfciA3SGCEk7mb9IzwW61cKKmlTpHXG6DUE5Ubx+MGQ==", + "version": "6.38.6", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.6.tgz", + "integrity": "sha512-qiS0z1bKs5WOvHIAC0Cybmv4AJSkAXgX5aD6Mqd2epSLlVJsQl8NG23jCVouIgkh4All/mrbdsf2UOLFnJw0tw==", "dev": true, "license": "MIT", "dependencies": { @@ -696,9 +710,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", - "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, "license": "MIT", "dependencies": { @@ -715,9 +729,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, "license": "MIT", "engines": { @@ -725,14 +739,14 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", - "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", "dev": true, "license": "Apache-2.0", "peer": true, "dependencies": { - "@eslint/object-schema": "^2.1.6", + "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.2" }, @@ -767,20 +781,23 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", - "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", "dev": true, "license": "Apache-2.0", "peer": true, + "dependencies": { + "@eslint/core": "^0.17.0" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/core": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", - "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, "license": "Apache-2.0", "peer": true, @@ -792,9 +809,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", - "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", + "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", "dev": true, "license": "MIT", "peer": true, @@ -805,7 +822,7 @@ "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", + "js-yaml": "^4.1.1", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" }, @@ -854,9 +871,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.31.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", - "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", + "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", "dev": true, "license": "MIT", "peer": true, @@ -868,9 +885,9 @@ } }, "node_modules/@eslint/object-schema": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", - "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", "dev": true, "license": "Apache-2.0", "peer": true, @@ -879,14 +896,14 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", - "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, "license": "Apache-2.0", "peer": true, "dependencies": { - "@eslint/core": "^0.15.2", + "@eslint/core": "^0.17.0", "levn": "^0.4.1" }, "engines": { @@ -905,35 +922,20 @@ } }, "node_modules/@humanfs/node": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", "dev": true, "license": "Apache-2.0", "peer": true, "dependencies": { "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" + "@humanwhocodes/retry": "^0.4.0" }, "engines": { "node": ">=18.18.0" } }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -988,6 +990,16 @@ "mlly": "^1.7.4" } }, + "node_modules/@iconify/utils/node_modules/@antfu/utils": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-8.1.1.tgz", + "integrity": "sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/@iconify/utils/node_modules/globals": { "version": "15.15.0", "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", @@ -1002,26 +1014,26 @@ } }, "node_modules/@lezer/common": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.3.tgz", - "integrity": "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.5.0.tgz", + "integrity": "sha512-PNGcolp9hr4PJdXR4ix7XtixDrClScvtSCYW3rQG106oVMOOI+jFb+0+J3mbeL/53g1Zd6s0kJzaw6Ri68GmAA==", "dev": true, "license": "MIT" }, "node_modules/@lezer/highlight": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.1.tgz", - "integrity": "sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.3.tgz", + "integrity": "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==", "dev": true, "license": "MIT", "dependencies": { - "@lezer/common": "^1.0.0" + "@lezer/common": "^1.3.0" } }, "node_modules/@lezer/lr": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.2.tgz", - "integrity": "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==", + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.8.tgz", + "integrity": "sha512-bPWa0Pgx69ylNlMlPvBPryqeLYQjyJjqPx+Aupm5zydLIF3NE+6MMLT8Yi23Bd9cif9VS00aUebn+6fDIGBcDA==", "dev": true, "license": "MIT", "dependencies": { @@ -1046,9 +1058,9 @@ } }, "node_modules/@napi-rs/canvas": { - "version": "0.1.80", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.80.tgz", - "integrity": "sha512-DxuT1ClnIPts1kQx8FBmkk4BQDTfI5kIzywAaMjQSXfNnra5UFU9PwurXrl+Je3bJ6BGsp/zmshVVFbCmyI+ww==", + "version": "0.1.88", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.88.tgz", + "integrity": "sha512-/p08f93LEbsL5mDZFQ3DBxcPv/I4QG9EDYRRq1WNlCOXVfAHBTHMSVMwxlqG/AtnSfUr9+vgfN7MKiyDo0+Weg==", "dev": true, "license": "MIT", "optional": true, @@ -1058,23 +1070,28 @@ "engines": { "node": ">= 10" }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, "optionalDependencies": { - "@napi-rs/canvas-android-arm64": "0.1.80", - "@napi-rs/canvas-darwin-arm64": "0.1.80", - "@napi-rs/canvas-darwin-x64": "0.1.80", - "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.80", - "@napi-rs/canvas-linux-arm64-gnu": "0.1.80", - "@napi-rs/canvas-linux-arm64-musl": "0.1.80", - "@napi-rs/canvas-linux-riscv64-gnu": "0.1.80", - "@napi-rs/canvas-linux-x64-gnu": "0.1.80", - "@napi-rs/canvas-linux-x64-musl": "0.1.80", - "@napi-rs/canvas-win32-x64-msvc": "0.1.80" + "@napi-rs/canvas-android-arm64": "0.1.88", + "@napi-rs/canvas-darwin-arm64": "0.1.88", + "@napi-rs/canvas-darwin-x64": "0.1.88", + "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.88", + "@napi-rs/canvas-linux-arm64-gnu": "0.1.88", + "@napi-rs/canvas-linux-arm64-musl": "0.1.88", + "@napi-rs/canvas-linux-riscv64-gnu": "0.1.88", + "@napi-rs/canvas-linux-x64-gnu": "0.1.88", + "@napi-rs/canvas-linux-x64-musl": "0.1.88", + "@napi-rs/canvas-win32-arm64-msvc": "0.1.88", + "@napi-rs/canvas-win32-x64-msvc": "0.1.88" } }, "node_modules/@napi-rs/canvas-android-arm64": { - "version": "0.1.80", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.80.tgz", - "integrity": "sha512-sk7xhN/MoXeuExlggf91pNziBxLPVUqF2CAVnB57KLG/pz7+U5TKG8eXdc3pm0d7Od0WreB6ZKLj37sX9muGOQ==", + "version": "0.1.88", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.88.tgz", + "integrity": "sha512-KEaClPnZuVxJ8smUWjV1wWFkByBO/D+vy4lN+Dm5DFH514oqwukxKGeck9xcKJhaWJGjfruGmYGiwRe//+/zQQ==", "cpu": [ "arm64" ], @@ -1086,12 +1103,16 @@ ], "engines": { "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" } }, "node_modules/@napi-rs/canvas-darwin-arm64": { - "version": "0.1.80", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.80.tgz", - "integrity": "sha512-O64APRTXRUiAz0P8gErkfEr3lipLJgM6pjATwavZ22ebhjYl/SUbpgM0xcWPQBNMP1n29afAC/Us5PX1vg+JNQ==", + "version": "0.1.88", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.88.tgz", + "integrity": "sha512-Xgywz0dDxOKSgx3eZnK85WgGMmGrQEW7ZLA/E7raZdlEE+xXCozobgqz2ZvYigpB6DJFYkqnwHjqCOTSDGlFdg==", "cpu": [ "arm64" ], @@ -1103,12 +1124,16 @@ ], "engines": { "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" } }, "node_modules/@napi-rs/canvas-darwin-x64": { - "version": "0.1.80", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.80.tgz", - "integrity": "sha512-FqqSU7qFce0Cp3pwnTjVkKjjOtxMqRe6lmINxpIZYaZNnVI0H5FtsaraZJ36SiTHNjZlUB69/HhxNDT1Aaa9vA==", + "version": "0.1.88", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.88.tgz", + "integrity": "sha512-Yz4wSCIQOUgNucgk+8NFtQxQxZV5NO8VKRl9ePKE6XoNyNVC8JDqtvhh3b3TPqKK8W5p2EQpAr1rjjm0mfBxdg==", "cpu": [ "x64" ], @@ -1120,12 +1145,16 @@ ], "engines": { "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" } }, "node_modules/@napi-rs/canvas-linux-arm-gnueabihf": { - "version": "0.1.80", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.80.tgz", - "integrity": "sha512-eyWz0ddBDQc7/JbAtY4OtZ5SpK8tR4JsCYEZjCE3dI8pqoWUC8oMwYSBGCYfsx2w47cQgQCgMVRVTFiiO38hHQ==", + "version": "0.1.88", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.88.tgz", + "integrity": "sha512-9gQM2SlTo76hYhxHi2XxWTAqpTOb+JtxMPEIr+H5nAhHhyEtNmTSDRtz93SP7mGd2G3Ojf2oF5tP9OdgtgXyKg==", "cpu": [ "arm" ], @@ -1137,12 +1166,16 @@ ], "engines": { "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" } }, "node_modules/@napi-rs/canvas-linux-arm64-gnu": { - "version": "0.1.80", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.80.tgz", - "integrity": "sha512-qwA63t8A86bnxhuA/GwOkK3jvb+XTQaTiVML0vAWoHyoZYTjNs7BzoOONDgTnNtr8/yHrq64XXzUoLqDzU+Uuw==", + "version": "0.1.88", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.88.tgz", + "integrity": "sha512-7qgaOBMXuVRk9Fzztzr3BchQKXDxGbY+nwsovD3I/Sx81e+sX0ReEDYHTItNb0Je4NHbAl7D0MKyd4SvUc04sg==", "cpu": [ "arm64" ], @@ -1154,12 +1187,16 @@ ], "engines": { "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" } }, "node_modules/@napi-rs/canvas-linux-arm64-musl": { - "version": "0.1.80", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.80.tgz", - "integrity": "sha512-1XbCOz/ymhj24lFaIXtWnwv/6eFHXDrjP0jYkc6iHQ9q8oXKzUX1Lc6bu+wuGiLhGh2GS/2JlfORC5ZcXimRcg==", + "version": "0.1.88", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.88.tgz", + "integrity": "sha512-kYyNrUsHLkoGHBc77u4Unh067GrfiCUMbGHC2+OTxbeWfZkPt2o32UOQkhnSswKd9Fko/wSqqGkY956bIUzruA==", "cpu": [ "arm64" ], @@ -1171,12 +1208,16 @@ ], "engines": { "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" } }, "node_modules/@napi-rs/canvas-linux-riscv64-gnu": { - "version": "0.1.80", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.80.tgz", - "integrity": "sha512-XTzR125w5ZMs0lJcxRlS1K3P5RaZ9RmUsPtd1uGt+EfDyYMu4c6SEROYsxyatbbu/2+lPe7MPHOO/0a0x7L/gw==", + "version": "0.1.88", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.88.tgz", + "integrity": "sha512-HVuH7QgzB0yavYdNZDRyAsn/ejoXB0hn8twwFnOqUbCCdkV+REna7RXjSR7+PdfW0qMQ2YYWsLvVBT5iL/mGpw==", "cpu": [ "riscv64" ], @@ -1188,12 +1229,16 @@ ], "engines": { "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" } }, "node_modules/@napi-rs/canvas-linux-x64-gnu": { - "version": "0.1.80", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.80.tgz", - "integrity": "sha512-BeXAmhKg1kX3UCrJsYbdQd3hIMDH/K6HnP/pG2LuITaXhXBiNdh//TVVVVCBbJzVQaV5gK/4ZOCMrQW9mvuTqA==", + "version": "0.1.88", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.88.tgz", + "integrity": "sha512-hvcvKIcPEQrvvJtJnwD35B3qk6umFJ8dFIr8bSymfrSMem0EQsfn1ztys8ETIFndTwdNWJKWluvxztA41ivsEw==", "cpu": [ "x64" ], @@ -1205,12 +1250,16 @@ ], "engines": { "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" } }, "node_modules/@napi-rs/canvas-linux-x64-musl": { - "version": "0.1.80", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.80.tgz", - "integrity": "sha512-x0XvZWdHbkgdgucJsRxprX/4o4sEed7qo9rCQA9ugiS9qE2QvP0RIiEugtZhfLH3cyI+jIRFJHV4Fuz+1BHHMg==", + "version": "0.1.88", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.88.tgz", + "integrity": "sha512-eSMpGYY2xnZSQ6UxYJ6plDboxq4KeJ4zT5HaVkUnbObNN6DlbJe0Mclh3wifAmquXfrlgTZt6zhHsUgz++AK6g==", "cpu": [ "x64" ], @@ -1222,12 +1271,37 @@ ], "engines": { "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-win32-arm64-msvc": { + "version": "0.1.88", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-arm64-msvc/-/canvas-win32-arm64-msvc-0.1.88.tgz", + "integrity": "sha512-qcIFfEgHrchyYqRrxsCeTQgpJZ/GqHiqPcU/Fvw/ARVlQeDX1VyFH+X+0gCR2tca6UJrq96vnW+5o7buCq+erA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" } }, "node_modules/@napi-rs/canvas-win32-x64-msvc": { - "version": "0.1.80", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.80.tgz", - "integrity": "sha512-Z8jPsM6df5V8B1HrCHB05+bDiCxjE9QA//3YrkKIdVDEwn5RKaqOxCJDRJkl48cJbylcrJbW4HxZbTte8juuPg==", + "version": "0.1.88", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.88.tgz", + "integrity": "sha512-ROVqbfS4QyZxYkqmaIBBpbz/BQvAR+05FXM5PAtTYVc0uyY8Y4BHJSMdGAaMf6TdIVRsQsiq+FG/dH9XhvWCFQ==", "cpu": [ "x64" ], @@ -1239,6 +1313,10 @@ ], "engines": { "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" } }, "node_modules/@nodelib/fs.scandir": { @@ -2012,9 +2090,9 @@ "license": "MIT" }, "node_modules/@types/d3-shape": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz", - "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz", + "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", "dev": true, "license": "MIT", "dependencies": { @@ -2131,22 +2209,15 @@ } }, "node_modules/@types/node": { - "version": "24.0.14", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.14.tgz", - "integrity": "sha512-4zXMWD91vBLGRtHK3YbIoFMia+1nqEz72coM42C5ETjnNCa/heoj7NT1G67iAfOqMmcfhuCZ4uNpyz8EjlAejw==", + "version": "24.10.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.9.tgz", + "integrity": "sha512-ne4A0IpG3+2ETuREInjPNhUGis1SFjv1d5asp8MzEAGtOZeTeHVDOYqOgqfhvseqg/iXty2hjBf1zAOb7RNiNw==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.8.0" + "undici-types": "~7.16.0" } }, - "node_modules/@types/node/node_modules/undici-types": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", - "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/offscreencanvas": { "version": "2019.7.3", "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.3.tgz", @@ -2162,23 +2233,23 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "19.1.8", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz", - "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==", + "version": "19.2.9", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.9.tgz", + "integrity": "sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA==", "devOptional": true, "license": "MIT", "dependencies": { - "csstype": "^3.0.2" + "csstype": "^3.2.2" } }, "node_modules/@types/react-dom": { - "version": "19.1.6", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.6.tgz", - "integrity": "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==", + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "dev": true, "license": "MIT", "peerDependencies": { - "@types/react": "^19.0.0" + "@types/react": "^19.2.0" } }, "node_modules/@types/responselike": { @@ -2192,9 +2263,9 @@ } }, "node_modules/@types/sortablejs": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@types/sortablejs/-/sortablejs-1.15.8.tgz", - "integrity": "sha512-b79830lW+RZfwaztgs1aVPgbasJ8e7AXtZYHTELNXZPsERt4ymJdjV4OccDbHQAvHrCcFpbF78jkm0R6h/pZVg==", + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/@types/sortablejs/-/sortablejs-1.15.9.tgz", + "integrity": "sha512-7HP+rZGE2p886PKV9c9OJzLBI6BBJu1O7lJGYnPyG3fS4/duUCcngkNCjsLwIMV+WMqANe3tt4irrXHSIe68OQ==", "dev": true, "license": "MIT" }, @@ -2476,12 +2547,12 @@ } }, "node_modules/@xyflow/react": { - "version": "12.9.3", - "resolved": "https://registry.npmjs.org/@xyflow/react/-/react-12.9.3.tgz", - "integrity": "sha512-PSWoJ8vHiEqSIkLIkge+0eiHWiw4C6dyFDA03VKWJkqbU4A13VlDIVwKqf/Znuysn2GQw/zA61zpHE4rGgax7Q==", + "version": "12.10.0", + "resolved": "https://registry.npmjs.org/@xyflow/react/-/react-12.10.0.tgz", + "integrity": "sha512-eOtz3whDMWrB4KWVatIBrKuxECHqip6PfA8fTpaS2RUGVpiEAe+nqDKsLqkViVWxDGreq0lWX71Xth/SPAzXiw==", "license": "MIT", "dependencies": { - "@xyflow/system": "0.0.73", + "@xyflow/system": "0.0.74", "classcat": "^5.0.3", "zustand": "^4.4.0" }, @@ -2491,9 +2562,9 @@ } }, "node_modules/@xyflow/system": { - "version": "0.0.73", - "resolved": "https://registry.npmjs.org/@xyflow/system/-/system-0.0.73.tgz", - "integrity": "sha512-C2ymH2V4mYDkdVSiRx0D7R0s3dvfXiupVBcko6tXP5K4tVdSBMo22/e3V9yRNdn+2HQFv44RFKzwOyCcUUDAVQ==", + "version": "0.0.74", + "resolved": "https://registry.npmjs.org/@xyflow/system/-/system-0.0.74.tgz", + "integrity": "sha512-7v7B/PkiVrkdZzSbL+inGAo6tkR/WQHHG0/jhSvLQToCsfa8YubOGmBYd1s08tpKpihdHDZFwzQZeR69QSBb4Q==", "license": "MIT", "dependencies": { "@types/d3-drag": "^3.0.7", @@ -2753,6 +2824,13 @@ "chevrotain": "^11.0.0" } }, + "node_modules/chevrotain/node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "dev": true, + "license": "MIT" + }, "node_modules/classcat": { "version": "5.0.5", "resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.5.tgz", @@ -2887,9 +2965,9 @@ } }, "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "devOptional": true, "license": "MIT" }, @@ -3162,9 +3240,9 @@ } }, "node_modules/d3-format": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", - "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", + "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", "dev": true, "license": "ISC", "engines": { @@ -3426,16 +3504,16 @@ } }, "node_modules/dayjs": { - "version": "1.11.18", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.18.tgz", - "integrity": "sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==", + "version": "1.11.19", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", + "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==", "dev": true, "license": "MIT" }, "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "license": "MIT", "dependencies": { @@ -3554,9 +3632,9 @@ "optional": true }, "node_modules/dompurify": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.0.tgz", - "integrity": "sha512-r+f6MYR1gGN1eJv0TVQbhA7if/U7P87cdPl3HN5rikqaBSBxLiCb/b9O+2eG0cxz0ghyU+mU1QkbsOwERMYlWQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.1.tgz", + "integrity": "sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==", "dev": true, "license": "(MPL-2.0 OR Apache-2.0)", "optionalDependencies": { @@ -3586,15 +3664,15 @@ "license": "ISC" }, "node_modules/electron": { - "version": "37.2.2", - "resolved": "https://registry.npmjs.org/electron/-/electron-37.2.2.tgz", - "integrity": "sha512-qEIUs+3elu7wOfzLEtLz4Innfe+8nQyhLh9qjrJY0d0MxdRolLMDUD9QwAQ743vDZ+bUg+gqwigzP7kV78S3hA==", + "version": "40.0.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-40.0.0.tgz", + "integrity": "sha512-UyBy5yJ0/wm4gNugCtNPjvddjAknMTuXR2aCHioXicH7aKRKGDBPp4xqTEi/doVcB3R+MN3wfU9o8d/9pwgK2A==", "dev": true, "hasInstallScript": true, "license": "MIT", "dependencies": { "@electron/get": "^2.0.0", - "@types/node": "^22.7.7", + "@types/node": "^24.9.0", "extract-zip": "^2.0.1" }, "bin": { @@ -3604,16 +3682,6 @@ "node": ">= 12.20.55" } }, - "node_modules/electron/node_modules/@types/node": { - "version": "22.16.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.16.4.tgz", - "integrity": "sha512-PYRhNtZdm2wH/NT2k/oAJ6/f2VD2N2Dag0lGlx2vWgMSJXGNmlce5MiTQzoWAiIJtso30mjnfQCOKVH+kAQC/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, "node_modules/end-of-stream": { "version": "1.4.5", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", @@ -3744,26 +3812,25 @@ } }, "node_modules/eslint": { - "version": "9.31.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", - "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", + "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.0", - "@eslint/config-helpers": "^0.3.0", - "@eslint/core": "^0.15.0", + "@eslint/config-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.31.0", - "@eslint/plugin-kit": "^0.3.1", + "@eslint/js": "9.39.2", + "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", @@ -3921,9 +3988,9 @@ } }, "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, "license": "BSD-3-Clause", "peer": true, @@ -3978,9 +4045,9 @@ "license": "MIT" }, "node_modules/exsolve": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz", - "integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", + "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", "dev": true, "license": "MIT" }, @@ -4060,9 +4127,9 @@ "peer": true }, "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", "dev": true, "license": "ISC", "dependencies": { @@ -4425,9 +4492,9 @@ } }, "node_modules/i18next": { - "version": "25.5.3", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.5.3.tgz", - "integrity": "sha512-joFqorDeQ6YpIXni944upwnuHBf5IoPMuqAchGVeQLdWC2JOjxgM9V8UGLhNIIH/Q8QleRxIi0BSRQehSrDLcg==", + "version": "25.8.0", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.8.0.tgz", + "integrity": "sha512-urrg4HMFFMQZ2bbKRK7IZ8/CTE7D8H4JRlAwqA2ZwDRFfdd0K/4cdbNNLgfn9mo+I/h9wJu61qJzH7jCFAhUZQ==", "funding": [ { "type": "individual", @@ -4444,7 +4511,7 @@ ], "license": "MIT", "dependencies": { - "@babel/runtime": "^7.27.6" + "@babel/runtime": "^7.28.4" }, "peerDependencies": { "typescript": "^5" @@ -4566,9 +4633,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "license": "MIT", "peer": true, @@ -4621,9 +4688,9 @@ } }, "node_modules/katex": { - "version": "0.16.25", - "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.25.tgz", - "integrity": "sha512-woHRUZ/iF23GBP1dkDQMh1QBad9dmr8/PAwNA54VrSOVYgI12MAcE14TqnDdQOdzyEonGzMepYnqBMYdsoAr8Q==", + "version": "0.16.28", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.28.tgz", + "integrity": "sha512-YHzO7721WbmAL6Ov1uzN/l5mY5WWWhJBSW+jq4tkfZfsxmo1hu6frS0EOswvjBUnWE6NtjEs48SFn5CQESRLZg==", "dev": true, "funding": [ "https://opencollective.com/katex", @@ -4745,9 +4812,9 @@ } }, "node_modules/lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.23.tgz", + "integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==", "dev": true, "license": "MIT" }, @@ -5000,9 +5067,9 @@ } }, "node_modules/obsidian": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.10.0.tgz", - "integrity": "sha512-F7hhnmGRQD1TanDPFT//LD3iKNUVd7N8sKL7flCCHRszfTxpDJ39j3T7LHbcGpyid906i6lD5oO+cnfLBzJMKw==", + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.11.4.tgz", + "integrity": "sha512-n0KD3S+VndgaByrEtEe8NELy0ya6/s+KZ7OcxA6xOm5NN4thxKpQjo6eqEudHEvfGCeT/TYToAKJzitQ1I3XTg==", "dev": true, "license": "MIT", "dependencies": { @@ -5011,7 +5078,7 @@ }, "peerDependencies": { "@codemirror/state": "6.5.0", - "@codemirror/view": "6.38.1" + "@codemirror/view": "6.38.6" } }, "node_modules/obsidian-typings": { @@ -5052,14 +5119,16 @@ "typescript": ">=4.8.0" } }, - "node_modules/obsidian-typings/node_modules/@antfu/utils": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-9.2.0.tgz", - "integrity": "sha512-Oq1d9BGZakE/FyoEtcNeSwM7MpDO2vUBi11RWBZXf75zPsbUVWmUs03EqkRFrcgbXyKTas0BdZWC1wcuSoqSAw==", + "node_modules/obsidian-typings/node_modules/@codemirror/search": { + "version": "6.5.11", + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.11.tgz", + "integrity": "sha512-KmWepDE6jUdL6n8cAAqIpRmLPBZ5ZKnicE8oGU/s3QrAVID+0VhLFrzUucVKHG5035/BSykhExDL/Xm7dHthiA==", "dev": true, "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/antfu" + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "crelt": "^1.0.5" } }, "node_modules/obsidian-typings/node_modules/@codemirror/state": { @@ -5072,6 +5141,13 @@ "@marijn/find-cluster-break": "^1.0.0" } }, + "node_modules/obsidian-typings/node_modules/@lezer/common": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.3.tgz", + "integrity": "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==", + "dev": true, + "license": "MIT" + }, "node_modules/obsidian-typings/node_modules/@types/codemirror": { "version": "5.60.16", "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.16.tgz", @@ -5139,15 +5215,12 @@ "@types/tern": "*" } }, - "node_modules/obsidian-typings/node_modules/obsidian/node_modules/moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "node_modules/obsidian-typings/node_modules/style-mod": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz", + "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==", "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } + "license": "MIT" }, "node_modules/once": { "version": "1.4.0", @@ -5223,9 +5296,9 @@ } }, "node_modules/package-manager-detector": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.4.1.tgz", - "integrity": "sha512-dSMiVLBEA4XaNJ0PRb4N5cV/SEP4BWrWZKBmfF+OUm2pQTiZ6DDkKeWaltwu3JRhLoy59ayIkJ00cx9K9CaYTg==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.6.0.tgz", + "integrity": "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==", "dev": true, "license": "MIT" }, @@ -5442,9 +5515,9 @@ } }, "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -5509,24 +5582,24 @@ } }, "node_modules/react": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", - "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", + "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", - "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", "license": "MIT", "dependencies": { - "scheduler": "^0.26.0" + "scheduler": "^0.27.0" }, "peerDependencies": { - "react": "^19.1.0" + "react": "^19.2.3" } }, "node_modules/react-icons": { @@ -5658,9 +5731,9 @@ "license": "MIT" }, "node_modules/scheduler": { - "version": "0.26.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", - "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", "license": "MIT" }, "node_modules/scrypt-js": { @@ -5671,9 +5744,9 @@ "license": "MIT" }, "node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, "license": "ISC", "bin": { @@ -5708,20 +5781,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/serialize-error/node_modules/type-fest": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", - "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "optional": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -5852,9 +5911,9 @@ } }, "node_modules/style-mod": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz", - "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz", + "integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==", "dev": true, "license": "MIT" }, @@ -5893,11 +5952,14 @@ } }, "node_modules/tinyexec": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz", - "integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/to-regex-range": { "version": "5.0.1", @@ -5913,9 +5975,9 @@ } }, "node_modules/ts-api-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", - "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", + "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", "dev": true, "license": "MIT", "engines": { @@ -5956,6 +6018,20 @@ "node": ">= 0.8.0" } }, + "node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/typescript": { "version": "5.8.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", @@ -5971,16 +6047,16 @@ } }, "node_modules/ufo": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", - "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==", + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz", + "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==", "dev": true, "license": "MIT" }, "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", "dev": true, "license": "MIT" }, @@ -6027,9 +6103,9 @@ "license": "MIT" }, "node_modules/use-sync-external-store": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", - "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", "license": "MIT", "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" From 4b5ffcd245955820ad5e9813acf8aeff7ee6cf33 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Tue, 27 Jan 2026 11:07:55 +0530 Subject: [PATCH 037/236] feat : auto hide the nav bar on column scroll --- src/components/KanbanView/LazyColumn.tsx | 45 ++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/components/KanbanView/LazyColumn.tsx b/src/components/KanbanView/LazyColumn.tsx index dfea321f..26dd0398 100644 --- a/src/components/KanbanView/LazyColumn.tsx +++ b/src/components/KanbanView/LazyColumn.tsx @@ -69,6 +69,12 @@ const LazyColumn: React.FC = ({ const rafRef = useRef(null); const [localTasks, setLocalTasks] = useState(tasksForThisColumn); + // Navigation visibility state + const prevScrollTopRef = useRef(0); + const isNavHiddenRef = useRef(false); + const scrollPositionWhenHiddenRef = useRef(0); + const SCROLL_UP_THRESHOLD = 10; + const scheduleSetInsertIndex = (pos: number | null) => { if (insertIndexRef.current === pos) return; if (rafRef.current) { @@ -101,6 +107,38 @@ const LazyColumn: React.FC = ({ setLocalTasks(tasksForThisColumn); }, [tasksForThisColumn]); + const handleNavVisibility = () => { + const container = tasksContainerRef.current; + if (!container) return; + + const currentScrollTop = container.scrollTop; + const isScrollingDown = currentScrollTop > prevScrollTopRef.current; + const scrollDifference = Math.abs(currentScrollTop - prevScrollTopRef.current); + console.log("LazyColumn.tsx...\ncurrentScrollTop:", currentScrollTop, "\nisScrollingDown :", isScrollingDown, "\nscrollDifference :", scrollDifference, "\nisNavHiddenRef :", isNavHiddenRef.current); + + // Only update if there's a meaningful scroll (> 0) + if (scrollDifference === 0) return; + + const htmlElement = document.documentElement; + + if (isScrollingDown && !isNavHiddenRef.current) { + // User is scrolling down - hide navigation + htmlElement.classList.add('is-hidden-nav'); + isNavHiddenRef.current = true; + scrollPositionWhenHiddenRef.current = currentScrollTop; + } else if (!isScrollingDown && isNavHiddenRef.current) { + // User is scrolling up - show navigation after scrolling up by threshold + const scrolledUpDistance = scrollPositionWhenHiddenRef.current - currentScrollTop; + if (scrolledUpDistance >= SCROLL_UP_THRESHOLD) { + htmlElement.classList.remove('is-hidden-nav'); + isNavHiddenRef.current = false; + } + } + + // Update previous scroll position for next iteration + prevScrollTopRef.current = currentScrollTop; + }; + // Scroll event handler const handleScroll = useCallback(() => { const container = tasksContainerRef.current; @@ -129,6 +167,8 @@ const LazyColumn: React.FC = ({ if (throttleTimeout) return; throttleTimeout = setTimeout(() => { handleScroll(); + + handleNavVisibility(); throttleTimeout = null; }, 100); }; @@ -713,6 +753,11 @@ const LazyColumn: React.FC = ({ cancelAnimationFrame(rafRef.current); rafRef.current = null; } + // Clean up navigation visibility class when component unmounts + if (isNavHiddenRef.current) { + document.documentElement.classList.remove('is-hidden-nav'); + isNavHiddenRef.current = false; + } }; }, []); From c8a48bc7b5a7fa6d34ac2ee075a1dfc3683e5673 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Tue, 27 Jan 2026 11:08:08 +0530 Subject: [PATCH 038/236] fix : few minor styling fixes --- styles.css | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/styles.css b/styles.css index 7c2b42c1..31cdf6bb 100644 --- a/styles.css +++ b/styles.css @@ -138,8 +138,8 @@ button.clickable-icon.view-action.taskboardScanVaultBtn.highlight { } .taskBoardViewSection-mobile { - height: 100%; - /* max-height: 93%; */ + /* height: 100%; */ + max-height: 92%; /* margin-block-end: 20px; */ padding-block-end: 4.2rem; } @@ -1997,7 +1997,7 @@ button.clickable-icon.view-action.taskboardScanVaultBtn.highlight { } .boardConfigModalSidebarBtnArea-btn { - border-radius: var(--button-radius); + border-radius: 1rem; border: var(--border-width) solid var(--table-header-border-color); padding: 8px; cursor: pointer; @@ -2045,6 +2045,7 @@ button.clickable-icon.view-action.taskboardScanVaultBtn.highlight { padding-left: 1em; /* overflow-y: auto; */ max-width: 50vw; + width: -webkit-fill-available; max-height: calc(var(--dialog-max-height) - 6vh); } @@ -2247,7 +2248,7 @@ button.clickable-icon.view-action.taskboardScanVaultBtn.highlight { .boardConfigModalMainContent { max-width: 95vw; - width: 95vw; + width: -webkit-fill-available; padding-left: 0 !important; overflow-y: unset !important; } @@ -2716,6 +2717,11 @@ li:hover .taskboard-hidden-property { transition: background-color 0.2s ease, box-shadow 0.2s ease; } +.setting-item.tag-color-row-element { + border: none !important; + background-color: transparent !important; +} + .tag-color-row-element .setting-item-info { display: none !important; } @@ -2813,6 +2819,10 @@ li:hover .taskboard-hidden-property { cursor: pointer !important; } +.task-board-settingtab-add-tag-color-btn { + background-color: transparent !important; +} + .global-setting-tab-live-preview { padding: 5px; padding-block: 1em; @@ -4742,7 +4752,7 @@ svg.react-flow__connectionline { } .react-flow__panel.bottom { - bottom: 0; + bottom: 120px; } .react-flow__panel.top.center, From e7232d696a9d394dceacbb9ae6f2fd0851bcfc50 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Tue, 27 Jan 2026 11:22:57 +0530 Subject: [PATCH 039/236] small improvements --- src/components/KanbanView/LazyColumn.tsx | 28 ++++++++++++------------ 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/components/KanbanView/LazyColumn.tsx b/src/components/KanbanView/LazyColumn.tsx index 26dd0398..c7bb87c7 100644 --- a/src/components/KanbanView/LazyColumn.tsx +++ b/src/components/KanbanView/LazyColumn.tsx @@ -11,7 +11,6 @@ import { taskItem } from 'src/interfaces/TaskItem'; import { Menu, Notice, Platform } from 'obsidian'; import { ViewTaskFilterPopover } from 'src/components/BoardFilters/ViewTaskFilterPopover'; import { eventEmitter } from 'src/services/EventEmitter'; -import { bugReporter } from 'src/services/OpenModals'; import { ViewTaskFilterModal } from 'src/components/BoardFilters'; import { ConfigureColumnSortingModal } from 'src/modals/ConfigureColumnSortingModal'; import { matchTagsWithWildcards } from 'src/utils/algorithms/ScanningFilterer'; @@ -71,7 +70,7 @@ const LazyColumn: React.FC = ({ // Navigation visibility state const prevScrollTopRef = useRef(0); - const isNavHiddenRef = useRef(false); + // const isNavHiddenRef = useRef(false); const scrollPositionWhenHiddenRef = useRef(0); const SCROLL_UP_THRESHOLD = 10; @@ -114,24 +113,23 @@ const LazyColumn: React.FC = ({ const currentScrollTop = container.scrollTop; const isScrollingDown = currentScrollTop > prevScrollTopRef.current; const scrollDifference = Math.abs(currentScrollTop - prevScrollTopRef.current); - console.log("LazyColumn.tsx...\ncurrentScrollTop:", currentScrollTop, "\nisScrollingDown :", isScrollingDown, "\nscrollDifference :", scrollDifference, "\nisNavHiddenRef :", isNavHiddenRef.current); + // console.log("LazyColumn.tsx...\ncurrentScrollTop:", currentScrollTop, "\nisScrollingDown :", isScrollingDown, "\nscrollDifference :", scrollDifference, "\nisNavHiddenRef :"); - // Only update if there's a meaningful scroll (> 0) - if (scrollDifference === 0) return; + if (scrollDifference < 1) return; const htmlElement = document.documentElement; - if (isScrollingDown && !isNavHiddenRef.current) { + if (isScrollingDown) { // User is scrolling down - hide navigation htmlElement.classList.add('is-hidden-nav'); - isNavHiddenRef.current = true; + // isNavHiddenRef.current = true; scrollPositionWhenHiddenRef.current = currentScrollTop; - } else if (!isScrollingDown && isNavHiddenRef.current) { + } else if (!isScrollingDown) { // User is scrolling up - show navigation after scrolling up by threshold const scrolledUpDistance = scrollPositionWhenHiddenRef.current - currentScrollTop; if (scrolledUpDistance >= SCROLL_UP_THRESHOLD) { htmlElement.classList.remove('is-hidden-nav'); - isNavHiddenRef.current = false; + // isNavHiddenRef.current = false; } } @@ -168,7 +166,9 @@ const LazyColumn: React.FC = ({ throttleTimeout = setTimeout(() => { handleScroll(); - handleNavVisibility(); + if (Platform.isMobile) + handleNavVisibility(); + throttleTimeout = null; }, 100); }; @@ -754,10 +754,10 @@ const LazyColumn: React.FC = ({ rafRef.current = null; } // Clean up navigation visibility class when component unmounts - if (isNavHiddenRef.current) { - document.documentElement.classList.remove('is-hidden-nav'); - isNavHiddenRef.current = false; - } + // if (isNavHiddenRef.current) { + document.documentElement.classList.remove('is-hidden-nav'); + // isNavHiddenRef.current = false; + // } }; }, []); From 9d217653716fe3b70ad3db53452be1358ba32c9d Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Tue, 27 Jan 2026 11:23:06 +0530 Subject: [PATCH 040/236] test the latest changes | working --- data.json | 20 ++++++++------------ task-board-logs.log | 24 ++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/data.json b/data.json index 99446f04..b567e50a 100644 --- a/data.json +++ b/data.json @@ -23,17 +23,13 @@ } ], "tasksIdManualOrder": [ + "3670624679", + 263, "367", "372", - "398696308", "373", "370", - "3077781059", - "4184100420", - "3052793295", "1506909345", - 274, - "4031700447", "362" ] }, @@ -75,8 +71,8 @@ "filterGroups": [] }, "taskCount": { - "pending": 65, - "completed": 19 + "pending": 64, + "completed": 14 } }, { @@ -229,8 +225,8 @@ }, "description": "Project to manage all the tasks related to plugin release.", "taskCount": { - "pending": 4, - "completed": 1 + "pending": 64, + "completed": 14 } }, { @@ -1055,9 +1051,9 @@ "searchQuery": "", "lastViewHistory": { "viewedType": "kanban", - "boardIndex": 2, + "boardIndex": 0, "taskId": "", - "settingTab": 7 + "settingTab": 1 }, "taskNoteIdentifierTag": "task", "doubleClickCardToEdit": "noteInTab", diff --git a/task-board-logs.log b/task-board-logs.log index 7b45e334..ad4c7aad 100644 --- a/task-board-logs.log +++ b/task-board-logs.log @@ -61,3 +61,27 @@ Context : MapView.tsx/initialNodes ```log ERROR: Same id is present on two tasks ``` + +------------- + +Timestamp : 2026-01-27T10:48:04 +ID : 17 +Message : Following duplicate IDs has been found for tasks with IDs: "490". This may cause unexpected behavior. Please consider changing the IDs of these tasks. +Context : MapView.tsx/initialNodes + +#### Bug Content +```log +ERROR: Same id is present on two tasks +``` + +------------- + +Timestamp : 2026-01-27T11:12:10 +ID : 17 +Message : Following duplicate IDs has been found for tasks with IDs: "490". This may cause unexpected behavior. Please consider changing the IDs of these tasks. +Context : MapView.tsx/initialNodes + +#### Bug Content +```log +ERROR: Same id is present on two tasks +``` From 1ae90cf3fb98c03a93c8a7b543f8914c449835c6 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Tue, 27 Jan 2026 13:19:46 +0530 Subject: [PATCH 041/236] fix : english translations and a small case handled --- src/modals/CustomStatusConfigurator.ts | 54 ++++++++++---------------- src/settings/SettingConstructUI.ts | 4 +- src/utils/lang/locale/en.ts | 11 +++++- 3 files changed, 33 insertions(+), 36 deletions(-) diff --git a/src/modals/CustomStatusConfigurator.ts b/src/modals/CustomStatusConfigurator.ts index 629ff63f..0882cc38 100644 --- a/src/modals/CustomStatusConfigurator.ts +++ b/src/modals/CustomStatusConfigurator.ts @@ -1,7 +1,10 @@ import { Modal, Notice, Setting, TextComponent } from "obsidian"; import type { Plugin } from "obsidian"; import { statusTypeNames } from "src/interfaces/Enums"; -import { StatusConfiguration, StatusType } from "src/interfaces/StatusConfiguration"; +import { + StatusConfiguration, + StatusType, +} from "src/interfaces/StatusConfiguration"; import { t } from "src/utils/lang/helper"; import type { CustomStatus } from "src/interfaces/GlobalSettings"; @@ -18,7 +21,7 @@ export class CustomStatusModal extends Modal { constructor( public plugin: Plugin, statusType: CustomStatus | StatusConfiguration, - isCoreStatus: boolean + isCoreStatus: boolean, ) { super(plugin.app); const status = statusType as any; @@ -27,7 +30,7 @@ export class CustomStatusModal extends Modal { this.statusNextSymbol = status.nextStatusSymbol; this.statusAvailableAsCommand = status.availableAsCommand; // Ensure type is a valid StatusType enum value - if (typeof status.type === 'string') { + if (typeof status.type === "string") { this.type = status.type as StatusType; } else { this.type = status.type; @@ -45,7 +48,7 @@ export class CustomStatusModal extends Modal { this.statusName, this.statusNextSymbol, this.statusAvailableAsCommand, - this.type + this.type, ); } @@ -59,12 +62,8 @@ export class CustomStatusModal extends Modal { let statusSymbolText: TextComponent; new Setting(settingDiv) - .setName(t("Task status symbol")) - .setDesc( - t( - "This is the character between the square braces in case of inline-tasks. Also, this is used to store in the case." - ) - ) + .setName(t("task-status-symbol")) + .setDesc(t("task-status-symbol-info")) .addText((text) => { statusSymbolText = text; text.setValue(this.statusSymbol).onChange((v) => { @@ -78,12 +77,8 @@ export class CustomStatusModal extends Modal { let statusNameText: TextComponent; new Setting(settingDiv) - .setName(t("Task status name")) - .setDesc( - t( - "Map a unique name to the above status symbol. This name will be used in the task-note frontmatter." - ) - ) + .setName(t("task-status-name")) + .setDesc(t("task-status-name-info")) .addText((text) => { statusNameText = text; text.setValue(this.statusName).onChange((v) => { @@ -93,12 +88,8 @@ export class CustomStatusModal extends Modal { .then((_setting) => {}); new Setting(settingDiv) - .setName(t("Task status type")) - .setDesc( - t( - "Select what kind of status is this. If this status if of type DONE or CANCELLED, then the task will appear inside the 'completed' type column." - ) - ) + .setName(t("task-status-type")) + .setDesc(t("task-status-type-info")) .addDropdown((dropdown) => { const types = [ statusTypeNames.TODO, @@ -118,13 +109,10 @@ export class CustomStatusModal extends Modal { let statusNextSymbolText: TextComponent; new Setting(settingDiv) - .setName(t("Cycle to the following status")) - .setDesc( - t( - "Once you click on the above status, cycle to this status. Also, dont forget to create a new entry for this status type." - ) - ) + .setName(t("cycle-to-following-status")) + .setDesc(t("cycle-to-following-status-info")) .addText((text) => { + text.setPlaceholder("eg.: /"); statusNextSymbolText = text; text.setValue(this.statusNextSymbol).onChange((v) => { this.statusNextSymbol = v; @@ -135,12 +123,10 @@ export class CustomStatusModal extends Modal { const footerEl = contentEl.createDiv(); const footerButtons = new Setting(footerEl); footerButtons.addButton((b) => { - b.setTooltip(t("save")) - .setIcon("checkmark") - .onClick(async () => { - this.saved = true; - this.close(); - }); + b.setTooltip(t("save")).onClick(async () => { + this.saved = true; + this.close(); + }); return b; }); footerButtons.addExtraButton((b) => { diff --git a/src/settings/SettingConstructUI.ts b/src/settings/SettingConstructUI.ts index e233383b..04db2f2e 100644 --- a/src/settings/SettingConstructUI.ts +++ b/src/settings/SettingConstructUI.ts @@ -1450,7 +1450,9 @@ export class SettingsManager { symbol: updatedStatus.symbol, name: updatedStatus.name, nextStatusSymbol: - updatedStatus.nextStatusSymbol, + updatedStatus.nextStatusSymbol === "" + ? " " + : updatedStatus.nextStatusSymbol, availableAsCommand: updatedStatus.availableAsCommand, type: updatedStatus.type, diff --git a/src/utils/lang/locale/en.ts b/src/utils/lang/locale/en.ts index 9f1eefb9..1747513c 100644 --- a/src/utils/lang/locale/en.ts +++ b/src/utils/lang/locale/en.ts @@ -559,7 +559,16 @@ const en: Lang = { "delete-note": "Delete note", "open-note-to-right": "Open note to right", "copy-task-title-successful": "Successfully copied the task title.", - "copy-task-title-unsuccessful": "There was an error while copying the task title." + "copy-task-title-unsuccessful": "There was an error while copying the task title.", + "task-status-symbol": "Task status symbol", + "task-status-symbol-info": "This is the character between the square braces in case of inline-tasks. Also, this is used to store in the case.", + "task-status-name": "Task status name", + "task-status-name-info": "Map a unique name to the above status symbol. This name will be used in the task-note frontmatter.", + "task-status-type": "Task status type", + "task-status-type-info": "Select what kind of status is this. If this status if of type DONE or CANCELLED, then the task will appear inside the 'completed' type column.", + "cycle-to-following-status": "Cycle to the following status", + "cycle-to-following-status-info": "Enter the status symbol, eg. /. Once you click on the above status, cycle to this status. Also, dont forget to create a new entry for this status type.", + }; export default en; From c8e3d3011987efceddfef22535f6ec616f650482 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Tue, 27 Jan 2026 14:25:57 +0530 Subject: [PATCH 042/236] DEPRECATE : lazy loading configs from global settings --- src/interfaces/GlobalSettings.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/interfaces/GlobalSettings.ts b/src/interfaces/GlobalSettings.ts index 90084608..a7ccbc02 100644 --- a/src/interfaces/GlobalSettings.ts +++ b/src/interfaces/GlobalSettings.ts @@ -129,12 +129,6 @@ export interface globalSettingsData { taskId?: string; }; boundTaskCompletionToChildTasks: boolean; - kanbanView: { - lazyLoadingEnabled: boolean; - initialTaskCount: number; - loadMoreCount: number; - scrollThresholdPercent: number; - }; mapView: { background: string; mapOrientation: string; @@ -675,12 +669,6 @@ export const DEFAULT_SETTINGS: PluginDataJson = { settingTab: 0, }, boundTaskCompletionToChildTasks: false, - kanbanView: { - lazyLoadingEnabled: true, - initialTaskCount: 20, - loadMoreCount: 10, - scrollThresholdPercent: 80, - }, mapView: { background: mapViewBackgrounVariantTypes.none, mapOrientation: mapViewNodeMapOrientation.horizontal, From c545d948c0dc481166ea81a3051c85173c5bbf78 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Tue, 27 Jan 2026 14:26:30 +0530 Subject: [PATCH 043/236] remove the deprecated configs and few other refactoring --- src/components/KanbanView/KanbanBoardView.tsx | 3 --- src/components/KanbanView/KanbanSwimlanesContainer.tsx | 4 ---- src/components/KanbanView/LazyColumn.tsx | 9 ++++----- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/components/KanbanView/KanbanBoardView.tsx b/src/components/KanbanView/KanbanBoardView.tsx index d9dfe12c..1b3cbbcc 100644 --- a/src/components/KanbanView/KanbanBoardView.tsx +++ b/src/components/KanbanView/KanbanBoardView.tsx @@ -22,7 +22,6 @@ interface KanbanBoardProps { const KanbanBoard: React.FC = ({ plugin, board, allTasks, tasksPerColumn, loading, freshInstall }) => { // Check if lazy loading is enabled - const lazyLoadingEnabled = plugin.settings.data.globalSettings.kanbanView?.lazyLoadingEnabled ?? false; const ColumnComponent = LazyColumn; // lazyLoadingEnabled ? LazyColumn : Column; return ( @@ -55,9 +54,7 @@ const KanbanBoard: React.FC = ({ plugin, board, allTasks, task ) : ( board?.columns diff --git a/src/components/KanbanView/KanbanSwimlanesContainer.tsx b/src/components/KanbanView/KanbanSwimlanesContainer.tsx index 0d955996..064b7055 100644 --- a/src/components/KanbanView/KanbanSwimlanesContainer.tsx +++ b/src/components/KanbanView/KanbanSwimlanesContainer.tsx @@ -13,9 +13,7 @@ import { bugReporterManagerInsatance } from 'src/managers/BugReporter'; interface KanbanSwimlanesContainerProps { plugin: TaskBoard; board: Board; - allTasks: taskJsonMerged | undefined; tasksPerColumn: taskItem[][]; - lazyLoadingEnabled: boolean; } interface SwimlaneRow { @@ -28,9 +26,7 @@ interface SwimlaneRow { const KanbanSwimlanesContainer: React.FC = ({ plugin, board, - allTasks, tasksPerColumn, - lazyLoadingEnabled, }) => { const ColumnComponent = LazyColumn; // lazyLoadingEnabled ? LazyColumn : Column; diff --git a/src/components/KanbanView/LazyColumn.tsx b/src/components/KanbanView/LazyColumn.tsx index c7bb87c7..dd843a47 100644 --- a/src/components/KanbanView/LazyColumn.tsx +++ b/src/components/KanbanView/LazyColumn.tsx @@ -51,11 +51,10 @@ const LazyColumn: React.FC = ({ return null; // Don't render the column if it has no tasks and empty columns are hidden } - // Lazy loading settings from plugin - const lazySettings = plugin.settings.data.globalSettings.kanbanView; - const initialTaskCount = lazySettings.initialTaskCount || 20; - const loadMoreCount = lazySettings.loadMoreCount || 10; - const scrollThresholdPercent = lazySettings.scrollThresholdPercent || 80; + // Lazy loading configs + const initialTaskCount = 20; + const loadMoreCount = 10; + const scrollThresholdPercent = 80; // State for managing visible tasks const [visibleTaskCount, setVisibleTaskCount] = useState(initialTaskCount); From e69653dc6adbc3986995ffdd0698d85edd435c39 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Tue, 27 Jan 2026 14:26:44 +0530 Subject: [PATCH 044/236] feat : lazy loading functionality for tasks importer panel --- src/components/MapView/TasksImporterPanel.tsx | 109 +++++++++++++++--- 1 file changed, 90 insertions(+), 19 deletions(-) diff --git a/src/components/MapView/TasksImporterPanel.tsx b/src/components/MapView/TasksImporterPanel.tsx index 2450f5b8..98959a93 100644 --- a/src/components/MapView/TasksImporterPanel.tsx +++ b/src/components/MapView/TasksImporterPanel.tsx @@ -1,7 +1,7 @@ // /src/components/MapView/TasksImporterPanel.tsx -import React, { useState, useEffect, useMemo } from 'react'; -import { X } from 'lucide-react'; +import React, { useState, useEffect, useMemo, useRef, useCallback } from 'react'; +import { X, ChevronDown } from 'lucide-react'; import { taskItem } from 'src/interfaces/TaskItem'; import TaskItem from '../KanbanView/TaskItem'; import TaskBoard from 'main'; @@ -28,6 +28,16 @@ export const TasksImporterPanel: React.FC = ({ }) => { const [searchQuery, setSearchQuery] = useState(''); const [importedTaskIds, setImportedTaskIds] = useState>(new Set()); + const tasksContentRef = useRef(null); + + // Lazy loading configs + // Lazy loading configs + const initialTaskCount = 20; + const loadMoreCount = 10; + const scrollThresholdPercent = 80; + + // State for managing visible tasks + const [visibleTaskCount, setVisibleTaskCount] = useState(initialTaskCount); // Get all tasks without an ID (legacyId is empty) const tasksWithoutId = useMemo(() => { @@ -48,6 +58,12 @@ export const TasksImporterPanel: React.FC = ({ ); }, [tasksWithoutId, searchQuery]); + // Memoize visible tasks based on count + const visibleTasks = useMemo(() => { + if (!filteredTasks || filteredTasks.length < 1) return []; + return filteredTasks.slice(0, visibleTaskCount); + }, [filteredTasks, visibleTaskCount]); + const handleImportTask = async (task: taskItem) => { try { const newId = await applyIdToTaskItem(plugin, task); @@ -71,6 +87,50 @@ export const TasksImporterPanel: React.FC = ({ } }; + // Reset visible count when filtered tasks change (e.g., due to search) + useEffect(() => { + setVisibleTaskCount(initialTaskCount); + }, [filteredTasks, initialTaskCount]); + + // Scroll event handler + const handleScroll = useCallback(() => { + const container = tasksContentRef.current; + if (!container) return; + + const { scrollTop, scrollHeight, clientHeight } = container; + const scrollPercentage = ((scrollTop + clientHeight) / scrollHeight) * 100; + + // Load more tasks when scroll threshold is reached and there are more tasks to load + if (scrollPercentage >= scrollThresholdPercent && visibleTaskCount < filteredTasks.length) { + setVisibleTaskCount((prevCount) => { + const newCount = Math.min(prevCount + loadMoreCount, filteredTasks.length); + return newCount; + }); + } + }, [scrollThresholdPercent, visibleTaskCount, filteredTasks.length, loadMoreCount]); + + // Attach scroll listener + useEffect(() => { + const container = tasksContentRef.current; + if (!container) return; + + // Throttle scroll events for performance + let throttleTimeout: NodeJS.Timeout | null = null; + const throttledScroll = () => { + if (throttleTimeout) return; + throttleTimeout = setTimeout(() => { + handleScroll(); + throttleTimeout = null; + }, 100); + }; + + container.addEventListener('scroll', throttledScroll); + return () => { + container.removeEventListener('scroll', throttledScroll); + if (throttleTimeout) clearTimeout(throttleTimeout); + }; + }, [handleScroll]); + // Reset imported tasks when panel is closed useEffect(() => { if (!isVisible) { @@ -133,7 +193,7 @@ export const TasksImporterPanel: React.FC = ({ )}
-
+
{filteredTasks.length === 0 ? (

@@ -143,23 +203,34 @@ export const TasksImporterPanel: React.FC = ({

) : ( -
- {filteredTasks.map((task, index) => ( -
handleImportTask(task)} - > - +
+ {visibleTasks.map((task, index) => ( +
-
- ))} -
+ className="tasksImporterPanelTaskItemWrapper" + onClick={() => handleImportTask(task)} + > + +
+ ))} +
+ {visibleTaskCount < filteredTasks.length && ( + + )} + )}
From de1270953c79bf9216682af5995d61320d4e3820 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Tue, 27 Jan 2026 14:52:59 +0530 Subject: [PATCH 045/236] fix: control buttons in map view are a little high --- styles.css | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/styles.css b/styles.css index 31cdf6bb..2eae0e72 100644 --- a/styles.css +++ b/styles.css @@ -4752,7 +4752,11 @@ svg.react-flow__connectionline { } .react-flow__panel.bottom { - bottom: 120px; + bottom: 0; +} + +.is-mobile .react-flow__panel.bottom { + bottom: 60px; } .react-flow__panel.top.center, From f973a3ec6649b224b8542dbd0e228be565140041 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Tue, 27 Jan 2026 14:53:09 +0530 Subject: [PATCH 046/236] test the latest changes | working --- data.json | 5 +- task-board-logs.log | 180 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 170 insertions(+), 15 deletions(-) diff --git a/data.json b/data.json index b567e50a..91f03a3c 100644 --- a/data.json +++ b/data.json @@ -29,7 +29,6 @@ "372", "373", "370", - "1506909345", "362" ] }, @@ -1050,10 +1049,10 @@ "experimentalFeatures": true, "searchQuery": "", "lastViewHistory": { - "viewedType": "kanban", + "viewedType": "map", "boardIndex": 0, "taskId": "", - "settingTab": 1 + "settingTab": 2 }, "taskNoteIdentifierTag": "task", "doubleClickCardToEdit": "noteInTab", diff --git a/task-board-logs.log b/task-board-logs.log index ad4c7aad..b02a75ae 100644 --- a/task-board-logs.log +++ b/task-board-logs.log @@ -4,18 +4,6 @@ ## Recent Bug Reports -Timestamp : 2026-01-22T14:29:32.899Z -ID : 73 -Message : Failed to move tasks.json file to new path -Context : JsonFileOperations.ts/moveTasksCacheFileToNewPath - -#### Bug Content -```log -Error: ENOENT: no such file or directory, rename 'D:\Personal_Projects_Hub\IDE_Wise_Projects\Obsidian\TemplateToDevelopPlugin\Meta\Task_Board\Cache\task-board-cache.json' -> 'D:\Personal_Projects_Hub\IDE_Wise_Projects\Obsidian\TemplateToDevelopPlugin\Meta\Task_Board\task-board-cache.json' -``` - -------------- - Timestamp : 2026-01-22T14:41:12.790Z ID : 73 Message : Failed to move tasks.json file to new path @@ -85,3 +73,171 @@ Context : MapView.tsx/initialNodes ```log ERROR: Same id is present on two tasks ``` + +------------- + +Timestamp : 2026-01-27T13:31:39 +ID : 17 +Message : Following duplicate IDs has been found for tasks with IDs: "268". This may cause unexpected behavior. Please consider changing the IDs of these tasks. +Context : MapView.tsx/initialNodes + +#### Bug Content +```log +ERROR: Same id is present on two tasks +``` + +------------- + +Timestamp : 2026-01-27T14:36:39 +ID : 17 +Message : Following duplicate IDs has been found for tasks with IDs: "268". This may cause unexpected behavior. Please consider changing the IDs of these tasks. +Context : MapView.tsx/initialNodes + +#### Bug Content +```log +ERROR: Same id is present on two tasks +``` + +------------- + +Timestamp : 2026-01-27T14:37:55 +ID : 17 +Message : Following duplicate IDs has been found for tasks with IDs: "268". This may cause unexpected behavior. Please consider changing the IDs of these tasks. +Context : MapView.tsx/initialNodes + +#### Bug Content +```log +ERROR: Same id is present on two tasks +``` + +------------- + +Timestamp : 2026-01-27T14:39:23 +ID : 17 +Message : Following duplicate IDs has been found for tasks with IDs: "268". This may cause unexpected behavior. Please consider changing the IDs of these tasks. +Context : MapView.tsx/initialNodes + +#### Bug Content +```log +ERROR: Same id is present on two tasks +``` + +------------- + +Timestamp : 2026-01-27T14:40:34 +ID : 17 +Message : Following duplicate IDs has been found for tasks with IDs: "268". This may cause unexpected behavior. Please consider changing the IDs of these tasks. +Context : MapView.tsx/initialNodes + +#### Bug Content +```log +ERROR: Same id is present on two tasks +``` + +------------- + +Timestamp : 2026-01-27T14:41:44 +ID : 17 +Message : Following duplicate IDs has been found for tasks with IDs: "268". This may cause unexpected behavior. Please consider changing the IDs of these tasks. +Context : MapView.tsx/initialNodes + +#### Bug Content +```log +ERROR: Same id is present on two tasks +``` + +------------- + +Timestamp : 2026-01-27T14:47:48 +ID : 17 +Message : Following duplicate IDs has been found for tasks with IDs: "268". This may cause unexpected behavior. Please consider changing the IDs of these tasks. +Context : MapView.tsx/initialNodes + +#### Bug Content +```log +ERROR: Same id is present on two tasks +``` + +------------- + +Timestamp : 2026-01-27T14:48:11 +ID : 17 +Message : Following duplicate IDs has been found for tasks with IDs: "268". This may cause unexpected behavior. Please consider changing the IDs of these tasks. +Context : MapView.tsx/initialNodes + +#### Bug Content +```log +ERROR: Same id is present on two tasks +``` + +------------- + +Timestamp : 2026-01-27T14:48:29 +ID : 17 +Message : Following duplicate IDs has been found for tasks with IDs: "268". This may cause unexpected behavior. Please consider changing the IDs of these tasks. +Context : MapView.tsx/initialNodes + +#### Bug Content +```log +ERROR: Same id is present on two tasks +``` + +------------- + +Timestamp : 2026-01-27T14:49:11 +ID : 17 +Message : Following duplicate IDs has been found for tasks with IDs: "268". This may cause unexpected behavior. Please consider changing the IDs of these tasks. +Context : MapView.tsx/initialNodes + +#### Bug Content +```log +ERROR: Same id is present on two tasks +``` + +------------- + +Timestamp : 2026-01-27T14:49:24 +ID : 17 +Message : Following duplicate IDs has been found for tasks with IDs: "268". This may cause unexpected behavior. Please consider changing the IDs of these tasks. +Context : MapView.tsx/initialNodes + +#### Bug Content +```log +ERROR: Same id is present on two tasks +``` + +------------- + +Timestamp : 2026-01-27T14:49:37 +ID : 17 +Message : Following duplicate IDs has been found for tasks with IDs: "268". This may cause unexpected behavior. Please consider changing the IDs of these tasks. +Context : MapView.tsx/initialNodes + +#### Bug Content +```log +ERROR: Same id is present on two tasks +``` + +------------- + +Timestamp : 2026-01-27T14:49:50 +ID : 17 +Message : Following duplicate IDs has been found for tasks with IDs: "268". This may cause unexpected behavior. Please consider changing the IDs of these tasks. +Context : MapView.tsx/initialNodes + +#### Bug Content +```log +ERROR: Same id is present on two tasks +``` + +------------- + +Timestamp : 2026-01-27T14:51:52 +ID : 17 +Message : Following duplicate IDs has been found for tasks with IDs: "268". This may cause unexpected behavior. Please consider changing the IDs of these tasks. +Context : MapView.tsx/initialNodes + +#### Bug Content +```log +ERROR: Same id is present on two tasks +``` From 377293bfd7ae3ba12ae5996dd515685e943cf39e Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Thu, 29 Jan 2026 10:41:27 +0530 Subject: [PATCH 047/236] remove the "file" label in the AddorEditTaskModal editor header --- src/components/AddOrEditTaskRC.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/AddOrEditTaskRC.tsx b/src/components/AddOrEditTaskRC.tsx index 2349bb58..7804d259 100644 --- a/src/components/AddOrEditTaskRC.tsx +++ b/src/components/AddOrEditTaskRC.tsx @@ -1205,7 +1205,7 @@ export const AddOrEditTaskRC: React.FC<{
*/}
-
{(communityPlugins.isQuickAddPluginIntegrationEnabled() && !taskExists && !isTaskNote && !activeNote) ? t("quickadd-plugin-choice") : t("file")} +
Date: Thu, 29 Jan 2026 10:42:37 +0530 Subject: [PATCH 048/236] fix : add missing english translations --- src/modals/CustomStatusConfigurator.ts | 3 ++- src/settings/SettingConstructUI.ts | 28 +++++++++++++++++++------- src/utils/lang/locale/en.ts | 22 +++++++++++++------- 3 files changed, 38 insertions(+), 15 deletions(-) diff --git a/src/modals/CustomStatusConfigurator.ts b/src/modals/CustomStatusConfigurator.ts index 0882cc38..bcd93b63 100644 --- a/src/modals/CustomStatusConfigurator.ts +++ b/src/modals/CustomStatusConfigurator.ts @@ -36,7 +36,7 @@ export class CustomStatusModal extends Modal { this.type = status.type; } this.isCoreStatus = isCoreStatus; - this.setTitle(t("Configure status entry")); + this.setTitle(t("configure-status-entry")); } /** @@ -123,6 +123,7 @@ export class CustomStatusModal extends Modal { const footerEl = contentEl.createDiv(); const footerButtons = new Setting(footerEl); footerButtons.addButton((b) => { + b.setButtonText(t("save")); b.setTooltip(t("save")).onClick(async () => { this.saved = true; this.close(); diff --git a/src/settings/SettingConstructUI.ts b/src/settings/SettingConstructUI.ts index 04db2f2e..d6438c50 100644 --- a/src/settings/SettingConstructUI.ts +++ b/src/settings/SettingConstructUI.ts @@ -634,7 +634,14 @@ export class SettingsManager { // New setting for updating language file new Setting(contentEl) .setName(t("update-language-translations")) - .setDesc(t("update-language-translations-info")) + .setDesc( + createFragmentWithHTML( + t("update-language-translations-info") + + " " + + t("task-board-docs") + + ".", + ), + ) .addButton((button) => button.setButtonText("Update").onClick(async () => { const result = await downloadAndApplyLanguageFile( @@ -729,6 +736,13 @@ export class SettingsManager { "" + t("manual-sorting-feature-info") + "" + + "
  • " + + "" + + "Task card menu" + + " : " + + "" + + "Easily change various properties of tasks and access quick actions through the menu. Specially useful on mobile as an alternative to drag and drop feature." + + "
  • " + "", ), ) @@ -2304,8 +2318,8 @@ export class SettingsManager { // Setting for Auto Adding Created Date while creating new Tasks through AddTaskModal new Setting(contentEl) - .setName(t("auto-add-created-date-to-tasks")) - .setDesc(t("auto-add-created-date-to-tasks-desc")) + .setName(t("auto-add-created-date")) + .setDesc(t("auto-add-created-date-desc")) .addToggle((toggle) => toggle.setValue(autoAddCreatedDate).onChange(async (value) => { this.globalSettings!.autoAddCreatedDate = value; @@ -2315,8 +2329,8 @@ export class SettingsManager { // Setting for Auto Adding Created Date while creating new Tasks through AddTaskModal new Setting(contentEl) - .setName(t("auto-add-completed-date-to-tasks")) - .setDesc(t("auto-add-created-date-to-tasks-desc")) + .setName(t("auto-add-completed-date")) + .setDesc(t("auto-add-completed-date-desc")) .addToggle((toggle) => toggle .setValue(autoAddCompletedDate) @@ -2328,8 +2342,8 @@ export class SettingsManager { // Setting for Auto Adding Created Date while creating new Tasks through AddTaskModal new Setting(contentEl) - .setName(t("auto-add-cancelled-date-to-tasks")) - .setDesc(t("auto-add-created-date-to-tasks-desc")) + .setName(t("auto-add-cancelled-date")) + .setDesc(t("auto-add-cancelled-date-desc")) .addToggle((toggle) => toggle .setValue(autoAddCancelledDate) diff --git a/src/utils/lang/locale/en.ts b/src/utils/lang/locale/en.ts index 1747513c..43a4f0d3 100644 --- a/src/utils/lang/locale/en.ts +++ b/src/utils/lang/locale/en.ts @@ -192,7 +192,7 @@ const en: Lang = { "text-of-the-tag": "Text of the tag", "background-of-the-card": "Background of the card", "liveEditor": "Live Editor", - "rawEditor": "Raw Editor", + "rawEditor": "Source mode", "child-tasks": "Child tasks", "child-tasks-section-description": "Connect the tasks which are blocking the current task.", "task-board-bug-reporter": "Task Board bug reporter", @@ -213,8 +213,12 @@ const en: Lang = { "default-note-for-new-tasks-description": "Select the default note in which all the newly added tasks through 'add new task modal' should be saved.", "universal-date": "Universal date", "universal-date-description": "Select what type of date you want task board to consider as your universal date for all the automation operations.", - "auto-add-created-date-to-tasks": "Auto add created date to tasks", - "auto-add-created-date-to-tasks-desc": "When enabled, todays date will be automatically added as the created date to the task.", + "auto-add-created-date": "Auto add created date", + "auto-add-created-date-desc": "When enabled, todays date will be automatically added as the created date to the task.", + "auto-add-completed-date": "Auto add completed date", + "auto-add-completed-date-desc": "Enable this feature to automatically add current date-time value as the completed date property to the task when you will mark the task as complete through checkbox or through status change to status of type 'TODO'", + "auto-add-cancelled-date": "Auto add cancelled date", + "auto-add-cancelled-date-desc": "Enable this feature to automatically add current date-time value as the cancelled date property to the task when you will mark the task as cancelled through checkbox or through status change to status of type 'CANCELLED'", "file-for-archived-tasks": "File for archived tasks", "file-for-archived-tasks-description": "Select the note in which the archived tasks should be moved. Leave this setting empty to simply comment out the task in the parent file itself", "default-quickadd-choice": "Default quickadd plugin choice", @@ -244,7 +248,7 @@ const en: Lang = { "update": "Update", "archive-instead": "Archive instead", "update-language-translations": "Update language translations", - "update-language-translations-info": "Click on the update button to download the latest language translations.", + "update-language-translations-info": "If you have changed the inteface language in Obsidian settinsg, click on the update button to download the latest language translations for that language. You can locally update the translations in your own language and submit your changes to contribute to this plugin. Read more here : ", "show-note-frontmatter-tags-in-the-card-header": "Inherit frontmatter tags for inline-tasks (read-only)", "show-note-frontmatter-tags-in-the-card-header-info": "Enable this feature to use the tags from the note frontmatter applied to your tasks. You cannot actually change this tags through task board. These frontmatter tags will be only used for filtering tasks.", "scan-tasks-from-the-vault-info-1": "Scan your whole vault at once.", @@ -511,7 +515,7 @@ const en: Lang = { "kanban-swimlanes": "Kanban swimlanes", "kanban-swimlanes-feature-info": "This feature will help you to group tasks based on their various properties. Additionally, this plugin has its first of kind implementation of swimlanes, to have custom sorting and aggregation of tasks.", "manual-sorting": "Manual sorting", - "manual-sorting-feature-info": "This feature will help you to drag and re-order the tasks within a column..", + "manual-sorting-feature-info": "This feature will help you to drag and re-order the tasks within a column.", "status-name": "Status name", "status-symbol": "Status symbol", "status-action": "Status action", @@ -525,7 +529,7 @@ const en: Lang = { "minimized": "Minimized", "checkbox": "Checkbox", "task-card-style": "Task card style", - "task-card-style-info": "Select the UI of the task card. With the 'bases card style' option you may also use an additional CSS snippet to get more dataview properties look. Get the CSS snippet from here : ", + "task-card-style-info": "Select the UI of the task card. With the 'bases card style' option you may also use an additional CSS snippet to get more dataview properties kind of look.", "tasks-plugin-emoji": "Tasks plugin emoji", "bases-cards-style": "Bases card style", "work-limit": "Work limit", @@ -568,7 +572,11 @@ const en: Lang = { "task-status-type-info": "Select what kind of status is this. If this status if of type DONE or CANCELLED, then the task will appear inside the 'completed' type column.", "cycle-to-following-status": "Cycle to the following status", "cycle-to-following-status-info": "Enter the status symbol, eg. /. Once you click on the above status, cycle to this status. Also, dont forget to create a new entry for this status type.", - + "export-logs": "Export logs", + "export-logs-info": "This plugin stores all the warnings and errors in a log file inside the plugin folder. You may click on the export button to export it wherever you want in a txt file so you can debug the issue on your own or submit it to the developer if they have asked you for the logs. Before submitting the log file, please ensure that no vault content has been captured inside the logs. Also it will be advisable to submit the logs on email instead of on GitHub.", + "configure-status-entry": "Configure status entry", + "edit": "Edit", + "no-custom-statuses-configured": "No custom statuses configured" }; export default en; From dcc8079767cfd4feff099cf263b7cf05a65f66b2 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Thu, 29 Jan 2026 10:43:01 +0530 Subject: [PATCH 049/236] fix : minor styling improvements --- styles.css | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/styles.css b/styles.css index 2eae0e72..40b24312 100644 --- a/styles.css +++ b/styles.css @@ -688,6 +688,7 @@ button.clickable-icon.view-action.taskboardScanVaultBtn.highlight { mask-image: unset; -webkit-mask-image: unset; margin-block-start: 3px !important; + vertical-align: middle; } .taskItemCheckbox-checked { @@ -1663,10 +1664,6 @@ button.clickable-icon.view-action.taskboardScanVaultBtn.highlight { margin-block: 0 !important; } -.taskItemCheckbox { - vertical-align: middle; -} - .EditTaskModalLiveEditor { min-height: 20vh; /* max-height: 40vh; */ @@ -3368,6 +3365,7 @@ body:not(.alt-chkbx-off) .taskItemCheckbox.taskItemCheckbox-checked:is([data-tas cursor: pointer; mask-image: unset; -webkit-mask-image: unset; + background-image: none !important; } body:not(.alt-chkbx-off) .view-content .taskItemCheckbox[data-task]:is([data-task=X], @@ -3528,6 +3526,7 @@ body:not(.alt-chkbx-off) .taskItemCheckbox-checked:is([data-task=X], mask-image: unset; -webkit-mask-image: unset; border: none !important; + background-image: none !important; } /*--Checkbox Type Icons--*/ @@ -3841,6 +3840,7 @@ body:not(.alt-chkbx-off) .taskItemCheckbox-checked[data-task=s] p>input[type=che [data-task="H"], [data-task="<"]) { border: 0 !important; + background-image: none !important; /* This will remove the border of the checkbox so only the icon is visible*/ } @@ -6870,6 +6870,7 @@ input[type="checkbox"].swimlanesConfigCheckbox { display: flex; flex-direction: row; gap: 5px; + backdrop-filter: blur(5px); } .swimlanesHeaderRow .TaskBoardColumnsSection { From 7b9e7375695ee518f97425c9fed5dd4dbc974e72 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Thu, 29 Jan 2026 12:43:04 +0530 Subject: [PATCH 050/236] fix : dont prepend 'copy-' if task note file already exists --- src/services/OpenModals.ts | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/services/OpenModals.ts b/src/services/OpenModals.ts index 250458a0..7e5293b7 100644 --- a/src/services/OpenModals.ts +++ b/src/services/OpenModals.ts @@ -182,25 +182,29 @@ export const openAddNewTaskNoteModal = (app: App, plugin: TaskBoard) => { }); }); } else { + const newName = getCurrentLocalTimeString(); + const parts = newTask.filePath.split("/"); + const dirPath = parts.slice(0, -1).join("/").trim(); + const newPath = normalizePath( + `${dirPath}/${newName}.md`, + ); new Notice( t("file-note-already-exists") + t( - "creating a new file with the following name :", + "creating a new file with the current timestamp as note name :", ) + - ` Copy-${newTask.filePath}`, + ` ${newName}.md`, 10000, ); - await plugin.app.vault - .create(`Copy-${newTask.filePath}`, noteContent) - .then(() => { - // This is required to rescan the updated file and refresh the board. - plugin.realTimeScanner.onFileModified( - `Copy-${newTask.filePath}`, - ); - sleep(1000).then(() => { - plugin.realTimeScanner.processAllUpdatedFiles(); - }); + await plugin.app.vault.create(newPath, "").then(() => { + // This is required to rescan the updated file and refresh the board. + plugin.realTimeScanner.onFileModified( + `Copy-${newTask.filePath}`, + ); + sleep(1000).then(() => { + plugin.realTimeScanner.processAllUpdatedFiles(); }); + }); } } catch (error) { bugReporterManagerInsatance.addToLogs( From 0853ac214e9dde2936def29d76c527347221be70 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Thu, 29 Jan 2026 12:44:09 +0530 Subject: [PATCH 051/236] fix : normalize all paths before using and create folder if not exists --- main.ts | 6 ++-- src/components/AddOrEditTaskRC.tsx | 4 +-- src/interfaces/GlobalSettings.ts | 6 ++-- src/modals/AddOrEditTaskModal.tsx | 10 +++--- src/services/OpenModals.ts | 8 +++-- src/settings/SettingConstructUI.ts | 48 +++++++++++++++++++++-------- src/utils/lang/locale/en.ts | 5 +-- src/utils/taskLine/TaskLineUtils.ts | 36 ++++++++++++++-------- src/views/AddOrEditTaskView.tsx | 22 ++++++------- 9 files changed, 95 insertions(+), 50 deletions(-) diff --git a/main.ts b/main.ts index fc471a8c..129e1712 100644 --- a/main.ts +++ b/main.ts @@ -2,6 +2,7 @@ import { App, + normalizePath, Notice, Plugin, PluginManifest, @@ -698,8 +699,9 @@ export default class TaskBoard extends Plugin { return; } - const archivedPath = - this.settings.data.globalSettings.archivedTBNotesFolderPath; + const archivedPath = normalizePath( + this.settings.data.globalSettings.archivedTBNotesFolderPath, + ); const totalFiles = this.renameQueue.length; // Show progress notice diff --git a/src/components/AddOrEditTaskRC.tsx b/src/components/AddOrEditTaskRC.tsx index 7804d259..e483dc99 100644 --- a/src/components/AddOrEditTaskRC.tsx +++ b/src/components/AddOrEditTaskRC.tsx @@ -534,7 +534,7 @@ export const AddOrEditTaskRC: React.FC<{ const taskNoteItem: taskItem = { ...modifiedTask, - title: title, + title: title === "" ? taskNoteFilePath.split('/').pop() ?? "No title" : title, body: formattedTaskContent ? formattedTaskContent.split('\n').filter(line => isTaskLine(line)) : [], createdDate: newCreatedDate, startDate: newStartDate, @@ -1208,7 +1208,7 @@ export const AddOrEditTaskRC: React.FC<{
    :"/\\|?*]/g, '_'); this.filePath = normalizePath(`${defaultLocation}/${sanitizedName}.md`); + this.task.title = ""; } - - if (!this.task.title) this.task.title = this.filePath.split('/').pop()?.replace(allowedFileExtensionsRegEx, "") ?? "Untitled"; } else { if (!this.taskExists) this.task.title = "- [ ] "; diff --git a/src/services/OpenModals.ts b/src/services/OpenModals.ts index 7e5293b7..efcc63d4 100644 --- a/src/services/OpenModals.ts +++ b/src/services/OpenModals.ts @@ -1,6 +1,6 @@ // src/services/OpenModals.ts -import { App, Notice, TFile, WorkspaceLeaf } from "obsidian"; +import { App, normalizePath, Notice, TFile, WorkspaceLeaf } from "obsidian"; import { addTaskInNote, updateTaskInFile, @@ -29,6 +29,7 @@ import { TaskBoardActionsModal } from "src/modals/TaskBoardActionsModal"; import { bugReporterManagerInsatance } from "src/managers/BugReporter"; import { DatePickerModal } from "src/modals/date_picker"; import { updateTaskItemDate } from "src/utils/UserTaskEvents"; +import { getCurrentLocalTimeString } from "src/utils/DateTimeCalculations"; // Function to open the BoardConfigModal export const openBoardConfigModal = ( @@ -136,7 +137,7 @@ export const openAddNewTaskModal = ( undefined, activeTFile ? activeTFile.path - : plugin.settings.data.globalSettings.preDefinedNote, + : normalizePath(plugin.settings.data.globalSettings.preDefinedNote), ); AddTaskModal.open(); }; @@ -162,6 +163,9 @@ export const openAddNewTaskNoteModal = (app: App, plugin: TaskBoard) => { if (!(await plugin.app.vault.adapter.exists(dirPath))) { await plugin.app.vault.createFolder(dirPath); } + + // Required for Obsidian to create the folder and index it. + sleep(200); } // Create or update the file diff --git a/src/settings/SettingConstructUI.ts b/src/settings/SettingConstructUI.ts index d6438c50..f488a931 100644 --- a/src/settings/SettingConstructUI.ts +++ b/src/settings/SettingConstructUI.ts @@ -1741,8 +1741,10 @@ export class SettingsManager { .setDesc(t("default-note-for-new-tasks-description")) .addText((text) => { text.setValue(preDefinedNote).onChange((value) => { - if (this.globalSettings) - this.globalSettings.preDefinedNote = value; + if (this.globalSettings) { + const normalized = normalizePath(value); + this.globalSettings.preDefinedNote = normalized; + } }); const inputEl = text.inputEl; @@ -1769,8 +1771,10 @@ export class SettingsManager { .setDesc(t("file-for-archived-tasks-description")) .addText((text) => { text.setValue(archivedTasksFilePath).onChange((value) => { - if (this.globalSettings) - this.globalSettings.archivedTasksFilePath = value; + if (this.globalSettings) { + const normalized = normalizePath(value); + this.globalSettings.archivedTasksFilePath = normalized; + } }); const inputEl = text.inputEl; @@ -1851,20 +1855,38 @@ export class SettingsManager { inputEl.placeholder = "e.g., #taskNote"; }); + const folderSuggestions = getFolderSuggestions(this.app); + // Setting for choosing the default location for task notes new Setting(contentEl) .setName(t("default-location-for-new-task-notes")) .setDesc(t("default-location-for-new-task-notes-description")) .addText((text) => { text.setValue(taskNoteDefaultLocation).onChange((value) => { - if (this.globalSettings) - this.globalSettings.taskNoteDefaultLocation = value; + if (this.globalSettings) { + const normalized = normalizePath(value); + this.globalSettings.taskNoteDefaultLocation = + normalized; + } }); const inputEl = text.inputEl; - // For folders, we could use folder suggestions or just allow text input - // For now, let's keep it simple with text input inputEl.placeholder = "e.g., Task Notes/"; + const onSelectCallback = async (selectedPath: string) => { + if (this.globalSettings) { + this.globalSettings.taskNoteDefaultLocation = + selectedPath; + } + text.setValue(selectedPath); + await this.saveSettings(); + }; + + new MultiSuggest( + inputEl, + new Set(folderSuggestions), + onSelectCallback, + this.app, + ); }); // Setting for choosing the default file to archive tasks @@ -1873,13 +1895,15 @@ export class SettingsManager { .setDesc(t("folder-for-archived-task-notes-description")) .addText((text) => { text.setValue(archivedTBNotesFolderPath).onChange((value) => { - if (this.globalSettings) - this.globalSettings.archivedTBNotesFolderPath = value; + if (this.globalSettings) { + const normalized = normalizePath(value); + this.globalSettings.archivedTBNotesFolderPath = + normalized; + } }); const inputEl = text.inputEl; inputEl.placeholder = "e.g., TaskBoard/TaskNotes"; - const suggestionContent = getFolderSuggestions(this.app); const onSelectCallback = async (selectedPath: string) => { if (this.globalSettings) { this.globalSettings.archivedTBNotesFolderPath = @@ -1891,7 +1915,7 @@ export class SettingsManager { new MultiSuggest( inputEl, - new Set(suggestionContent), + new Set(folderSuggestions), onSelectCallback, this.app, ); diff --git a/src/utils/lang/locale/en.ts b/src/utils/lang/locale/en.ts index 43a4f0d3..ac2250a3 100644 --- a/src/utils/lang/locale/en.ts +++ b/src/utils/lang/locale/en.ts @@ -220,7 +220,7 @@ const en: Lang = { "auto-add-cancelled-date": "Auto add cancelled date", "auto-add-cancelled-date-desc": "Enable this feature to automatically add current date-time value as the cancelled date property to the task when you will mark the task as cancelled through checkbox or through status change to status of type 'CANCELLED'", "file-for-archived-tasks": "File for archived tasks", - "file-for-archived-tasks-description": "Select the note in which the archived tasks should be moved. Leave this setting empty to simply comment out the task in the parent file itself", + "file-for-archived-tasks-description": "Select the note in which the archived tasks should be moved. Leave this setting empty, if you want to simply comment out the task in the parent file itself when you will click on 'archive instead' button.", "default-quickadd-choice": "Default quickadd plugin choice", "default-quickadd-choice-description": "Select which of your choice from your quickadd plugin configuration should be used as the default choice in the 'add new task modal'. Please install the quickadd plugin first and create a choice in the quickadd plugin.", "quickadd-plugin-not-enabled": "Quickadd plugin is not enabled. Please install the plugin and enable it.", @@ -576,7 +576,8 @@ const en: Lang = { "export-logs-info": "This plugin stores all the warnings and errors in a log file inside the plugin folder. You may click on the export button to export it wherever you want in a txt file so you can debug the issue on your own or submit it to the developer if they have asked you for the logs. Before submitting the log file, please ensure that no vault content has been captured inside the logs. Also it will be advisable to submit the logs on email instead of on GitHub.", "configure-status-entry": "Configure status entry", "edit": "Edit", - "no-custom-statuses-configured": "No custom statuses configured" + "no-custom-statuses-configured": "No custom statuses configured", + "task-note-title-placeholder": "Enter a simple title for the task..." }; export default en; diff --git a/src/utils/taskLine/TaskLineUtils.ts b/src/utils/taskLine/TaskLineUtils.ts index 6a802dc8..9bfafbbb 100644 --- a/src/utils/taskLine/TaskLineUtils.ts +++ b/src/utils/taskLine/TaskLineUtils.ts @@ -10,7 +10,7 @@ import { writeDataToVaultFile, } from "../MarkdownFileOperations"; -import { Notice } from "obsidian"; +import { normalizePath, Notice } from "obsidian"; import TaskBoard from "main"; import { TasksPluginApi } from "src/services/tasks-plugin/api"; import { @@ -52,21 +52,33 @@ export const addTaskInNote = async ( editorActive: boolean, cursorPosition?: { line: number; ch: number } | undefined, ): Promise => { - const filePath = allowedFileExtensionsRegEx.test(newTask.filePath) + const filePath = newTask.filePath.endsWith("md") ? newTask.filePath : `${newTask.filePath}.md`; - // Clean the task title to ensure it doesn't contain any special characters - if (!(await plugin.fileExists(filePath))) { - new Notice( - `New note created since it does not exists : "${filePath}"`, - 5000, - ); - // Create a new file if it doesn't exist - await plugin.app.vault.create(filePath, ""); - } - try { + // Clean the task title to ensure it doesn't contain any special characters + if (!(await plugin.fileExists(filePath))) { + new Notice( + `New note created since it does not exists : "${filePath}"`, + 5000, + ); + const normalizedPath = normalizePath(filePath); + // Check if the directory exists, create if not + const parts = normalizedPath.split("/"); + if (parts.length > 1) { + const dirPath = parts.slice(0, -1).join("/").trim(); + if (!(await plugin.app.vault.adapter.exists(dirPath))) { + await plugin.app.vault.createFolder(dirPath); + } + } + + // Create a new file if it doesn't exist + await plugin.app.vault.create(normalizedPath, ""); + + await sleep(200); + } + let completeTask = await getFormattedTaskContent(newTask); const { formattedTaskContent, newId } = await addIdToTaskContent( plugin, diff --git a/src/views/AddOrEditTaskView.tsx b/src/views/AddOrEditTaskView.tsx index 0e370ca3..567c95b1 100644 --- a/src/views/AddOrEditTaskView.tsx +++ b/src/views/AddOrEditTaskView.tsx @@ -13,6 +13,7 @@ import { allowedFileExtensionsRegEx } from "src/regularExpressions/Miscelleneous import { taskItemEmpty } from "src/interfaces/Mapping"; import { taskItem } from "src/interfaces/TaskItem"; import { generateTaskId } from "src/utils/TaskItemUtils"; +import { DEFAULT_SETTINGS } from "src/interfaces/GlobalSettings"; export class AddOrEditTaskView extends ItemView { @@ -66,35 +67,34 @@ export class AddOrEditTaskView extends ItemView { // container.empty(); container.setAttribute('data-type', 'task-board-view'); - if (!this.isTaskNote && this.plugin.settings.data.globalSettings.autoAddUniqueID && (!this.taskExists || !this.task.id)) { + if (this.plugin.settings.data.globalSettings.autoAddUniqueID && (!this.taskExists || !this.task.id)) { this.task.id = generateTaskId(this.plugin); - this.task.legacyId = String(this.task.id); + this.task.legacyId = this.task.id; } // Some processing, if this is a Task-Note let noteContent: string = ""; if (this.isTaskNote) { - if (this.filePath) { + if (this.taskExists) { const data = await readDataOfVaultFile(this.plugin, this.filePath); if (data == null) this.onClose(); else noteContent = data; + + if (!this.task.title) this.task.title = this.filePath.split('/').pop()?.replace(allowedFileExtensionsRegEx, "") ?? ""; } else { noteContent = "---\ntitle: \n---\n"; - const defaultLocation = this.plugin.settings.data.globalSettings.taskNoteDefaultLocation || 'Meta/Task_Board/Task_Notes'; + const defaultLocation = normalizePath(this.plugin.settings.data.globalSettings.taskNoteDefaultLocation || DEFAULT_SETTINGS.data.globalSettings.taskNoteDefaultLocation); const noteName = this.task.title || getCurrentLocalTimeString(); // Sanitize filename const sanitizedName = noteName.replace(/[<>:"/\\|?*]/g, '_'); this.filePath = normalizePath(`${defaultLocation}/${sanitizedName}.md`); + this.task.title = ""; } - - if (!this.task.title) this.task.title = this.filePath.split('/').pop()?.replace(allowedFileExtensionsRegEx, "") ?? "Untitled"; - - if (this.plugin.settings.data.globalSettings.autoAddUniqueID && (!this.taskExists || !this.task.legacyId)) { - this.task.id = generateTaskId(this.plugin); - this.task.legacyId = this.task.id; - } + } else { + if (!this.taskExists) + this.task.title = "- [ ] "; } this.root = createRoot(container); From 24849abf9726a84cae168c97ba638b5821e9993c Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Thu, 29 Jan 2026 12:44:23 +0530 Subject: [PATCH 052/236] test latest changes | working --- data.json | 20 +++++++++---------- task-board-logs.log | 48 ++++++++++++++++++++++----------------------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/data.json b/data.json index 91f03a3c..825b87f3 100644 --- a/data.json +++ b/data.json @@ -23,13 +23,13 @@ } ], "tasksIdManualOrder": [ - "3670624679", - 263, + "362", "367", - "372", + "3670624679", "373", "370", - "362" + 263, + "372" ] }, { @@ -856,7 +856,7 @@ "verticalHeaderUI": false }, "taskCount": { - "pending": 64, + "pending": 65, "completed": 14 } } @@ -1021,7 +1021,7 @@ "dayPlannerPlugin": true, "quickAddPlugin": false }, - "preDefinedNote": "Task_Board_Temp_Tasks.md", + "preDefinedNote": "Meta/TaskBoard/All Tasks.md", "quickAddPluginDefaultChoice": "Task Board Temp Tasks", "autoAddCreatedDate": true, "autoAddUniversalDate": true, @@ -1045,14 +1045,14 @@ "taskPropertyFormat": "3", "taskNoteDefaultLocation": "", "autoAddUniqueID": true, - "uniqueIdCounter": 588, + "uniqueIdCounter": 594, "experimentalFeatures": true, "searchQuery": "", "lastViewHistory": { - "viewedType": "map", - "boardIndex": 0, + "viewedType": "kanban", + "boardIndex": 4, "taskId": "", - "settingTab": 2 + "settingTab": 4 }, "taskNoteIdentifierTag": "task", "doubleClickCardToEdit": "noteInTab", diff --git a/task-board-logs.log b/task-board-logs.log index b02a75ae..836ebd7f 100644 --- a/task-board-logs.log +++ b/task-board-logs.log @@ -4,30 +4,6 @@ ## Recent Bug Reports -Timestamp : 2026-01-22T14:41:12.790Z -ID : 73 -Message : Failed to move tasks.json file to new path -Context : JsonFileOperations.ts/moveTasksCacheFileToNewPath - -#### Bug Content -```log -Error: Destination file already exists! -``` - -------------- - -Timestamp : 2026-01-22T15:32:49.517Z -ID : 73 -Message : Failed to move tasks.json file to new path -Context : JsonFileOperations.ts/moveTasksCacheFileToNewPath - -#### Bug Content -```log -Error: ENOENT: no such file or directory, rename 'D:\Personal_Projects_Hub\IDE_Wise_Projects\Obsidian\TemplateToDevelopPlugin\Meta\task-board-cache.json' -> 'D:\Personal_Projects_Hub\IDE_Wise_Projects\Obsidian\TemplateToDevelopPlugin\Meta\Task_Board\task-board-cache.json' -``` - -------------- - Timestamp : 2026-01-22T15:39:09.095Z ID : 73 Message : Failed to move tasks.json file to new path @@ -241,3 +217,27 @@ Context : MapView.tsx/initialNodes ```log ERROR: Same id is present on two tasks ``` + +------------- + +Timestamp : 2026-01-28T13:05:51 +ID : 17 +Message : Following duplicate IDs has been found for tasks with IDs: "276,224". This may cause unexpected behavior. Please consider changing the IDs of these tasks. +Context : MapView.tsx/initialNodes + +#### Bug Content +```log +ERROR: Same id is present on two tasks +``` + +------------- + +Timestamp : 2026-01-29T10:56:43 +ID : 103 +Message : +Context : MultiSuggest.ts/getQuickAddPluginChoices + +#### Bug Content +```log +Error: QuickAdd plugin object is undefined. +``` From 75c56f874a6042a8effc52d815bb0f1a844395ff Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Sat, 31 Jan 2026 22:24:00 +0530 Subject: [PATCH 053/236] feat : a new option for tag colors type --- src/components/KanbanView/TaskItem.tsx | 92 ++++++++++++------------ src/components/KanbanView/TaskItemV2.tsx | 90 ++++++++++++----------- src/interfaces/Enums.ts | 5 +- src/interfaces/GlobalSettings.ts | 2 +- src/settings/SettingConstructUI.ts | 29 ++++---- 5 files changed, 114 insertions(+), 104 deletions(-) diff --git a/src/components/KanbanView/TaskItem.tsx b/src/components/KanbanView/TaskItem.tsx index 835fb660..9bec0d7d 100644 --- a/src/components/KanbanView/TaskItem.tsx +++ b/src/components/KanbanView/TaskItem.tsx @@ -16,7 +16,7 @@ import { Board } from 'src/interfaces/BoardConfigs'; import { TaskRegularExpressions, TASKS_PLUGIN_DEFAULT_SYMBOLS } from 'src/regularExpressions/TasksPluginRegularExpr'; import { deleteTaskNote, getStatusNameFromStatusSymbol, isTaskNotePresentInTags } from 'src/utils/taskNote/TaskNoteUtils'; import { ChevronDown, EllipsisVertical, Grip } from 'lucide-react'; -import { EditButtonMode, viewTypeNames, colTypeNames, taskPropertiesNames } from 'src/interfaces/Enums'; +import { EditButtonMode, viewTypeNames, colTypeNames, taskPropertiesNames, TagColorType } from 'src/interfaces/Enums'; import { getCustomStatusOptionsForDropdown, getPriorityOptionsForDropdown, priorityEmojis } from 'src/interfaces/Mapping'; import { taskItem, UpdateTaskEventData } from 'src/interfaces/TaskItem'; import { matchTagsWithWildcards, verifySubtasksAndChildtasksAreComplete } from 'src/utils/algorithms/ScanningFilterer'; @@ -419,57 +419,58 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin // Function to get the card background color based on tags function getCardBgBasedOnTag(tags: string[]): string | undefined { - if (plugin.settings.data.globalSettings.tagColorsType === "text") { - return undefined; - } + if (globalSettings.tagColorsType === TagColorType.CardBg) { - const tagColors = plugin.settings.data.globalSettings.tagColors; + const tagColors = plugin.settings.data.globalSettings.tagColors; - if (!Array.isArray(tagColors) || tagColors.length === 0) { - return undefined; - } + if (!Array.isArray(tagColors) || tagColors.length === 0) { + return undefined; + } - // Prepare a map for faster lookup - const tagColorMap = new Map(tagColors.map((t) => [t.name, t])); + // Prepare a map for faster lookup + const tagColorMap = new Map(tagColors.map((t) => [t.name, t])); - let highestPriorityTag: { name: string; color: string; priority: number } | undefined = undefined; + let highestPriorityTag: { name: string; color: string; priority: number } | undefined = undefined; - for (const rawTag of tags) { - const tagName = rawTag.replace('#', ''); - let tagData = tagColorMap.get(tagName); + for (const rawTag of tags) { + const tagName = rawTag.replace('#', ''); + let tagData = tagColorMap.get(tagName); - if (!tagData) { - tagColorMap.forEach((tagColor, tagNameKey, mapValue) => { - const result = matchTagsWithWildcards(tagNameKey, tagName || ''); - // Return the first match found - if (result) tagData = tagColor; - }); - } + if (!tagData) { + tagColorMap.forEach((tagColor, tagNameKey, mapValue) => { + const result = matchTagsWithWildcards(tagNameKey, tagName || ''); + // Return the first match found + if (result) tagData = tagColor; + }); + } - if (tagData) { - if ( - !highestPriorityTag || - (tagData.priority) < (highestPriorityTag.priority) - ) { - highestPriorityTag = tagData; + if (tagData) { + if ( + !highestPriorityTag || + (tagData.priority) < (highestPriorityTag.priority) + ) { + highestPriorityTag = tagData; + } } } - } - const getOpacityValue = (color: string): number => { - const rgbaMatch = color.match(/rgba?\((\d+), (\d+), (\d+)(, (\d+(\.\d+)?))?\)/); - if (rgbaMatch) { - const opacity = rgbaMatch[5] ? parseFloat(rgbaMatch[5]) : 1; - return opacity; + const getOpacityValue = (color: string): number => { + const rgbaMatch = color.match(/rgba?\((\d+), (\d+), (\d+)(, (\d+(\.\d+)?))?\)/); + if (rgbaMatch) { + const opacity = rgbaMatch[5] ? parseFloat(rgbaMatch[5]) : 1; + return opacity; + } + return 1; + }; + + if (highestPriorityTag && getOpacityValue(highestPriorityTag.color) > 0.2) { + return updateRGBAOpacity(highestPriorityTag.color, 0.2); } - return 1; - }; - if (highestPriorityTag && getOpacityValue(highestPriorityTag.color) > 0.2) { - return updateRGBAOpacity(highestPriorityTag.color, 0.2); + return highestPriorityTag?.color; } - return highestPriorityTag?.color; + return undefined; } // ======================================== @@ -934,11 +935,14 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin
    {/* Render line tags (editable) */} {task.tags.map((tag: string) => { + const isTagBg = globalSettings.tagColorsType === TagColorType.TagBg; + const tagName = tag.replace('#', ''); - const customTag = plugin.settings.data.globalSettings.tagColorsType === "text" ? plugin.settings.data.globalSettings.tagColors.find(t => t.name === tagName) : undefined; - const tagColor = customTag?.color || `var(--tag-color)`; - const backgroundColor = customTag ? updateRGBAOpacity(customTag.color, 0.1) : `var(--tag-background)`; // 10% opacity background - const borderColor = customTag ? updateRGBAOpacity(customTag.color, 0.5) : `var(--tag-color-hover)`; + const customTag = plugin.settings.data.globalSettings.tagColorsType === TagColorType.CardBg ? undefined : plugin.settings.data.globalSettings.tagColors.find(t => t.name === tagName); + + const tagColor = customTag?.color; + const dimmedTagColor = customTag ? updateRGBAOpacity(customTag.color, 0.1) : `var(--tag-background)`; // 10% opacity background + // const borderColor = customTag ? updateRGBAOpacity(customTag.color, 0.5) : `var(--tag-color-hover)`; // If columnIndex is defined, proceed to get the column if ( @@ -957,9 +961,9 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin key={tagKey} className="taskItemTag" style={{ - color: tagColor, + color: isTagBg && tagColor ? 'white' : tagColor, // border: `1px solid ${borderColor}`, - backgroundColor: backgroundColor + backgroundColor: isTagBg ? tagColor : dimmedTagColor }} > {tag} diff --git a/src/components/KanbanView/TaskItemV2.tsx b/src/components/KanbanView/TaskItemV2.tsx index 0e116d69..83f78089 100644 --- a/src/components/KanbanView/TaskItemV2.tsx +++ b/src/components/KanbanView/TaskItemV2.tsx @@ -18,7 +18,7 @@ import { getStatusNameFromStatusSymbol, isTaskNotePresentInTags } from 'src/util import { allowedFileExtensionsRegEx } from 'src/regularExpressions/MiscelleneousRegExpr'; import { bugReporter, openDateInputModal } from 'src/services/OpenModals'; import { ChevronDown, EllipsisVertical, Grip } from 'lucide-react'; -import { EditButtonMode, viewTypeNames, colTypeNames, taskPropertiesNames } from 'src/interfaces/Enums'; +import { EditButtonMode, viewTypeNames, colTypeNames, taskPropertiesNames, TagColorType } from 'src/interfaces/Enums'; import { getCustomStatusOptionsForDropdown, getPriorityOptionsForDropdown, priorityEmojis } from 'src/interfaces/Mapping'; import { taskItem, UpdateTaskEventData } from 'src/interfaces/TaskItem'; import { matchTagsWithWildcards, verifySubtasksAndChildtasksAreComplete } from 'src/utils/algorithms/ScanningFilterer'; @@ -424,57 +424,58 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act // Function to get the card background color based on tags function getCardBgBasedOnTag(tags: string[]): string | undefined { - if (plugin.settings.data.globalSettings.tagColorsType === "text") { - return undefined; - } + if (globalSettings.tagColorsType === TagColorType.CardBg) { - const tagColors = plugin.settings.data.globalSettings.tagColors; + const tagColors = plugin.settings.data.globalSettings.tagColors; - if (!Array.isArray(tagColors) || tagColors.length === 0) { - return undefined; - } + if (!Array.isArray(tagColors) || tagColors.length === 0) { + return undefined; + } - // Prepare a map for faster lookup - const tagColorMap = new Map(tagColors.map((t) => [t.name, t])); + // Prepare a map for faster lookup + const tagColorMap = new Map(tagColors.map((t) => [t.name, t])); - let highestPriorityTag: { name: string; color: string; priority: number } | undefined = undefined; + let highestPriorityTag: { name: string; color: string; priority: number } | undefined = undefined; - for (const rawTag of tags) { - const tagName = rawTag.replace('#', ''); - let tagData = tagColorMap.get(tagName); + for (const rawTag of tags) { + const tagName = rawTag.replace('#', ''); + let tagData = tagColorMap.get(tagName); - if (!tagData) { - tagColorMap.forEach((tagColor, tagNameKey, mapValue) => { - const result = matchTagsWithWildcards(tagNameKey, tagName || ''); - // Return the first match found - if (result) tagData = tagColor; - }); - } + if (!tagData) { + tagColorMap.forEach((tagColor, tagNameKey, mapValue) => { + const result = matchTagsWithWildcards(tagNameKey, tagName || ''); + // Return the first match found + if (result) tagData = tagColor; + }); + } - if (tagData) { - if ( - !highestPriorityTag || - (tagData.priority) < (highestPriorityTag.priority) - ) { - highestPriorityTag = tagData; + if (tagData) { + if ( + !highestPriorityTag || + (tagData.priority) < (highestPriorityTag.priority) + ) { + highestPriorityTag = tagData; + } } } - } - const getOpacityValue = (color: string): number => { - const rgbaMatch = color.match(/rgba?\((\d+), (\d+), (\d+)(, (\d+(\.\d+)?))?\)/); - if (rgbaMatch) { - const opacity = rgbaMatch[5] ? parseFloat(rgbaMatch[5]) : 1; - return opacity; + const getOpacityValue = (color: string): number => { + const rgbaMatch = color.match(/rgba?\((\d+), (\d+), (\d+)(, (\d+(\.\d+)?))?\)/); + if (rgbaMatch) { + const opacity = rgbaMatch[5] ? parseFloat(rgbaMatch[5]) : 1; + return opacity; + } + return 1; + }; + + if (highestPriorityTag && getOpacityValue(highestPriorityTag.color) > 0.2) { + return updateRGBAOpacity(highestPriorityTag.color, 0.2); } - return 1; - }; - if (highestPriorityTag && getOpacityValue(highestPriorityTag.color) > 0.2) { - return updateRGBAOpacity(highestPriorityTag.color, 0.2); + return highestPriorityTag?.color; } - return highestPriorityTag?.color; + return undefined; } // ======================================== @@ -942,11 +943,14 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act
    {/* Render line tags (editable) */} {task.tags.map((tag: string) => { + const isTagBg = globalSettings.tagColorsType === TagColorType.TagBg; + const tagName = tag.replace('#', ''); - const customTag = plugin.settings.data.globalSettings.tagColorsType === "text" ? plugin.settings.data.globalSettings.tagColors.find(t => t.name === tagName) : undefined; + const customTag = plugin.settings.data.globalSettings.tagColorsType === TagColorType.CardBg ? undefined : plugin.settings.data.globalSettings.tagColors.find(t => t.name === tagName); + const tagColor = customTag?.color || `var(--tag-color)`; - const backgroundColor = customTag ? updateRGBAOpacity(customTag.color, 0.1) : `var(--tag-background)`; // 10% opacity background - const borderColor = customTag ? updateRGBAOpacity(customTag.color, 0.5) : `var(--tag-color-hover)`; + const dimmedTagColor = customTag ? updateRGBAOpacity(customTag.color, 0.1) : `var(--tag-background)`; // 10% opacity background + // const borderColor = customTag ? updateRGBAOpacity(customTag.color, 0.5) : `var(--tag-color-hover)`; // If columnIndex is defined, proceed to get the column if ( @@ -965,9 +969,9 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act key={tagKey} className="taskItemTag" style={{ - color: tagColor, + color: isTagBg ? 'white' : tagColor, // border: `1px solid ${borderColor}`, - backgroundColor: backgroundColor + backgroundColor: isTagBg ? tagColor : dimmedTagColor }} > {tag} diff --git a/src/interfaces/Enums.ts b/src/interfaces/Enums.ts index 8460b2dc..f2bc97bb 100644 --- a/src/interfaces/Enums.ts +++ b/src/interfaces/Enums.ts @@ -23,8 +23,9 @@ export enum UniversalDateOptions { } export enum TagColorType { - Text = "text", - Background = "background", + TagText = "text", + TagBg= "tagBg", + CardBg = "background", } export enum NotificationService { diff --git a/src/interfaces/GlobalSettings.ts b/src/interfaces/GlobalSettings.ts index fb47637d..24fb5300 100644 --- a/src/interfaces/GlobalSettings.ts +++ b/src/interfaces/GlobalSettings.ts @@ -477,7 +477,7 @@ export const DEFAULT_SETTINGS: PluginDataJson = { editButtonAction: EditButtonMode.Modal, doubleClickCardToEdit: EditButtonMode.None, universalDate: UniversalDateOptions.dueDate, - tagColorsType: TagColorType.Background, + tagColorsType: TagColorType.TagText, customStatuses: [ { symbol: defaultTaskStatuses.todo, diff --git a/src/settings/SettingConstructUI.ts b/src/settings/SettingConstructUI.ts index f488a931..69167ce1 100644 --- a/src/settings/SettingConstructUI.ts +++ b/src/settings/SettingConstructUI.ts @@ -1009,8 +1009,9 @@ export class SettingsManager { .addDropdown((dropdown) => dropdown .addOptions({ - [TagColorType.Text]: t("text-of-the-tag"), - [TagColorType.Background]: t("background-of-the-card"), + [TagColorType.TagText]: t("text-of-the-tag"), + [TagColorType.TagBg]: t("tag-background"), + [TagColorType.CardBg]: t("background-of-the-card"), }) .setValue(tagColorsType) .onChange(async (value) => { @@ -1086,18 +1087,18 @@ export class SettingsManager { .buttonEl.setCssStyles({ cursor: "grab", backgroundColor: - this.globalSettings!.tagColorsType === - TagColorType.Background + this.globalSettings!.tagColorsType !== + TagColorType.TagText ? tag.color : "", color: this.globalSettings!.tagColorsType === - TagColorType.Text + TagColorType.TagText ? tag.color : "", border: this.globalSettings!.tagColorsType === - TagColorType.Text + TagColorType.TagText ? `1px solid ${tag.color}` : "", maxWidth: "max-content !important", @@ -1114,18 +1115,18 @@ export class SettingsManager { }) .inputEl.setCssStyles({ backgroundColor: - this.globalSettings!.tagColorsType === - TagColorType.Background + this.globalSettings!.tagColorsType !== + TagColorType.TagText ? tag.color : "", color: this.globalSettings!.tagColorsType === - TagColorType.Text + TagColorType.TagText ? tag.color : "", border: this.globalSettings!.tagColorsType === - TagColorType.Text + TagColorType.TagText ? `1px solid ${tag.color}` : "", minWidth: "23vw !important", @@ -1208,18 +1209,18 @@ export class SettingsManager { }) .inputEl.setCssStyles({ backgroundColor: - this.globalSettings!.tagColorsType === - TagColorType.Background + this.globalSettings!.tagColorsType !== + TagColorType.TagText ? tag.color : "", color: this.globalSettings!.tagColorsType === - TagColorType.Text + TagColorType.TagText ? tag.color : "", border: this.globalSettings!.tagColorsType === - TagColorType.Text + TagColorType.TagText ? `1px solid ${tag.color}` : "", minWidth: "23vw !important", From 345ad88d2067855588691c680d83c35bd02576d2 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Sat, 31 Jan 2026 22:24:50 +0530 Subject: [PATCH 054/236] store Obsidian closed time in localStorage --- main.ts | 33 ++++++++++++++++++++++++++------- src/interfaces/Constants.ts | 1 + 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/main.ts b/main.ts index 129e1712..16fa58a1 100644 --- a/main.ts +++ b/main.ts @@ -32,6 +32,7 @@ import { TaskBoardSettingTab } from "./src/settings/TaskBoardSettingTab"; import { ModifiedFilesModal } from "src/modals/ModifiedFilesModal"; import { newReleaseVersion, + OBSIDIAN_CLOSED_TIME_KEY, VIEW_TYPE_TASKBOARD, } from "src/interfaces/Constants"; import { isReminderPluginInstalled } from "src/services/CommunityPlugins"; @@ -51,6 +52,7 @@ import { migrateSettings } from "src/settings/SettingSynchronizer"; import { dragDropTasksManagerInsatance } from "src/managers/DragDropTasksManager"; import { eventEmitter } from "src/services/EventEmitter"; import { bugReporterManagerInsatance } from "src/managers/BugReporter"; +import { getCurrentLocalTimeString } from "src/utils/DateTimeCalculations"; export default class TaskBoard extends Plugin { app: App; @@ -926,13 +928,20 @@ export default class TaskBoard extends Plugin { } async findModifiedFilesOnAppAbsense() { - if (this.vaultScanner.tasksCache.Modified_at) { - const LAST_UPDATED_TIME = Date.parse( - this.vaultScanner.tasksCache.Modified_at, - ); + let OBSIDIAN_CLOSED_TIME = this.app.loadLocalStorage( + OBSIDIAN_CLOSED_TIME_KEY, + ); + console.log("DATA loaded from localStorage :", OBSIDIAN_CLOSED_TIME); + + if (!OBSIDIAN_CLOSED_TIME) + OBSIDIAN_CLOSED_TIME = this.vaultScanner.tasksCache.Modified_at; + console.log("DATA loaded from cache :", OBSIDIAN_CLOSED_TIME); + + if (OBSIDIAN_CLOSED_TIME) { + OBSIDIAN_CLOSED_TIME = Date.parse(OBSIDIAN_CLOSED_TIME); console.log( "Task Board : Fetching all modified files...\nLast modified time :", - LAST_UPDATED_TIME, + OBSIDIAN_CLOSED_TIME, ); let filesScannedCount = 0; const modifiedCreatedRenamedFiles = this.app.vault @@ -940,8 +949,8 @@ export default class TaskBoard extends Plugin { .filter((file) => { filesScannedCount++; return ( - file.stat.mtime > LAST_UPDATED_TIME || - file.stat.ctime > LAST_UPDATED_TIME + file.stat.mtime > OBSIDIAN_CLOSED_TIME || + file.stat.ctime > OBSIDIAN_CLOSED_TIME ); }); @@ -1163,6 +1172,16 @@ export default class TaskBoard extends Plugin { }); } + this.registerEvent( + this.app.workspace.on("quit", () => { + const currentTime = getCurrentLocalTimeString(); + this.app.saveLocalStorage( + OBSIDIAN_CLOSED_TIME_KEY, + currentTime, + ); + }), + ); + // const closeButton = document.querySelector( // ".titlebar-button.mod-close" // ); diff --git a/src/interfaces/Constants.ts b/src/interfaces/Constants.ts index a65cc030..beb93aab 100644 --- a/src/interfaces/Constants.ts +++ b/src/interfaces/Constants.ts @@ -10,6 +10,7 @@ export const PENDING_SCAN_FILE_STACK = "taskBoard_file_stack"; export const NODE_POSITIONS_STORAGE_KEY = "taskboard_map_node_positions"; // now stores board-wise export const NODE_SIZE_STORAGE_KEY = "taskboard_map_node_sizes"; export const VIEWPORT_STORAGE_KEY = "taskboard_map_viewport"; +export const OBSIDIAN_CLOSED_TIME_KEY = "OBSIDIAN_CLOSED_TIME"; // Default file names and paths export const DEFAULT_TASKS_CACHE_FILE = "task-board-data.json"; From 48276ca6f2ded2181fb49ac3e1d69acc22a021ed Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Sat, 31 Jan 2026 22:25:17 +0530 Subject: [PATCH 055/236] only recurring tasks will be handled by Tasks plugin API --- src/utils/taskLine/TaskItemEventHandlers.ts | 210 ++++++++++---------- 1 file changed, 102 insertions(+), 108 deletions(-) diff --git a/src/utils/taskLine/TaskItemEventHandlers.ts b/src/utils/taskLine/TaskItemEventHandlers.ts index e706dbf0..14773be4 100644 --- a/src/utils/taskLine/TaskItemEventHandlers.ts +++ b/src/utils/taskLine/TaskItemEventHandlers.ts @@ -34,97 +34,114 @@ export const handleCheckboxChange = (plugin: TaskBoard, task: taskItem) => { const tasksPlugin = new TasksPluginApi(plugin); console.log("handleCheckboxChange...\nTask =", task); - if (!tasksPlugin.isTasksPluginEnabled()) { - if (!isTaskRecurring(task.title)) { - // Check if the task is completed - const newStatus = checkboxStateSwitcher(plugin, task.status); - const newTitle = sanitizeStatus( - plugin.settings.data.globalSettings, - task.title, - newStatus.newSymbol, - newStatus.newSymbolType, + if (!isTaskRecurring(task.title)) { + // Check if the task is completed + const newStatus = checkboxStateSwitcher(plugin, task.status); + const newTitle = sanitizeStatus( + plugin.settings.data.globalSettings, + task.title, + newStatus.newSymbol, + newStatus.newSymbolType, + ); + const taskWithUpdatedStatus = { + ...task, + title: newTitle, + status: newStatus.newSymbol, + }; + + updateTaskInFile(plugin, taskWithUpdatedStatus, task).then((newId) => { + plugin.realTimeScanner.processAllUpdatedFiles( + task.filePath, + task.legacyId, ); - const taskWithUpdatedStatus = { - ...task, - title: newTitle, - status: newStatus.newSymbol, - }; - - updateTaskInFile(plugin, taskWithUpdatedStatus, task).then( - (newId) => { + + // DEPRECATED : See notes from //src/utils/TaskItemCacheOperations.ts file + // moveFromCompletedToPending(plugin, taskWithUpdatedStatus); + }); + + // if (isTaskCompleted(`- [${task.status}]`, false, plugin.settings)) { + // const newStatusType = + // plugin.settings.data.globalSettings.customStatuses.find( + // (status) => status.symbol === newStatus + // )?.type ?? statusTypeNames.TODO; + // let newTitle = sanitizeStatus(task.title, newStatus.newSymbol); + + // const taskWithUpdatedStatus = { + // ...task, + // title: newTitle, + // completion: "", + // status: newStatus.newSymbol, + // }; + + // updateTaskInFile(plugin, taskWithUpdatedStatus, task).then( + // (newId) => { + // plugin.realTimeScanner.processAllUpdatedFiles( + // task.filePath, + // task.legacyId + // ); + + // // DEPRECATED : See notes from //src/utils/TaskItemCacheOperations.ts file + // // moveFromCompletedToPending(plugin, taskWithUpdatedStatus); + // } + // ); + // } else { + // const globalSettings = plugin.settings.data.globalSettings; + // const moment = _moment as unknown as typeof _moment.default; + // const currentDateValue = moment().format( + // globalSettings?.taskCompletionDateTimePattern + // ); + // let newTitle = ""; + // if (newStatus.type === statusTypeNames.DONE) { + // newTitle = sanitizeCompletionDate( + // globalSettings, + // task.title, + // currentDateValue + // ); + // } else if (newStatus.type === statusTypeNames.CANCELLED) { + // newTitle = sanitizeCancelledDate( + // globalSettings, + // task.title, + // currentDateValue + // ); + // } + + // const taskWithUpdatedStatus = { + // ...task, + // title: newTitle, + // status: newStatus.newSymbol, + // }; + + // updateTaskInFile(plugin, taskWithUpdatedStatus, task).then( + // (newId) => { + // plugin.realTimeScanner.processAllUpdatedFiles( + // taskWithUpdatedStatus.filePath + // ); + + // // DEPRECATED : See notes from //src/utils/TaskItemCacheOperations.ts file + // // moveFromPendingToCompleted(plugin, taskWithUpdatedStatus); + // } + // ); + // } + } else { + if (tasksPlugin.isTasksPluginEnabled()) { + useTasksPluginToUpdateInFile(plugin, tasksPlugin, task) + .then(() => { plugin.realTimeScanner.processAllUpdatedFiles( task.filePath, task.legacyId, ); - // DEPRECATED : See notes from //src/utils/TaskItemCacheOperations.ts file - // moveFromCompletedToPending(plugin, taskWithUpdatedStatus); - }, - ); - - // if (isTaskCompleted(`- [${task.status}]`, false, plugin.settings)) { - // const newStatusType = - // plugin.settings.data.globalSettings.customStatuses.find( - // (status) => status.symbol === newStatus - // )?.type ?? statusTypeNames.TODO; - // let newTitle = sanitizeStatus(task.title, newStatus.newSymbol); - - // const taskWithUpdatedStatus = { - // ...task, - // title: newTitle, - // completion: "", - // status: newStatus.newSymbol, - // }; - - // updateTaskInFile(plugin, taskWithUpdatedStatus, task).then( - // (newId) => { - // plugin.realTimeScanner.processAllUpdatedFiles( - // task.filePath, - // task.legacyId - // ); - - // // DEPRECATED : See notes from //src/utils/TaskItemCacheOperations.ts file - // // moveFromCompletedToPending(plugin, taskWithUpdatedStatus); - // } - // ); - // } else { - // const globalSettings = plugin.settings.data.globalSettings; - // const moment = _moment as unknown as typeof _moment.default; - // const currentDateValue = moment().format( - // globalSettings?.taskCompletionDateTimePattern - // ); - // let newTitle = ""; - // if (newStatus.type === statusTypeNames.DONE) { - // newTitle = sanitizeCompletionDate( - // globalSettings, - // task.title, - // currentDateValue - // ); - // } else if (newStatus.type === statusTypeNames.CANCELLED) { - // newTitle = sanitizeCancelledDate( - // globalSettings, - // task.title, - // currentDateValue - // ); - // } - - // const taskWithUpdatedStatus = { - // ...task, - // title: newTitle, - // status: newStatus.newSymbol, - // }; - - // updateTaskInFile(plugin, taskWithUpdatedStatus, task).then( - // (newId) => { - // plugin.realTimeScanner.processAllUpdatedFiles( - // taskWithUpdatedStatus.filePath - // ); - - // // DEPRECATED : See notes from //src/utils/TaskItemCacheOperations.ts file - // // moveFromPendingToCompleted(plugin, taskWithUpdatedStatus); - // } - // ); - // } + // NOTE : This is not necessary any more as I am scanning the file after it has been updated. + // // Move from Pending to Completed + // moveFromPendingToCompleted(plugin, taskWithUpdatedStatus); + }) + .catch((err) => { + bugReporterManagerInsatance.addToLogs( + 152, + String(err), + "TaskItemEventHandlers.ts/handleCheckboxChange", + ); + }); } else { bugReporterManagerInsatance.showNotice( 45, @@ -146,30 +163,7 @@ export const handleCheckboxChange = (plugin: TaskBoard, task: taskItem) => { // ); // }); } - - // // Move from Pending to Completed - // moveFromPendingToCompleted(plugin, taskWithUpdatedStatus); - } else { - useTasksPluginToUpdateInFile(plugin, tasksPlugin, task) - .then(() => { - plugin.realTimeScanner.processAllUpdatedFiles( - task.filePath, - task.legacyId, - ); - - // NOTE : This is not necessary any more as I am scanning the file after it has been updated. - // // Move from Pending to Completed - // moveFromPendingToCompleted(plugin, taskWithUpdatedStatus); - }) - .catch((err) => { - bugReporterManagerInsatance.addToLogs( - 152, - String(err), - "TaskItemEventHandlers.ts/handleCheckboxChange", - ); - }); } - // NOTE : The eventEmitter.emit("REFRESH_COLUMN") is being sent from the moveFromPendingToCompleted functions, because if i add that here, then all the things are getting executed parallely instead of sequential. }; /** From f833d10c71a2f9e11bb4dcf1c418df289d93568b Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Sat, 31 Jan 2026 22:25:33 +0530 Subject: [PATCH 056/236] add migrations for the statuses settings --- src/settings/SettingSynchronizer.ts | 43 +++++++++++++++-------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/src/settings/SettingSynchronizer.ts b/src/settings/SettingSynchronizer.ts index d72f2330..ac12dabe 100644 --- a/src/settings/SettingSynchronizer.ts +++ b/src/settings/SettingSynchronizer.ts @@ -23,26 +23,29 @@ export function migrateSettings(defaults: any, settings: any): PluginDataJson { if (!(key in settings)) { // This is a cumpulsory migration which will be required in every new version update, since a new field should be added into the users settings. settings[key] = defaults[key]; - } else if ( - !Array.isArray(settings[key]) && - key === "tagColors" && - typeof settings[key] === "object" && - settings[key] !== null - ) { - // This is a temporary migration applied since version 1.2.0. Can be removed, after around 6 months. - settings[key] = Object.entries( - settings[key] as Record, - ).map( - ([name, color], idx) => - ({ - name, - color, - priority: idx + 1, - }) as any, - ); - } else if (key === "boardConfigs" && Array.isArray(settings[key])) { - // This is a temporary solution to sync the boardConfigs. Will need to replace the range object with the new 'datedBasedColumn', which will have three values 'dateType', 'from' and 'to'. So, basically I want to copy `range.rangedata.from` value to `datedBasedColumn.from` and similarly for `range.rangedatato`. And for `datedBasedColumn.dateType`, put the value this.settings.data.globalSettings.universalDate - // This migration was applied since version 1.5.0. + } + + // ----------------------------------- + /** + * @since v1.9.0 + * @type Temporary + * @note Remove this on the next version release where this migration will run. + * + * This is migration is only applied to replace the older settings available in users configs with the new settings as per the new Settinsg section added in the global settings. + */ + else if (key === "customStatuses") { + settings[key] = DEFAULT_SETTINGS.data.globalSettings.customStatuses; + } + + // ------------------------------------- + /** + * @since v1.5.0 + * @type Temporary + * @note Remove this in 6 months. + * + * This is a temporary solution to sync the boardConfigs. This is required to replace the range object with the new 'datedBasedColumn', which will have three values 'dateType', 'from' and 'to'. So, basically we need to copy `range.rangedata.from` value to `datedBasedColumn.from` and similarly for `range.rangedatato`. And for `datedBasedColumn.dateType`, put the value this.settings.data.globalSettings.universalDate + */ + else if (key === "boardConfigs" && Array.isArray(settings[key])) { settings[key].forEach((boardConfig: Board) => { boardConfig.columns.forEach((column: ColumnData) => { if (!column.id) { From 09683802d2cd53bb32f406f0c21703471dc72626 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Sat, 31 Jan 2026 22:25:49 +0530 Subject: [PATCH 057/236] english strings and few styling --- src/utils/lang/locale/en.ts | 5 +++-- styles.css | 12 ++++++------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/utils/lang/locale/en.ts b/src/utils/lang/locale/en.ts index ac2250a3..08e3c8a3 100644 --- a/src/utils/lang/locale/en.ts +++ b/src/utils/lang/locale/en.ts @@ -189,8 +189,9 @@ const en: Lang = { "reminder-label": "Reminder", "tag-color-indicator-type": "Tag color indicator type", "tag-color-indicator-type-info": "Select how the tag color should be applied. Note that, if you have selected \"Text of the tag\" as the tag color indicator type, then the opacity of the color will be reduced to 0.2 for better visibility of the text.", - "text-of-the-tag": "Text of the tag", - "background-of-the-card": "Background of the card", + "text-of-the-tag": "Tag text", + "tag-background": "Tag background", + "background-of-the-card": "Card background", "liveEditor": "Live Editor", "rawEditor": "Source mode", "child-tasks": "Child tasks", diff --git a/styles.css b/styles.css index 40b24312..583b6d60 100644 --- a/styles.css +++ b/styles.css @@ -637,6 +637,7 @@ button.clickable-icon.view-action.taskboardScanVaultBtn.highlight { display: flex; flex-direction: row; min-width: 100%; + align-items: flex-start; } .taskItemBodyContent { @@ -687,7 +688,6 @@ button.clickable-icon.view-action.taskboardScanVaultBtn.highlight { cursor: pointer; mask-image: unset; -webkit-mask-image: unset; - margin-block-start: 3px !important; vertical-align: middle; } @@ -831,7 +831,7 @@ button.clickable-icon.view-action.taskboardScanVaultBtn.highlight { .taskItemMainBodyDescriptionSectionVisible { margin-top: 5px; - margin-inline: -5px; + margin-inline: -8px; } .taskItemMainBodyDescriptionSectionToggler { @@ -1103,15 +1103,15 @@ button.clickable-icon.view-action.taskboardScanVaultBtn.highlight { } .prioirtyCircle.p2 { - background-color: var(--status-in-progress-color); + background-color: var(--color-blue); } .prioirtyCircle.p3 { - background-color: var(--status-in-progress-color); + background-color: var(--color-blue); } .prioirtyCircle.p4 { - background-color: var(--status-in-progress-color); + background-color: var(--color-blue); } .prioirtyCircle.p5 { @@ -1237,7 +1237,7 @@ button.clickable-icon.view-action.taskboardScanVaultBtn.highlight { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; - max-width: calc(var(--task-board-column-width) - 4rem); + max-width: calc(var(--task-board-column-width) - 5rem); } /*----------------------------------------------------------- From cb51d504641f0b69b68352c8cd02f475b99e16ff Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Sat, 31 Jan 2026 23:09:50 +0530 Subject: [PATCH 058/236] minor changes --- main.ts | 1 - src/components/KanbanView/TaskItemV2.tsx | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/main.ts b/main.ts index 16fa58a1..0f7849b3 100644 --- a/main.ts +++ b/main.ts @@ -44,7 +44,6 @@ import { taskPropertyHidingExtension, } from "src/editor-extensions/task-operations/property-hiding"; import { - fetchTasksPluginCustomStatuses, isTasksPluginEnabled, } from "src/services/tasks-plugin/helpers"; import { scanModeOptions, taskPropertiesNames } from "src/interfaces/Enums"; diff --git a/src/components/KanbanView/TaskItemV2.tsx b/src/components/KanbanView/TaskItemV2.tsx index 83f78089..5c322f0f 100644 --- a/src/components/KanbanView/TaskItemV2.tsx +++ b/src/components/KanbanView/TaskItemV2.tsx @@ -969,7 +969,7 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act key={tagKey} className="taskItemTag" style={{ - color: isTagBg ? 'white' : tagColor, + color: isTagBg && tagColor ? 'white' : tagColor, // border: `1px solid ${borderColor}`, backgroundColor: isTagBg ? tagColor : dimmedTagColor }} From ca140da18046d6c33d4c8012e2a09e27705fad2e Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Sat, 31 Jan 2026 23:10:05 +0530 Subject: [PATCH 059/236] test the latest changes | working --- data.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data.json b/data.json index 825b87f3..6514f23b 100644 --- a/data.json +++ b/data.json @@ -1013,7 +1013,7 @@ } ], "showTaskWithoutMetadata": false, - "tagColorsType": "text", + "tagColorsType": "tagBg", "compatiblePlugins": { "dailyNotesPlugin": false, "tasksPlugin": false, @@ -1052,7 +1052,7 @@ "viewedType": "kanban", "boardIndex": 4, "taskId": "", - "settingTab": 4 + "settingTab": 1 }, "taskNoteIdentifierTag": "task", "doubleClickCardToEdit": "noteInTab", From 893c0a022ecad98e787af2065a480e500ece0f8d Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Sun, 1 Feb 2026 19:30:27 +0530 Subject: [PATCH 060/236] feat : a custom notice on plugin update --- main.ts | 31 ++++++++++++++++++++++++++++--- styles.css | 13 +++++++++++++ 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/main.ts b/main.ts index 0f7849b3..03b3dbd7 100644 --- a/main.ts +++ b/main.ts @@ -43,15 +43,14 @@ import { getTaskPropertyRegexPatterns, taskPropertyHidingExtension, } from "src/editor-extensions/task-operations/property-hiding"; -import { - isTasksPluginEnabled, -} from "src/services/tasks-plugin/helpers"; +import { isTasksPluginEnabled } from "src/services/tasks-plugin/helpers"; import { scanModeOptions, taskPropertiesNames } from "src/interfaces/Enums"; import { migrateSettings } from "src/settings/SettingSynchronizer"; import { dragDropTasksManagerInsatance } from "src/managers/DragDropTasksManager"; import { eventEmitter } from "src/services/EventEmitter"; import { bugReporterManagerInsatance } from "src/managers/BugReporter"; import { getCurrentLocalTimeString } from "src/utils/DateTimeCalculations"; +import { createFragmentWithHTML } from "src/utils/UIHelpers"; export default class TaskBoard extends Plugin { app: App; @@ -1473,6 +1472,32 @@ export default class TaskBoard extends Plugin { if (previousVersion == "" || currentVersion !== previousVersion) { // make the localStorage flag, 'manadatoryScan' to True + if (previousVersion !== "") { + const customMessage = new Notice("", 0); + + const messageContainer = customMessage.containerEl; + + const customMessageContainer = messageContainer.createDiv({ + cls: "taskboardCustomMessageContainer", + }); + + customMessageContainer.createEl("h3", { text: "Task Board" }); + customMessageContainer.createEl("p", { + text: "Note for existing users", + cls: "taskboardCustomMessageContainerBold", + }); + customMessageContainer.createEl("span", { + text: "If you were using the custom statuses from Tasks plugin configs. Please import them in Task Board's setting, using a button in the new Custom Statuses setting section. Task Board will no longer import the custom statuses from Tasks plugin automatically.", + }); + customMessageContainer.createEl("p", { + text: "Read the release notes for all the latest features : ", + }); + customMessageContainer.createEl("a", { + text: "Task Board v1.9.0", + href: `https://github.com/tu2-atmanand/Task-Board/releases/tag/${newReleaseVersion}`, + }); + } + if (previousVersion === "" || runMandatoryScan) { localStorage.setItem("manadatoryScan", "true"); const smallMessage = diff --git a/styles.css b/styles.css index 583b6d60..a886468b 100644 --- a/styles.css +++ b/styles.css @@ -7587,3 +7587,16 @@ Date picker modal styles .menu-item-title .contains-task-list>li>input[type=checkbox]:checked::after { padding-block-start: 4px; } + + +/* ------------------------------------------------------ + CUSTOM NOTICE CONTAINER +*/ + +.taskboardCustomMessageContainer h3 { + margin-top: 0 !important; +} + +.taskboardCustomMessageContainerBold { + font-weight: 600; +} From 2f191ffad13d28ecfd89e06294201dcbcc7661b1 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Sun, 1 Feb 2026 19:43:35 +0530 Subject: [PATCH 061/236] clean up all unused imports --- main.ts | 1 - src/components/AddOrEditTaskRC.tsx | 8 +- src/components/BoardFilters/ViewTaskFilter.ts | 126 +++++++++--------- src/components/BoardFilters/index.ts | 1 + .../KanbanView/KanbanSwimlanesContainer.tsx | 4 +- src/components/KanbanView/TaskItem.tsx | 5 +- src/components/KanbanView/TaskItemV2.tsx | 11 +- src/components/MapView/MapView.tsx | 1 - src/components/TaskBoardViewContent.tsx | 6 +- src/managers/BugReporter.ts | 2 +- src/managers/DragDropTasksManager.ts | 1 - src/managers/RealTimeScanner.ts | 1 - src/managers/VaultScanner.ts | 10 +- src/modals/BoardConfigModal.tsx | 1 - src/modals/BugReporterModal.ts | 2 +- src/modals/CustomStatusConfigurator.ts | 2 +- src/modals/DiffContentCompareModal.ts | 2 +- src/modals/EditTagsModal.ts | 2 +- src/modals/SwimlanesConfigModal.tsx | 3 +- src/modals/date_picker/DatePickerComponent.ts | 10 +- src/modals/date_picker/DatePickerModal.ts | 4 +- src/services/OpenModals.ts | 1 - src/services/tasks-plugin/helpers.ts | 1 - src/settings/SettingConstructUI.ts | 4 +- src/utils/JsonFileOperations.ts | 1 - src/utils/MarkdownFileOperations.ts | 1 - src/utils/TaskItemUtils.ts | 1 - src/utils/UIHelpers.ts | 1 - .../algorithms/ColumnSortingAlgorithm.ts | 1 - src/utils/lang/helper.ts | 1 - src/utils/taskLine/TaskContentFormatter.ts | 1 - src/utils/taskLine/TaskItemEventHandlers.ts | 10 +- src/utils/taskLine/TaskLineUtils.ts | 5 +- src/utils/taskNote/FrontmatterOperations.ts | 1 - src/utils/taskNote/TaskNoteUtils.ts | 1 - 35 files changed, 94 insertions(+), 139 deletions(-) diff --git a/main.ts b/main.ts index 03b3dbd7..c709647b 100644 --- a/main.ts +++ b/main.ts @@ -50,7 +50,6 @@ import { dragDropTasksManagerInsatance } from "src/managers/DragDropTasksManager import { eventEmitter } from "src/services/EventEmitter"; import { bugReporterManagerInsatance } from "src/managers/BugReporter"; import { getCurrentLocalTimeString } from "src/utils/DateTimeCalculations"; -import { createFragmentWithHTML } from "src/utils/UIHelpers"; export default class TaskBoard extends Plugin { app: App; diff --git a/src/components/AddOrEditTaskRC.tsx b/src/components/AddOrEditTaskRC.tsx index e483dc99..0ad9dc28 100644 --- a/src/components/AddOrEditTaskRC.tsx +++ b/src/components/AddOrEditTaskRC.tsx @@ -10,12 +10,12 @@ import { moment as _moment } from "obsidian"; import TaskBoard from "main"; import { updateRGBAOpacity } from "src/utils/UIHelpers"; import { t } from "src/utils/lang/helper"; -import { cleanTaskTitleLegacy, getFormattedTaskContentSync, sanitizeCancelledDate, sanitizeCompletionDate, sanitizeCreatedDate, sanitizeDependsOn, sanitizeDueDate, sanitizePriority, sanitizeReminder, sanitizeScheduledDate, sanitizeStartDate, sanitizeStatus, sanitizeTags, sanitizeTime } from "src/utils/taskLine/TaskContentFormatter"; +import { cleanTaskTitleLegacy, getFormattedTaskContentSync, sanitizeCreatedDate, sanitizeDependsOn, sanitizeDueDate, sanitizePriority, sanitizeReminder, sanitizeScheduledDate, sanitizeStartDate, sanitizeStatus, sanitizeTags, sanitizeTime } from "src/utils/taskLine/TaskContentFormatter"; import { buildTaskFromRawContent } from "src/managers/VaultScanner"; -import { DeleteIcon, EditIcon, FileInput, Network, PanelRightOpenIcon, RefreshCcw } from "lucide-react"; +import { DeleteIcon, EditIcon, FileInput, Network, PanelRightOpenIcon } from "lucide-react"; import { MultiSuggest, getFileSuggestions, getPendingTasksSuggestions, getQuickAddPluginChoices, getTagSuggestions } from "src/services/MultiSuggest"; import { CommunityPlugins } from "src/services/CommunityPlugins"; -import { bugReporter, openEditTaskView } from "src/services/OpenModals"; +import { openEditTaskView } from "src/services/OpenModals"; import { MarkdownUIRenderer } from "src/services/MarkdownUIRenderer"; import { getObsidianIndentationSetting, isTaskLine } from "src/utils/CheckBoxUtils"; import { formatTaskNoteContent, isTaskNotePresentInTags } from "src/utils/taskNote/TaskNoteUtils"; @@ -24,7 +24,7 @@ import { allowedFileExtensionsRegEx } from "src/regularExpressions/Miscelleneous import { markdownButtonHoverPreviewEvent } from "src/services/MarkdownHoverPreview"; import { ViewUpdate } from "@codemirror/view"; import { createEmbeddableMarkdownEditor, EmbeddableMarkdownEditor } from "src/services/MarkdownEditor"; -import { UniversalDateOptions, EditButtonMode, NotificationService, statusTypeNames, onCompletionOptions } from "src/interfaces/Enums"; +import { UniversalDateOptions, EditButtonMode, NotificationService, statusTypeNames } from "src/interfaces/Enums"; import { getPriorityOptionsForDropdown, taskItemEmpty } from "src/interfaces/Mapping"; import { applyIdToTaskItem, getTaskFromId } from "src/utils/TaskItemUtils"; import { handleEditTask } from "src/utils/UserTaskEvents"; diff --git a/src/components/BoardFilters/ViewTaskFilter.ts b/src/components/BoardFilters/ViewTaskFilter.ts index 48463f34..562fc12a 100644 --- a/src/components/BoardFilters/ViewTaskFilter.ts +++ b/src/components/BoardFilters/ViewTaskFilter.ts @@ -9,7 +9,7 @@ import { setTooltip, Notice, } from "obsidian"; -import Sortable, { get } from "sortablejs"; +import Sortable from "sortablejs"; import { FilterConfigModal } from "./FilterConfigModal"; import type TaskBoard from "main"; import { t } from "src/utils/lang/helper"; @@ -23,7 +23,6 @@ import { MultiSuggest, getTagSuggestions, getFileSuggestions, - getStatusSuggestions, } from "src/services/MultiSuggest"; import { getPriorityOptionsForDropdown, @@ -59,7 +58,7 @@ export class TaskFilterComponent extends Component { app: App, private leafId?: string | undefined, activeBoardIndex?: number, - private initialFilterState?: RootFilterState + private initialFilterState?: RootFilterState, ) { super(); this.hostEl = hostEl; @@ -159,7 +158,7 @@ export class TaskFilterComponent extends Component { }); const rootConditionDropdown = new DropdownComponent( - rootConditionSection + rootConditionSection, ) .addOptions({ any: t("any"), @@ -207,7 +206,7 @@ export class TaskFilterComponent extends Component { }, (iconEl) => { setIcon(iconEl, "plus"); - } + }, ); el.createEl("span", { cls: "add-filter-group-btn-text", @@ -217,7 +216,7 @@ export class TaskFilterComponent extends Component { this.registerDomEvent(el, "click", () => { this.addFilterGroup(); }); - } + }, ); // Filter Configuration Buttons Section (only show if plugin is available) @@ -241,13 +240,13 @@ export class TaskFilterComponent extends Component { (iconEl) => { setIcon(iconEl, "save"); setTooltip(el, t("save-current-filter")); - } + }, ); this.registerDomEvent(el, "click", async () => { this.openSaveConfigModal(); }); - } + }, ); // Load Configuration Button @@ -265,13 +264,13 @@ export class TaskFilterComponent extends Component { (iconEl) => { setIcon(iconEl, "folder-open"); setTooltip(el, t("load-saved-filter")); - } + }, ); this.registerDomEvent(el, "click", async () => { this.openLoadConfigModal(); }); - } + }, ); } @@ -312,9 +311,9 @@ export class TaskFilterComponent extends Component { }, (iconEl) => { setIcon(iconEl, "grip-vertical"); - } + }, ); - } + }, ); groupHeaderLeft.createEl("label", { @@ -334,13 +333,13 @@ export class TaskFilterComponent extends Component { this.saveStateToLocalStorage(); this.updateFilterConjunctions( newGroupEl.querySelector(".filters-list") as HTMLElement, - selectedValue + selectedValue, ); }) .setValue(groupData.groupCondition); groupConditionSelect.selectEl.toggleClass( ["group-condition-select", "compact-select"], - true + true, ); groupHeaderLeft.createEl("span", { @@ -378,7 +377,7 @@ export class TaskFilterComponent extends Component { .setTooltip(t("remove-filter-group")) .onClick(() => { const filtersListElForSortable = newGroupEl.querySelector( - ".filters-list" + ".filters-list", ) as HTMLElement; if ( filtersListElForSortable && @@ -392,7 +391,7 @@ export class TaskFilterComponent extends Component { this.rootFilterState.filterGroups = this.rootFilterState.filterGroups.filter( - (g) => g.id !== groupData.id + (g) => g.id !== groupData.id, ); this.saveStateToLocalStorage(); newGroupEl.remove(); @@ -400,7 +399,7 @@ export class TaskFilterComponent extends Component { if ( nextSibling && nextSibling.classList.contains( - "filter-group-separator-container" + "filter-group-separator-container", ) ) { nextSibling.remove(); @@ -409,7 +408,7 @@ export class TaskFilterComponent extends Component { if ( prevSibling && prevSibling.classList.contains( - "filter-group-separator-container" + "filter-group-separator-container", ) ) { prevSibling.remove(); @@ -429,7 +428,7 @@ export class TaskFilterComponent extends Component { groupData.filters.forEach((filterData) => { const filterElement = this.createFilterItemElement( filterData, - groupData + groupData, ); filtersListEl.appendChild(filterElement); }); @@ -452,7 +451,7 @@ export class TaskFilterComponent extends Component { }, (iconEl) => { setIcon(iconEl, "plus"); - } + }, ); el.createEl("span", { cls: "add-filter-btn-text", @@ -462,7 +461,7 @@ export class TaskFilterComponent extends Component { this.registerDomEvent(el, "click", () => { this.addFilterToGroup(groupData, filtersListEl); }); - } + }, ); return newGroupEl; @@ -470,12 +469,12 @@ export class TaskFilterComponent extends Component { private addFilterGroup( groupDataToClone: FilterGroup | null = null, - insertAfterElement: HTMLElement | null = null + insertAfterElement: HTMLElement | null = null, ): void { // Ensure the container is initialized if (!this.filterGroupsContainerEl) { console.warn( - "TaskFilterComponent: filterGroupsContainerEl not initialized yet" + "TaskFilterComponent: filterGroupsContainerEl not initialized yet", ); return; } @@ -504,8 +503,8 @@ export class TaskFilterComponent extends Component { const groupIndex = insertAfterElement ? this.rootFilterState.filterGroups.findIndex( - (g) => g.id === insertAfterElement.id - ) + 1 + (g) => g.id === insertAfterElement.id, + ) + 1 : this.rootFilterState.filterGroups.length; this.rootFilterState.filterGroups.splice(groupIndex, 0, newGroupData); @@ -518,7 +517,7 @@ export class TaskFilterComponent extends Component { ) { this.filterGroupsContainerEl.insertBefore( newGroupElement, - insertAfterElement.nextSibling + insertAfterElement.nextSibling, ); } else { this.filterGroupsContainerEl.appendChild(newGroupElement); @@ -530,7 +529,7 @@ export class TaskFilterComponent extends Component { ) { this.addFilterToGroup( newGroupData, - newGroupElement.querySelector(".filters-list") as HTMLElement + newGroupElement.querySelector(".filters-list") as HTMLElement, ); } else if ( groupDataToClone && @@ -539,7 +538,7 @@ export class TaskFilterComponent extends Component { ) { this.addFilterToGroup( newGroupData, - newGroupElement.querySelector(".filters-list") as HTMLElement + newGroupElement.querySelector(".filters-list") as HTMLElement, ); } @@ -550,7 +549,7 @@ export class TaskFilterComponent extends Component { // --- Filter Item Management --- private createFilterItemElement( filterData: Filter, - groupData: FilterGroup + groupData: FilterGroup, ): HTMLElement { const newFilterEl = this.hostEl.createEl("div", { attr: { id: filterData.id }, @@ -613,13 +612,13 @@ export class TaskFilterComponent extends Component { conditionSelect, valueInput, valueSelect, - dropdownInputContainer + dropdownInputContainer, ); }); const toggleValueInputVisibility = ( currentCond: string, - propertyType: string + propertyType: string, ) => { const conditionsRequiringValue = [ "equals", @@ -701,13 +700,13 @@ export class TaskFilterComponent extends Component { .setTooltip(t("remove-filter")) .onClick(() => { groupData.filters = groupData.filters.filter( - (f) => f.id !== filterData.id + (f) => f.id !== filterData.id, ); this.saveStateToLocalStorage(); newFilterEl.remove(); this.updateFilterConjunctions( newFilterEl.parentElement as HTMLElement, - groupData.groupCondition + groupData.groupCondition, ); }); removeFilterBtn.extraSettingsEl.addClasses([ @@ -722,7 +721,7 @@ export class TaskFilterComponent extends Component { conditionSelect, valueInput, valueSelect, - dropdownInputContainer + dropdownInputContainer, ); return newFilterEl; @@ -730,7 +729,7 @@ export class TaskFilterComponent extends Component { private addFilterToGroup( groupData: FilterGroup, - filtersListEl: HTMLElement + filtersListEl: HTMLElement, ): void { const newFilterId = generateIdForFilters(); const newFilterData: Filter = { @@ -744,7 +743,7 @@ export class TaskFilterComponent extends Component { const newFilterElement = this.createFilterItemElement( newFilterData, - groupData + groupData, ); filtersListEl.appendChild(newFilterElement); @@ -758,7 +757,7 @@ export class TaskFilterComponent extends Component { conditionSelect: DropdownComponent, valueInput: HTMLInputElement, valueSelect: DropdownComponent, - dropdownInputContainer: HTMLElement + dropdownInputContainer: HTMLElement, ): void { const property = filterData.property; @@ -858,22 +857,21 @@ export class TaskFilterComponent extends Component { // } valueSelect.addOptions( getCustomStatusOptionsForDropdown( - this.plugin.settings.data.globalSettings - .customStatuses + this.plugin.settings.data.globalSettings.customStatuses, ).reduce( ( acc: Record, - opt: statusDropDownOption + opt: statusDropDownOption, ) => { acc[opt.value] = opt.text; return acc; }, - {} - ) + {}, + ), ); valueSelect.setValue( filterData.value || - getPriorityOptionsForDropdown()[0].value.toString() + getPriorityOptionsForDropdown()[0].value.toString(), ); valueSelect.onChange((newValue) => { filterData.value = newValue; @@ -936,17 +934,17 @@ export class TaskFilterComponent extends Component { getPriorityOptionsForDropdown().reduce( ( acc: Record, - opt: priorityDropDownOption + opt: priorityDropDownOption, ) => { acc[opt.value] = opt.text; return acc; }, - {} - ) + {}, + ), ); valueSelect.setValue( filterData.value || - getPriorityOptionsForDropdown()[0].value.toString() + getPriorityOptionsForDropdown()[0].value.toString(), ); valueSelect.onChange((newValue) => { filterData.value = Number(newValue); @@ -1153,14 +1151,14 @@ export class TaskFilterComponent extends Component { conditionSelect.selectEl.empty(); conditionOptions.forEach((opt) => - conditionSelect.addOption(opt.value, opt.text) + conditionSelect.addOption(opt.value, opt.text), ); const currentSelectedCondition = filterData.condition; let conditionChanged = false; if ( conditionOptions.some( - (opt) => opt.value === currentSelectedCondition + (opt) => opt.value === currentSelectedCondition, ) ) { conditionSelect.setValue(currentSelectedCondition); @@ -1254,7 +1252,7 @@ export class TaskFilterComponent extends Component { private setupMultiSuggest( property: string, valueInput: HTMLInputElement, - filterData: Filter + filterData: Filter, ): void { // Only setup suggestions for specific properties const propertiesWithSuggestions = ["tags", "filePath"]; @@ -1298,7 +1296,7 @@ export class TaskFilterComponent extends Component { valueInput, new Set(suggestions), onSelectCallback, - this.app + this.app, ); // Store instance in WeakMap for cleanup @@ -1308,13 +1306,13 @@ export class TaskFilterComponent extends Component { // --- UI Updates (Conjunctions, Separators) --- private updateFilterConjunctions( filtersListEl: HTMLElement | null, - groupCondition: "all" | "any" | "none" = "all" + groupCondition: "all" | "any" | "none" = "all", ): void { if (!filtersListEl) return; const filters = filtersListEl.querySelectorAll(".filter-item"); filters.forEach((filter, index) => { const conjunctionElement = filter.querySelector( - ".filter-conjunction" + ".filter-conjunction", ) as HTMLElement; if (conjunctionElement) { if (index !== 0) { @@ -1346,7 +1344,7 @@ export class TaskFilterComponent extends Component { .forEach((sep) => sep.remove()); const groups = Array.from( - this.filterGroupsContainerEl?.children || [] + this.filterGroupsContainerEl?.children || [], ).filter((child) => child.classList.contains("filter-group")); if (groups.length > 1) { @@ -1367,7 +1365,7 @@ export class TaskFilterComponent extends Component { separator.textContent = separatorText.toUpperCase(); group.parentNode?.insertBefore( separatorContainer, - group.nextSibling + group.nextSibling, ); } }); @@ -1398,12 +1396,12 @@ export class TaskFilterComponent extends Component { const movedGroup = this.rootFilterState.filterGroups.splice( sortableEvent.oldDraggableIndex, - 1 + 1, )[0]; this.rootFilterState.filterGroups.splice( sortableEvent.newDraggableIndex, 0, - movedGroup + movedGroup, ); this.saveStateToLocalStorage(); this.updateGroupSeparators(); @@ -1414,7 +1412,7 @@ export class TaskFilterComponent extends Component { // --- Filter State Management --- private updateFilterState( filterGroups: FilterGroup[], - rootCondition: "all" | "any" | "none" + rootCondition: "all" | "any" | "none", ): void { this.rootFilterState.filterGroups = filterGroups; this.rootFilterState.rootCondition = rootCondition; @@ -1460,7 +1458,7 @@ export class TaskFilterComponent extends Component { } catch (error) { console.warn( "Error destroying filter list sortable:", - error + error, ); (listEl as any).sortableInstance = undefined; } @@ -1474,7 +1472,7 @@ export class TaskFilterComponent extends Component { // --- Local Storage Management --- private saveStateToLocalStorage( - triggerRealtimeUpdate: boolean = true + triggerRealtimeUpdate: boolean = true, ): void { /** * This feature is in disabled state, hence no need to store anything in localStorage. @@ -1511,9 +1509,9 @@ export class TaskFilterComponent extends Component { (config: SavedFilterConfig) => { // Optional: Handle successful save new Notice( - `${t("filter-configs-saved-successfully")} : ${config.name}` + `${t("filter-configs-saved-successfully")} : ${config.name}`, ); - } + }, ); modal.setCloseCallback(() => { this.isConfigModalOpen = false; @@ -1538,9 +1536,9 @@ export class TaskFilterComponent extends Component { new Notice( `${t("filter-configuration-loaded-successfully")} : ${ config.name - }` + }`, ); - } + }, ); modal.setCloseCallback(() => { this.isConfigModalOpen = false; diff --git a/src/components/BoardFilters/index.ts b/src/components/BoardFilters/index.ts index 7046694b..058023fd 100644 --- a/src/components/BoardFilters/index.ts +++ b/src/components/BoardFilters/index.ts @@ -1,5 +1,6 @@ /** * This BoardFilters component has been inspired from Bases filter and Task Genius plugin filters. All credits for this component go to the developer of Task Genius plugin. + * * Changes made to the original code: * - Added type safetly at various places. * - This component can be used for both board as well as column. diff --git a/src/components/KanbanView/KanbanSwimlanesContainer.tsx b/src/components/KanbanView/KanbanSwimlanesContainer.tsx index 064b7055..f25776a5 100644 --- a/src/components/KanbanView/KanbanSwimlanesContainer.tsx +++ b/src/components/KanbanView/KanbanSwimlanesContainer.tsx @@ -2,11 +2,11 @@ import React, { useMemo, memo } from 'react'; import { Board, ColumnData } from 'src/interfaces/BoardConfigs'; -import { taskItem, taskJsonMerged } from 'src/interfaces/TaskItem'; +import { taskItem } from 'src/interfaces/TaskItem'; import LazyColumn from './LazyColumn'; import type TaskBoard from 'main'; import { t } from 'src/utils/lang/helper'; -import { ChevronDown, ChevronLast, ChevronLeft, ChevronRight } from 'lucide-react'; +import { ChevronDown, ChevronRight } from 'lucide-react'; import { eventEmitter } from 'src/services/EventEmitter'; import { bugReporterManagerInsatance } from 'src/managers/BugReporter'; diff --git a/src/components/KanbanView/TaskItem.tsx b/src/components/KanbanView/TaskItem.tsx index 9bec0d7d..98b38de7 100644 --- a/src/components/KanbanView/TaskItem.tsx +++ b/src/components/KanbanView/TaskItem.tsx @@ -14,7 +14,7 @@ import { t } from 'src/utils/lang/helper'; import TaskBoard from 'main'; import { Board } from 'src/interfaces/BoardConfigs'; import { TaskRegularExpressions, TASKS_PLUGIN_DEFAULT_SYMBOLS } from 'src/regularExpressions/TasksPluginRegularExpr'; -import { deleteTaskNote, getStatusNameFromStatusSymbol, isTaskNotePresentInTags } from 'src/utils/taskNote/TaskNoteUtils'; +import { getStatusNameFromStatusSymbol, isTaskNotePresentInTags } from 'src/utils/taskNote/TaskNoteUtils'; import { ChevronDown, EllipsisVertical, Grip } from 'lucide-react'; import { EditButtonMode, viewTypeNames, colTypeNames, taskPropertiesNames, TagColorType } from 'src/interfaces/Enums'; import { getCustomStatusOptionsForDropdown, getPriorityOptionsForDropdown, priorityEmojis } from 'src/interfaces/Mapping'; @@ -24,8 +24,7 @@ import { handleTaskNoteStatusChange, handleTaskNoteBodyChange } from 'src/utils/ import { eventEmitter } from 'src/services/EventEmitter'; import { getUniversalDateFromTask, parseUniversalDate } from 'src/utils/DateTimeCalculations'; import { getTaskFromId } from 'src/utils/TaskItemUtils'; -import { handleEditTask, updateTaskItemStatus, updateTaskItemPriority, updateTaskItemDate, updateTaskItemReminder, updateTaskItemTags } from 'src/utils/UserTaskEvents'; -import EditTagsModal from 'src/modals/EditTagsModal'; +import { handleEditTask, updateTaskItemStatus, updateTaskItemPriority, updateTaskItemDate} from 'src/utils/UserTaskEvents'; import { dragDropTasksManagerInsatance, currentDragDataPayload } from 'src/managers/DragDropTasksManager'; import { bugReporterManagerInsatance } from 'src/managers/BugReporter'; import { openDateInputModal } from 'src/services/OpenModals'; diff --git a/src/components/KanbanView/TaskItemV2.tsx b/src/components/KanbanView/TaskItemV2.tsx index 5c322f0f..c01614b7 100644 --- a/src/components/KanbanView/TaskItemV2.tsx +++ b/src/components/KanbanView/TaskItemV2.tsx @@ -15,20 +15,17 @@ import TaskBoard from 'main'; import { Board } from 'src/interfaces/BoardConfigs'; import { TaskRegularExpressions, TASKS_PLUGIN_DEFAULT_SYMBOLS } from 'src/regularExpressions/TasksPluginRegularExpr'; import { getStatusNameFromStatusSymbol, isTaskNotePresentInTags } from 'src/utils/taskNote/TaskNoteUtils'; -import { allowedFileExtensionsRegEx } from 'src/regularExpressions/MiscelleneousRegExpr'; -import { bugReporter, openDateInputModal } from 'src/services/OpenModals'; +import { openDateInputModal } from 'src/services/OpenModals'; import { ChevronDown, EllipsisVertical, Grip } from 'lucide-react'; import { EditButtonMode, viewTypeNames, colTypeNames, taskPropertiesNames, TagColorType } from 'src/interfaces/Enums'; -import { getCustomStatusOptionsForDropdown, getPriorityOptionsForDropdown, priorityEmojis } from 'src/interfaces/Mapping'; +import { getCustomStatusOptionsForDropdown, getPriorityOptionsForDropdown } from 'src/interfaces/Mapping'; import { taskItem, UpdateTaskEventData } from 'src/interfaces/TaskItem'; import { matchTagsWithWildcards, verifySubtasksAndChildtasksAreComplete } from 'src/utils/algorithms/ScanningFilterer'; import { handleTaskNoteStatusChange, handleTaskNoteBodyChange } from 'src/utils/taskNote/TaskNoteEventHandlers'; import { eventEmitter } from 'src/services/EventEmitter'; -import { RxDragHandleDots2 } from 'react-icons/rx'; -import { getUniversalDateEmoji, getUniversalDateFromTask, parseUniversalDate } from 'src/utils/DateTimeCalculations'; +import { getUniversalDateFromTask, parseUniversalDate } from 'src/utils/DateTimeCalculations'; import { getTaskFromId } from 'src/utils/TaskItemUtils'; -import { handleEditTask, updateTaskItemStatus, updateTaskItemPriority, updateTaskItemDate, updateTaskItemReminder, updateTaskItemTags } from 'src/utils/UserTaskEvents'; -import EditTagsModal from 'src/modals/EditTagsModal'; +import { handleEditTask, updateTaskItemStatus, updateTaskItemPriority, updateTaskItemDate } from 'src/utils/UserTaskEvents'; // Helper modal functions may be provided elsewhere; declare them for TypeScript declare function showTextInputModal(app: any, options: { title?: string; placeholder?: string; initialValue?: string }): Promise; diff --git a/src/components/MapView/MapView.tsx b/src/components/MapView/MapView.tsx index 89e82030..5312fcc1 100644 --- a/src/components/MapView/MapView.tsx +++ b/src/components/MapView/MapView.tsx @@ -28,7 +28,6 @@ import { t } from 'src/utils/lang/helper'; import { MapViewMinimap } from './MapViewMinimap'; import { mapViewArrowDirection, mapViewBackgrounVariantTypes, mapViewScrollAction } from 'src/interfaces/Enums'; import { eventEmitter } from 'src/services/EventEmitter'; -import { bugReporter } from 'src/services/OpenModals'; import { PanelLeftOpenIcon } from 'lucide-react'; import { TasksImporterPanel } from './TasksImporterPanel'; import { isTaskNotePresentInTags, updateFrontmatterInMarkdownFile } from 'src/utils/taskNote/TaskNoteUtils'; diff --git a/src/components/TaskBoardViewContent.tsx b/src/components/TaskBoardViewContent.tsx index cd8a1070..9fa3518c 100644 --- a/src/components/TaskBoardViewContent.tsx +++ b/src/components/TaskBoardViewContent.tsx @@ -4,18 +4,18 @@ import { Board, ColumnData, RootFilterState } from "../interfaces/BoardConfigs"; import { CirclePlus, RefreshCcw, Search, SearchX, Filter, Menu as MenuICon, Settings, EllipsisVertical, List } from 'lucide-react'; import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { loadBoardsData, loadTasksAndMerge } from "src/utils/JsonFileOperations"; -import { taskItem, taskJsonMerged } from "src/interfaces/TaskItem"; +import { taskJsonMerged } from "src/interfaces/TaskItem"; import { App, debounce, Platform, Menu } from "obsidian"; import type TaskBoard from "main"; import { eventEmitter } from "src/services/EventEmitter"; import { handleUpdateBoards } from "../utils/BoardOperations"; -import { bugReporter, openAddNewTaskModal, openBoardConfigModal, openScanVaultModal, openTaskBoardActionsModal } from "../services/OpenModals"; +import { openAddNewTaskModal, openBoardConfigModal, openScanVaultModal, openTaskBoardActionsModal } from "../services/OpenModals"; import { columnSegregator } from 'src/utils/algorithms/ColumnSegregator'; import { t } from "src/utils/lang/helper"; import KanbanBoard from "./KanbanView/KanbanBoardView"; import MapView from "./MapView/MapView"; -import { PENDING_SCAN_FILE_STACK, VIEW_TYPE_TASKBOARD } from "src/interfaces/Constants"; +import { VIEW_TYPE_TASKBOARD } from "src/interfaces/Constants"; import { ViewTaskFilterPopover } from "./BoardFilters/ViewTaskFilterPopover"; import { boardFilterer } from "src/utils/algorithms/BoardFilterer"; import { ViewTaskFilterModal } from 'src/components/BoardFilters'; diff --git a/src/managers/BugReporter.ts b/src/managers/BugReporter.ts index 90723496..95d4a925 100644 --- a/src/managers/BugReporter.ts +++ b/src/managers/BugReporter.ts @@ -1,6 +1,6 @@ import { t } from "i18next"; import TaskBoard from "main"; -import { Notice, TFolder } from "obsidian"; +import { Notice, } from "obsidian"; import { BugReporterModal } from "src/modals/BugReporterModal"; import { fsPromises } from "src/services/FileSystem"; import { getObsidianDebugInfo } from "src/services/ObsidianDebugInfo"; diff --git a/src/managers/DragDropTasksManager.ts b/src/managers/DragDropTasksManager.ts index ac7e49e2..64e148df 100644 --- a/src/managers/DragDropTasksManager.ts +++ b/src/managers/DragDropTasksManager.ts @@ -24,7 +24,6 @@ import { import { updateTaskInFile } from "src/utils/taskLine/TaskLineUtils"; import { globalSettingsData } from "src/interfaces/GlobalSettings"; import { getAllDatesInRelativeRange } from "src/utils/DateTimeCalculations"; -import { bugReporter } from "src/services/OpenModals"; import { DatePickerModal } from "src/modals/date_picker"; import { bugReporterManagerInsatance } from "./BugReporter"; diff --git a/src/managers/RealTimeScanner.ts b/src/managers/RealTimeScanner.ts index 49b9ef9c..6af985b8 100644 --- a/src/managers/RealTimeScanner.ts +++ b/src/managers/RealTimeScanner.ts @@ -4,7 +4,6 @@ import { App, TAbstractFile, TFile, TFolder } from "obsidian"; import type VaultScanner from "src/managers/VaultScanner"; import type TaskBoard from "main"; -import { bugReporter } from "src/services/OpenModals"; import { eventEmitter } from "src/services/EventEmitter"; import { PENDING_SCAN_FILE_STACK } from "src/interfaces/Constants"; import { bugReporterManagerInsatance } from "./BugReporter"; diff --git a/src/managers/VaultScanner.ts b/src/managers/VaultScanner.ts index 77d928ba..44efc52f 100644 --- a/src/managers/VaultScanner.ts +++ b/src/managers/VaultScanner.ts @@ -11,7 +11,7 @@ import { loadJsonCacheDataFromDisk, writeJsonCacheDataToDisk, } from "../utils/JsonFileOperations"; -import { jsonCacheData, noteItem, taskItem } from "src/interfaces/TaskItem"; +import { jsonCacheData, taskItem } from "src/interfaces/TaskItem"; import { extractTaskNoteProperties, isTaskNotePresentInFrontmatter, @@ -31,14 +31,10 @@ import { extractFrontmatterTags, } from "../utils/taskNote/FrontmatterOperations"; import { t } from "../utils/lang/helper"; -import { - allowedFileExtensionsRegEx, - notAllowedFileExtensionsRegEx, -} from "src/regularExpressions/MiscelleneousRegExpr"; -import { bugReporter } from "src/services/OpenModals"; +import { allowedFileExtensionsRegEx } from "src/regularExpressions/MiscelleneousRegExpr"; import { getCurrentLocalTimeString } from "../utils/DateTimeCalculations"; import { priorityEmojis } from "src/interfaces/Mapping"; -import { scanModeOptions, UniversalDateOptions } from "src/interfaces/Enums"; +import { UniversalDateOptions } from "src/interfaces/Enums"; import { scanFilterForFilesNFoldersNFrontmatter, scanFilterForTags, diff --git a/src/modals/BoardConfigModal.tsx b/src/modals/BoardConfigModal.tsx index 88b049c9..7bbea523 100644 --- a/src/modals/BoardConfigModal.tsx +++ b/src/modals/BoardConfigModal.tsx @@ -13,7 +13,6 @@ import { SettingsManager } from "src/settings/SettingConstructUI"; import TaskBoard from "main"; import { t } from "src/utils/lang/helper"; import { ClosePopupConfrimationModal } from "./ClosePopupConfrimationModal"; -import { bugReporter } from "src/services/OpenModals"; import { MultiSuggest, getFileSuggestions, getTagSuggestions } from "src/services/MultiSuggest"; import { colTypeNames, UniversalDateOptions } from "src/interfaces/Enums"; import { Board, swimlaneConfigs } from "src/interfaces/BoardConfigs"; diff --git a/src/modals/BugReporterModal.ts b/src/modals/BugReporterModal.ts index f06e1cce..a6c47656 100644 --- a/src/modals/BugReporterModal.ts +++ b/src/modals/BugReporterModal.ts @@ -1,7 +1,7 @@ // /src/modal/BugReporter.ts import type TaskBoard from "main"; -import { Modal, Notice, PluginManifest } from "obsidian"; +import { Modal, Notice } from "obsidian"; import { getObsidianDebugInfo } from "src/services/ObsidianDebugInfo"; import { createFragmentWithHTML } from "src/utils/UIHelpers"; import { t } from "src/utils/lang/helper"; diff --git a/src/modals/CustomStatusConfigurator.ts b/src/modals/CustomStatusConfigurator.ts index bcd93b63..108635f1 100644 --- a/src/modals/CustomStatusConfigurator.ts +++ b/src/modals/CustomStatusConfigurator.ts @@ -1,4 +1,4 @@ -import { Modal, Notice, Setting, TextComponent } from "obsidian"; +import { Modal, Setting, TextComponent } from "obsidian"; import type { Plugin } from "obsidian"; import { statusTypeNames } from "src/interfaces/Enums"; import { diff --git a/src/modals/DiffContentCompareModal.ts b/src/modals/DiffContentCompareModal.ts index 93a5fcfd..788f2912 100644 --- a/src/modals/DiffContentCompareModal.ts +++ b/src/modals/DiffContentCompareModal.ts @@ -1,7 +1,7 @@ // /src/modal/DiffContentCompareModal.ts import TaskBoard from "main"; -import { App, Modal, ButtonComponent } from "obsidian"; +import { Modal, ButtonComponent } from "obsidian"; export type DiffSelection = "old" | "new"; diff --git a/src/modals/EditTagsModal.ts b/src/modals/EditTagsModal.ts index c6298bff..a19156cb 100644 --- a/src/modals/EditTagsModal.ts +++ b/src/modals/EditTagsModal.ts @@ -1,4 +1,4 @@ -import { Modal, Setting } from 'obsidian'; +import { Modal } from 'obsidian'; import TaskBoard from 'main'; export default class EditTagsModal extends Modal { diff --git a/src/modals/SwimlanesConfigModal.tsx b/src/modals/SwimlanesConfigModal.tsx index e80a7d8a..998e52a0 100644 --- a/src/modals/SwimlanesConfigModal.tsx +++ b/src/modals/SwimlanesConfigModal.tsx @@ -1,9 +1,8 @@ // /src/modals/SwimlanesConfigModal.tsx import React, { useState, useRef, useEffect } from 'react'; -import { Modal, App, BooleanValue } from 'obsidian'; +import { Modal, App } from 'obsidian'; import ReactDOM from "react-dom/client"; -import TaskBoard from 'main'; import { t } from 'src/utils/lang/helper'; import Sortable from 'sortablejs'; import { RxDragHandleDots2 } from 'react-icons/rx'; diff --git a/src/modals/date_picker/DatePickerComponent.ts b/src/modals/date_picker/DatePickerComponent.ts index ed5ec030..88081989 100644 --- a/src/modals/date_picker/DatePickerComponent.ts +++ b/src/modals/date_picker/DatePickerComponent.ts @@ -1,12 +1,4 @@ -import { - Component, - ExtraButtonComponent, - setIcon, - DropdownComponent, - ButtonComponent, - App, - setTooltip, -} from "obsidian"; +import { Component, setIcon, App } from "obsidian"; import { moment as _moment } from "obsidian"; import TaskBoard from "main"; import { t } from "src/utils/lang/helper"; diff --git a/src/modals/date_picker/DatePickerModal.ts b/src/modals/date_picker/DatePickerModal.ts index 1a91bb9b..b4d029ef 100644 --- a/src/modals/date_picker/DatePickerModal.ts +++ b/src/modals/date_picker/DatePickerModal.ts @@ -1,5 +1,5 @@ -import { App, Modal } from "obsidian"; -import { DatePickerComponent, DatePickerState } from "./DatePickerComponent"; +import { Modal } from "obsidian"; +import { DatePickerComponent } from "./DatePickerComponent"; import TaskBoard from "main"; export class DatePickerModal extends Modal { diff --git a/src/services/OpenModals.ts b/src/services/OpenModals.ts index efcc63d4..50691620 100644 --- a/src/services/OpenModals.ts +++ b/src/services/OpenModals.ts @@ -28,7 +28,6 @@ import { ScanVaultModal } from "src/modals/ScanVaultModal"; import { TaskBoardActionsModal } from "src/modals/TaskBoardActionsModal"; import { bugReporterManagerInsatance } from "src/managers/BugReporter"; import { DatePickerModal } from "src/modals/date_picker"; -import { updateTaskItemDate } from "src/utils/UserTaskEvents"; import { getCurrentLocalTimeString } from "src/utils/DateTimeCalculations"; // Function to open the BoardConfigModal diff --git a/src/services/tasks-plugin/helpers.ts b/src/services/tasks-plugin/helpers.ts index 3e43c5d0..823eaa56 100644 --- a/src/services/tasks-plugin/helpers.ts +++ b/src/services/tasks-plugin/helpers.ts @@ -1,7 +1,6 @@ // /src/services/tasks-plugin/helpers.ts import { TasksPluginApi } from "./api"; -import { bugReporter } from "../OpenModals"; import TaskBoard from "main"; import { taskItem } from "src/interfaces/TaskItem"; import { diff --git a/src/settings/SettingConstructUI.ts b/src/settings/SettingConstructUI.ts index 69167ce1..f472ae1d 100644 --- a/src/settings/SettingConstructUI.ts +++ b/src/settings/SettingConstructUI.ts @@ -4,7 +4,6 @@ import { App, Notice, Setting, - SettingGroup, normalizePath, setIcon, } from "obsidian"; @@ -20,7 +19,7 @@ import { getQuickAddPluginChoices, } from "src/services/MultiSuggest"; import { CommunityPlugins } from "src/services/CommunityPlugins"; -import { bugReporter, openScanFiltersModal } from "src/services/OpenModals"; +import { openScanFiltersModal } from "src/services/OpenModals"; import { CustomStatusModal } from "src/modals/CustomStatusConfigurator"; import { moveTasksCacheFileToNewPath } from "src/utils/JsonFileOperations"; import { @@ -42,7 +41,6 @@ import { mapViewArrowDirection, mapViewScrollAction, mapViewEdgeType, - defaultTaskStatuses, statusTypeNames, scanModeOptions, } from "src/interfaces/Enums"; diff --git a/src/utils/JsonFileOperations.ts b/src/utils/JsonFileOperations.ts index b8509973..07ef5da1 100644 --- a/src/utils/JsonFileOperations.ts +++ b/src/utils/JsonFileOperations.ts @@ -8,7 +8,6 @@ import { import { Board } from "../interfaces/BoardConfigs"; import type TaskBoard from "main"; -import { bugReporter } from "src/services/OpenModals"; import { bugReporterManagerInsatance } from "src/managers/BugReporter"; import { App } from "obsidian"; diff --git a/src/utils/MarkdownFileOperations.ts b/src/utils/MarkdownFileOperations.ts index 4b311702..bbec9594 100644 --- a/src/utils/MarkdownFileOperations.ts +++ b/src/utils/MarkdownFileOperations.ts @@ -2,7 +2,6 @@ import { TFile } from "obsidian"; import type TaskBoard from "main"; -import { bugReporter } from "src/services/OpenModals"; import { bugReporterManagerInsatance } from "src/managers/BugReporter"; /** diff --git a/src/utils/TaskItemUtils.ts b/src/utils/TaskItemUtils.ts index aba13360..d7261661 100644 --- a/src/utils/TaskItemUtils.ts +++ b/src/utils/TaskItemUtils.ts @@ -1,6 +1,5 @@ import TaskBoard from "main"; import { taskItem } from "src/interfaces/TaskItem"; -import { bugReporter } from "src/services/OpenModals"; import { updateTaskInFile } from "./taskLine/TaskLineUtils"; import { isTaskNotePresentInTags, diff --git a/src/utils/UIHelpers.ts b/src/utils/UIHelpers.ts index 03ff10f3..2afa72a9 100644 --- a/src/utils/UIHelpers.ts +++ b/src/utils/UIHelpers.ts @@ -1,4 +1,3 @@ -import type TaskBoard from "main"; import { sanitizeHTMLToDom } from "obsidian"; import { bugReporterManagerInsatance } from "src/managers/BugReporter"; diff --git a/src/utils/algorithms/ColumnSortingAlgorithm.ts b/src/utils/algorithms/ColumnSortingAlgorithm.ts index 59979124..5d04871e 100644 --- a/src/utils/algorithms/ColumnSortingAlgorithm.ts +++ b/src/utils/algorithms/ColumnSortingAlgorithm.ts @@ -1,6 +1,5 @@ // /src/utils/ColumnSortingAlgorithm.ts -import type TaskBoard from "main"; import { columnSortingCriteria } from "src/interfaces/BoardConfigs"; import { taskItem } from "src/interfaces/TaskItem"; diff --git a/src/utils/lang/helper.ts b/src/utils/lang/helper.ts index 5e21f57b..43ea468c 100644 --- a/src/utils/lang/helper.ts +++ b/src/utils/lang/helper.ts @@ -5,7 +5,6 @@ import i18next from "i18next"; import en from "./locale/en"; import TaskBoard from "main"; import { langCodes } from "src/interfaces/GlobalSettings"; -import { bugReporter } from "src/services/OpenModals"; import { NODE_POSITIONS_STORAGE_KEY, NODE_SIZE_STORAGE_KEY, diff --git a/src/utils/taskLine/TaskContentFormatter.ts b/src/utils/taskLine/TaskContentFormatter.ts index 17d107de..f3f33d05 100644 --- a/src/utils/taskLine/TaskContentFormatter.ts +++ b/src/utils/taskLine/TaskContentFormatter.ts @@ -15,7 +15,6 @@ import { taskPropertyFormatOptions, NotificationService, taskPropertiesNames, - UniversalDateOptions, statusTypeNames, } from "src/interfaces/Enums"; import { globalSettingsData } from "src/interfaces/GlobalSettings"; diff --git a/src/utils/taskLine/TaskItemEventHandlers.ts b/src/utils/taskLine/TaskItemEventHandlers.ts index 14773be4..728bfd5d 100644 --- a/src/utils/taskLine/TaskItemEventHandlers.ts +++ b/src/utils/taskLine/TaskItemEventHandlers.ts @@ -1,4 +1,4 @@ -import { checkboxStateSwitcher, isTaskCompleted } from "../CheckBoxUtils"; +import { checkboxStateSwitcher } from "../CheckBoxUtils"; import { archiveTask, deleteTaskFromFile, @@ -10,17 +10,11 @@ import TaskBoard from "main"; import { moment as _moment } from "obsidian"; import { t } from "../lang/helper"; import { taskItem } from "src/interfaces/TaskItem"; -import { bugReporter } from "src/services/OpenModals"; import { TasksPluginApi } from "src/services/tasks-plugin/api"; import { archiveTaskNote, deleteTaskNote } from "../taskNote/TaskNoteUtils"; -import { statusTypeNames } from "src/interfaces/Enums"; import { DeleteConfirmationModal } from "src/modals/DeleteConfirmationModal"; import { eventEmitter } from "src/services/EventEmitter"; -import { - sanitizeCancelledDate, - sanitizeCompletionDate, - sanitizeStatus, -} from "./TaskContentFormatter"; +import { sanitizeStatus } from "./TaskContentFormatter"; import { bugReporterManagerInsatance } from "src/managers/BugReporter"; /** diff --git a/src/utils/taskLine/TaskLineUtils.ts b/src/utils/taskLine/TaskLineUtils.ts index 9bfafbbb..a4f389fb 100644 --- a/src/utils/taskLine/TaskLineUtils.ts +++ b/src/utils/taskLine/TaskLineUtils.ts @@ -13,10 +13,7 @@ import { import { normalizePath, Notice } from "obsidian"; import TaskBoard from "main"; import { TasksPluginApi } from "src/services/tasks-plugin/api"; -import { - bugReporter, - openDiffContentCompareModal, -} from "src/services/OpenModals"; +import { openDiffContentCompareModal } from "src/services/OpenModals"; import { allowedFileExtensionsRegEx } from "src/regularExpressions/MiscelleneousRegExpr"; import { isTheContentDiffAreOnlySpaces_V2 } from "src/modals/DiffContentCompareModal"; import { bugReporterManagerInsatance } from "src/managers/BugReporter"; diff --git a/src/utils/taskNote/FrontmatterOperations.ts b/src/utils/taskNote/FrontmatterOperations.ts index 95ea56de..9fac60a8 100644 --- a/src/utils/taskNote/FrontmatterOperations.ts +++ b/src/utils/taskNote/FrontmatterOperations.ts @@ -10,7 +10,6 @@ import { frontmatterFormatting } from "src/interfaces/GlobalSettings"; import { generateTaskId } from "../TaskItemUtils"; import { statusTypeNames } from "src/interfaces/Enums"; import { moment as _moment } from "obsidian"; -import { getCurrentLocalTimeString } from "../DateTimeCalculations"; import { bugReporterManagerInsatance } from "src/managers/BugReporter"; /** diff --git a/src/utils/taskNote/TaskNoteUtils.ts b/src/utils/taskNote/TaskNoteUtils.ts index 471f5d79..02f261e2 100644 --- a/src/utils/taskNote/TaskNoteUtils.ts +++ b/src/utils/taskNote/TaskNoteUtils.ts @@ -14,7 +14,6 @@ import { PluginDataJson, } from "src/interfaces/GlobalSettings"; import { Notice, normalizePath } from "obsidian"; -import { bugReporter } from "src/services/OpenModals"; import { defaultTaskStatuses } from "src/interfaces/Enums"; import { bugReporterManagerInsatance } from "src/managers/BugReporter"; From 52396b89b97707579420f16a87cd09c2ba96162e Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Sun, 1 Feb 2026 20:18:23 +0530 Subject: [PATCH 062/236] clean up all console logs and replace others with logger instance --- main.ts | 34 --- src/components/AddOrEditTaskRC.tsx | 1 - src/components/BoardFilters/ViewTaskFilter.ts | 23 +- src/components/KanbanView/KanbanBoardView.tsx | 226 ------------------ src/components/KanbanView/TaskItem.tsx | 2 - src/components/KanbanView/TaskItemV2.tsx | 3 - src/components/MapView/EdgeWithToolbar.tsx | 7 +- src/components/MapView/MapView.tsx | 7 +- src/components/TaskBoardViewContent.tsx | 11 +- src/managers/BugReporter.ts | 3 +- src/managers/DragDropTasksManager.ts | 50 +--- src/managers/VaultScanner.ts | 4 - src/modals/date_picker/DatePickerComponent.ts | 2 - src/services/FrontmatterRenderer.ts | 41 ++-- src/services/OpenModals.ts | 6 +- src/services/tasks-plugin/helpers.ts | 6 - src/utils/JsonFileOperations.ts | 26 +- src/utils/MarkdownFileOperations.ts | 3 - src/utils/UserTaskEvents.ts | 86 +++---- src/utils/algorithms/ColumnSegregator.ts | 7 +- src/utils/lang/helper.ts | 5 - src/utils/taskLine/TaskContentFormatter.ts | 10 - src/utils/taskLine/TaskItemEventHandlers.ts | 16 +- src/utils/taskNote/FrontmatterOperations.ts | 12 +- src/utils/taskNote/TaskNoteEventHandlers.ts | 1 - src/utils/taskNote/TaskNoteUtils.ts | 6 - 26 files changed, 136 insertions(+), 462 deletions(-) diff --git a/main.ts b/main.ts index c709647b..3ccc7641 100644 --- a/main.ts +++ b/main.ts @@ -136,7 +136,6 @@ export default class TaskBoard extends Plugin { // Register events and commands only on Layout is ready this.app.workspace.onLayoutReady(() => { - console.log("Task Board : Running onLayoutReady..."); this.compatiblePluginsAvailabilityCheck(); //Creates a Icon on Ribbon Bar (after i18n is initialized) @@ -167,10 +166,7 @@ export default class TaskBoard extends Plugin { this.registerReadingModePostProcessor(); setTimeout(() => this.findModifiedFilesOnAppAbsense(), 10000); - - console.log("Task Board : onLayoutReady FINISHED."); }); - console.log("Task Board : onload funcion FINISHED."); } onunload() { @@ -358,15 +354,12 @@ export default class TaskBoard extends Plugin { const tasksPlugin = new TasksPluginApi(this); if (!tasksPlugin.isTasksPluginEnabled()) { this.registerMarkdownPostProcessor((element, context) => { - // console.log("Element : ", element, "\nContent :", context); // Only process if we have properties to hide - // Find all list items that could be tasks const listItems = element.querySelectorAll("li"); listItems.forEach((listItem) => { // const textContent = listItem.textContent || ""; - // console.log("Text Content :", textContent); // Check if this is a task (starts with checkbox syntax) if (listItem.querySelector(".contains-task-list")) { this.hidePropertiesInElement( @@ -928,18 +921,12 @@ export default class TaskBoard extends Plugin { let OBSIDIAN_CLOSED_TIME = this.app.loadLocalStorage( OBSIDIAN_CLOSED_TIME_KEY, ); - console.log("DATA loaded from localStorage :", OBSIDIAN_CLOSED_TIME); if (!OBSIDIAN_CLOSED_TIME) OBSIDIAN_CLOSED_TIME = this.vaultScanner.tasksCache.Modified_at; - console.log("DATA loaded from cache :", OBSIDIAN_CLOSED_TIME); if (OBSIDIAN_CLOSED_TIME) { OBSIDIAN_CLOSED_TIME = Date.parse(OBSIDIAN_CLOSED_TIME); - console.log( - "Task Board : Fetching all modified files...\nLast modified time :", - OBSIDIAN_CLOSED_TIME, - ); let filesScannedCount = 0; const modifiedCreatedRenamedFiles = this.app.vault .getFiles() @@ -971,14 +958,6 @@ export default class TaskBoard extends Plugin { file, ), ); - console.log( - "Task Board : Fetching complete.\nModified files :", - changed_files, - "\nDeleted files :", - deletedFilesList, - "\nFiles scanned :", - filesScannedCount, - ); const totalFilesLength = changed_files.length + deletedFilesList.length; @@ -1022,16 +1001,10 @@ export default class TaskBoard extends Plugin { false, ) .then(async () => { - console.log( - "Task Board : Will now going to update the deleted files cache...", - ); if (deletedFilesList.length > 0) { await this.plugin.vaultScanner.deleteCacheForFiles( deletedFilesList, ); - console.log( - "Task Board : Completed deleting cache of deleted files...", - ); } }); @@ -1096,7 +1069,6 @@ export default class TaskBoard extends Plugin { registerEvents() { this.registerEvent( this.app.vault.on("modify", async (file: TAbstractFile) => { - console.log("Modify event is fired..."); if ( fileTypeAllowedForScanning( this.plugin.settings.data.globalSettings, @@ -1123,21 +1095,18 @@ export default class TaskBoard extends Plugin { ); this.registerEvent( this.app.vault.on("rename", (file, oldPath) => { - console.log("Rename event is fired..."); // Queue the file for processing instead of processing immediately this.queueFileForRename(file, oldPath); }), ); this.registerEvent( this.app.vault.on("delete", (file) => { - console.log("Delete event is fired..."); // Queue the file for processing instead of processing immediately this.queueFileForDeletion(file); }), ); this.registerEvent( this.app.vault.on("create", (file) => { - console.log("Create event is fired..."); if (file instanceof TFile) { // Queue the file for processing instead of processing immediately this.queueFileForCreation(file); @@ -1154,18 +1123,15 @@ export default class TaskBoard extends Plugin { this.app.workspace.on( "active-leaf-change", (leaf: WorkspaceLeaf | null) => { - console.log("On Active Leaf Change...\nLeaf =", leaf); this.onFileModifiedAndLostFocus(); }, ), ); this.registerDomEvent(window, "blur", () => { this.onFileModifiedAndLostFocus(); - console.log("Focusing out of the window..."); }); this.registerDomEvent(window, "focus", () => { this.onFileModifiedAndLostFocus(); - console.log("Focusing in the window..."); }); } diff --git a/src/components/AddOrEditTaskRC.tsx b/src/components/AddOrEditTaskRC.tsx index 0ad9dc28..17fefa14 100644 --- a/src/components/AddOrEditTaskRC.tsx +++ b/src/components/AddOrEditTaskRC.tsx @@ -1141,7 +1141,6 @@ export const AddOrEditTaskRC: React.FC<{ easing: "cubic-bezier(1, 0, 0, 1)", onSort: (evt) => { try { - console.log("Lets go..."); if (evt.oldIndex === undefined || evt.newIndex === undefined) return; // Reorder the dependsOn array based on the drag and drop diff --git a/src/components/BoardFilters/ViewTaskFilter.ts b/src/components/BoardFilters/ViewTaskFilter.ts index 562fc12a..dc1afc63 100644 --- a/src/components/BoardFilters/ViewTaskFilter.ts +++ b/src/components/BoardFilters/ViewTaskFilter.ts @@ -31,6 +31,7 @@ import { priorityDropDownOption, } from "src/interfaces/Mapping"; import { PluginDataJson } from "src/interfaces/GlobalSettings"; +import { bugReporterManagerInsatance } from "src/managers/BugReporter"; export class TaskFilterComponent extends Component { private hostEl: HTMLElement; @@ -94,7 +95,7 @@ export class TaskFilterComponent extends Component { // } else { // if (savedState) { // // If it exists but failed validation - // console.warn( + // bugreporterInstance.addToLogs( // "Task Filter: Invalid data in local storage. Resetting to default state." // ); // } @@ -473,8 +474,10 @@ export class TaskFilterComponent extends Component { ): void { // Ensure the container is initialized if (!this.filterGroupsContainerEl) { - console.warn( - "TaskFilterComponent: filterGroupsContainerEl not initialized yet", + bugReporterManagerInsatance.addToLogs( + 168, + "filterGroupsContainerEl not initialized yet", + "ViewTaskFilter.ts/addFilterGroup", ); return; } @@ -788,7 +791,6 @@ export class TaskFilterComponent extends Component { // let dropdownInput: DropdownComponent | null = null; // filterItemEl.removeChild(dropdownInputContainer); if (valueSelect) { - console.log("Removing dropdown input"); // dropdownInput.disabled = true; // dropdownInput.type = "text"; // dropdownInput = null; @@ -1440,7 +1442,11 @@ export class TaskFilterComponent extends Component { this.groupsSortable = undefined; } } catch (error) { - console.warn("Error destroying groups sortable:", error); + bugReporterManagerInsatance.addToLogs( + 169, + `Error destroying groups sortable: ${String(error)}`, + "ViewTaskFilter.ts/loadFilterState", + ); this.groupsSortable = undefined; } @@ -1456,9 +1462,10 @@ export class TaskFilterComponent extends Component { (listEl as any).sortableInstance = undefined; } } catch (error) { - console.warn( - "Error destroying filter list sortable:", - error, + bugReporterManagerInsatance.addToLogs( + 170, + `Error destroying filter list sortable: ${String(error)}`, + "ViewTaskFilter.ts/loadFilterState", ); (listEl as any).sortableInstance = undefined; } diff --git a/src/components/KanbanView/KanbanBoardView.tsx b/src/components/KanbanView/KanbanBoardView.tsx index 1b3cbbcc..da382f3c 100644 --- a/src/components/KanbanView/KanbanBoardView.tsx +++ b/src/components/KanbanView/KanbanBoardView.tsx @@ -94,229 +94,3 @@ const MemoizedColumn = memo<{ }); export default memo(KanbanBoard); - - - - - - - - - - -// // src/components/KanbanBoard.tsx - V1 - -// import { Board, ColumnData } from "../interfaces/BoardConfigs"; -// import { Bolt, CirclePlus, RefreshCcw, Tally1 } from 'lucide-react'; -// import React, { memo, useCallback, useEffect, useMemo, useState } from "react"; -// import { loadBoardsData, loadTasksAndMerge } from "src/utils/JsonFileOperations"; -// import { taskJsonMerged } from "src/interfaces/TaskItem"; - -// import { App } from "obsidian"; -// import Column from "./Column"; -// import type TaskBoard from "main"; -// import debounce from "debounce"; -// import { eventEmitter } from "src/services/EventEmitter"; -// import { handleUpdateBoards } from "../utils/BoardOperations"; -// import { bugReporter, openAddNewTaskModal, openBoardConfigModal } from "../services/OpenModals"; -// import { columnSegregator } from 'src/utils/RenderColumns'; -// import { t } from "src/utils/lang/helper"; - -// const KanbanBoard: React.FC<{ app: App, plugin: TaskBoard, boardConfigs: Board[] }> = ({ app, plugin, boardConfigs }) => { -// const [boards, setBoards] = useState(boardConfigs); -// const [activeBoardIndex, setActiveBoardIndex] = useState(0); -// const [allTasks, setAllTasks] = useState(); -// // const [allTasksArrangedPerColumn, setAllTasksArrangedPerColumn] = useState([]); -// const [refreshCount, setRefreshCount] = useState(0); -// const [loading, setLoading] = useState(true); -// const [freshInstall, setFreshInstall] = useState(false); - -// useEffect(() => { -// const fetchData = async () => { -// try { -// const data = await loadBoardsData(plugin); -// setBoards(data); - -// const allTasks = await loadTasksAndMerge(plugin); -// // console.log("KanbanBoard.tsx : Data in allTasks :", allTasks); -// if (allTasks) { -// setAllTasks(allTasks); -// setFreshInstall(false); -// } -// } catch (error) { -// setFreshInstall(true); -// // bugReporterManagerInsatance.showNotice(2, "Error loading boards or tasks data", error as string, "KanbanBoard.tsx/useEffect"); -// } -// }; - -// fetchData(); -// // fetchData().finally(() => setLoading(false)); -// }, [refreshCount]); - -// const allTasksArrangedPerColumn = useMemo(() => { -// if (allTasks && boards[activeBoardIndex]) { -// return boards[activeBoardIndex].columns -// .filter((column) => column.active) -// .map((column: ColumnData) => -// columnSegregator(plugin, activeBoardIndex, column, allTasks) -// ); -// } -// return []; -// }, [allTasks, boards, activeBoardIndex]); - -// useEffect(() => { -// if (allTasksArrangedPerColumn.length > 0) { -// setLoading(false); -// } -// }, [allTasksArrangedPerColumn]); - - -// // // Load tasks only once when the board is refreshed -// // useEffect(() => { -// // refreshBoardData(setBoards, async () => { -// // try { -// // const data = await loadBoardsData(plugin); // Fetch updated board data -// // setBoards(data); // Update the state with the new data -// // const allTasks = await loadTasksAndMerge(plugin); -// // console.log("KanbanBoard.tsx : Data in allTasks :", allTasks); -// // if (allTasks) { -// // setAllTasks(allTasks); -// // } -// // } catch (error) { -// // console.error("refreshBoardData : Error loading tasks:", error); -// // } -// // }); -// // }, []); - -// // useEffect(() => { -// // if (allTasks && boards[activeBoardIndex]) { -// // const columns = boards[activeBoardIndex].columns; -// // const arrangedTasks = columns.map((column: ColumnData) => { -// // return columnSegregator(plugin, activeBoardIndex, column, allTasks); -// // }); -// // console.log("KanbanBoard.tsx : Data in setAllTasksArrangedPerColumn:", arrangedTasks); -// // setAllTasksArrangedPerColumn(arrangedTasks); -// // } -// // }, [allTasks, boards, activeBoardIndex]); - -// const debouncedRefreshColumn = useCallback(debounce(async () => { -// try { -// const allTasks = await loadTasksAndMerge(plugin); -// setAllTasks(allTasks); -// } catch (error) { -// bugReporterManagerInsatance.showNotice(3, "Error loading tasks on column refresh", error as string, "KanbanBoard.tsx/debouncedRefreshColumn"); -// } -// }, 300), [plugin]); - -// useEffect(() => { -// eventEmitter.on('REFRESH_COLUMN', debouncedRefreshColumn); -// return () => { -// eventEmitter.off('REFRESH_COLUMN', debouncedRefreshColumn); -// }; -// }, [debouncedRefreshColumn]); - -// // Pub Sub method similar to Kafka to read events/messages. -// useEffect(() => { -// const refreshBoardListener = () => { -// // Clear the tasks array -// // setAllTasks(undefined); -// setRefreshCount((prev) => prev + 1); -// }; - -// // const refreshColumnListener = async () => { -// // try { -// // const allTasks = await loadTasksAndMerge(plugin); -// // // setAllTasksArrangedPerColumn([]); -// // setAllTasks(allTasks); -// // } catch (error) { -// // console.error("Error loading tasks:", error); -// // } -// // }; - -// eventEmitter.on('REFRESH_BOARD', refreshBoardListener); -// // eventEmitter.on('REFRESH_COLUMN', refreshColumnListener); - -// // Clean up the listener when component unmounts -// return () => { -// eventEmitter.off('REFRESH_BOARD', refreshBoardListener); -// // eventEmitter.off('REFRESH_COLUMN', refreshColumnListener); -// }; -// }, []); - -// // Memoized refreshBoardButton to avoid re-creating the function on every render -// const refreshBoardButton = useCallback(async () => { -// if (plugin.settings.data.globalSettings.realTimeScanner) { -// eventEmitter.emit("REFRESH_BOARD"); -// } else { -// if ( -// localStorage.getItem(PENDING_SCAN_FILE_STACK)?.at(0) !== undefined -// ) { -// await plugin.realTimeScanner.processAllUpdatedFiles(); -// } -// eventEmitter.emit("REFRESH_BOARD"); -// } -// }, [plugin]); - -// function handleOpenAddNewTaskModal() { -// openAddNewTaskModal(app, plugin); -// } - -// // const isLoading = !boards[activeBoardIndex]?.columns.every( -// // (_, index) => allTasksArrangedPerColumn[index]?.length > 0 -// // ); - -// // // If you prefer a more robust check that verifies whether the data is not only populated but also corresponds correctly to the columns: -// // const isLoading = -// // allTasksArrangedPerColumn.length !== boards[activeBoardIndex]?.columns.length || -// // allTasksArrangedPerColumn.some((tasks) => tasks.length === 0); - -// return ( -//
    -//
    -// {loading ? ( -//
    -// {freshInstall ? ( -//

    -// {t("fresh-install-1")} -//
    -//
    -// {t("fresh-install-2")} -//
    -//
    -// {t("fresh-install-3")} -//

    -// ) : ( -// <> -//
    -//

    {t('loading-tasks')}

    -// -// )} -//
    -// ) : ( -// boards[activeBoardIndex]?.columns -// .filter((column) => column.active) -// .map((column, index) => ( -// -// )) -// )} -//
    -//
    -// ); -// }; - -// // Wrap Column in React.memo -// const MemoizedColumn = memo(Column, (prevProps, nextProps) => { -// return ( -// prevProps.tasksForThisColumn === nextProps.tasksForThisColumn && -// prevProps.columnData === nextProps.columnData -// ); -// }); - -// export default memo(KanbanBoard); diff --git a/src/components/KanbanView/TaskItem.tsx b/src/components/KanbanView/TaskItem.tsx index 98b38de7..fb9b77c7 100644 --- a/src/components/KanbanView/TaskItem.tsx +++ b/src/components/KanbanView/TaskItem.tsx @@ -850,7 +850,6 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin // Handlers for drag and drop const handleDragStart = useCallback((e: React.DragEvent) => { - console.log("TaskItem : handleDragStart..."); if (!columnData) { e.preventDefault(); bugReporterManagerInsatance.addToLogs(91, `Column data : undefined`, "TaskItem.tsx/handleDragStart"); @@ -869,7 +868,6 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin e.dataTransfer?.setDragImage(el, 0, 0); requestAnimationFrame(() => { clone.classList.add("task-item-dragging"); - console.log("TaskItem : handleDragStart... done : ", el); }); // Also set a drag image from the whole task element so the preview is the full card diff --git a/src/components/KanbanView/TaskItemV2.tsx b/src/components/KanbanView/TaskItemV2.tsx index c01614b7..b33b5427 100644 --- a/src/components/KanbanView/TaskItemV2.tsx +++ b/src/components/KanbanView/TaskItemV2.tsx @@ -853,7 +853,6 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act // Handlers for drag and drop const handleDragStart = useCallback((e: React.DragEvent) => { - console.log("TaskItem : handleDragStart..."); if (!columnData) { e.preventDefault(); bugReporterManagerInsatance.addToLogs(91, `Column data : undefined`, "TaskItem.tsx/handleDragStart"); @@ -872,7 +871,6 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act e.dataTransfer?.setDragImage(el, 0, 0); requestAnimationFrame(() => { clone.classList.add("task-item-dragging"); - console.log("TaskItem : handleDragStart... done : ", el); }); // Also set a drag image from the whole task element so the preview is the full card @@ -1023,7 +1021,6 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, act const completed = allSubTasks.filter(line => isTaskCompleted(line, false, plugin.settings)).length; const showSubTaskSummaryBar = globalSettings.visiblePropertiesList?.includes(taskPropertiesNames.SubTasksMinimized); - console.log("Show subtasks :", showSubtasks, "\nShow subtasks summary :", showSubTaskSummaryBar); return ( <> diff --git a/src/components/MapView/EdgeWithToolbar.tsx b/src/components/MapView/EdgeWithToolbar.tsx index 07b04a9b..465d69e4 100644 --- a/src/components/MapView/EdgeWithToolbar.tsx +++ b/src/components/MapView/EdgeWithToolbar.tsx @@ -13,6 +13,7 @@ import TaskBoard from 'main'; import { taskItem } from 'src/interfaces/TaskItem'; import { updateTaskInFile } from 'src/utils/taskLine/TaskLineUtils'; import { sanitizeDependsOn } from 'src/utils/taskLine/TaskContentFormatter'; +import { bugReporterManagerInsatance } from 'src/managers/BugReporter'; // interface EdgeWithToolbarProps extends EdgeProps { // plugin: TaskBoard; @@ -58,7 +59,11 @@ export function EdgeWithToolbar(props: EdgeProps) { // ); // if (!targetTask) { - // console.warn('Target task not found for edge deletion'); + // bugReporterManagerInsatance.addToLogs( + // 171, + // `Target task not found for edge deletion`, + // "EdgeWithToolbar.tsx/deleteEdge", + // ); // return; // } diff --git a/src/components/MapView/MapView.tsx b/src/components/MapView/MapView.tsx index 5312fcc1..322ea90f 100644 --- a/src/components/MapView/MapView.tsx +++ b/src/components/MapView/MapView.tsx @@ -253,7 +253,6 @@ const MapView: React.FC = ({ if (task.legacyId) { const id = task.legacyId; if (usedIds.has(id)) { - // console.warn('Duplicate node id detected:', id, "\nTitle : ", task.title); duplicateIds.add(id); return; // Skip duplicate } @@ -346,7 +345,11 @@ const MapView: React.FC = ({ // try { // localStorage.setItem(NODE_SIZE_STORAGE_KEY, JSON.stringify(updatedSizes)); // } catch (error) { - // console.warn('Failed to save node sizes:', error); + // bugReporterManagerInsatance.addToLogs( + // 179, + // `Failed to save node sizes: ${String(error)}`, + // "MapView.tsx/updateSingleNodeSizeOnDiskDebounced", + // ); // } // } // }, diff --git a/src/components/TaskBoardViewContent.tsx b/src/components/TaskBoardViewContent.tsx index 9fa3518c..095a8e04 100644 --- a/src/components/TaskBoardViewContent.tsx +++ b/src/components/TaskBoardViewContent.tsx @@ -208,13 +208,12 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs }, []); const refreshBoardButton = useCallback(() => { - plugin.realTimeScanner.processAllUpdatedFiles().then(() => console.log("Finished processing all updated files.")); - plugin.processCreateQueue().then(() => console.log("Finished processing create queue.")); - plugin.processDeleteQueue().then(() => console.log("Finished processing delete queue.")); - plugin.processRenameQueue().then(() => console.log("Finished processing rename queue.")); + plugin.realTimeScanner.processAllUpdatedFiles(); //.then(() => console.log("Finished processing all updated files.")); + plugin.processCreateQueue(); //.then(() => console.log("Finished processing create queue.")); + plugin.processDeleteQueue(); //.then(() => console.log("Finished processing delete queue.")); + plugin.processRenameQueue(); //.then(() => console.log("Finished processing rename queue.")); setTimeout(() => { - console.log("Now will emit REFRESH_BOARD event..."); eventEmitter.emit("REFRESH_BOARD"); }, 100) }, []); @@ -431,13 +430,11 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs function togglePropertyNameInSettings(propertyName: string) { let visibleProperties = plugin.settings.data.globalSettings.visiblePropertiesList || []; - console.log("Current properties list :", visibleProperties, "\nRemove following property :", propertyName, "\nWill remove from the following index :", visibleProperties.indexOf(propertyName)); if (visibleProperties.includes(propertyName)) { visibleProperties.splice(visibleProperties.indexOf(propertyName), 1); plugin.settings.data.globalSettings.visiblePropertiesList = visibleProperties; } else { - console.log("Property Name:", propertyName); let index = -1; switch (propertyName) { case taskPropertiesNames.SubTasks: diff --git a/src/managers/BugReporter.ts b/src/managers/BugReporter.ts index 95d4a925..cefff115 100644 --- a/src/managers/BugReporter.ts +++ b/src/managers/BugReporter.ts @@ -30,7 +30,7 @@ class BugReporterManager { private alreadyShownBugsIDs: number[] = []; private LOG_FILE_PATH = ""; private readonly MAX_RECENT_LOGS = 20; - private readonly MAX_USED_ID = 165; // This constant will not be used anywhere, its simply to keep track of the the recent ID used. + private readonly MAX_USED_ID = 167; // This constant will not be used anywhere, its simply to keep track of the the recent ID used. private constructor() { // Private constructor to enforce singleton pattern @@ -370,7 +370,6 @@ ${entry.bugContent} await this.ensureLogFileExists(); const data = await vault.adapter.read(this.LOG_FILE_PATH); - console.log("data\n", data); const exportFileName = "task-board-logs.txt"; // const fileContent = JSON.stringify(data, null, 2); diff --git a/src/managers/DragDropTasksManager.ts b/src/managers/DragDropTasksManager.ts index 64e148df..e121a480 100644 --- a/src/managers/DragDropTasksManager.ts +++ b/src/managers/DragDropTasksManager.ts @@ -91,7 +91,6 @@ class DragDropTasksManager { * Store current drag payload (called from dragstart) */ setCurrentDragData(data: currentDragDataPayload) { - console.log("setCurrentDragData", data); this.currentDragData = data; } @@ -177,7 +176,6 @@ class DragDropTasksManager { oldTask.tags, newTask.tags, ); - console.log("Sanitized title after tag update:", newTask.title); updateTaskInFile(plugin, newTask, oldTask).then(() => { plugin.realTimeScanner.processAllUpdatedFiles( oldTask.filePath, @@ -246,7 +244,6 @@ class DragDropTasksManager { targetColumnSwimlaneData, plugin.settings.data.globalSettings, ); - console.log("newTask after swimlane change:", newTask); } // ----------------------------------------------- @@ -276,11 +273,6 @@ class DragDropTasksManager { // newTask.tags // ); - console.log( - "handleTaskMove_namedTag_to_namedTag...\nnewTask=", - newTask, - ); - // ----------------------------------------------- // STEP 4 - Finally update the task in the note, so that its automatically scanned again. Which will trigger screen refresh. // ----------------------------------------------- @@ -364,7 +356,6 @@ class DragDropTasksManager { targetColumnSwimlaneData, plugin.settings.data.globalSettings, ); - console.log("newTask after swimlane change:", newTask); } if ( @@ -469,7 +460,6 @@ class DragDropTasksManager { targetColumnSwimlaneData, plugin.settings.data.globalSettings, ); - console.log("newTask after swimlane change:", newTask); } // Extract the priority value from the source column @@ -524,7 +514,6 @@ class DragDropTasksManager { targetColumnSwimlaneData, plugin.settings.data.globalSettings, ); - console.log("newTask after swimlane change:", newTask); } // Extract the status value from the source column @@ -605,7 +594,6 @@ class DragDropTasksManager { targetColumnSwimlaneData, plugin.settings.data.globalSettings, ); - console.log("newTask after swimlane change:", newTask); } if (sourceColumn.colType === colTypeNames.completed) { @@ -709,10 +697,8 @@ class DragDropTasksManager { targetColumnSwimlaneData, plugin.settings.data.globalSettings, ); - console.log("newTask after swimlane change:", newTask); } - console.log("Should hit this one..."); if (sourceColumn.colType === colTypeNames.completed) { newTask = this.handleTaskMove_DONE_to_TODO(plugin, newTask); } @@ -777,7 +763,6 @@ class DragDropTasksManager { targetColumnSwimlaneData, plugin.settings.data.globalSettings, ); - console.log("newTask after swimlane change:", newTask); } if (sourceColumn.colType === colTypeNames.completed) { @@ -834,7 +819,6 @@ class DragDropTasksManager { targetColumnSwimlaneData, plugin.settings.data.globalSettings, ); - console.log("newTask after swimlane change:", newTask); } if (sourceColumn.colType === colTypeNames.completed) { @@ -885,7 +869,6 @@ class DragDropTasksManager { targetColumnSwimlaneData, plugin.settings.data.globalSettings, ); - console.log("newTask after swimlane change:", newTask); } const newStatus = @@ -910,14 +893,6 @@ class DragDropTasksManager { targetColumnData: ColumnData, desiredIndex: number | null, ): void => { - console.log( - "handleTasksOrderChange called...\ncurrentDragData=", - currentDragData, - "\ntargetColumnData=", - targetColumnData, - "\ndesiredIndex=", - desiredIndex, - ); if ( !( targetColumnData?.sortCriteria && @@ -1106,8 +1081,6 @@ class DragDropTasksManager { if (!this.plugin) return; if (!cardEl || !cardEl.parentElement) return; - console.log("cardEl", cardEl, "\nparentEl", cardEl.parentElement); - // Create indicator if not already created if (!this.dropIndicator) { this.dropIndicator = document.createElement("div"); @@ -1219,7 +1192,11 @@ class DragDropTasksManager { // ); // } catch (err) { // // some browsers may throw on setData for complex types - // console.warn("Could not set JSON dataTransfer payload", err); + // bugReporterManagerInsatance.addToLogs( + // 180, + // `Could not set JSON dataTransfer payload: ${String(err)}`, + // "DragDropTasksManager.ts", + // ); // try { // e.dataTransfer.setData("text/plain", currentDragData.task.id); // } catch {} @@ -1330,10 +1307,6 @@ class DragDropTasksManager { e.dataTransfer!.dropEffect = "move"; return true; } else { - console.log( - "Task drop not allowed from column:", - sourceColumnData.name, - ); // Apply CSS styling for not allowed drop targetColumnContainer.classList.add("drag-over-not-allowed"); targetColumnContainer.classList.remove("drag-over-allowed"); @@ -1394,7 +1367,6 @@ class DragDropTasksManager { targetColumnContainer: HTMLDivElement, targetColumnSwimlaneData: swimlaneDataProp | null | undefined, ): void { - console.log("DragDropTasksManager : handleDrop called..."); e.preventDefault(); // All checks before proceeding with the calculations... @@ -1439,12 +1411,12 @@ class DragDropTasksManager { // Perform required operations to update task properties // This is where the actual task update logic will be implemented - console.log("Task drop allowed. Updating task properties..."); - console.log("Source column:", sourceColumnData); - console.log("Target column:", targetColumnData); - console.log("Current drag data:", this.currentDragData); - console.log("Current drag index:", this.desiredDropIndex); - console.log("targetSwimilaneData", targetColumnSwimlaneData); + // console.log("Task drop allowed. Updating task properties..."); + // console.log("Source column:", sourceColumnData); + // console.log("Target column:", targetColumnData); + // console.log("Current drag data:", this.currentDragData); + // console.log("Current drag index:", this.desiredDropIndex); + // console.log("targetSwimilaneData", targetColumnSwimlaneData); // Determine the operation based on source and target column types diff --git a/src/managers/VaultScanner.ts b/src/managers/VaultScanner.ts index 44efc52f..74155ae3 100644 --- a/src/managers/VaultScanner.ts +++ b/src/managers/VaultScanner.ts @@ -681,8 +681,6 @@ export function fileTypeAllowedForScanning( // plugin.settings.data.globalSettings.archivedTasksFilePath, "\nCondition 3 :", , "\nCondition 4 :", ) const filePath = file.path.toLocaleLowerCase(); - console.log("Filepath :", filePath); - if ( // notAllowedFileExtensionsRegEx.test(file.path) || !allowedFileExtensionsRegEx.test(file.path) || @@ -1009,7 +1007,6 @@ export function extractPriority(text: string): number { .map((match) => match.trim()) // Trim spaces .filter((match) => match.length > 0 && match !== "0"); // Remove empty or zero values - console.log("What is the priority emoji : ", validMatches); // Find the first match in the priorityEmojis mapping for (const emoji of validMatches) { const priorityMatch = Object.entries(priorityEmojis).find( @@ -1103,7 +1100,6 @@ export function extractDependsOn(text: string): RegExpMatchArray | null { .dependsOnRegex, ); if (match && match[1]) { - console.log("What is the match : ", match); return match; } diff --git a/src/modals/date_picker/DatePickerComponent.ts b/src/modals/date_picker/DatePickerComponent.ts index 88081989..c574342f 100644 --- a/src/modals/date_picker/DatePickerComponent.ts +++ b/src/modals/date_picker/DatePickerComponent.ts @@ -57,7 +57,6 @@ export class DatePickerComponent extends Component { } setSelectedDate(date: string | null): void { - console.log("DatePickerComponent : Sumbitting following date :", date); this.state.selectedDate = date; this.updateSelectedDateDisplay(); // Only pass the date string, let the caller handle formatting @@ -276,7 +275,6 @@ export class DatePickerComponent extends Component { } private navigateMonth(direction: number): void { - console.log(`Navigating month: ${direction}`); this.currentViewDate.add(direction, "month"); this.render(); } diff --git a/src/services/FrontmatterRenderer.ts b/src/services/FrontmatterRenderer.ts index 66e37ded..7a53ffef 100644 --- a/src/services/FrontmatterRenderer.ts +++ b/src/services/FrontmatterRenderer.ts @@ -3,10 +3,10 @@ /** * @module FrontmatterRenderer * Utility class for rendering frontmatter properties using Obsidian's PropertyWidget API - * The below code has been referenced from the following sources : + * The below code has been referenced from the following sources : * @see https://github.com/Fevol/obsidian-typings/blob/release/obsidian-public/1.9.14/src/obsidian/internals/PropertyWidget.d.ts * @see https://github.com/unxok/obsidian-better-properties/blob/main/src/classes/PropertyComponent/index.ts - * + * * In the current version, this FrontmatterRenderer is using a simple custom styling method to render the properties. */ @@ -51,12 +51,12 @@ export class FrontmatterRenderer { public extractContentWithoutFrontmatter( content: string, - frontmatterContent: string + frontmatterContent: string, ): string { if (!frontmatterContent) return content; const contentWithoutFrontmatter = content.substring( - frontmatterContent.length + frontmatterContent.length, ); return contentWithoutFrontmatter; } @@ -85,7 +85,11 @@ export class FrontmatterRenderer { // const frontmatter = parseYaml(yamlContent); // return frontmatter as Record; // } catch (error) { - // console.warn("Failed to parse frontmatter:", error); + // bugReporterManagerInsatance.addToLogs( + // 172, + // `Failed to parse frontmatter: ${String(error)}`, + // "FrontmatterRenderer.ts/extractFrontmatterObject", + // ); // return null; // } // } @@ -100,7 +104,7 @@ export class FrontmatterRenderer { public renderCollapsibleFrontmatter( containerEl: HTMLElement, content: string, - file?: TFile + file?: TFile, ): { frontmatterContainer: HTMLElement | null; contentWithoutFrontmatter: string; @@ -127,7 +131,7 @@ export class FrontmatterRenderer { // Get the content after frontmatter const contentWithoutFrontmatter = this.extractContentWithoutFrontmatter( content, - frontmatterContent + frontmatterContent, ); // Create the frontmatter section container @@ -154,7 +158,7 @@ export class FrontmatterRenderer { // Add property count const propertyCount = Object.keys(frontmatter).filter( - (key) => key !== "position" + (key) => key !== "position", ).length; header.createSpan({ cls: "taskboard-frontmatter-property-count", @@ -188,7 +192,7 @@ export class FrontmatterRenderer { frontmatterSection.toggleClass( "is-collapsed", - this.isFrontmatterContainerCollapsed + this.isFrontmatterContainerCollapsed, ); }); @@ -207,7 +211,7 @@ export class FrontmatterRenderer { private renderProperties( containerEl: HTMLElement, frontmatter: Record, - file?: TFile + file?: TFile, ): void { this.renderPropertiesSimple(containerEl, frontmatter); return; @@ -305,10 +309,11 @@ export class FrontmatterRenderer { this.renderPropertyValueSimple(valueEl, value); } } catch (error) { - console.warn( - `FALLBACK : Failed to render property ${key} with PropertyWidget:`, - error - ); + bugReporterManagerInsatance.addToLogs( + 174, + `FALLBACK : Failed to render property ${key} with PropertyWidget: ${String(error)}`, + "FrontmatterRenderer.ts/renderProperties", + ); // Fallback to simple rendering this.renderPropertyValueSimple(valueEl, value); } @@ -323,10 +328,10 @@ export class FrontmatterRenderer { */ private renderPropertiesSimple( containerEl: HTMLElement, - frontmatter: Record + frontmatter: Record, ): void { const propertiesToRender = Object.entries(frontmatter).filter( - ([key]) => key !== "position" + ([key]) => key !== "position", ); for (const [key, value] of propertiesToRender) { @@ -336,7 +341,7 @@ export class FrontmatterRenderer { propertyRow.addEventListener("click", () => { new Notice( - "This frontmatter section is read-only. A fully-functional frontmatter editor is under development." + "This frontmatter section is read-only. A fully-functional frontmatter editor is under development.", ); }); @@ -360,7 +365,7 @@ export class FrontmatterRenderer { */ private renderPropertyValueSimple( containerEl: HTMLElement, - value: any + value: any, ): void { if (Array.isArray(value)) { const list = containerEl.createEl("ul", { diff --git a/src/services/OpenModals.ts b/src/services/OpenModals.ts index 50691620..9096b0ab 100644 --- a/src/services/OpenModals.ts +++ b/src/services/OpenModals.ts @@ -150,7 +150,11 @@ export const openAddNewTaskNoteModal = (app: App, plugin: TaskBoard) => { noteContent: string | undefined, ) => { if (!noteContent) { - // console.warn("This code should not run..."); + bugReporterManagerInsatance.addToLogs( + 178, + `This code will only run if the content passed is empty : ${noteContent}`, + "OpenModals.ts/openAddNewTaskNoteModal", + ); } else { // If noteContent is provided, it means user wants to save this task as a TaskNote. // Create the note content with frontmatter diff --git a/src/services/tasks-plugin/helpers.ts b/src/services/tasks-plugin/helpers.ts index 823eaa56..9311b5b7 100644 --- a/src/services/tasks-plugin/helpers.ts +++ b/src/services/tasks-plugin/helpers.ts @@ -31,12 +31,6 @@ export async function fetchTasksPluginCustomStatuses( ): Promise { try { const tasksPluginO = new TasksPluginApi(plugin); - console.log( - "Tasks Plugin API:", - tasksPluginO, - "\nIs tasks plugin enabled?", - tasksPluginO.isTasksPluginEnabled(), - ); // if( plugin.app.plugins.getPlugin("obsidian-tasks-plugin")) { if (tasksPluginO.isTasksPluginEnabled()) { plugin.settings.data.globalSettings.compatiblePlugins.tasksPlugin = true; diff --git a/src/utils/JsonFileOperations.ts b/src/utils/JsonFileOperations.ts index 07ef5da1..67793db7 100644 --- a/src/utils/JsonFileOperations.ts +++ b/src/utils/JsonFileOperations.ts @@ -185,8 +185,10 @@ const writeFileWithRetry = async ( // Wait with exponential backoff before retry const delay = baseDelay * Math.pow(2, attempt - 1); - console.warn( - `Task Board: File write timeout (attempt ${attempt}/${maxRetries}), retrying in ${delay}ms...`, + bugReporterManagerInsatance.addToLogs( + 177, + `File write timeout due to following error: ${String(error)}....retrying again...`, + "TaskItemEventHandlers.ts/writeFileWithRetry", ); await new Promise((resolve) => setTimeout(resolve, delay)); } @@ -262,12 +264,6 @@ export const moveTasksCacheFileToNewPath = ( } } - console.log( - "moveTasksCacheFileToNewPath...\nOld path :", - oldPath, - "\nNew path :", - newPath, - ); if (newPath === "") newPath = `${app.vault.configDir}/plugins/task-board/tasks.json`; if (oldPath === "") @@ -345,14 +341,11 @@ export const loadTasksAndMerge = async ( return allTasksMerged; } catch (error) { - // console.error("Failed to load tasks from tasks.json:", error); - // bugReporterManagerInsatance.showNotice( - // 74, - // "Failed to load tasks from tasks.json file. If this is your fresh install kindly run the scan vault using the top right corner button and open the board again. If the issue persists, please report it to the developer using steps mentioned below.", - // String(error), - // "JsonFileOperations.ts/loadTasksAndMerge" - // ); - console.log("Is this running.."); + bugReporterManagerInsatance.addToLogs( + 74, + String(error), + "JsonFileOperations.ts/loadTasksAndMerge", + ); throw error; } }; @@ -363,7 +356,6 @@ export const loadTasksAndMerge = async ( // return allTasksMerged; // Ensure it returns the merged tasks // }) // .catch((error) => { -// console.error("Error while loading tasks:", error); // // Return an empty taskJsonMerged object to avoid 'undefined' // return { Pending: [], Completed: [] }; // }); diff --git a/src/utils/MarkdownFileOperations.ts b/src/utils/MarkdownFileOperations.ts index bbec9594..5b47ce61 100644 --- a/src/utils/MarkdownFileOperations.ts +++ b/src/utils/MarkdownFileOperations.ts @@ -21,7 +21,6 @@ export const readDataOfVaultFile = async ( return fileData; // Return the raw content of the file } else { // new Notice(`${t("file-not-found-at-path")} ${filePath}`); - // console.error(`File not found at path: ${filePath}`); bugReporterManagerInsatance.showNotice( 75, "File not found in vault.", @@ -90,10 +89,8 @@ export const writeDataToVaultFile = async ( // await plugin.app.vault.process(file, () => newContent); // } else { // new Notice(`File not found at path: ${filePath}`); -// console.error(`File not found at path: ${filePath}`); // } // } catch (error) { -// console.error("Error writing to file in vault:", error); // throw error; // } // }; diff --git a/src/utils/UserTaskEvents.ts b/src/utils/UserTaskEvents.ts index d36deb40..5eec8e25 100644 --- a/src/utils/UserTaskEvents.ts +++ b/src/utils/UserTaskEvents.ts @@ -42,13 +42,13 @@ import { bugReporterManagerInsatance } from "src/managers/BugReporter"; export const handleEditTask = ( plugin: TaskBoard, task: taskItem, - settingOption: string + settingOption: string, ) => { const taskNoteIdentifierTag = plugin.settings.data.globalSettings.taskNoteIdentifierTag; const isThisATaskNote = isTaskNotePresentInTags( taskNoteIdentifierTag, - task.tags + task.tags, ); switch (settingOption) { case EditButtonMode.Modal: @@ -66,7 +66,7 @@ export const handleEditTask = ( true, task, task.filePath, - "window" + "window", ); break; case EditButtonMode.TasksPluginModal: @@ -93,7 +93,7 @@ export const handleEditTask = ( 85, "This should never happen, looks like you have not set the setting for Edit button mode or double click action correctly. Or the setting has been corrupted. Please try to change the setting first. If issue still persists, report it to the developer.", "NA", - "TaskItemEventHandlers.ts/handleEditTask" + "TaskItemEventHandlers.ts/handleEditTask", ); // markdownButtonHoverPreviewEvent(app, event, task.filePath); break; @@ -112,7 +112,7 @@ export const handleEditTask = ( export const openFileAndHighlightTask = async ( plugin: TaskBoard, task: taskItem, - mode: string + mode: string, ) => { const file = plugin.app.vault.getAbstractFileByPath(task.filePath); let leaf: WorkspaceLeaf | null = null; @@ -137,7 +137,7 @@ export const openFileAndHighlightTask = async ( 86, "This is a low priority error and it should never happen. Looks like you have not set the setting for Edit button mode or double click action correctly. Or the setting has been corrupted. Please try to change the setting first. If issue still persists, report it to the developer.", "NA", - "TaskItemEventHandlers.ts/handleEditTask" + "TaskItemEventHandlers.ts/handleEditTask", ); // markdownButtonHoverPreviewEvent(app, event, task.filePath); break; @@ -154,7 +154,7 @@ export const openFileAndHighlightTask = async ( `Trying to open the following file: ${task.filePath}.\nLeaf type: ${ leaf ? leaf.constructor.name : "undefined" }`, - "AddOrEditTaskModal.tsx/EditTaskContent/onOpenFilBtnClicked" + "AddOrEditTaskModal.tsx/EditTaskContent/onOpenFilBtnClicked", ); } @@ -199,7 +199,7 @@ export const openFileAndHighlightTask = async ( export const updateTaskItemStatus = ( plugin: TaskBoard, oldTask: taskItem, - newStatus: string + newStatus: string, ) => { let newTask = { ...oldTask }; newTask.status = newStatus; @@ -212,7 +212,7 @@ export const updateTaskItemStatus = ( const isThisTaskNote = isTaskNotePresentInTags( plugin.settings.data.globalSettings.taskNoteIdentifierTag, - oldTask.tags + oldTask.tags, ); if (isThisTaskNote) { updateFrontmatterInMarkdownFile(plugin, newTask).then(() => { @@ -221,25 +221,25 @@ export const updateTaskItemStatus = ( // This is required to rescan the updated file and refresh the board. plugin.realTimeScanner.processAllUpdatedFiles( oldTask.filePath, - oldTask.id + oldTask.id, ); }); }); } else { const newStatusType = plugin.settings.data.globalSettings.customStatuses.find( - (status) => status.symbol === newStatus + (status) => status.symbol === newStatus, )?.type ?? statusTypeNames.TODO; newTask.title = sanitizeStatus( plugin.settings.data.globalSettings, newTask.title, newStatus, - newStatusType + newStatusType, ); updateTaskInFile(plugin, newTask, oldTask).then((newId) => { plugin.realTimeScanner.processAllUpdatedFiles( oldTask.filePath, - oldTask.id + oldTask.id, ); }); } @@ -258,7 +258,7 @@ export const updateTaskItemStatus = ( export const updateTaskItemPriority = ( plugin: TaskBoard, oldTask: taskItem, - newPriority: number + newPriority: number, ) => { let newTask = { ...oldTask } as taskItem; newTask.priority = newPriority; @@ -271,7 +271,7 @@ export const updateTaskItemPriority = ( const isThisTaskNote = isTaskNotePresentInTags( plugin.settings.data.globalSettings.taskNoteIdentifierTag, - oldTask.tags + oldTask.tags, ); if (isThisTaskNote) { @@ -279,7 +279,7 @@ export const updateTaskItemPriority = ( sleep(1000).then(() => { plugin.realTimeScanner.processAllUpdatedFiles( oldTask.filePath, - oldTask.id + oldTask.id, ); }); }); @@ -287,12 +287,12 @@ export const updateTaskItemPriority = ( newTask.title = sanitizePriority( plugin.settings.data.globalSettings, newTask.title, - newPriority + newPriority, ); updateTaskInFile(plugin, newTask, oldTask).then(() => { plugin.realTimeScanner.processAllUpdatedFiles( oldTask.filePath, - oldTask.id + oldTask.id, ); }); } @@ -313,7 +313,7 @@ export const updateTaskItemDate = ( plugin: TaskBoard, oldTask: taskItem, dateType: "startDate" | "scheduledDate" | "due", - newDate: string + newDate: string, ): void => { let newTask = { ...oldTask } as taskItem; switch (dateType) { @@ -327,8 +327,10 @@ export const updateTaskItemDate = ( newTask.due = newDate; break; default: - console.log( - "error while updating the date value. Date type unknown." + bugReporterManagerInsatance.addToLogs( + 166, + "error while updating the date value. Date type unknown.", + `dateType = ${dateType}`, ); } @@ -336,7 +338,7 @@ export const updateTaskItemDate = ( const isThisTaskNote = isTaskNotePresentInTags( plugin.settings.data.globalSettings.taskNoteIdentifierTag, - oldTask.tags + oldTask.tags, ); if (isThisTaskNote) { @@ -344,7 +346,7 @@ export const updateTaskItemDate = ( sleep(1000).then(() => { plugin.realTimeScanner.processAllUpdatedFiles( oldTask.filePath, - oldTask.id + oldTask.id, ); }); }); @@ -354,33 +356,35 @@ export const updateTaskItemDate = ( newTask.title = sanitizeStartDate( plugin.settings.data.globalSettings, newTask.title, - newDate + newDate, ); break; case UniversalDateOptions.scheduledDate: newTask.title = sanitizeScheduledDate( plugin.settings.data.globalSettings, newTask.title, - newDate + newDate, ); break; case UniversalDateOptions.dueDate: newTask.title = sanitizeDueDate( plugin.settings.data.globalSettings, newTask.title, - newDate + newDate, ); break; default: - console.log( - "error while updating the date value. Date type unknown." + bugReporterManagerInsatance.addToLogs( + 167, + "error while updating the date value. Date type unknown.", + `dateType = ${dateType}`, ); } updateTaskInFile(plugin, newTask, oldTask).then(() => { plugin.realTimeScanner.processAllUpdatedFiles( oldTask.filePath, - oldTask.id + oldTask.id, ); }); } @@ -399,7 +403,7 @@ export const updateTaskItemDate = ( export const updateTaskItemReminder = ( plugin: TaskBoard, oldTask: taskItem, - newReminder: string + newReminder: string, ) => { const newTask = { ...oldTask } as taskItem; newTask.reminder = newReminder; @@ -408,7 +412,7 @@ export const updateTaskItemReminder = ( const isThisTaskNote = isTaskNotePresentInTags( plugin.settings.data.globalSettings.taskNoteIdentifierTag, - oldTask.tags + oldTask.tags, ); if (isThisTaskNote) { @@ -416,7 +420,7 @@ export const updateTaskItemReminder = ( sleep(1000).then(() => { plugin.realTimeScanner.processAllUpdatedFiles( oldTask.filePath, - oldTask.id + oldTask.id, ); }); }); @@ -424,7 +428,7 @@ export const updateTaskItemReminder = ( updateTaskInFile(plugin, newTask, oldTask).then(() => { plugin.realTimeScanner.processAllUpdatedFiles( oldTask.filePath, - oldTask.id + oldTask.id, ); }); } @@ -444,7 +448,7 @@ export const updateTaskItemTags = ( plugin: TaskBoard, oldTask: taskItem, newTask: taskItem, - newTags: string[] + newTags: string[], ) => { // let newTask = { ...oldTask }; newTask.tags = newTags; @@ -453,7 +457,7 @@ export const updateTaskItemTags = ( const isThisTaskNote = isTaskNotePresentInTags( plugin.settings.data.globalSettings.taskNoteIdentifierTag, - oldTask.tags + oldTask.tags, ); if (isThisTaskNote) { @@ -461,7 +465,7 @@ export const updateTaskItemTags = ( sleep(1000).then(() => { plugin.realTimeScanner.processAllUpdatedFiles( oldTask.filePath, - oldTask.id + oldTask.id, ); }); }); @@ -470,7 +474,7 @@ export const updateTaskItemTags = ( updateTaskInFile(plugin, newTask, oldTask).then(() => { plugin.realTimeScanner.processAllUpdatedFiles( oldTask.filePath, - oldTask.id + oldTask.id, ); }); } @@ -490,12 +494,12 @@ export const updateTaskItemProperty = async ( globalSettings: globalSettingsData, property: string, oldValue: string | number | string[], - newValue: string | number | string[] + newValue: string | number | string[], ): Promise => { const updatedTask: taskItem = { ...task }; const isThisTaskNote = isTaskNotePresentInTags( globalSettings.taskNoteIdentifierTag, - task.tags + task.tags, ); switch (property) { @@ -505,7 +509,7 @@ export const updateTaskItemProperty = async ( updatedTask.title = sanitizeTags( task.title, oldValue as string[], - newValue as string[] + newValue as string[], ); } break; @@ -518,7 +522,7 @@ export const updateTaskItemProperty = async ( updatedTask.title = sanitizePriority( globalSettings, task.title, - newValue as number + newValue as number, ); } break; diff --git a/src/utils/algorithms/ColumnSegregator.ts b/src/utils/algorithms/ColumnSegregator.ts index fbd308b3..91e195be 100644 --- a/src/utils/algorithms/ColumnSegregator.ts +++ b/src/utils/algorithms/ColumnSegregator.ts @@ -350,12 +350,7 @@ export const columnSegregator = ( // columnData.tasksIdManualOrder = newTasksIdManualOrder; currentBoardData.columns[columnData.index - 1].tasksIdManualOrder = newTasksIdManualOrder; - console.log( - "columnSegregator...\nNew manual order :", - newTasksIdManualOrder, - "\nOld manual order :", - columnData.tasksIdManualOrder - ); + if (onBoardDataChange && didTasksIdManualOrderChange) { onBoardDataChange(currentBoardData); } diff --git a/src/utils/lang/helper.ts b/src/utils/lang/helper.ts index 43ea468c..a74e9e4b 100644 --- a/src/utils/lang/helper.ts +++ b/src/utils/lang/helper.ts @@ -60,7 +60,6 @@ export const loadTranslationsOnStartup = async (plugin: TaskBoard) => { // Main translation function export function t(key: string): string { // if (!isI18nInitialized) { // INFO : Cannot use this method, since I dont have access to plugin instance to access the isI18nInitialized variable. - // console.warn("i18n not initialized, falling back to English"); // return en?.[key] || `Missing translation for "${key}"`; // } @@ -75,10 +74,6 @@ export function t(key: string): string { // transString === null || // transString === undefined // ) { - // console.warn( - // `Translation for "${key}" returned invalid value:`, - // transString - // ); // return en?.[key] || `Missing translation for "${key}"`; // } diff --git a/src/utils/taskLine/TaskContentFormatter.ts b/src/utils/taskLine/TaskContentFormatter.ts index f3f33d05..5ae56155 100644 --- a/src/utils/taskLine/TaskContentFormatter.ts +++ b/src/utils/taskLine/TaskContentFormatter.ts @@ -954,16 +954,6 @@ export const sanitizeTags = ( newTagsList: string[], cursorLocation?: cursorLocation, ): string => { - console.log( - "sanitizeTags...\ntitle: ", - title, - "\noldTagsList: ", - oldTagsList, - "\nnewTagsList: ", - newTagsList, - "\ncursorLocation: ", - cursorLocation, - ); // Remove and tags before processing let updatedTitle = title; const tempTitle = title.replace(/<(mark|font).*?>/g, ""); diff --git a/src/utils/taskLine/TaskItemEventHandlers.ts b/src/utils/taskLine/TaskItemEventHandlers.ts index 728bfd5d..8a1a4ea7 100644 --- a/src/utils/taskLine/TaskItemEventHandlers.ts +++ b/src/utils/taskLine/TaskItemEventHandlers.ts @@ -27,7 +27,6 @@ export const handleCheckboxChange = (plugin: TaskBoard, task: taskItem) => { // setTasks(updatedTasks); // This two lines were not required at all since, anyways the `writeDataToVaultFile` is running and sending and refresh emit signal. const tasksPlugin = new TasksPluginApi(plugin); - console.log("handleCheckboxChange...\nTask =", task); if (!isTaskRecurring(task.title)) { // Check if the task is completed const newStatus = checkboxStateSwitcher(plugin, task.status); @@ -139,23 +138,10 @@ export const handleCheckboxChange = (plugin: TaskBoard, task: taskItem) => { } else { bugReporterManagerInsatance.showNotice( 45, - "Tasks plugin is must for handling recurring tasks. Since the task you are trying to update is a recurring task and Task Board cannot handle recurring tasks as of now. Hence the plugin has not updated your content.", + "Task Board do not support recurring tasks yet. Tasks plugin is required to work with recurring inline-tasks. No changes has been made.", `Tasks plugin installed and enabled: ${tasksPlugin.isTasksPluginEnabled()}`, "TaskItemUtils.ts/useTasksPluginToUpdateInFile", ); - - // useTasksPluginToUpdateInFile(plugin, tasksPlugin, task) - // .then(() => { - // plugin.realTimeScanner.processAllUpdatedFiles( - // task.filePath - // ); - // }) - // .catch((error) => { - // console.error( - // "TaskItemEventHandlers.ts : Error updating recurring task in file", - // error - // ); - // }); } } }; diff --git a/src/utils/taskNote/FrontmatterOperations.ts b/src/utils/taskNote/FrontmatterOperations.ts index 9fac60a8..98efd37e 100644 --- a/src/utils/taskNote/FrontmatterOperations.ts +++ b/src/utils/taskNote/FrontmatterOperations.ts @@ -42,7 +42,11 @@ export function extractFrontmatterFromFile( // const frontmatter = yaml.load(yamlContent); // return frontmatter; // } catch (error) { - // console.warn("Failed to parse frontmatter:", error); + // bugReporterManagerInsatance.addToLogs( + // 175, + // `Failed to parse frontmatter: ${String(error)}`, + // "FrontmatterOperations.ts/extractFrontmatterFromFile", + // ); // return null; // } @@ -59,7 +63,11 @@ export function extractFrontmatterFromFile( return frontmatterAsObject; } catch (error) { - // console.warn("Failed to parse frontmatter:", error); + // bugReporterManagerInsatance.addToLogs( + // 176, + // `Failed to parse frontmatter: ${String(error)}`, + // "FrontmatterOperations.ts/extractFrontmatterFromFile", + // ); return undefined; } } diff --git a/src/utils/taskNote/TaskNoteEventHandlers.ts b/src/utils/taskNote/TaskNoteEventHandlers.ts index e6db227d..3aa99669 100644 --- a/src/utils/taskNote/TaskNoteEventHandlers.ts +++ b/src/utils/taskNote/TaskNoteEventHandlers.ts @@ -34,7 +34,6 @@ export const handleTaskNoteStatusChange = async ( globalSettings?.taskCompletionDateTimePattern, ); - console.log("handleTaskNoteStatusChange..\nnewStatus = ", newStatus); const updatedTask = { ...task, status: newStatus.newSymbol, diff --git a/src/utils/taskNote/TaskNoteUtils.ts b/src/utils/taskNote/TaskNoteUtils.ts index 02f261e2..6284cba8 100644 --- a/src/utils/taskNote/TaskNoteUtils.ts +++ b/src/utils/taskNote/TaskNoteUtils.ts @@ -353,12 +353,6 @@ export async function updateFrontmatterInMarkdownFile( task, forceId, ); - console.log( - "updateFrontmatterInMarkdownFile...\nUpdated frontmatter", - updated, - "\nold frontmatter", - existing, - ); for (const key of Object.keys(updated)) { existing[key] = updated[key]; } From 8f88910d8182d90b563954eb9480869271ac1c18 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Sun, 1 Feb 2026 20:20:43 +0530 Subject: [PATCH 063/236] update en.json with latest strings --- src/utils/lang/locale/en.json | 163 ++++++++++++++++++++++++++++------ 1 file changed, 135 insertions(+), 28 deletions(-) diff --git a/src/utils/lang/locale/en.json b/src/utils/lang/locale/en.json index 29ac8407..d8940f2f 100644 --- a/src/utils/lang/locale/en.json +++ b/src/utils/lang/locale/en.json @@ -85,13 +85,16 @@ "add-tag-color": "Add tag color", "tag-name": "Tag name", "automation": "Automations", - "real-time-scanning": "Real-time scanning", - "real-time-scanning-info": "After you loose focus from the file you have edited, the task will be immediately refreshed on the task boad.\nIf you disable this feature, you will be required to manually click on the refresh button to get your latest changes.", + "scanning-mode": "Scanning mode", + "scanning-mode-info": "Select the scanning mode you would like Task Board to use :", + "balanced": "Balanced", + "balanced-scanning-mode-info": "The modified files will be scanned once you have finished editing them and when you will be switching the tab or window.", + "manual": "Manual", + "manual-scanning-mode-info": "You will be required to click on the refresh button when you would like to see the latest content on the board. The refresh button will shown an indicator.", + "real-time": "Real-time", + "real-time-scanning-mode-info": "You will see live changes happening on the board whenever you will be making changes to your task content in the note, with a 1 second delay.", "auto-add-universal-date-to-tasks": "Auto add the selected universal date", "auto-add-universal-date-to-tasks-info": "When enabled, when you will create a new task using the 'add new task' modal, then today's date will be added as the universal date you have selected.", - "auto-scan-the-vault-on-obsidian-startup": "Auto scan the vault on Obsidian startup", - "auto-scan-the-vault-on-obsidian-startup-info": "Only use this feature if you edit the vault files outside Obsidian. Usually all your newly added/edited tasks from Obsidian will be detected automatically.", - "auto-scan-the-vault-on-obsidian-startup-info-2": "If your vault contains lot of files with huge data, this might affect the startup time of Obsidian.", "compatible-plugins": "Compatible plugins", "plugin-compatibility": "plugin compatibility", "day-planner-plugin-compatibility": "If you have installed 'day planner' plugin, enable this to see the time at the start of the task title, instead in the metadata. After enabling this feature, the time will be shown according to the day planner plugin inside Markdown files, but in the task card, the time will be shown in the footer as usual.", @@ -124,7 +127,7 @@ "task-board": "Task Board", "open-task-board": "Open task board", "open-task-board-in-new-window": "Open task board in new window", - "update-tasks-from-this-file": "Update tasks from this file", + "update-tasks-from-this-file": "Scan tasks from this file", "add-file-in-scan-filter": "Add file in scan filter", "add-folder-in-scan-filter": "Add folder in scan filter", "board-filters": "Board filters", @@ -173,8 +176,8 @@ "go-back": "Go back and save", "edit-task-modal-close-confirm-mssg": "You have unsaved changes. Do you want to save or discard them?", "task-status": "Task status", - "show-task-without-metadata": "Show task without metadata", - "show-task-without-metadata-info": "Enable this setting if you want to see the task without any metadata/properties on the task board. But if you prefer viewing the task with metadata, disable this setting along with disabling the above two settings, if you dont prefer to see the data twice.", + "show-task-without-metadata": "Clean task title for inline-tasks", + "show-task-without-metadata-info": "Enable this setting if you like to see the inline-task title without its metadata inside the task card. Since, the properties are already shown inside the header as seperate elements, it makes more sense to use this setting to get a cleaner card.", "dummy-task-title": "This is how your task will look like.", "loading-tasks": "Loading your tasks...", "fresh-install-1": "Looks like this is a fresh install.", @@ -186,10 +189,11 @@ "reminder-label": "Reminder", "tag-color-indicator-type": "Tag color indicator type", "tag-color-indicator-type-info": "Select how the tag color should be applied. Note that, if you have selected \"Text of the tag\" as the tag color indicator type, then the opacity of the color will be reduced to 0.2 for better visibility of the text.", - "text-of-the-tag": "Text of the tag", - "background-of-the-card": "Background of the card", + "text-of-the-tag": "Tag text", + "tag-background": "Tag background", + "background-of-the-card": "Card background", "liveEditor": "Live Editor", - "rawEditor": "Raw Editor", + "rawEditor": "Source mode", "child-tasks": "Child tasks", "child-tasks-section-description": "Connect the tasks which are blocking the current task.", "task-board-bug-reporter": "Task Board bug reporter", @@ -204,18 +208,20 @@ "enter-tag-placeholder": "enter number from 1-5", "delete-tag-color": "Delete tag color", "pick-color-for-tag": "Pick color for tag", - "show-note-name-in-task-header": "Show note name in task header", - "show-note-name-in-task-header-description": "Enable this setting to show the note name in the task header. Hover over the name to see the full path of the note.", "debug-info": "Debug info", "copied-to-clipboard": "Copied to clipboard", "default-note-for-adding-new-tasks": "Default note for adding new tasks", "default-note-for-new-tasks-description": "Select the default note in which all the newly added tasks through 'add new task modal' should be saved.", "universal-date": "Universal date", "universal-date-description": "Select what type of date you want task board to consider as your universal date for all the automation operations.", - "auto-add-created-date-to-tasks": "Auto add created date to tasks", - "auto-add-created-date-to-tasks-desc": "When enabled, todays date will be automatically added as the created date to the task.", + "auto-add-created-date": "Auto add created date", + "auto-add-created-date-desc": "When enabled, todays date will be automatically added as the created date to the task.", + "auto-add-completed-date": "Auto add completed date", + "auto-add-completed-date-desc": "Enable this feature to automatically add current date-time value as the completed date property to the task when you will mark the task as complete through checkbox or through status change to status of type 'TODO'", + "auto-add-cancelled-date": "Auto add cancelled date", + "auto-add-cancelled-date-desc": "Enable this feature to automatically add current date-time value as the cancelled date property to the task when you will mark the task as cancelled through checkbox or through status change to status of type 'CANCELLED'", "file-for-archived-tasks": "File for archived tasks", - "file-for-archived-tasks-description": "Select the note in which the archived tasks should be moved. Leave this setting empty to simply comment out the task in the parent file itself", + "file-for-archived-tasks-description": "Select the note in which the archived tasks should be moved. Leave this setting empty, if you want to simply comment out the task in the parent file itself when you will click on 'archive instead' button.", "default-quickadd-choice": "Default quickadd plugin choice", "default-quickadd-choice-description": "Select which of your choice from your quickadd plugin configuration should be used as the default choice in the 'add new task modal'. Please install the quickadd plugin first and create a choice in the quickadd plugin.", "quickadd-plugin-not-enabled": "Quickadd plugin is not enabled. Please install the plugin and enable it.", @@ -243,8 +249,8 @@ "update": "Update", "archive-instead": "Archive instead", "update-language-translations": "Update language translations", - "update-language-translations-info": "Click on the update button to download the latest language translations.", - "show-note-frontmatter-tags-in-the-card-header": "Show note frontmatter tags in the card header", + "update-language-translations-info": "If you have changed the inteface language in Obsidian settinsg, click on the update button to download the latest language translations for that language. You can locally update the translations in your own language and submit your changes to contribute to this plugin. Read more here : ", + "show-note-frontmatter-tags-in-the-card-header": "Inherit frontmatter tags for inline-tasks (read-only)", "show-note-frontmatter-tags-in-the-card-header-info": "Enable this feature to use the tags from the note frontmatter applied to your tasks. You cannot actually change this tags through task board. These frontmatter tags will be only used for filtering tasks.", "scan-tasks-from-the-vault-info-1": "Scan your whole vault at once.", "scan-tasks-from-the-vault-info-2": "Use filters for scanning from setting if you want to exclude certain files, folders or tasks to be scanned.", @@ -256,18 +262,18 @@ "export": "Export", "import-export-configurations": "Import/Export configurations", "import-export-configurations-info": "Use this feature to import or export your task board configurations. This is very useful for backing up your settings or transferring them to another device. Also in the case when you want to re-install the plugin.", - "hide-specific-properties-in-notes": "Hide specific properties in notes", - "hide-specific-properties-in-notes-description": "Select which task properties should be hidden in live-editor and in reading mode. These properties will be present in the notes, reveal them by hovering over the task or moving the cursor within the task line.", + "hide-specific-properties-in-notes": "Hide specific properties in inline tasks", + "hide-specific-properties-in-notes-description": "Select which task properties should be hidden in live-editor and in reading mode. These properties will be present in the inline task's content, but only kept hidden to reduce visual noise. Reveal them by hovering over the task or moving the cursor within the task line.", "auto-add-unique-id": "Auto add unique id", - "auto-add-unique-id-description": "When enabled, a unique id will be automatically added to the task line whenever you will add a new task or interact with an existing task. This setting is compulsory to enable, if you want to use the below features :", + "auto-add-unique-id-description": "When enabled, a unique id will be automatically added to the inline-task or task-note frontmatter whenever you will add a new task or interact with an existing task. This setting is vital for the below features to work properly or to enable them :", + "manual-sorting-inside-each-column-upcoming": "Manual sorting inside columns (upcoming)", "pin-tasks-on-top-in-each-column-upcoming": "Pin tasks on top in each column (upcoming)", - "manual-sorting-inside-each-column-upcoming": "Manual sorting inside each column (upcoming)", - "map-view-upcoming": "Map view (upcoming)", + "map-view-feature": "Map view feature", "open-in-map-view": "Open in map view", "enable-experimental-features-message": "This is an experimental feature released in this version. Please enable experimental feature from setting if you want to test it out.", "enable-experimental-features": "Enable experimental features", "enable-experimental-features-info-1": "Enable this setting to test out the experimental features present in this version.", - "enable-experimental-features-info-2": "Although these features have been completely developed, I need to make sure that they are working fine in different environments and setups. Hence, if you are willing to help in testing, enable this setting and test out these features and share whatever bugs you find or any suggestions you have in mind. After thorough testing, these features will be released for normal use in the next version. These experimental features includes :", + "enable-experimental-features-info-2": "Although these features are fully-functional, they can still produce issues in different environments and setups. Hence, if you are willing to help in testing, enable this setting and test out these features and share whatever bugs you find or any suggestions you have to improve them. After thorough testing, these features will be released for normal use in the upcoming versions.", "map-view": "Map view", "reading-mode-preview": "Reading mode preview", "source-mode-preview": "Source mode preview", @@ -355,19 +361,25 @@ "sub-tasks": "Sub tasks", "content": "Content", "body": "Body", + "description": "Description", "body-content": "Body content", "status": "Status", "tag": "Tag", "tags": "Tags", "priority": "Priority", + "time": "time", "start-time": "Start time", "end-time": "End time", "created-date": "Created date", + "start": "Start", "start-date": "Start date", + "scheduled": "Scheduled", "scheduled-date": "Scheduled date", + "due": "Due", "due-date": "Due date", "completed": "Completed", "completed-date": "Completed date", + "cancelled": "Cancelled", "recurrence": "Recurrence", "file-path": "File path", "column-filters-for": "Column filters for", @@ -390,15 +402,15 @@ "board-description-info": "Enter a description for this board. (optional)", "live-editor-and-reading-mode": "Live editor and reading mode", "task-note": "Task note", - "task-note-description": "Task note can be any simple note which you can show as a task card on the task board for reminders, etc.", + "task-note-description": "a task note can be any simple note which you can show as a task card on the task board for reminders, etc.", "tbnote": "TB note", - "tbnote-description": "A TB Notes is essentially a task note only, but it becomes a TB note when you enable activity tracking for the task. This is under development, read more here : ", + "tbnote-description": "A TB note is essentially a task note only, but it becomes a TB note when you enable activity tracking for the task. This is under development, read more here : ", "task-note-vs-tbnote": "Task note vs TB note", "tbnote-development-ticker": "TB note development ticket", "folder-for-archived-task-notes": "Folder for archived task notes", "folder-for-archived-task-notes-description": "Enter the folder path where all your archived task note or tb notes should be moved.", "frontmatter-formatting": "Frontmatter formatting", - "frontmatter-formatting-description": "The default properties which you see below are the supported properties for a task. You are free to ", + "frontmatter-formatting-description": "Below is the mapping of the task properties supported by this plugin and the frontmatter key name for the corresponding property. This is useful, if you already have task-notes created by other plugin or normal notes with existing properties, which you want task board to pick up as tasks properties. Some of this properties like 'task location' will be available after the 'tb notes' feature has been implemented.", "add-sticky-note": "Add sticky note", "under-development-feature-message": "This feature is under development and will be released in the next version.", "background-style": "Background style", @@ -471,5 +483,100 @@ "tasks-on-this-board-have-no-id-message-2": "When you will import tasks, an ID property will be added to the task content in the note.", "column-delete-confirmation-message": "Are you sure you want to delete the following column : ", "enter-task-content-to-search": "Enter keyword and hit enter to search.", - "scroll-to-load-more": "Scroll to load more" + "scroll-to-load-more": "Scroll to load more", + "configure-kanban-swimlanes": "Configure kanban swimlanes", + "configure-kanban-swimlanes-info": "Configure kanban swimlanes for this board.", + "configure": "Configure", + "enable-swimlanes": "Enable swimlanes", + "enable-swimlanes-info-1": "Swimlanes divides your kanban board into horizontal lanes. This feature of kanban methodology helps you to group related tasks based on their various properties.", + "enable-swimlanes-info-2": "This plugin has implemented the swimlanes feature with one of its own kind functionality, to have custom sorting and aggregation of tasks.", + "task-property": "Task property", + "task-property-info": "Select the task property to group the tasks based on the same value.", + "custom-property-key": "Custom property key", + "custom-property-key-info": "Enter your custom property name which you are using in the frontmatter in case of task-notes and dataview property in case of inline-tasks.", + "max-swimlane-height": "Max swimlane height", + "max-swimlane-height-info": "Set a maximum height for the swimlane to avoid consuming a lot of space. Smaller height helps optimized rendering. Enter the value along with units. For example \"300px\" or \"20vh\", etc.", + "swimlane-sort-order": "Swimlane sort order", + "swimlane-sort-order-info": "Select the order in which the swimlanes should be sorted. Ascending and descending are the common options. But custom sorting will help you with more flexibility. This new sorting option has been specifically designed to adopt \"Story Map\" methodology in this plugin.", + "manual-sorting-mapping": "Manual sorting mapping", + "manual-sorting-mapping-info": "Enter the values for the property you have selected and arrange their order to have a custom sorting.", + "enter-property-value": "Enter property value", + "add-row": "Add row", + "hide-empty-swimlanes": "Hide empty swimlanes", + "hide-empty-swimlanes-info": "Enable this setting to automatically hide the particular swimlane, if it doesnt contain any tasks.", + "aggregator-swimlane": "Aggregator swimlane", + "aggregator-swimlane-info": "Enabling this feature will help you to have a single swimlane at the bottom, which will group all the rest of the swimlanes which are not valid in your configured custom swimlanes.", + "custom-sorting": "Custom sorting", + "custom-property": "Custom property", + "manual-order": "Manual order", + "manual-order-notice": "The manual sorting will only work as expected if all the tasks within the column or any new added task in the column have unique id on them. This feature might look like it is working for tasks with no id, but eventually the data wont be persisted. Hence unique is must for this feature to work properly.", + "cannot-add-more-sorting-criteria-with-manual-order": "Cannot add more sorting criteria with manual order", + "drag-and-drop": "Drag and drop tasks", + "drag-and-drop-feature-info": "This feature will help you to move tasks between columns.", + "kanban-swimlanes": "Kanban swimlanes", + "kanban-swimlanes-feature-info": "This feature will help you to group tasks based on their various properties. Additionally, this plugin has its first of kind implementation of swimlanes, to have custom sorting and aggregation of tasks.", + "manual-sorting": "Manual sorting", + "manual-sorting-feature-info": "This feature will help you to drag and re-order the tasks within a column.", + "status-name": "Status name", + "status-symbol": "Status symbol", + "status-action": "Status action", + "add-new-status": "Add new status", + "import-from-tasks-plugin": "Import from tasks plugin", + "custom-statuses": "Custom statuses", + "custom-statuses-info": "Add custom statuses, their mapped symbol and name to work for both inline-tasks and task-notes. Also, configure the next symbol it should cycle to.", + "refreshing": "Refreshing...", + "show-hide-properties": "Show/Hide properties", + "visible": "Visible", + "minimized": "Minimized", + "checkbox": "Checkbox", + "task-card-style": "Task card style", + "task-card-style-info": "Select the UI of the task card. With the 'bases card style' option you may also use an additional CSS snippet to get more dataview properties kind of look.", + "tasks-plugin-emoji": "Tasks plugin emoji", + "bases-cards-style": "Bases card style", + "work-limit": "Work limit", + "work-limit-info": "Enter the number of maximum tasks you should work parallely in this column. This is an optional property and its just for reminder.", + "work-limit-alert": "Click me!", + "show-me": "Show me", + "scan-them": "Scan them", + "show-modified-files-message-on-startup": "Show modified files message on Obsidian startup", + "show-modified-files-message-on-startup-info": "If any of the vault files has been modified when Obsidian was inactive. These modified files will be auto-detected at the application start time, after a 10 second delay. This message will provide you to provide the statistics of which files has been modified. If you disable this setting, the modified files will be auto-scanned without needing your attention.", + "properties": "Properties", + "task-notes": "Task notes", + "inline-tasks": "Inline tasks", + "transfer": "Transfer", + "reset": "Reset", + "copy-task-title": "Copy task title", + "open-note": "Open note", + "note-actions": "Note actions", + "dates": "Dates", + "today": "Today", + "tomorrow": "Tomorrow", + "in-2-days": "In 2 days", + "in-3-days": "In 3 days", + "in-1-week": "In 1 week", + "in-2-weeks": "In 2 weeks", + "in-1-month": "In 1 month", + "in-2-months": "In 2 months", + "in-6-months": "In 6 months", + "in-1-year": "In 1 year", + "clear-date": "Clear date", + "rename-note": "Rename note", + "delete-note": "Delete note", + "open-note-to-right": "Open note to right", + "copy-task-title-successful": "Successfully copied the task title.", + "copy-task-title-unsuccessful": "There was an error while copying the task title.", + "task-status-symbol": "Task status symbol", + "task-status-symbol-info": "This is the character between the square braces in case of inline-tasks. Also, this is used to store in the case.", + "task-status-name": "Task status name", + "task-status-name-info": "Map a unique name to the above status symbol. This name will be used in the task-note frontmatter.", + "task-status-type": "Task status type", + "task-status-type-info": "Select what kind of status is this. If this status if of type DONE or CANCELLED, then the task will appear inside the 'completed' type column.", + "cycle-to-following-status": "Cycle to the following status", + "cycle-to-following-status-info": "Enter the status symbol, eg. /. Once you click on the above status, cycle to this status. Also, dont forget to create a new entry for this status type.", + "export-logs": "Export logs", + "export-logs-info": "This plugin stores all the warnings and errors in a log file inside the plugin folder. You may click on the export button to export it wherever you want in a txt file so you can debug the issue on your own or submit it to the developer if they have asked you for the logs. Before submitting the log file, please ensure that no vault content has been captured inside the logs. Also it will be advisable to submit the logs on email instead of on GitHub.", + "configure-status-entry": "Configure status entry", + "edit": "Edit", + "no-custom-statuses-configured": "No custom statuses configured", + "task-note-title-placeholder": "Enter a simple title for the task..." } From 590c298e67258cb58744e568d6977cde7c907513 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Sun, 1 Feb 2026 20:21:44 +0530 Subject: [PATCH 064/236] test latest changes | working --- data.json | 53 +++++++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/data.json b/data.json index 6514f23b..1aa6772a 100644 --- a/data.json +++ b/data.json @@ -964,17 +964,24 @@ "customStatuses": [ { "symbol": " ", - "name": "Incomplete", + "name": "Todo", "nextStatusSymbol": "x", "availableAsCommand": false, "type": "TODO" }, { - "symbol": "X", - "name": "Complete", - "nextStatusSymbol": " ", + "symbol": "<", + "name": "Ready to start", + "nextStatusSymbol": "x", "availableAsCommand": false, - "type": "DONE" + "type": "TODO" + }, + { + "symbol": "?", + "name": "In Review", + "nextStatusSymbol": "x", + "availableAsCommand": false, + "type": "TODO" }, { "symbol": "/", @@ -984,32 +991,25 @@ "type": "IN_PROGRESS" }, { - "symbol": "-", - "name": "Cancelled", + "symbol": "x", + "name": "Done", "nextStatusSymbol": " ", "availableAsCommand": true, - "type": "CANCELLED" + "type": "DONE" }, { - "symbol": ">", - "name": "Deferred", - "nextStatusSymbol": "x", - "availableAsCommand": false, - "type": "TODO" + "symbol": "X", + "name": "Completed", + "nextStatusSymbol": " ", + "availableAsCommand": true, + "type": "DONE" }, { - "symbol": "!", - "name": "Important", + "symbol": "-", + "name": "Cancelled", "nextStatusSymbol": "x", - "availableAsCommand": false, - "type": "TODO" - }, - { - "symbol": "x", - "name": "Finished", - "nextStatusSymbol": " ", - "availableAsCommand": false, - "type": "DONE" + "availableAsCommand": true, + "type": "CANCELLED" } ], "showTaskWithoutMetadata": false, @@ -1045,7 +1045,7 @@ "taskPropertyFormat": "3", "taskNoteDefaultLocation": "", "autoAddUniqueID": true, - "uniqueIdCounter": 594, + "uniqueIdCounter": 593, "experimentalFeatures": true, "searchQuery": "", "lastViewHistory": { @@ -1183,7 +1183,8 @@ "taskCardStyle": "emoji", "autoAddCompletedDate": true, "autoAddCancelledDate": true, - "scanMode": "manual" + "scanMode": "manual", + "showModifiedFilesNotice": true } } } \ No newline at end of file From 281943050e9deb79f5c5c5e69d97b2706b28cf85 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Sun, 1 Feb 2026 22:46:16 +0530 Subject: [PATCH 065/236] release version 1.9.0 --- manifest.json | 2 +- package-lock.json | 4 ++-- package.json | 2 +- src/interfaces/Constants.ts | 2 +- versions.json | 3 ++- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/manifest.json b/manifest.json index 02757492..1057fb1f 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "task-board", "name": "Task Board", - "version": "1.8.7", + "version": "1.9.0", "minAppVersion": "1.4.13", "description": "Manage all your tasks throughout your vault from a single board and much more...", "author": "Atmanand Gauns", diff --git a/package-lock.json b/package-lock.json index 3f49bff0..7a32f152 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "task-board", - "version": "1.8.7", + "version": "1.9.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "task-board", - "version": "1.8.7", + "version": "1.9.0", "license": "GPL-3.0", "dependencies": { "@popperjs/core": "^2.11.8", diff --git a/package.json b/package.json index 4e05ed16..381346d7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "task-board", - "version": "1.8.7", + "version": "1.9.0", "description": "An Obsidian plugin to manage small to large projects using tasks from the whole vault on a centralized board using various kinds of views like Kanban, map, list, etc.", "main": "main.js", "type": "module", diff --git a/src/interfaces/Constants.ts b/src/interfaces/Constants.ts index beb93aab..3529cbd4 100644 --- a/src/interfaces/Constants.ts +++ b/src/interfaces/Constants.ts @@ -1,4 +1,4 @@ -export const newReleaseVersion = "1.8.7"; +export const newReleaseVersion = "1.9.0"; // Plugin view type identifiers export const VIEW_TYPE_TASKBOARD = "task-board-view"; export const VIEW_TYPE_ADD_OR_EDIT_TASK = "add-or-edit-task-view"; diff --git a/versions.json b/versions.json index 75cdbe70..db0a4043 100644 --- a/versions.json +++ b/versions.json @@ -42,5 +42,6 @@ "1.8.4": "1.4.13", "1.8.5": "1.4.13", "1.8.6": "1.4.13", - "1.8.7": "1.4.13" + "1.8.7": "1.4.13", + "1.9.0": "1.4.13" } From dfd4977b464d1a9097593916c050814d5e07f57f Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Thu, 5 Feb 2026 22:55:05 +0530 Subject: [PATCH 066/236] standarized unique id generator functions --- .../BoardFilters/FilterConfigModal.ts | 5 ++-- src/components/BoardFilters/ViewTaskFilter.ts | 3 +- src/components/KanbanView/KanbanBoardView.tsx | 4 +-- src/modals/AddColumnModal.ts | 28 ++++++++++++++----- src/modals/ConfigureColumnSortingModal.ts | 15 +++++----- src/settings/SettingSynchronizer.ts | 6 ++-- src/utils/TaskItemUtils.ts | 26 +++++++++++++---- 7 files changed, 58 insertions(+), 29 deletions(-) diff --git a/src/components/BoardFilters/FilterConfigModal.ts b/src/components/BoardFilters/FilterConfigModal.ts index 608ef4e5..803dca8c 100644 --- a/src/components/BoardFilters/FilterConfigModal.ts +++ b/src/components/BoardFilters/FilterConfigModal.ts @@ -7,6 +7,7 @@ import { SavedFilterConfig, } from "src/interfaces/BoardConfigs"; import { bugReporterManagerInsatance } from "src/managers/BugReporter"; +import { generateRandomTempTaskId } from "src/utils/TaskItemUtils"; export class FilterConfigModal extends Modal { private plugin: TaskBoard; @@ -247,9 +248,7 @@ export class FilterConfigModal extends Modal { const now = new Date().toISOString(); const config: SavedFilterConfig = { - id: `filter-config-${Date.now()}-${Math.random() - .toString(36) - .substr(2, 9)}`, + id: `filter-config-${Date.now()}-${generateRandomTempTaskId()}`, name: name.trim(), description: description.trim() || undefined, filterState: JSON.parse(JSON.stringify(this.currentFilterState)), diff --git a/src/components/BoardFilters/ViewTaskFilter.ts b/src/components/BoardFilters/ViewTaskFilter.ts index dc1afc63..29b8bc8b 100644 --- a/src/components/BoardFilters/ViewTaskFilter.ts +++ b/src/components/BoardFilters/ViewTaskFilter.ts @@ -32,6 +32,7 @@ import { } from "src/interfaces/Mapping"; import { PluginDataJson } from "src/interfaces/GlobalSettings"; import { bugReporterManagerInsatance } from "src/managers/BugReporter"; +import { generateRandomTempTaskId } from "src/utils/TaskItemUtils"; export class TaskFilterComponent extends Component { private hostEl: HTMLElement; @@ -1556,5 +1557,5 @@ export class TaskFilterComponent extends Component { } export function generateIdForFilters(): string { - return `id-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`; + return `id-${Date.now()}-${generateRandomTempTaskId()}`; } diff --git a/src/components/KanbanView/KanbanBoardView.tsx b/src/components/KanbanView/KanbanBoardView.tsx index da382f3c..bbd71fc3 100644 --- a/src/components/KanbanView/KanbanBoardView.tsx +++ b/src/components/KanbanView/KanbanBoardView.tsx @@ -1,6 +1,6 @@ // src/components/KanbanBoard.tsx -import { Board } from "../../interfaces/BoardConfigs"; +import { Board, ColumnData } from "../../interfaces/BoardConfigs"; import React, { memo } from "react"; import { taskItem, taskJsonMerged } from "src/interfaces/TaskItem"; @@ -80,7 +80,7 @@ const MemoizedColumn = memo<{ plugin: TaskBoard; columnIndex: number; activeBoardData: Board; - columnData: any; + columnData: ColumnData; tasksForThisColumn: taskItem[]; Component: typeof LazyColumn; }>(({ Component, ...props }) => { diff --git a/src/modals/AddColumnModal.ts b/src/modals/AddColumnModal.ts index 2bf9f702..71b318c1 100644 --- a/src/modals/AddColumnModal.ts +++ b/src/modals/AddColumnModal.ts @@ -156,7 +156,9 @@ export class AddColumnModal extends Modal { this.colType === colTypeNames.undated ) { this.onSubmit({ - id: crypto.getRandomValues(new Uint32Array(1))[0], // Generate a random ID + id: generateRandomNumber(), + index: 9999, + active: true, colType: this.colType, name: this.name, datedBasedColumn: { @@ -167,42 +169,54 @@ export class AddColumnModal extends Modal { }); // Add range data } else if (this.colType === colTypeNames.namedTag) { this.onSubmit({ - id: crypto.getRandomValues(new Uint32Array(1))[0], + id: generateRandomNumber(), + index: 9999, + active: true, colType: this.colType, name: this.name, coltag: "", }); } else if (this.colType === colTypeNames.taskStatus) { this.onSubmit({ - id: crypto.getRandomValues(new Uint32Array(1))[0], + id: generateRandomNumber(), + index: 9999, + active: true, colType: this.colType, name: this.name, taskStatus: "", }); } else if (this.colType === colTypeNames.taskPriority) { this.onSubmit({ - id: crypto.getRandomValues(new Uint32Array(1))[0], + id: generateRandomNumber(), + index: 9999, + active: true, colType: this.colType, name: this.name, taskPriority: 1, }); } else if (this.colType === "completed") { this.onSubmit({ - id: crypto.getRandomValues(new Uint32Array(1))[0], + id: generateRandomNumber(), + index: 9999, + active: true, colType: this.colType, name: this.name, limit: 20, }); // Add limit } else if (this.colType === colTypeNames.pathFiltered) { this.onSubmit({ - id: crypto.getRandomValues(new Uint32Array(1))[0], + id: generateRandomNumber(), + index: 9999, + active: true, colType: this.colType, name: this.name, filePaths: "", }); // Add path filter } else { this.onSubmit({ - id: crypto.getRandomValues(new Uint32Array(1))[0], + id: generateRandomNumber(), + index: 9999, + active: true, colType: this.colType, name: this.name, }); diff --git a/src/modals/ConfigureColumnSortingModal.ts b/src/modals/ConfigureColumnSortingModal.ts index 58157670..ef99cdce 100644 --- a/src/modals/ConfigureColumnSortingModal.ts +++ b/src/modals/ConfigureColumnSortingModal.ts @@ -6,6 +6,7 @@ import Sortable from "sortablejs"; import { ColumnData, columnSortingCriteria } from "src/interfaces/BoardConfigs"; import { t } from "src/utils/lang/helper"; import { ClosePopupConfrimationModal } from "./ClosePopupConfrimationModal"; +import { generateRandomTempTaskId } from "src/utils/TaskItemUtils"; export class ConfigureColumnSortingModal extends Modal { plugin: TaskBoard; @@ -40,12 +41,12 @@ export class ConfigureColumnSortingModal extends Modal { this.columnConfiguration.sortCriteria = []; } else { this.columnConfiguration.sortCriteria = - this.columnConfiguration.sortCriteria.map((c) => ({ - ...c, - uid: - (c as any).uid || - Math.random().toString(36).slice(2, 10), - })); + this.columnConfiguration.sortCriteria.map( + (criteria: columnSortingCriteria) => ({ + ...criteria, + uid: criteria.uid || generateRandomTempTaskId(), + }), + ); } } @@ -291,7 +292,7 @@ export class ConfigureColumnSortingModal extends Modal { priority: (this.columnConfiguration.sortCriteria?.length || 0) + 1, - uid: Math.random().toString(36).slice(2, 10), + uid: generateRandomTempTaskId(), }; if (!this.columnConfiguration.sortCriteria) { this.columnConfiguration.sortCriteria = []; diff --git a/src/settings/SettingSynchronizer.ts b/src/settings/SettingSynchronizer.ts index ac12dabe..4da64618 100644 --- a/src/settings/SettingSynchronizer.ts +++ b/src/settings/SettingSynchronizer.ts @@ -48,9 +48,9 @@ export function migrateSettings(defaults: any, settings: any): PluginDataJson { else if (key === "boardConfigs" && Array.isArray(settings[key])) { settings[key].forEach((boardConfig: Board) => { boardConfig.columns.forEach((column: ColumnData) => { - if (!column.id) { - column.id = Math.floor(Math.random() * 1000000); - } + // Older IDs were smaller number. Will change them to 10 digit numbers. + column.id = generateRandomNumber(); + if ( column.colType === colTypeNames.dated || (column.colType === colTypeNames.undated && diff --git a/src/utils/TaskItemUtils.ts b/src/utils/TaskItemUtils.ts index d7261661..9651c160 100644 --- a/src/utils/TaskItemUtils.ts +++ b/src/utils/TaskItemUtils.ts @@ -27,7 +27,7 @@ export const getAllTaskTags = (task: taskItem): string[] => { */ export const getTaskFromId = async ( plugin: TaskBoard, - id: string | number + id: string | number, ): Promise => { try { let foundTask: taskItem | undefined | null; @@ -37,7 +37,7 @@ export const getTaskFromId = async ( for (const tasks of Object.values(pendingTasksObj)) { if (id) { foundTask = tasks.find( - (task) => task.legacyId === id || task.id === id + (task) => task.legacyId === id || task.id === id, ); } if (foundTask) return foundTask; @@ -49,7 +49,7 @@ export const getTaskFromId = async ( for (const tasks of Object.values(completedTasksObj)) { if (id) { foundTask = tasks.find( - (task) => task.legacyId === id || task.id === id + (task) => task.legacyId === id || task.id === id, ); } if (foundTask) return foundTask; @@ -61,7 +61,7 @@ export const getTaskFromId = async ( 82, "Error retrieving task from tasksCache using ID", String(error), - "TaskItemUtils.ts/getTaskFromId" + "TaskItemUtils.ts/getTaskFromId", ); return null; } @@ -76,6 +76,8 @@ export const getTaskFromId = async ( /** * Generates a random unique ID using the Web Crypto API. + * + * For example : '1851955511'. * @return {string} a random unique ID for a task */ export function generateRandomTempTaskId(): string { @@ -84,6 +86,18 @@ export function generateRandomTempTaskId(): string { return String(array[0]); } +/** + * Generates a random unique ID using the Web Crypto API. + * + * For example : 1851955511. + * @return a random unique 10 digit number + */ +export function generateRandomNumber(): number { + const array = new Uint32Array(1); + crypto.getRandomValues(array); + return array[0]; +} + /** * Generates a unique ID for a task based on the plugin's settings. * It increments the plugin's settings data globalSettings.uniqueIdCounter by 1 and then saves the updated settings. @@ -112,12 +126,12 @@ export function generateTaskId(plugin: TaskBoard): string { */ export const applyIdToTaskItem = async ( plugin: TaskBoard, - task: taskItem + task: taskItem, ): Promise => { if ( isTaskNotePresentInTags( plugin.settings.data.globalSettings.taskNoteIdentifierTag, - task.tags + task.tags, ) ) { let newId; From 21b0f44b92129b15d25e31d94d491322130748cf Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Thu, 5 Feb 2026 23:07:01 +0530 Subject: [PATCH 067/236] fix : the sorting criteria is not saving and also not able to delete | #652 --- .../KanbanView/KanbanSwimlanesContainer.tsx | 13 +- src/components/KanbanView/LazyColumn.tsx | 10 +- src/modals/ConfigureColumnSortingModal.ts | 121 ++++++++++++------ src/settings/SettingSynchronizer.ts | 18 ++- src/utils/algorithms/ColumnSegregator.ts | 2 +- 5 files changed, 114 insertions(+), 50 deletions(-) diff --git a/src/components/KanbanView/KanbanSwimlanesContainer.tsx b/src/components/KanbanView/KanbanSwimlanesContainer.tsx index f25776a5..39f28f8e 100644 --- a/src/components/KanbanView/KanbanSwimlanesContainer.tsx +++ b/src/components/KanbanView/KanbanSwimlanesContainer.tsx @@ -42,14 +42,21 @@ const KanbanSwimlanesContainer: React.FC = ({ minimized } = board.swimlanes; + const activeColumns = board.columns + .filter((col) => col.active); + // .map((col) => ({ + // // create a shallow copy so we don't mutate original board state + // ...col, + // sortCriteria: col.sortCriteria || [], + // tasksIdManualOrder: Array.isArray(col.tasksIdManualOrder) ? col.tasksIdManualOrder : [], + // })); + const swimlanes: SwimlaneRow[] = useMemo(() => { if (!board.swimlanes?.enabled || !tasksPerColumn) { return []; } - // Get all active columns - const activeColumns = board.columns.filter((col) => col.active); if (activeColumns.length === 0) return []; // Extract unique values for the swimlane property from tasksPerColumn @@ -220,8 +227,6 @@ const KanbanSwimlanesContainer: React.FC = ({ // ); // } - const activeColumns = board.columns.filter((col) => col.active); - async function handleSwimlaneMinimize(rowIndex: number) { try { const swimlaneName = swimlanes[rowIndex]?.swimlaneName; diff --git a/src/components/KanbanView/LazyColumn.tsx b/src/components/KanbanView/LazyColumn.tsx index dd843a47..4ff3de9a 100644 --- a/src/components/KanbanView/LazyColumn.tsx +++ b/src/components/KanbanView/LazyColumn.tsx @@ -47,6 +47,7 @@ const LazyColumn: React.FC = ({ hideColumnHeader = false, headerOnly = false, }) => { + // console.log("Column Data :", columnData); if (!headerOnly && activeBoardData?.hideEmptyColumns && (tasksForThisColumn === undefined || tasksForThisColumn?.length === 0)) { return null; // Don't render the column if it has no tasks and empty columns are hidden } @@ -283,11 +284,16 @@ const LazyColumn: React.FC = ({ if (columnIndex !== -1) { // Update the column configuration plugin.settings.data.boardConfigs[boardIndex].columns[columnIndex] = updatedColumnConfiguration; + const newSettings = plugin.settings; // Save the settings - plugin.saveSettings(); + plugin.saveSettings(newSettings).then(() => { + setTimeout(() => { + console.log("Refreshing now.."); + eventEmitter.emit('REFRESH_BOARD'); + }, 200) + }) - eventEmitter.emit('REFRESH_BOARD'); } } }, diff --git a/src/modals/ConfigureColumnSortingModal.ts b/src/modals/ConfigureColumnSortingModal.ts index ef99cdce..6cc066f7 100644 --- a/src/modals/ConfigureColumnSortingModal.ts +++ b/src/modals/ConfigureColumnSortingModal.ts @@ -7,6 +7,7 @@ import { ColumnData, columnSortingCriteria } from "src/interfaces/BoardConfigs"; import { t } from "src/utils/lang/helper"; import { ClosePopupConfrimationModal } from "./ClosePopupConfrimationModal"; import { generateRandomTempTaskId } from "src/utils/TaskItemUtils"; +import { bugReporterManagerInsatance } from "src/managers/BugReporter"; export class ConfigureColumnSortingModal extends Modal { plugin: TaskBoard; @@ -19,20 +20,23 @@ export class ConfigureColumnSortingModal extends Modal { plugin: TaskBoard, columnConfiguration: ColumnData, onSave: (updatedColumnConfiguration: ColumnData) => void, - onCancel: () => void + onCancel: () => void, ) { super(plugin.app); this.plugin = plugin; this.isEdited = false; - // Deep-copy columnConfiguration to avoid mutating caller's object (avoid stale/unsaved changes) - try { - this.columnConfiguration = JSON.parse( - JSON.stringify(columnConfiguration) - ); - } catch (e) { - // Fallback to shallow copy if stringify fails - this.columnConfiguration = { ...columnConfiguration }; - } + // // Deep-copy columnConfiguration to avoid mutating caller's object (avoid stale/unsaved changes) + // try { + // this.columnConfiguration = JSON.parse( + // JSON.stringify(columnConfiguration), + // ); + // } catch (e) { + // // Fallback to shallow copy if stringify fails + // this.columnConfiguration = { ...columnConfiguration }; + // } + + this.columnConfiguration = columnConfiguration; + this.onSave = onSave; this.onCancel = onCancel; @@ -54,12 +58,12 @@ export class ConfigureColumnSortingModal extends Modal { const { contentEl } = this; this.setTitle( - t("sorting-criterias-for") + " " + this.columnConfiguration.name + t("sorting-criterias-for") + " " + this.columnConfiguration.name, ); this.modalEl.setAttribute( "data-type", - "task-board-column-sorting-configure" + "task-board-column-sorting-configure", ); const homeComponent = contentEl.createEl("span", { @@ -92,7 +96,7 @@ export class ConfigureColumnSortingModal extends Modal { onSort: async () => { // Use stable uid attributes on DOM rows to determine new order const uidsInDom = Array.from(sortingCriteriaList.children).map( - (child) => child.getAttribute("data-uid") + (child) => child.getAttribute("data-uid"), ); const existing = this.columnConfiguration.sortCriteria || []; const newOrder: ColumnData["sortCriteria"] = []; @@ -129,15 +133,15 @@ export class ConfigureColumnSortingModal extends Modal { new Setting(row) .setClass( - "configureColumnSortingModalHomeSortingCriteriaListItem" + "configureColumnSortingModalHomeSortingCriteriaListItem", ) .addButton((drag) => drag .setTooltip("Hold and drag") .setIcon("grip-horizontal") .setClass( - "configureColumnSortingModalHomeSortingCriteriaListItemDragHandle" - ) + "configureColumnSortingModalHomeSortingCriteriaListItemDragHandle", + ), ) .addDropdown((dropdown) => { if ( @@ -146,7 +150,7 @@ export class ConfigureColumnSortingModal extends Modal { ) { dropdown.addOption( "manualOrder", - t("manual-order") + t("manual-order"), ); } @@ -171,18 +175,39 @@ export class ConfigureColumnSortingModal extends Modal { this.isEdited = true; if (this.columnConfiguration.sortCriteria) { if (value === "manualOrder") { - renderSortingCriterias(); // Re-render if manualOrder is selected // Remove all the other sort criteria this.columnConfiguration.sortCriteria = []; + + const newCriteria: columnSortingCriteria = + { + criteria: "manualOrder", + order: "asc", + priority: 1, + uid: generateRandomTempTaskId(), + }; // Add manualOrder sort criteria - this.columnConfiguration.sortCriteria[0].criteria = - value as columnSortingCriteria["criteria"]; + this.columnConfiguration.sortCriteria.push( + newCriteria, + ); + + renderSortingCriterias(); // Re-render if manualOrder is selected } else { - this.columnConfiguration.sortCriteria[ - index - ].criteria = - value as columnSortingCriteria["criteria"]; + const foundCriteria = + this.columnConfiguration + .sortCriteria[index]; + + if (foundCriteria) { + foundCriteria.criteria = + value as columnSortingCriteria["criteria"]; + } else { + bugReporterManagerInsatance.showNotice( + 179, + "There was an issue finding the exact sorting criterion using the index.", + `Sorting criteria : ${JSON.stringify(this.columnConfiguration.sortCriteria)}\nIndex : ${index}`, + "ConfigureColumnSortingModal.ts/renderSortingCritierias/onChange", + ); + } } } }); @@ -208,7 +233,7 @@ export class ConfigureColumnSortingModal extends Modal { // Add tooltips explaining what asc/desc means for each field type if possible if (sortCriteria.criteria === "priority") { dropdown.selectEl.title = t( - "column-sorting-criteria-priority-tooltip-numeric-properties" + "column-sorting-criteria-priority-tooltip-numeric-properties", ); } else if ( [ @@ -218,15 +243,15 @@ export class ConfigureColumnSortingModal extends Modal { ].includes(sortCriteria.criteria) ) { dropdown.selectEl.title = t( - "column-sorting-criteria-priority-tooltip-date-properties" + "column-sorting-criteria-priority-tooltip-date-properties", ); } else if (sortCriteria.criteria === "status") { dropdown.selectEl.title = t( - "column-sorting-criteria-priority-tooltip-status-properties" + "column-sorting-criteria-priority-tooltip-status-properties", ); } else { dropdown.selectEl.title = t( - "column-sorting-criteria-priority-tooltip-content-properties" + "column-sorting-criteria-priority-tooltip-content-properties", ); } } else { @@ -238,30 +263,42 @@ export class ConfigureColumnSortingModal extends Modal { .setButtonText("delete") .setIcon("trash") .setClass( - "configureColumnSortingModalHomeSortingCriteriaListItemDeleteCriterion" + "configureColumnSortingModalHomeSortingCriteriaListItemDeleteCriterion", ) .setTooltip(t("remove-sort-criterion")) .onClick(async () => { this.isEdited = true; - if (this.columnConfiguration.sortCriteria) { + if ( + this.columnConfiguration.sortCriteria && + this.columnConfiguration.sortCriteria + .length > index + ) { + console.log( + "Old sorting criterias :", + this.columnConfiguration, + ); this.columnConfiguration.sortCriteria.splice( index, - 1 + 1, + ); + console.log( + "New sorting criterias :", + this.columnConfiguration, ); renderSortingCriterias(); // Re-render after delete } - }) + }), ); }); if ( this.columnConfiguration.sortCriteria.some( - (c) => c.criteria === "manualOrder" + (c) => c.criteria === "manualOrder", ) ) { new Setting(sortingCriteriaList) .setClass( - "configureColumnSortingModalHomeSortingCriteriaListItemNotice" + "configureColumnSortingModalHomeSortingCriteriaListItemNotice", ) .setName(t("note")) .setDesc(t("manual-order-notice")); @@ -278,19 +315,19 @@ export class ConfigureColumnSortingModal extends Modal { addNewSortingButton.addEventListener("click", async () => { if ( this.columnConfiguration.sortCriteria?.some( - (c) => c.criteria === "manualOrder" + (c) => c.criteria === "manualOrder", ) ) { new Notice( - t("cannot-add-more-sorting-criteria-with-manual-order") + t("cannot-add-more-sorting-criteria-with-manual-order"), ); } else { this.isEdited = true; - const newCriteria: any = { + const newCriteria: columnSortingCriteria = { criteria: "content", order: "asc", priority: - (this.columnConfiguration.sortCriteria?.length || 0) + + (this.columnConfiguration.sortCriteria?.length ?? 0) + 1, uid: generateRandomTempTaskId(), }; @@ -304,7 +341,7 @@ export class ConfigureColumnSortingModal extends Modal { // Button container at bottom const buttonContainer = homeComponent.createDiv( - "configureColumnSortingModalHomeButtonContainer" + "configureColumnSortingModalHomeButtonContainer", ); const saveButton = buttonContainer.createEl("button", { @@ -312,6 +349,10 @@ export class ConfigureColumnSortingModal extends Modal { cls: "configureColumnSortingModalHomeButtonContainerSaveBtn", }); saveButton.addEventListener("click", () => { + console.log( + "On Save button clicked. Passing following column configs :", + this.columnConfiguration, + ); this.onSave(this.columnConfiguration); this.isEdited = false; this.close(); @@ -321,7 +362,7 @@ export class ConfigureColumnSortingModal extends Modal { text: t("cancel"), }); cancelButton.classList.add( - "configureColumnSortingModalHomeButtonContainerCancelBtn" + "configureColumnSortingModalHomeButtonContainerCancelBtn", ); cancelButton.addEventListener("click", () => { this.onCancel(); diff --git a/src/settings/SettingSynchronizer.ts b/src/settings/SettingSynchronizer.ts index 4da64618..56ec977e 100644 --- a/src/settings/SettingSynchronizer.ts +++ b/src/settings/SettingSynchronizer.ts @@ -10,6 +10,7 @@ import { Board, ColumnData } from "src/interfaces/BoardConfigs"; import { generateIdForFilters } from "src/components/BoardFilters/ViewTaskFilter"; import { colTypeNames } from "src/interfaces/Enums"; import { bugReporterManagerInsatance } from "src/managers/BugReporter"; +import { generateRandomNumber } from "src/utils/TaskItemUtils"; /** * Recursively migrates settings by adding missing fields from defaults to settings. @@ -46,7 +47,7 @@ export function migrateSettings(defaults: any, settings: any): PluginDataJson { * This is a temporary solution to sync the boardConfigs. This is required to replace the range object with the new 'datedBasedColumn', which will have three values 'dateType', 'from' and 'to'. So, basically we need to copy `range.rangedata.from` value to `datedBasedColumn.from` and similarly for `range.rangedatato`. And for `datedBasedColumn.dateType`, put the value this.settings.data.globalSettings.universalDate */ else if (key === "boardConfigs" && Array.isArray(settings[key])) { - settings[key].forEach((boardConfig: Board) => { + settings[key].forEach((boardConfig: Board, index: number) => { boardConfig.columns.forEach((column: ColumnData) => { // Older IDs were smaller number. Will change them to 10 digit numbers. column.id = generateRandomNumber(); @@ -67,8 +68,11 @@ export function migrateSettings(defaults: any, settings: any): PluginDataJson { } }); + // FIX : This is a fix becauase of my silly mistake, in the third board I hardcoded the index as 1 instead of 2. + boardConfig.index = index; + // Migration applied since version 1.4.0 - if (!boardConfig.hideEmptyColumns) { + if (!boardConfig?.hideEmptyColumns) { boardConfig.hideEmptyColumns = false; } @@ -101,7 +105,15 @@ export function migrateSettings(defaults: any, settings: any): PluginDataJson { } } }); - } else if ( + } + + // ------------------------------------- + /** + * @type Reqruired + * + * This is a cumpulsory case, which will recursively iterate all the object type settings. + */ + else if ( typeof defaults[key] === "object" && defaults[key] !== null && !Array.isArray(defaults[key]) diff --git a/src/utils/algorithms/ColumnSegregator.ts b/src/utils/algorithms/ColumnSegregator.ts index 91e195be..f554bcca 100644 --- a/src/utils/algorithms/ColumnSegregator.ts +++ b/src/utils/algorithms/ColumnSegregator.ts @@ -301,7 +301,7 @@ export const columnSegregator = ( * SORTING * ------------------------------------------------------------- */ - if (columnData.sortCriteria && columnData.sortCriteria.length > 0) { + if (columnData?.sortCriteria && columnData.sortCriteria.length > 0) { // TODO : This code can be moved inside the ColumnSortingAlgorithm function. // If manualOrder is one of the sorting criteria, apply manual ordering using columnData.tasksIdManualOrder const hasManualOrder = columnData.sortCriteria.some( From 737557a5a967c03903778430e02c9a54391d564e Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Thu, 5 Feb 2026 23:07:40 +0530 Subject: [PATCH 068/236] UI : increase the right property config panel in AddOrEditTaskRC size --- src/components/AddOrEditTaskRC.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/AddOrEditTaskRC.tsx b/src/components/AddOrEditTaskRC.tsx index 17fefa14..65e8680c 100644 --- a/src/components/AddOrEditTaskRC.tsx +++ b/src/components/AddOrEditTaskRC.tsx @@ -1318,7 +1318,7 @@ export const AddOrEditTaskRC: React.FC<{
    From 784383146704425ec46b6b513f5b0e465d9d62f1 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Thu, 5 Feb 2026 23:08:13 +0530 Subject: [PATCH 069/236] update the default properties for better install experience --- src/interfaces/GlobalSettings.ts | 48 +++++++++++++++++++------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/src/interfaces/GlobalSettings.ts b/src/interfaces/GlobalSettings.ts index 24fb5300..a96b76b8 100644 --- a/src/interfaces/GlobalSettings.ts +++ b/src/interfaces/GlobalSettings.ts @@ -151,6 +151,11 @@ export interface PluginDataJson { }; } +/** + * @note There are hardcoded ids present in this data + * If you are changing the below configs, make sure the ids of + * two different objects are different. + */ export const DEFAULT_SETTINGS: PluginDataJson = { version: "", // Keep this empty only. Change the version number in the runOnPluginUpdate function inside main.ts file whenever you will going to release a new version. data: { @@ -158,7 +163,7 @@ export const DEFAULT_SETTINGS: PluginDataJson = { { columns: [ { - id: 1, + id: 1851955511, colType: colTypeNames.undated, active: true, collapsed: false, @@ -171,7 +176,7 @@ export const DEFAULT_SETTINGS: PluginDataJson = { }, }, { - id: 2, + id: 1851955512, colType: colTypeNames.dated, active: true, collapsed: false, @@ -184,7 +189,7 @@ export const DEFAULT_SETTINGS: PluginDataJson = { }, }, { - id: 3, + id: 1851955513, colType: colTypeNames.dated, active: true, collapsed: false, @@ -197,7 +202,7 @@ export const DEFAULT_SETTINGS: PluginDataJson = { }, }, { - id: 4, + id: 1851955514, colType: colTypeNames.dated, active: true, collapsed: false, @@ -210,7 +215,7 @@ export const DEFAULT_SETTINGS: PluginDataJson = { }, }, { - id: 5, + id: 1851955515, colType: colTypeNames.dated, active: true, collapsed: false, @@ -223,7 +228,7 @@ export const DEFAULT_SETTINGS: PluginDataJson = { }, }, { - id: 6, + id: 1851955516, colType: colTypeNames.completed, active: true, collapsed: false, @@ -254,7 +259,7 @@ export const DEFAULT_SETTINGS: PluginDataJson = { { columns: [ { - id: 7, + id: 1851955517, colType: colTypeNames.untagged, active: true, collapsed: false, @@ -262,7 +267,7 @@ export const DEFAULT_SETTINGS: PluginDataJson = { index: 1, }, { - id: 8, + id: 1851955518, colType: colTypeNames.namedTag, active: true, collapsed: false, @@ -271,7 +276,7 @@ export const DEFAULT_SETTINGS: PluginDataJson = { coltag: "important", }, { - id: 9, + id: 1851955519, colType: colTypeNames.namedTag, active: true, collapsed: false, @@ -280,7 +285,7 @@ export const DEFAULT_SETTINGS: PluginDataJson = { coltag: "wip", }, { - id: 11, + id: 1851955520, colType: colTypeNames.namedTag, active: true, collapsed: false, @@ -289,7 +294,7 @@ export const DEFAULT_SETTINGS: PluginDataJson = { coltag: "review", }, { - id: 12, + id: 1851955521, colType: colTypeNames.completed, active: true, collapsed: false, @@ -320,7 +325,7 @@ export const DEFAULT_SETTINGS: PluginDataJson = { { columns: [ { - id: 7, + id: 1851955522, colType: colTypeNames.taskStatus, taskStatus: defaultTaskStatuses.unchecked, active: true, @@ -329,7 +334,7 @@ export const DEFAULT_SETTINGS: PluginDataJson = { index: 1, }, { - id: 8, + id: 1851955523, colType: colTypeNames.taskStatus, taskStatus: defaultTaskStatuses.scheduled, active: true, @@ -338,7 +343,7 @@ export const DEFAULT_SETTINGS: PluginDataJson = { index: 2, }, { - id: 9, + id: 1851955524, colType: colTypeNames.taskStatus, taskStatus: defaultTaskStatuses.inprogress, active: true, @@ -347,7 +352,7 @@ export const DEFAULT_SETTINGS: PluginDataJson = { index: 3, }, { - id: 11, + id: 1851955525, colType: colTypeNames.taskStatus, taskStatus: defaultTaskStatuses.question, active: true, @@ -356,7 +361,7 @@ export const DEFAULT_SETTINGS: PluginDataJson = { index: 5, }, { - id: 12, + id: 1851955526, colType: colTypeNames.completed, active: true, collapsed: false, @@ -365,7 +370,7 @@ export const DEFAULT_SETTINGS: PluginDataJson = { name: "Completed", }, { - id: 13, + id: 1851955527, colType: colTypeNames.taskStatus, taskStatus: defaultTaskStatuses.dropped, active: true, @@ -434,12 +439,15 @@ export const DEFAULT_SETTINGS: PluginDataJson = { scanMode: scanModeOptions.AUTOMATIC, columnWidth: "300px", visiblePropertiesList: [ + taskPropertiesNames.Checkbox, taskPropertiesNames.ID, taskPropertiesNames.Title, - taskPropertiesNames.SubTasks, - taskPropertiesNames.Description, + taskPropertiesNames.SubTasksMinimized, + taskPropertiesNames.DescriptionMinimized, taskPropertiesNames.Status, taskPropertiesNames.Tags, + taskPropertiesNames.Time, + taskPropertiesNames.Reminder, taskPropertiesNames.Priority, taskPropertiesNames.CreatedDate, taskPropertiesNames.StartDate, @@ -447,7 +455,7 @@ export const DEFAULT_SETTINGS: PluginDataJson = { taskPropertiesNames.DueDate, taskPropertiesNames.CompletionDate, taskPropertiesNames.CancelledDate, - taskPropertiesNames.Reminder, + taskPropertiesNames.Dependencies, taskPropertiesNames.FilePath, ], taskCardStyle: taskCardStyleNames.EMOJI, From a7142d84808418004f0100c71dd5685ad3a4fe7b Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Thu, 5 Feb 2026 23:09:16 +0530 Subject: [PATCH 070/236] refactor : replace columnDataProp with ColumnData --- src/modals/AddColumnModal.ts | 31 ++++--------------------------- src/modals/BoardConfigModal.tsx | 8 ++++---- 2 files changed, 8 insertions(+), 31 deletions(-) diff --git a/src/modals/AddColumnModal.ts b/src/modals/AddColumnModal.ts index 71b318c1..0e6fe090 100644 --- a/src/modals/AddColumnModal.ts +++ b/src/modals/AddColumnModal.ts @@ -1,43 +1,20 @@ // /src/modal/AddColumnModal.ts import { App, Modal } from "obsidian"; +import { ColumnData } from "src/interfaces/BoardConfigs"; import { colTypeNames, UniversalDateOptions } from "src/interfaces/Enums"; import { columnTypeAndNameMapping } from "src/interfaces/Mapping"; - import { t } from "src/utils/lang/helper"; - -export type columnDataProp = { - id: number; - colType: string; - name: string; - active?: boolean; - datedBasedColumn?: { dateType: string; from: number; to: number }; - coltag?: string; - taskStatus?: string; - taskPriority?: number; - limit?: number; - filePaths?: string; -}; +import { generateRandomNumber } from "src/utils/TaskItemUtils"; interface AddColumnModalProps { app: App; onCancel: () => void; - onSubmit: (columnData: columnDataProp) => void; + onSubmit: (columnData: ColumnData) => void; } export class AddColumnModal extends Modal { - private onSubmit: (columnData: { - id: number; - colType: string; - name: string; - active?: boolean; - datedBasedColumn?: { dateType: string; from: number; to: number }; - coltag?: string; - taskStatus?: string; - taskPriority?: number; - limit?: number; - filePaths?: string; - }) => void; + private onSubmit: (columnData: ColumnData) => void; private onCancel: () => void; private colType: string; private name: string; diff --git a/src/modals/BoardConfigModal.tsx b/src/modals/BoardConfigModal.tsx index 7bbea523..177a852e 100644 --- a/src/modals/BoardConfigModal.tsx +++ b/src/modals/BoardConfigModal.tsx @@ -15,9 +15,9 @@ import { t } from "src/utils/lang/helper"; import { ClosePopupConfrimationModal } from "./ClosePopupConfrimationModal"; import { MultiSuggest, getFileSuggestions, getTagSuggestions } from "src/services/MultiSuggest"; import { colTypeNames, UniversalDateOptions } from "src/interfaces/Enums"; -import { Board, swimlaneConfigs } from "src/interfaces/BoardConfigs"; +import { Board, ColumnData, swimlaneConfigs } from "src/interfaces/BoardConfigs"; import { columnTypeAndNameMapping, getPriorityOptionsForDropdown } from "src/interfaces/Mapping"; -import { columnDataProp, AddColumnModal } from "./AddColumnModal"; +import { AddColumnModal } from "./AddColumnModal"; import { SwimlanesConfigModal } from "./SwimlanesConfigModal"; import { bugReporterManagerInsatance } from "src/managers/BugReporter"; @@ -173,7 +173,7 @@ const ConfigModalContent: React.FC = ({ swimlaneModal.open(); }; - const handleAddColumn = (boardIndex: number, columnData: columnDataProp) => { + const handleAddColumn = (boardIndex: number, columnData: ColumnData) => { const updatedBoards = [...localBoards]; updatedBoards[boardIndex].columns.push({ id: columnData.id, @@ -201,7 +201,7 @@ const ConfigModalContent: React.FC = ({ const modal = new AddColumnModal(plugin.app, { app: plugin.app, onCancel: handleCloseAddColumnModal, // Previously onClose - onSubmit: (columnData: columnDataProp) => handleAddColumn(selectedBoardIndex, columnData), + onSubmit: (columnData: ColumnData) => handleAddColumn(selectedBoardIndex, columnData), }); modal.open(); }; From bc390511ed72d178cda6b7f91ad73ea36a3891f3 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Thu, 5 Feb 2026 23:09:56 +0530 Subject: [PATCH 071/236] show the save button even on Global Settings screen. --- src/modals/BoardConfigModal.tsx | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/modals/BoardConfigModal.tsx b/src/modals/BoardConfigModal.tsx index 177a852e..616a8417 100644 --- a/src/modals/BoardConfigModal.tsx +++ b/src/modals/BoardConfigModal.tsx @@ -925,12 +925,8 @@ const ConfigModalContent: React.FC = ({
    -
    - - {selectedBoardIndex !== -1 && ( - - )} +
    @@ -940,9 +936,8 @@ const ConfigModalContent: React.FC = ({ }
    - {selectedBoardIndex !== -1 && ( - - )} + + ); }; From 9ac44e6f255a5283890072da78413a2f72b691b4 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Thu, 5 Feb 2026 23:10:22 +0530 Subject: [PATCH 072/236] minor code refactoring --- main.ts | 83 +++--------------------------- src/managers/BugReporter.ts | 2 +- src/settings/SettingConstructUI.ts | 2 +- 3 files changed, 8 insertions(+), 79 deletions(-) diff --git a/main.ts b/main.ts index 3ccc7641..04c2dfe7 100644 --- a/main.ts +++ b/main.ts @@ -1354,80 +1354,6 @@ export default class TaskBoard extends Plugin { isReminderPluginInstalled(this.plugin); } - // private migrateSettings(defaults: any, settings: any) { - // for (const key in defaults) { - // if (!(key in settings)) { - // settings[key] = defaults[key]; - // } else if ( - // // This is a temporary fix for the tagColors - // !Array.isArray(settings[key]) && - // key === "tagColors" && - // typeof settings[key] === "object" && - // settings[key] !== null - // ) { - // settings[key] = Object.entries( - // settings[key] as Record - // ).map( - // ([name, color], idx) => - // ({ - // name, - // color, - // priority: idx + 1, - // } as any) - // ); - // } else if (key === "boardConfigs" && Array.isArray(settings[key])) { - // // This is a temporary solution to sync the boardConfigs. I will need to replace the range object with the new 'datedBasedColumn', which will have three values 'dateType', 'from' and 'to'. So, basically I want to copy range.rangedata.from value to datedBasedColumn.from and similarly for to. And for datedBasedColumn.dateType, put the value this.settings.data.globalSettings.defaultDateType. - // settings[key].forEach((boardConfig: Board) => { - // boardConfig.columns.forEach((column: ColumnData) => { - // if (!column.id) { - // column.id = Math.floor(Math.random() * 1000000); - // } - // if ( - // column.colType === colType.dated || - // (column.colType === colType.undated && - // !column.datedBasedColumn) - // ) { - // column.datedBasedColumn = { - // dateType: - // this.settings.data.globalSettings - // .universalDate, - // from: column.datedBasedColumn?.from || 0, - // to: column.datedBasedColumn?.to || 0, - // }; - // delete column.range; - // } - // }); - - // if (!boardConfig.hideEmptyColumns) { - // boardConfig.hideEmptyColumns = false; - // } - // }); - // } else if ( - // typeof defaults[key] === "object" && - // defaults[key] !== null && - // !Array.isArray(defaults[key]) - // ) { - // // Recursively sync nested objects - // // console.log( - // // "Syncing settings for key:", - // // key, - // // "Defaults:", - // // defaults[key], - // // "Settings:", - // // settings[key] - // // ); - // this.migrateSettings(defaults[key], settings[key]); - // } else if (key === "tasksCacheFilePath" && settings[key] === "") { - // settings[ - // key - // ] = `${this.app.vault.configDir}/plugins/task-board/tasks.json`; - // } - // } - - // this.settings = settings; - // // this.saveSettings(); - // } - private runOnPluginUpdate() { // Check if the plugin version has changed const currentVersion = newReleaseVersion; // Change this whenever you will going to release a new version. @@ -1465,11 +1391,14 @@ export default class TaskBoard extends Plugin { if (previousVersion === "" || runMandatoryScan) { localStorage.setItem("manadatoryScan", "true"); - const smallMessage = - "Even being a minor release, this new version of Task Board requires a re-scan of your vault. Kindly re-scan using the top-right button in the task board tab."; - new Notice(smallMessage, 0); } + // if (runMandatoryScan) { + // const smallMessage = + // "Even being a minor release, this new version of Task Board requires a re-scan of your vault. Kindly re-scan using the top-right button in the task board tab."; + // new Notice(smallMessage, 0); + // } + this.settings.version = currentVersion; // Settings migrations should be only applied after plugin update. diff --git a/src/managers/BugReporter.ts b/src/managers/BugReporter.ts index cefff115..077f4140 100644 --- a/src/managers/BugReporter.ts +++ b/src/managers/BugReporter.ts @@ -30,7 +30,7 @@ class BugReporterManager { private alreadyShownBugsIDs: number[] = []; private LOG_FILE_PATH = ""; private readonly MAX_RECENT_LOGS = 20; - private readonly MAX_USED_ID = 167; // This constant will not be used anywhere, its simply to keep track of the the recent ID used. + private readonly MAX_USED_ID = 179; // This constant will not be used anywhere, its simply to keep track of the the recent ID used. private constructor() { // Private constructor to enforce singleton pattern diff --git a/src/settings/SettingConstructUI.ts b/src/settings/SettingConstructUI.ts index f472ae1d..1b2e3e25 100644 --- a/src/settings/SettingConstructUI.ts +++ b/src/settings/SettingConstructUI.ts @@ -320,7 +320,7 @@ export class SettingsManager { ), ); - ["files", "folders", "frontMatter", "tags"].forEach((type) => { + ["files", "folders", "frontmatter", "tags"].forEach((type) => { const filterType = type as keyof typeof scanFilters; const filter = scanFilters[filterType]; From 64c8968a05cccdd1aa9c4493225e1c0ca222e1aa Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Thu, 5 Feb 2026 23:10:35 +0530 Subject: [PATCH 073/236] small styling improvements --- styles.css | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/styles.css b/styles.css index a886468b..f206d030 100644 --- a/styles.css +++ b/styles.css @@ -1845,17 +1845,16 @@ button.clickable-icon.view-action.taskboardScanVaultBtn.highlight { } .EditTaskModalHomeToggleBtn { - background-color: var(--accent-color, blue); - color: white; border: none; - padding: 8px 12px; - border-radius: 4px; + border-radius: 7px; cursor: pointer; display: block; } - .EditTaskModalHomeToggleBtn:hover { - background-color: darkblue; + .EditTaskModalHomeToggleBtn svg { + display: flex; + align-content: center; + vertical-align: middle; } } @@ -2268,7 +2267,6 @@ button.clickable-icon.view-action.taskboardScanVaultBtn.highlight { left: 1em; width: 4.2em !important; height: 2.5em !important; - color: white; border: none; padding: 9px 20px !important; border-radius: 4px; @@ -2317,7 +2315,7 @@ button.clickable-icon.view-action.taskboardScanVaultBtn.highlight { /* right: 1px; */ /* left: 1px; */ align-self: center; - background-color: var(--color-green) !important; + border: 2px solid var(--color-green) !important; cursor: pointer; width: 90%; } @@ -5942,7 +5940,7 @@ svg.react-flow__connectionline { .boardSidebarCardTitle { font-weight: 500; font-size: 1rem !important; - color: var(--background-modifier-border-focus) !important; + color: var(--text-faint) !important; font-size: var(--font-size-default); } @@ -5959,7 +5957,7 @@ svg.react-flow__connectionline { width: 100%; font-size: 0.9rem; padding-block-start: 0.7rem; - color: var(--background-modifier-border-focus) !important; + color: var(--text-faint) !important; } .boardSidebarCard--active .boardSidebarCardDescription { @@ -7599,4 +7597,4 @@ Date picker modal styles .taskboardCustomMessageContainerBold { font-weight: 600; -} +} \ No newline at end of file From e1e68d60e6d8f22d2565fb5d7682b0438283b250 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Thu, 5 Feb 2026 23:10:50 +0530 Subject: [PATCH 074/236] test the latest changes | working --- data.json | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/data.json b/data.json index 1aa6772a..711445d7 100644 --- a/data.json +++ b/data.json @@ -1,5 +1,5 @@ { - "version": "1.8.7", + "version": "1.9.0", "data": { "boardConfigs": [ { @@ -505,7 +505,7 @@ ] }, "taskCount": { - "pending": 64, + "pending": 65, "completed": 14 }, "swimlanes": { @@ -692,7 +692,7 @@ "filterGroups": [] }, "taskCount": { - "pending": 64, + "pending": 65, "completed": 14 }, "swimlanes": { @@ -770,7 +770,15 @@ ] } ] - } + }, + "sortCriteria": [ + { + "criteria": "content", + "order": "asc", + "priority": 1, + "uid": "8izcxk47" + } + ] }, { "id": 3370807172, From 4836572406ac224fa4a8095e3e3e30db474dc7e8 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Thu, 5 Feb 2026 23:12:52 +0530 Subject: [PATCH 075/236] release version `1.9.1` --- manifest.json | 2 +- package-lock.json | 4 ++-- package.json | 2 +- src/interfaces/Constants.ts | 2 +- versions.json | 3 ++- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/manifest.json b/manifest.json index 1057fb1f..40520930 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "task-board", "name": "Task Board", - "version": "1.9.0", + "version": "1.9.1", "minAppVersion": "1.4.13", "description": "Manage all your tasks throughout your vault from a single board and much more...", "author": "Atmanand Gauns", diff --git a/package-lock.json b/package-lock.json index 7a32f152..626cf944 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "task-board", - "version": "1.9.0", + "version": "1.9.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "task-board", - "version": "1.9.0", + "version": "1.9.1", "license": "GPL-3.0", "dependencies": { "@popperjs/core": "^2.11.8", diff --git a/package.json b/package.json index 381346d7..225a4242 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "task-board", - "version": "1.9.0", + "version": "1.9.1", "description": "An Obsidian plugin to manage small to large projects using tasks from the whole vault on a centralized board using various kinds of views like Kanban, map, list, etc.", "main": "main.js", "type": "module", diff --git a/src/interfaces/Constants.ts b/src/interfaces/Constants.ts index 3529cbd4..4bf1c352 100644 --- a/src/interfaces/Constants.ts +++ b/src/interfaces/Constants.ts @@ -1,4 +1,4 @@ -export const newReleaseVersion = "1.9.0"; +export const newReleaseVersion = "1.9.1"; // Plugin view type identifiers export const VIEW_TYPE_TASKBOARD = "task-board-view"; export const VIEW_TYPE_ADD_OR_EDIT_TASK = "add-or-edit-task-view"; diff --git a/versions.json b/versions.json index db0a4043..b1109ba9 100644 --- a/versions.json +++ b/versions.json @@ -43,5 +43,6 @@ "1.8.5": "1.4.13", "1.8.6": "1.4.13", "1.8.7": "1.4.13", - "1.9.0": "1.4.13" + "1.9.0": "1.4.13", + "1.9.1": "1.4.13" } From c014d1b10a7242609461a49b2025f0207e803531 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Sun, 8 Feb 2026 09:55:08 +0530 Subject: [PATCH 076/236] fix : frontmatter and tag sections are not rendered | Discussed in #663 --- src/interfaces/GlobalSettings.ts | 4 ++-- src/modals/ScanFilterModal.ts | 17 +++++++++-------- src/services/OpenModals.ts | 3 ++- src/settings/SettingSynchronizer.ts | 13 +++++++++++++ src/utils/algorithms/ScanningFilterer.ts | 20 ++++++++++---------- 5 files changed, 36 insertions(+), 21 deletions(-) diff --git a/src/interfaces/GlobalSettings.ts b/src/interfaces/GlobalSettings.ts index a96b76b8..a6f79811 100644 --- a/src/interfaces/GlobalSettings.ts +++ b/src/interfaces/GlobalSettings.ts @@ -29,7 +29,7 @@ export interface scanFilters { polarity: number; values: string[]; }; - frontMatter: { + frontmatter: { polarity: number; values: string[]; }; @@ -411,7 +411,7 @@ export const DEFAULT_SETTINGS: PluginDataJson = { polarity: 3, values: [], }, - frontMatter: { + frontmatter: { polarity: 3, values: [], }, diff --git a/src/modals/ScanFilterModal.ts b/src/modals/ScanFilterModal.ts index d5e40d9f..06ef7329 100644 --- a/src/modals/ScanFilterModal.ts +++ b/src/modals/ScanFilterModal.ts @@ -9,6 +9,7 @@ import { } from "src/services/MultiSuggest"; import { trashIcon } from "src/interfaces/Icons"; import { t } from "src/utils/lang/helper"; +import { scanFilters } from "src/interfaces/GlobalSettings"; export class ScanFilterModal extends Modal { private inputEl!: HTMLInputElement; @@ -18,14 +19,14 @@ export class ScanFilterModal extends Modal { constructor( private plugin: TaskBoard, - private filterType: "files" | "folders" | "frontMatter" | "tags", - private onSave: (values: string[]) => void + private filterType: keyof scanFilters, + private onSave: (values: string[]) => void, ) { super(plugin.app); this.selectedValues = new Set( this.plugin.settings.data.globalSettings.scanFilters[ this.filterType - ].values + ].values, ); this.selectedValue = ""; } @@ -57,9 +58,9 @@ export class ScanFilterModal extends Modal { // Load suggestion content if (this.filterType === "files") { this.suggestionContent = new Set(getFileSuggestions(this.app)); - } else if (this.filterType === "frontMatter") { + } else if (this.filterType === "frontmatter") { this.suggestionContent = new Set( - getYAMLPropertySuggestions(this.app) + getYAMLPropertySuggestions(this.app), ); } else if (this.filterType === "folders") { this.suggestionContent = new Set(getFolderSuggestions(this.app)); @@ -78,7 +79,7 @@ export class ScanFilterModal extends Modal { } this.inputEl.value = ""; }, - this.app + this.app, ); // this.inputEl.blur(); @@ -94,13 +95,13 @@ export class ScanFilterModal extends Modal { .onClick(() => { this.onSave(Array.from(this.selectedValues)); this.close(); - }) + }), ) .addButton((btn) => btn .setButtonText(t("cancel")) .setTooltip(t("cancel")) - .onClick(() => this.close()) + .onClick(() => this.close()), ); } diff --git a/src/services/OpenModals.ts b/src/services/OpenModals.ts index 9096b0ab..61aa0e06 100644 --- a/src/services/OpenModals.ts +++ b/src/services/OpenModals.ts @@ -29,6 +29,7 @@ import { TaskBoardActionsModal } from "src/modals/TaskBoardActionsModal"; import { bugReporterManagerInsatance } from "src/managers/BugReporter"; import { DatePickerModal } from "src/modals/date_picker"; import { getCurrentLocalTimeString } from "src/utils/DateTimeCalculations"; +import { scanFilters } from "src/interfaces/GlobalSettings"; // Function to open the BoardConfigModal export const openBoardConfigModal = ( @@ -525,7 +526,7 @@ export const openTaskBoardActionsModal = ( export const openScanFiltersModal = ( plugin: TaskBoard, - filterType: "files" | "frontMatter" | "folders" | "tags", + filterType: keyof scanFilters, onSave: (scanFilters: string[]) => void, ) => { new ScanFilterModal(plugin, filterType, async (newValues) => { diff --git a/src/settings/SettingSynchronizer.ts b/src/settings/SettingSynchronizer.ts index 56ec977e..cb2ae6cd 100644 --- a/src/settings/SettingSynchronizer.ts +++ b/src/settings/SettingSynchronizer.ts @@ -38,6 +38,19 @@ export function migrateSettings(defaults: any, settings: any): PluginDataJson { settings[key] = DEFAULT_SETTINGS.data.globalSettings.customStatuses; } + // ----------------------------------- + /** + * @since v1.9.2 + * @type Temporary + * @note Remove this on the next version release where this migration will run. + * + * Because of the name change, we had to do this migration. + */ + if (key === "frontmatter") { + settings[key] = settings["frontMatter"]; + delete settings["frontMatter"]; + } + // ------------------------------------- /** * @since v1.5.0 diff --git a/src/utils/algorithms/ScanningFilterer.ts b/src/utils/algorithms/ScanningFilterer.ts index fa1ec778..6c33cc23 100644 --- a/src/utils/algorithms/ScanningFilterer.ts +++ b/src/utils/algorithms/ScanningFilterer.ts @@ -55,7 +55,7 @@ export function checkFrontMatterFilters( return; // No front matter found } const frontMatterInFilters = Object.keys(frontmatter).some((key) => { - const filterString = scanFilters.frontMatter.values.find( + const filterString = scanFilters.frontmatter.values.find( (filter: string) => filter.includes(`"${key}":`) ); if (filterString) { @@ -72,9 +72,9 @@ export function checkFrontMatterFilters( } return false; }); - if (frontMatterInFilters && scanFilters.frontMatter.polarity === 1) { + if (frontMatterInFilters && scanFilters.frontmatter.polarity === 1) { return true; - } else if (frontMatterInFilters && scanFilters.frontMatter.polarity === 2) { + } else if (frontMatterInFilters && scanFilters.frontmatter.polarity === 2) { return false; } } @@ -124,25 +124,25 @@ export function checkFolderFilters( if ( scanFilters.files.polarity === 1 && scanFilters.folders.polarity === 1 && - scanFilters.frontMatter.polarity === 1 + scanFilters.frontmatter.polarity === 1 ) { return false; } else if ( scanFilters.files.polarity === 2 && scanFilters.folders.polarity === 2 && - scanFilters.frontMatter.polarity === 2 + scanFilters.frontmatter.polarity === 2 ) { return true; } else if ( scanFilters.files.polarity === 1 || scanFilters.folders.polarity === 1 || - scanFilters.frontMatter.polarity === 1 + scanFilters.frontmatter.polarity === 1 ) { return true; } else if ( scanFilters.files.polarity === 2 || scanFilters.folders.polarity === 2 || - scanFilters.frontMatter.polarity === 2 + scanFilters.frontmatter.polarity === 2 ) { return true; } @@ -169,7 +169,7 @@ export function scanFilterForFilesNFoldersNFrontmatter( if ( scanFilters.files.polarity === 3 && - scanFilters.frontMatter.polarity === 3 && + scanFilters.frontmatter.polarity === 3 && scanFilters.folders.polarity === 3 ) { return true; @@ -192,8 +192,8 @@ export function scanFilterForFilesNFoldersNFrontmatter( } if ( - scanFilters.frontMatter.polarity !== 3 && - scanFilters.frontMatter.values.length > 0 + scanFilters.frontmatter.polarity !== 3 && + scanFilters.frontmatter.values.length > 0 ) { const result = checkFrontMatterFilters(plugin, file, scanFilters); if (result !== undefined) { From 98ccee756c9c289eb8a720b6a3d30ab3b39f8a2a Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Sun, 8 Feb 2026 09:55:37 +0530 Subject: [PATCH 077/236] BREAK : use if instead of if-else for settings migration --- src/settings/SettingSynchronizer.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/settings/SettingSynchronizer.ts b/src/settings/SettingSynchronizer.ts index cb2ae6cd..f50f7797 100644 --- a/src/settings/SettingSynchronizer.ts +++ b/src/settings/SettingSynchronizer.ts @@ -34,7 +34,7 @@ export function migrateSettings(defaults: any, settings: any): PluginDataJson { * * This is migration is only applied to replace the older settings available in users configs with the new settings as per the new Settinsg section added in the global settings. */ - else if (key === "customStatuses") { + if (key === "customStatuses") { settings[key] = DEFAULT_SETTINGS.data.globalSettings.customStatuses; } @@ -59,7 +59,7 @@ export function migrateSettings(defaults: any, settings: any): PluginDataJson { * * This is a temporary solution to sync the boardConfigs. This is required to replace the range object with the new 'datedBasedColumn', which will have three values 'dateType', 'from' and 'to'. So, basically we need to copy `range.rangedata.from` value to `datedBasedColumn.from` and similarly for `range.rangedatato`. And for `datedBasedColumn.dateType`, put the value this.settings.data.globalSettings.universalDate */ - else if (key === "boardConfigs" && Array.isArray(settings[key])) { + if (key === "boardConfigs" && Array.isArray(settings[key])) { settings[key].forEach((boardConfig: Board, index: number) => { boardConfig.columns.forEach((column: ColumnData) => { // Older IDs were smaller number. Will change them to 10 digit numbers. @@ -126,7 +126,7 @@ export function migrateSettings(defaults: any, settings: any): PluginDataJson { * * This is a cumpulsory case, which will recursively iterate all the object type settings. */ - else if ( + if ( typeof defaults[key] === "object" && defaults[key] !== null && !Array.isArray(defaults[key]) From ac7ea50a5fabb116e7ced4e2e588559a935af3f7 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Sun, 8 Feb 2026 18:07:57 +0530 Subject: [PATCH 078/236] fix : `showModifiedFilesNotice` setting is not in use | #660 --- main.ts | 186 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 101 insertions(+), 85 deletions(-) diff --git a/main.ts b/main.ts index 04c2dfe7..762ff82d 100644 --- a/main.ts +++ b/main.ts @@ -962,103 +962,118 @@ export default class TaskBoard extends Plugin { changed_files.length + deletedFilesList.length; if (totalFilesLength > 0) { - const modifiedFilesNotice = new Notice( - createFragment((f) => { - f.createDiv("bugReportNotice", (el) => { - el.createEl("p", { - text: `Task Board : ${totalFilesLength} files has been modified when Obsidian was inactive.`, - }); - el.createEl("button", { - text: t("show-me"), - cls: "reportBugButton", - onclick: () => { - // el.hide(); - - // Open a modal and show all these file names with their modified date-time in a nice UI. - const modifiedFilesModal = - new ModifiedFilesModal(this.app, { - modifiedFiles: changed_files, - deletedFiles: deletedFilesList, - }); - modifiedFilesModal.open(); - }, - }); - el.createEl("button", { - text: t("scan-them"), - cls: "ignoreBugButton", - onclick: async () => { - modifiedFilesNotice.hide(); - - // Show progress notice - this.currentProgressNotice = new Notice( - `Task Board : Processing modified files: 0/${totalFilesLength}`, - 0, - ); + const scanAllModifiedFiles = () => { + this.plugin.vaultScanner + .refreshTasksFromFiles(changed_files, false) + .then(async () => { + if (deletedFilesList.length > 0) { + await this.plugin.vaultScanner.deleteCacheForFiles( + deletedFilesList, + ); + } + }); + }; + + if (this.settings.data.globalSettings.showModifiedFilesNotice) { + const modifiedFilesNotice = new Notice( + createFragment((f) => { + f.createDiv("bugReportNotice", (el) => { + el.createEl("p", { + text: `Task Board : ${totalFilesLength} files has been modified when Obsidian was inactive.`, + }); + el.createEl("button", { + text: t("show-me"), + cls: "reportBugButton", + onclick: () => { + // el.hide(); + + // Open a modal and show all these file names with their modified date-time in a nice UI. + const modifiedFilesModal = + new ModifiedFilesModal(this.app, { + modifiedFiles: changed_files, + deletedFiles: deletedFilesList, + }); + modifiedFilesModal.open(); + }, + }); + el.createEl("button", { + text: t("scan-them"), + cls: "ignoreBugButton", + onclick: async () => { + try { + modifiedFilesNotice.hide(); - this.plugin.vaultScanner - .refreshTasksFromFiles( - changed_files, - false, - ) - .then(async () => { - if (deletedFilesList.length > 0) { - await this.plugin.vaultScanner.deleteCacheForFiles( - deletedFilesList, + // Show progress notice + this.currentProgressNotice = + new Notice( + `Task Board : Processing modified files: 0/${totalFilesLength}`, + 0, ); - } - }); - let modifiedFilesQueue = changed_files; - - let processed = 0; - while (modifiedFilesQueue.length > 0) { - const file = - modifiedFilesQueue.shift()!; - - try { - processed++; + scanAllModifiedFiles(); + + let modifiedFilesQueueLength = + changed_files?.length ?? 0; + + let processed = 0; + while ( + modifiedFilesQueueLength > 0 + ) { + modifiedFilesQueueLength = + modifiedFilesQueueLength - + 1; + + processed++; + + // Update progress notice + this.currentProgressNotice.messageEl.textContent = `Task Board : Processing created files: ${processed}/${totalFilesLength}`; + + // Add delay between processing each file to prevent blocking UI + if ( + modifiedFilesQueueLength > 0 + ) { + await new Promise( + (resolve) => + setTimeout( + resolve, + this + .PROCESSING_INTERVAL, + ), + ); + } + } - // Update progress notice - this.currentProgressNotice.messageEl.textContent = `Task Board : Processing created files: ${processed}/${totalFilesLength}`; + // Hide progress notice after completion + this.currentProgressNotice?.hide(); + this.currentProgressNotice = null; + new Notice( + `✓ Task Board : Finished processing ${totalFilesLength} created file(s)`, + ); } catch (error) { + this.currentProgressNotice?.hide(); bugReporterManagerInsatance.addToLogs( 165, String(error), "main.ts/findModifiedFilesOnAppAbsense", ); } - - // Add delay between processing each file to prevent blocking UI - if (modifiedFilesQueue.length > 0) { - await new Promise((resolve) => - setTimeout( - resolve, - this.PROCESSING_INTERVAL, - ), - ); - } - } - - // Hide progress notice after completion - this.currentProgressNotice?.hide(); - this.currentProgressNotice = null; - new Notice( - `✓ Task Board : Finished processing ${totalFilesLength} created file(s)`, - ); - }, + }, + }); }); - }); - }), - 0, - ); + }), + 0, + ); - modifiedFilesNotice.messageEl.onClickEvent((e) => { - if (e.target instanceof HTMLButtonElement) { - e.stopPropagation(); - e.preventDefault(); - e.stopImmediatePropagation(); - } - }); + modifiedFilesNotice.messageEl.onClickEvent((e) => { + if (e.target instanceof HTMLButtonElement) { + e.stopPropagation(); + e.preventDefault(); + e.stopImmediatePropagation(); + } + }); + } else { + scanAllModifiedFiles(); + } } } } @@ -1069,6 +1084,7 @@ export default class TaskBoard extends Plugin { registerEvents() { this.registerEvent( this.app.vault.on("modify", async (file: TAbstractFile) => { + console.log("File modifed : ", file.path); if ( fileTypeAllowedForScanning( this.plugin.settings.data.globalSettings, @@ -1131,7 +1147,7 @@ export default class TaskBoard extends Plugin { this.onFileModifiedAndLostFocus(); }); this.registerDomEvent(window, "focus", () => { - this.onFileModifiedAndLostFocus(); + setTimeout(() => this.onFileModifiedAndLostFocus(), 200); }); } From e671ac82b0baf5b187ec7ec5f3495d31ef838894 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Sun, 8 Feb 2026 18:09:01 +0530 Subject: [PATCH 079/236] enha : only show the quickAdd suggestion setting if the plugin is enabled --- src/settings/SettingConstructUI.ts | 116 +++++++++++++++-------------- 1 file changed, 61 insertions(+), 55 deletions(-) diff --git a/src/settings/SettingConstructUI.ts b/src/settings/SettingConstructUI.ts index 1b2e3e25..095bfad8 100644 --- a/src/settings/SettingConstructUI.ts +++ b/src/settings/SettingConstructUI.ts @@ -113,6 +113,12 @@ export class SettingsManager { return displayNames[property as keyof typeof displayNames] || null; } + /** + * Shows an Obsidian Notice to reload the application. + * It tracks if such a notice has already been shown. + * If already shown, will not show again. + * @returns void + */ private openReloadNoticeIfNeeded() { if (!this.reloadNoticeAlreadyShown) { sleep(100).then(() => { @@ -2415,68 +2421,68 @@ export class SettingsManager { new Setting(contentEl) .setName("QuickAdd " + t("plugin-compatibility")) .setDesc(t("quickadd-plugin-compatibility-description")) - .setTooltip( - t("Install and enable QuickAdd plugin to use this setting."), - ) - .addToggle((toggle) => - toggle - .setValue( - compatiblePlugins.quickAddPlugin && - communityPlugins.isQuickAddPluginEnabled(), - ) - .onChange(async (value) => { - if ( - this.globalSettings && - !communityPlugins.isQuickAddPluginEnabled() - ) { - new Notice(t("quickadd-plugin-not-enabled")); - this.globalSettings.compatiblePlugins.quickAddPlugin = false; - } else if (this.globalSettings) { - this.globalSettings.compatiblePlugins.quickAddPlugin = - value; + .addToggle((toggle) => { + toggle.onChange(async (value) => { + if ( + this.globalSettings && + !communityPlugins.isQuickAddPluginEnabled() + ) { + new Notice(t("quickadd-plugin-not-enabled")); + this.globalSettings.compatiblePlugins.quickAddPlugin = false; + } else if (this.globalSettings) { + this.globalSettings.compatiblePlugins.quickAddPlugin = + value; - this.openReloadNoticeIfNeeded(); - } + this.openReloadNoticeIfNeeded(); + } - await this.saveSettings(); - }), - ) + await this.saveSettings(); + }); + // toggle.onClick(); + }) .setDisabled(!communityPlugins.isQuickAddPluginEnabled()); - new Setting(contentEl) - .setName(t("default-quickadd-choice")) - .setDesc(t("default-quickadd-choice-description")) - .setTooltip(t("Enable the above setting to use this setting.")) - .addText((text) => { - text.setValue(quickAddPluginDefaultChoice).onChange((value) => { - if (this.globalSettings) - this.globalSettings.quickAddPluginDefaultChoice = value; - }); + if (communityPlugins.isQuickAddPluginEnabled()) { + new Setting(contentEl) + .setName(t("default-quickadd-choice")) + .setDesc(t("default-quickadd-choice-description")) + .setTooltip(t("Enable the above setting to use this setting.")) + .addText((text) => { + text.setValue(quickAddPluginDefaultChoice).onChange( + (value) => { + if (this.globalSettings) + this.globalSettings.quickAddPluginDefaultChoice = + value; + }, + ); - const inputEl = text.inputEl; - const suggestionContent = getQuickAddPluginChoices( - this.app, - communityPlugins.quickAddPlugin, - ); - const onSelectCallback = async (selectedChoiceName: string) => { - if (this.globalSettings) { - this.globalSettings.quickAddPluginDefaultChoice = - selectedChoiceName; - } - text.setValue(selectedChoiceName); - await this.saveSettings(); - }; + const inputEl = text.inputEl; + const suggestionContent = getQuickAddPluginChoices( + this.app, + communityPlugins.quickAddPlugin, + ); + const onSelectCallback = async ( + selectedChoiceName: string, + ) => { + if (this.globalSettings) { + this.globalSettings.quickAddPluginDefaultChoice = + selectedChoiceName; + } + text.setValue(selectedChoiceName); + await this.saveSettings(); + }; - new MultiSuggest( - inputEl, - new Set(suggestionContent), - onSelectCallback, - this.app, + new MultiSuggest( + inputEl, + new Set(suggestionContent), + onSelectCallback, + this.app, + ); + }) + .setDisabled( + !this.globalSettings!.compatiblePlugins.quickAddPlugin, ); - }) - .setDisabled( - !this.globalSettings!.compatiblePlugins.quickAddPlugin, - ); + } new Setting(contentEl) .setName(t("push-notification-compatibility")) From d08db8943736e4eac38857a28f7c04fb5228bf1f Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Sun, 8 Feb 2026 18:11:21 +0530 Subject: [PATCH 080/236] UI : A new look for the change view dropdown | #563 --- src/components/TaskBoardViewContent.tsx | 61 +++++++++++++++++++++---- 1 file changed, 52 insertions(+), 9 deletions(-) diff --git a/src/components/TaskBoardViewContent.tsx b/src/components/TaskBoardViewContent.tsx index 095a8e04..9f34890c 100644 --- a/src/components/TaskBoardViewContent.tsx +++ b/src/components/TaskBoardViewContent.tsx @@ -1,7 +1,7 @@ // src/components/TaskBoardViewContent.tsx import { Board, ColumnData, RootFilterState } from "../interfaces/BoardConfigs"; -import { CirclePlus, RefreshCcw, Search, SearchX, Filter, Menu as MenuICon, Settings, EllipsisVertical, List } from 'lucide-react'; +import { CirclePlus, RefreshCcw, Search, SearchX, Filter, Menu as MenuICon, Settings, EllipsisVertical, List, KanbanSquareIcon, Network, BrickWall, KanbanSquare, SquareKanban } from 'lucide-react'; import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { loadBoardsData, loadTasksAndMerge } from "src/utils/JsonFileOperations"; import { taskJsonMerged } from "src/interfaces/TaskItem"; @@ -774,6 +774,37 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs ); } + function handleViewChangeDropdownClick(event: React.MouseEvent) { + const viewMenu = new Menu(); + + viewMenu.addItem((item) => { + item.setTitle(t("kanban")); + item.setIcon("square-kanban"); + item.onClick(async () => { + const newViewType = viewTypeNames.kanban; + setViewType(newViewType); + plugin.settings.data.globalSettings.lastViewHistory.viewedType = newViewType; + plugin.saveSettings(); + }); + }); + + viewMenu.addItem((item) => { + item.setTitle(t("map")); + item.setIcon("network"); + item.onClick(async () => { + const newViewType = viewTypeNames.map; + setViewType(newViewType); + plugin.settings.data.globalSettings.lastViewHistory.viewedType = newViewType; + plugin.saveSettings(); + }); + }); + + // Use native event if available (React event has nativeEvent property) + viewMenu.showAtMouseEvent( + (event instanceof MouseEvent ? event : event.nativeEvent) + ); + } + // useEffect(() => { // const taskBoardLeaf = plugin.app.workspace.getLeavesOfType(VIEW_TYPE_TASKBOARD)[0]; // if (taskBoardLeaf) { @@ -795,6 +826,17 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs } }, [showBoardSidebar]); + const viewTypeIconComponent = () => { + switch (viewType) { + case viewTypeNames.kanban: + return ; + case viewTypeNames.map: + return ; + default: + return ; + } + } + return ( {/* Mobile board sidebar overlay */} - {!showAllElements && showBoardSidebar && ( -
    -
    e.stopPropagation()} // Prevent closing when clicking inside sidebar - > -
    -

    {t("your-boards")}

    -
    -
    -
    - {allBoardsData && allBoardsData.map((board, index) => ( -
    handleBoardSelection(index)} - > -
    - {board.name} -
    -
    - {board?.description} -
    -
    -
    -
    + { + !showAllElements && showBoardSidebar && ( +
    +
    e.stopPropagation()} // Prevent closing when clicking inside sidebar + > +
    +

    {t("your-boards")}

    +
    +
    +
    + {allBoardsData && allBoardsData.map((board, index) => ( +
    handleBoardSelection(index)} + > +
    + {board.name} +
    +
    + {board?.description} +
    +
    +
    +
    +
    + + {(board?.taskCount ? board.taskCount.completed : 0)} / {board?.taskCount ? board?.taskCount.pending + board?.taskCount?.completed : 0} +
    - - {(board?.taskCount ? board.taskCount.completed : 0)} / {board?.taskCount ? board?.taskCount.pending + board?.taskCount?.completed : 0} -
    -
    - ))} -
    -
    -
    +
    + + > + {t("configure-boards")} + +
    -
    - ) + ) }
    diff --git a/src/managers/TaskBoardFileManager.ts b/src/managers/TaskBoardFileManager.ts index 9258a4af..b12d97ed 100644 --- a/src/managers/TaskBoardFileManager.ts +++ b/src/managers/TaskBoardFileManager.ts @@ -39,12 +39,12 @@ export default class TaskBoardFileManager { // Read the file const file = await this.app.vault.adapter.readBinary(filePath); const decodedData = new TextDecoder().decode(file); - console.log( - "Loaded file :", - file, - "\nDecoded data :\n", - decodedData, - ); + // console.log( + // "Loaded file :", + // file, + // "\nDecoded data :\n", + // decodedData, + // ); if (!decodedData) { console.warn(`TaskBoard file is empty: ${filePath}`); @@ -53,10 +53,10 @@ export default class TaskBoardFileManager { // Parse JSON content const boardData: Board = JSON.parse(decodedData); - console.log( - `Successfully loaded board from: ${filePath}`, - boardData, - ); + // console.log( + // `Successfully loaded board from: ${filePath}`, + // boardData, + // ); return boardData; } catch (error) { console.error(`Error loading board from file ${filePath}:`, error); @@ -73,7 +73,7 @@ export default class TaskBoardFileManager { async loadBoardUsingIndex(boardIndex: number): Promise { try { const boardIndexToUse = boardIndex ?? this.currentBoardIndex; - console.log("All boards data :", this.allBoardsData); + // console.log("All boards data :", this.allBoardsData); // Validate board index if ( diff --git a/styles.css b/styles.css index eb6eec5d..2a2dd95c 100644 --- a/styles.css +++ b/styles.css @@ -274,6 +274,10 @@ button.clickable-icon.view-action.taskboardScanVaultBtn.highlight { box-shadow: 0px 0px 1px 1px var(--color-accent); } +.taskBoardMapViewSaveIcon { + color: var(--text-faint); +} + .taskBoardViewDropdown { display: flex; flex-direction: row; From 0746e322dfba21bef4b4e2017e1e137980f539d1 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Tue, 17 Feb 2026 22:21:59 +0530 Subject: [PATCH 121/236] OPTI : change allNodesData from `useState` to `useRef` --- src/components/MapView/MapView.tsx | 61 +++++++++++++----------------- 1 file changed, 26 insertions(+), 35 deletions(-) diff --git a/src/components/MapView/MapView.tsx b/src/components/MapView/MapView.tsx index 026b9a3c..cc252b4e 100644 --- a/src/components/MapView/MapView.tsx +++ b/src/components/MapView/MapView.tsx @@ -1,6 +1,6 @@ // /src/components/MapView/MapView.tsx -import React, { useState, useEffect, useRef, memo, useMemo } from 'react'; +import React, { useState, useEffect, useRef, memo, useMemo, useCallback } from 'react'; import { ReactFlow, ReactFlowProvider, @@ -81,7 +81,6 @@ const MapView: React.FC = ({ // Loading state for board map data (stored on the board object) const [storageLoaded, setStorageLoaded] = useState(false); // const [activeBoardSettings, setActiveBoardSettings] = useState(activeBoardData) - const [allNodesData, setAllNodesData] = useState({}); const [viewport, setViewport] = useState({ x: 10, y: 10, zoom: 1.5 }); const [boardChangeKey, setBoardChangeKey] = useState(0); // Track when board changes to force node recalculation const [isImporterPanelVisible, setIsImporterPanelVisible] = useState(false); // Task importer panel state @@ -89,20 +88,19 @@ const MapView: React.FC = ({ // ReactFlow instance ref so we can programmatically set viewport when switching boards // const reactFlowInstanceRef = useRef(null); const mapDataUpdated = useRef(false); - console.log("Is map component refreshes itself again and again...."); + // Store node positions in ref to avoid re-renders during drag operations + const allNodesData = useRef({}); useEffect(() => { const saveMapDataListener = () => { - console.log("saveMapDataListener called..."); if (!mapDataUpdated.current) return; let newBoardData = activeBoardData; newBoardData.mapView = { viewPortData: viewport, - nodesData: allNodesData, + nodesData: allNodesData.current, }; - console.log("Saving map data...\n", newBoardData); plugin.taskBoardFileManager.saveBoard(newBoardData); emitMapDataUpdatedSignal(false); @@ -110,7 +108,7 @@ const MapView: React.FC = ({ eventEmitter.on("SAVE_MAP", saveMapDataListener); return () => eventEmitter.off("SAVE_MAP", saveMapDataListener); - }, [activeBoardData]); + }, [activeBoardData, viewport]); const emitMapDataUpdatedSignal = (flag: boolean) => { console.log("emitMapDataUpdatedSignal called....\nflag : ", mapDataUpdated.current); @@ -174,8 +172,8 @@ const MapView: React.FC = ({ width: Number.isFinite(nodesData[id]?.width) ? nodesData[id].width : 300, }; }); - setAllNodesData(sanitizedPositions); - console.log("All nodes data loaded succussfully...\nData :", sanitizedPositions); + // Update useRef instead of state to avoid re-render + allNodesData.current = sanitizedPositions; // Load and sanitize viewport const rawVp = loadViewport(); @@ -202,10 +200,7 @@ const MapView: React.FC = ({ // Kanban-style initial layout, memoized const initialNodes: Node[] = useMemo(() => { // Don't calculate nodes until storage data is loaded - console.log("initialNodes : Value of storageLoaded = ", storageLoaded); if (!storageLoaded) { return []; } - - console.log("initialNodes : Will render nodes now..."); const newNodes: Node[] = []; const usedIds = new Set(); const duplicateIds = new Set(); @@ -246,7 +241,7 @@ const MapView: React.FC = ({ } usedIds.add(id); - const nodeData = allNodesData[id]; + const nodeData = allNodesData.current[id]; // Ensure width is always a valid number let nodeWidth = defaultWidth; @@ -375,6 +370,7 @@ const MapView: React.FC = ({ // Calculate edges from dependsOn property // TODO : Might be efficient to make use of the addEdge api of reactflow. function getEdgesFromTasks(): Edge[] { + console.log("getEdgesFromTasks : How many times is this running and when..."); const tasks: taskItem[] = allTasksFlattened; const edges: Edge[] = []; const idToTask = new Map(); @@ -445,26 +441,24 @@ const MapView: React.FC = ({ } const edges = useMemo(() => getEdgesFromTasks(), [allTasksFlattened, viewport]); - const handlenodePositionChange = () => { + const handlenodePositionChange = useCallback(() => { try { // Update positions for current board with validation - const nodesDataMap = nodes.reduce((acc, node) => { + // Only update useRef - no state update needed, avoiding re-render + const nodesDataMap: Record = {}; + for (const node of nodes) { const x = Number.isFinite(node.position?.x) ? node.position.x : 0; const y = Number.isFinite(node.position?.y) ? node.position.y : 0; const width = Number.isFinite(node?.width) ? node.width ?? 300 : 300; - acc[node.id] = { x, y, width }; - return acc; - }, {} as Record); + nodesDataMap[node.id] = { x, y, width }; + } - setAllNodesData(nodesDataMap); + allNodesData.current = nodesDataMap; emitMapDataUpdatedSignal(true); - - // if (!activeBoardData.mapView) activeBoardData.mapView = { viewPortData: viewport, nodesData: nodesDataMap }; - // else activeBoardData.mapView.nodesData = nodesDataMap; } catch (error) { bugReporterManagerInsatance.addToLogs(98, String(error), 'MapView.tsx/handlenodePositionTypeChange'); } - }; + }, [nodes, emitMapDataUpdatedSignal]); // Persist updated positions and sizes // const prevnodeSizeTypesRef = useRef>({}); @@ -607,7 +601,7 @@ const MapView: React.FC = ({ // }, 20000); const lastViewportSaveTime = useRef(0); - const debouncedSetViewportStorage = debounce((vp: viewPortType) => { + const debouncedSetViewportStorage = useCallback(debounce((vp: viewPortType) => { const now = Date.now(); if (now - lastViewportSaveTime.current > 2000) { // Validate viewport values before saving @@ -619,19 +613,16 @@ const MapView: React.FC = ({ try { // Update the in-memory board object; persisting to disk will be handled by SAVE_MAP elsewhere setViewport(safeViewport); - // if (!activeBoardData.mapView) activeBoardData.mapView = { viewPortData: safeViewport, nodesData: allNodesData }; - // else activeBoardData.mapView.viewPortData = safeViewport; + mapDataUpdated.current = true; lastViewportSaveTime.current = now; } catch (error) { bugReporterManagerInsatance.addToLogs(99, String(error), 'MapView.tsx/debouncedSetViewportStorage'); - } - } - }, 2000); + }, 2000), []); - const handlePaneContextMenu = (event: MouseEvent | React.MouseEvent) => { + const handlePaneContextMenu = useCallback((event: MouseEvent | React.MouseEvent) => { const sortMenu = new Menu(); @@ -736,7 +727,7 @@ const MapView: React.FC = ({ sortMenu.showAtMouseEvent( (event instanceof MouseEvent ? event : event.nativeEvent) ); - } + }, [mapViewSettings.background, mapViewSettings.showMinimap, mapViewSettings.animatedEdges]); // Will implement the below function if required in future. // const handleOnDragOver = () => { @@ -753,11 +744,11 @@ const MapView: React.FC = ({ // node.selected = false; // } - const toggleTasksImporterPanel = () => { + const toggleTasksImporterPanel = useCallback(() => { setIsImporterPanelVisible(prev => !prev); - } + }, []); - const handleEdgeClick = (event: any, edge: Edge) => { + const handleEdgeClick = useCallback((event: any, edge: Edge) => { // Show Obsidian menu for the selected edge const menu = new Menu(); menu.addItem((item) => { @@ -852,7 +843,7 @@ const MapView: React.FC = ({ }); menu.showAtMouseEvent(event instanceof MouseEvent ? event : event.nativeEvent); - } + }, [allTasksFlattened, taskNoteIdentifierTag, plugin]); if (allTasksFlattened.length === 0) { From b49f533f1e0a9f885daee193321a558f519aa079 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Tue, 17 Feb 2026 22:22:51 +0530 Subject: [PATCH 122/236] some UI improvements and refactoring --- src/components/TaskBoardViewContent.tsx | 4 ++-- src/managers/TaskBoardFileManager.ts | 17 ++++++++++------- styles.css | 5 +++-- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/components/TaskBoardViewContent.tsx b/src/components/TaskBoardViewContent.tsx index 38696429..950b7594 100644 --- a/src/components/TaskBoardViewContent.tsx +++ b/src/components/TaskBoardViewContent.tsx @@ -981,7 +981,7 @@ const TaskBoardViewContent: React.FC<{ plugin: TaskBoard, allBoards: Board[], cl
    {viewType === viewTypeNames.map && ( -
    { @@ -993,7 +993,7 @@ const TaskBoardViewContent: React.FC<{ plugin: TaskBoard, allBoards: Board[], cl }} > -
    + )}
    - {!showSearchInput && ( - {currentBoardData?.name} - )} -
    - ) : ( - // Desktop view: Original board titles -
    - {allBoardsData && allBoardsData.map((board, index) => ( + {allBoardsData && allBoardsData.length > 0 ? ( + + ) : ( - ))} + )} + {!showSearchInput && ( + {boardData?.name} + )}
    + ) : ( + // Desktop view: Original board titles (only render if allBoardsData is available) + allBoardsData && allBoardsData.length > 0 ? ( +
    + {allBoardsData.map((board, index) => ( + + ))} +
    + ) : ( +
    + ) )}
    @@ -958,15 +974,12 @@ const TaskBoardViewContent: React.FC<{ plugin: TaskBoard, allBoards: Board[], cl className={`ConfigureBtn ${(isMobileView || Platform.isMobile) ? "taskBoardViewHeaderHideElements" : ""}`} aria-label={t("board-configure-button")} onClick={() => - openBoardConfigModal(plugin, allBoardsData, activeBoardIndex, (updatedBoards, boardIndex) => { + openBoardConfigModal(plugin, currentBoardData, (updatedBoard: Board) => { // handleUpdateBoards(plugin, updatedBoards, setCurrentBoardData) - if (activeBoardIndex === boardIndex) { - setCurrentBoardData(updatedBoards[boardIndex]); - } - plugin.taskBoardFileManager.saveBoard(updatedBoards[boardIndex]); + setCurrentBoardData(updatedBoard); + plugin.taskBoardFileManager.saveBoard(updatedBoard); } - ) - } + )} > @@ -1012,9 +1025,9 @@ const TaskBoardViewContent: React.FC<{ plugin: TaskBoard, allBoards: Board[], cl
    - {/* Mobile board sidebar overlay */} + {/* Mobile board sidebar overlay (only render if allBoardsData is available) */} { - !showAllElements && showBoardSidebar && ( + allBoardsData && allBoardsData.length > 0 && !showAllElements && showBoardSidebar && (
    - {allBoardsData && allBoardsData.map((board, index) => ( + {allBoardsData.map((board, index) => (
    - openBoardConfigModal(plugin, allBoardsData!, activeBoardIndex, (updatedBoards, boardIndex) => { + openBoardConfigModal(plugin, currentBoardData, (updatedBoard: Board) => { // handleUpdateBoards(plugin, updatedBoards, setCurrentBoardData) - if (activeBoardIndex === boardIndex) { - setCurrentBoardData(updatedBoards[boardIndex]); - } - plugin.taskBoardFileManager.saveBoard(updatedBoards[boardIndex]); + setCurrentBoardData(updatedBoard); + plugin.taskBoardFileManager.saveBoard(updatedBoard); } ) } @@ -1077,11 +1088,11 @@ const TaskBoardViewContent: React.FC<{ plugin: TaskBoard, allBoards: Board[], cl }
    - {currentBoardData ? ( + {boardData ? ( viewType === viewTypeNames.kanban ? ( { + async loadBoardUsingPath(filePath: string): Promise { try { if (!filePath || filePath.trim() === "") { console.error(`No board file path provided to load the board`); - return null; + return undefined; } // Check if board is already cached in memory by file path @@ -200,10 +200,10 @@ export default class TaskBoardFileManager { ); } - return null; + return undefined; } catch (error) { console.error(`Error loading board from file ${filePath}:`, error); - return null; + return undefined; } } @@ -737,4 +737,53 @@ export default class TaskBoardFileManager { this.recentBoardsData = {}; console.log("Cleared cached board data"); } + + /** + * Store the mapping between a leaf ID and file path in localStorage + * This allows us to restore the correct board when a leaf tab is reopened + * @param leafID - The ID of the Obsidian leaf (tab) + * @param filePath - The path to the .taskboard file + */ + setFilepathToLeafID(leafID: string, filePath: string): void { + try { + if (!leafID || leafID.trim() === "") { + console.error("Cannot set filepath to leaf ID: leafID is empty"); + return; + } + if (!filePath || filePath.trim() === "") { + console.error("Cannot set filepath to leaf ID: filePath is empty"); + return; + } + const key = `taskboard-leaf-${leafID}`; + localStorage.setItem(key, filePath); + console.log(`Stored filepath mapping: leaf ${leafID} -> ${filePath}`); + } catch (error) { + console.error(`Error storing filepath mapping for leaf ${leafID}:`, error); + } + } + + /** + * Retrieve the file path associated with a leaf ID from localStorage + * @param leafID - The ID of the Obsidian leaf (tab) + * @returns The stored file path, or null if not found + */ + getFilepathFromLeafID(leafID: string): string | null { + try { + if (!leafID || leafID.trim() === "") { + console.error("Cannot get filepath from leaf ID: leafID is empty"); + return null; + } + const key = `taskboard-leaf-${leafID}`; + const filePath = localStorage.getItem(key); + if (filePath) { + console.log(`Retrieved filepath mapping: leaf ${leafID} -> ${filePath}`); + return filePath; + } + console.warn(`No filepath mapping found for leaf ${leafID}`); + return null; + } catch (error) { + console.error(`Error retrieving filepath mapping for leaf ${leafID}:`, error); + return null; + } + } } diff --git a/src/modals/BoardConfigModal.tsx b/src/modals/BoardConfigModal.tsx index 18ac1222..ade9dc15 100644 --- a/src/modals/BoardConfigModal.tsx +++ b/src/modals/BoardConfigModal.tsx @@ -25,9 +25,9 @@ import { generateRandomTempTaskId } from "src/utils/TaskItemUtils"; interface ConfigModalProps { plugin: TaskBoard; settingManager: SettingsManager; - boards: Board[]; - activeBoardIndex: number; - onSave: (updatedBoards: Board[], boardIndex: number) => void; + currentBoardData: Board; + allBoardsData?: Board[]; + onSave: (updatedBoard: Board) => void; onClose: () => void; setIsEdited: (value: boolean) => void; } @@ -35,22 +35,24 @@ interface ConfigModalProps { const ConfigModalContent: React.FC = ({ plugin, settingManager, - boards, - activeBoardIndex, + currentBoardData, + allBoardsData, onSave, onClose, setIsEdited, }) => { const [localBoards, setLocalBoards] = useState(() => { try { - return boards ? JSON.parse(JSON.stringify(boards)) : []; + return allBoardsData ? JSON.parse(JSON.stringify(allBoardsData)) : []; } catch (e) { bugReporterManagerInsatance.showNotice(34, "Error parsing boards data", e as string, "BoardConfigModal.tsx/localBoards"); return []; } }); - const [selectedBoardIndex, setSelectedBoardIndex] = useState(activeBoardIndex); + const [selectedBoardIndex, setSelectedBoardIndex] = useState(0); + const [activeBoardData, setActiveBoardData] = useState(currentBoardData); const [isAddColumnModalOpen, setIsAddColumnModalOpen] = useState(false); + const [modifiedBoardIds, setModifiedBoardIds] = useState>(new Set()); const globalSettingsHTMLSection = useRef(null); const columnListRef = useRef(null); @@ -98,31 +100,30 @@ const ConfigModalContent: React.FC = ({ return; } - const currentBoards = [...localBoards]; - const [movedBoard] = currentBoards.splice(evt.oldIndex, 1); - currentBoards.splice(evt.newIndex, 0, movedBoard); - - // Update board.index to be the new 0-based array index - const finalBoards = currentBoards.map((board, idx) => ({ - ...board, - index: idx // 0-based index - })); + // Reorder boards in local state + const updatedBoards = [...localBoards]; + const [movedBoard] = updatedBoards.splice(evt.oldIndex, 1); + updatedBoards.splice(evt.newIndex, 0, movedBoard); + + // Update the taskBoardFilesRegistry order based on the sorted boards + const newRegistry: { [key: string]: { boardId: string; filePath: string; boardName: string; boardDescription: string } } = {}; + updatedBoards.forEach((board) => { + const filePath = plugin.taskBoardFileManager.getBoardFilepathFromRegistry(board.id); + if (filePath) { + newRegistry[board.id] = { + boardId: board.id, + filePath: filePath, + boardName: board.name, + boardDescription: board.description || '' + }; + } + }); - setLocalBoards(finalBoards); + // Update plugin settings with new registry order + plugin.settings.data.taskBoardFilesRegistry = newRegistry; + plugin.saveSettings(); - // Update selectedBoardIndex (which is a 0-based array index) - if (selectedBoardIndex === evt.oldIndex) { - setSelectedBoardIndex(evt.newIndex); - } else { - // Adjust selectedBoardIndex if an item moved across it - if (evt.oldIndex < selectedBoardIndex && evt.newIndex >= selectedBoardIndex) { - // Item moved from before selected to at or after selected: selected moves left - setSelectedBoardIndex(prevIdx => prevIdx - 1); - } else if (evt.oldIndex > selectedBoardIndex && evt.newIndex <= selectedBoardIndex) { - // Item moved from after selected to at or before selected: selected moves right - setSelectedBoardIndex(prevIdx => prevIdx + 1); - } - } + setLocalBoards(updatedBoards); setIsEdited(true); }, }); @@ -167,6 +168,7 @@ const ConfigModalContent: React.FC = ({ const updatedBoards = [...localBoards]; updatedBoards[selectedBoardIndex].swimlanes = updatedConfig; setLocalBoards(updatedBoards); + setModifiedBoardIds(prev => new Set(prev).add(updatedBoards[selectedBoardIndex].id)); setIsEdited(true); } ); @@ -191,6 +193,7 @@ const ConfigModalContent: React.FC = ({ filePaths: columnData.filePaths, }); setLocalBoards(updatedBoards); + setModifiedBoardIds(prev => new Set(prev).add(updatedBoards[boardIndex].id)); handleCloseAddColumnModal(); setIsEdited(true); }; @@ -237,8 +240,10 @@ const ConfigModalContent: React.FC = ({ nodesData: {}, }, }; - setLocalBoards([...oldBoards, newBoard]); - setSelectedBoardIndex(localBoards.length); + const updatedBoards = [...oldBoards, newBoard]; + setLocalBoards(updatedBoards); + setModifiedBoardIds(prev => new Set(prev).add(newBoard.id)); + setSelectedBoardIndex(updatedBoards.length - 1); setIsEdited(true); }; @@ -276,6 +281,7 @@ const ConfigModalContent: React.FC = ({ const updatedBoards = [...localBoards, duplicatedBoard]; setLocalBoards(updatedBoards); + setModifiedBoardIds(prev => new Set(prev).add(duplicatedBoard.id)); setSelectedBoardIndex(updatedBoards.length - 1); setIsEdited(true); } @@ -320,13 +326,31 @@ const ConfigModalContent: React.FC = ({ const column = updatedBoards[boardIndex].columns[columnIndex]; column.active = !column.active; // Toggle the active state setLocalBoards(updatedBoards); // Update the state - // onSave(updatedBoards); // Save the updated state + setModifiedBoardIds(prev => new Set(prev).add(updatedBoards[boardIndex].id)); + setIsEdited(true); }; // Function to save changes - const handleSave = () => { - onSave(localBoards, selectedBoardIndex); - // onClose(); + const handleSave = async () => { + // Save only modified boards + for (const boardId of modifiedBoardIds) { + const boardToSave = localBoards.find(board => board.id === boardId); + if (boardToSave) { + const filePath = plugin.taskBoardFileManager.getBoardFilepathFromRegistry(boardId); + if (filePath) { + await plugin.taskBoardFileManager.saveBoardToFile(filePath, boardToSave); + } + } + } + + // Find and return the updated current board data + const updatedCurrentBoard = localBoards.find(board => board.id === currentBoardData.id); + if (updatedCurrentBoard) { + onSave(updatedCurrentBoard); + } + + // Reset modified boards tracking + setModifiedBoardIds(new Set()); }; const renderGlobalSettingsTab = (boardIndex: number) => { @@ -383,6 +407,7 @@ const ConfigModalContent: React.FC = ({ (updatedBoards[boardIndex].columns[columnIndex] as any)[field] = value; setLocalBoards(updatedBoards); + setModifiedBoardIds(prev => new Set(prev).add(updatedBoards[boardIndex].id)); setIsEdited(true); }; @@ -391,6 +416,7 @@ const ConfigModalContent: React.FC = ({ const updatedBoards = [...localBoards]; updatedBoards[index].name = newName; setLocalBoards(updatedBoards); + setModifiedBoardIds(prev => new Set(prev).add(updatedBoards[index].id)); setIsEdited(true); }; @@ -398,6 +424,7 @@ const ConfigModalContent: React.FC = ({ const updatedBoards = [...localBoards]; updatedBoards[index].description = description; setLocalBoards(updatedBoards); + setModifiedBoardIds(prev => new Set(prev).add(updatedBoards[index].id)); setIsEdited(true); }; @@ -424,6 +451,7 @@ const ConfigModalContent: React.FC = ({ updatedBoards[boardIndex][field] = value as boolean; } setLocalBoards(updatedBoards); + setModifiedBoardIds(prev => new Set(prev).add(updatedBoards[boardIndex].id)); setIsEdited(true); }; @@ -440,6 +468,7 @@ const ConfigModalContent: React.FC = ({ const updatedBoards = [...localBoards]; updatedBoards[boardIndex].columns.splice(columnIndex, 1); setLocalBoards(updatedBoards); + setModifiedBoardIds(prev => new Set(prev).add(updatedBoards[boardIndex].id)); setIsEdited(true); } else { bugReporterManagerInsatance.showNotice(35, "There was an error while trying to delete the column. The column index was -1 for some reason.", `RROR : Column index is -1\nColumn name :${localBoards[boardIndex].columns[columnIndex].name}`, "BoardConfigModal.tsx/handleDeleteColumnFromBoard"); @@ -482,6 +511,7 @@ const ConfigModalContent: React.FC = ({ }); setLocalBoards(updatedBoards); + setModifiedBoardIds(prev => new Set(prev).add(updatedBoards[selectedBoardIndex].id)); setIsEdited(true); // I need to re-render the columnListRef section here @@ -911,50 +941,61 @@ const ConfigModalContent: React.FC = ({ return ( <> {renderAddColumnModal()} - + {allBoardsData && allBoardsData.length > 0 && ( + + )}
    -
    -
    -
    { - setSelectedBoardIndex(-1); - toggleSidebar(); - }}>{t("global-settings")}
    - -
    - -
    {t("your-boards")}
    -
    - {localBoards.map((board, index) => ( -
    { - setSelectedBoardIndex(index); - toggleSidebar(); - }} - > - - {board.name} - - {/* Add drag handle */} + {allBoardsData && allBoardsData.length > 0 && ( + <> +
    +
    +
    { + setSelectedBoardIndex(-1); + toggleSidebar(); + }}>{t("global-settings")}
    + +
    + +
    {t("your-boards")}
    +
    + {localBoards.map((board, index) => ( +
    { + setSelectedBoardIndex(index); + toggleSidebar(); + }} + > + + {board.name} + + {/* Add drag handle */} +
    + ))}
    - ))} +
    +
    + +
    + +
    +
    + {selectedBoardIndex === -1 + ? <>{renderGlobalSettingsTab(selectedBoardIndex)} + :
    {renderBoardSettings(selectedBoardIndex)}
    + } +
    + + )} + {(!allBoardsData || allBoardsData.length === 0) && ( +
    +
    {renderBoardSettings(0)}
    -
    - -
    - -
    -
    -
    - {selectedBoardIndex === -1 - ? <>{renderGlobalSettingsTab(selectedBoardIndex)} - :
    {renderBoardSettings(selectedBoardIndex)}
    - } -
    + )}
    @@ -966,24 +1007,25 @@ const ConfigModalContent: React.FC = ({ export class BoardConfigureModal extends Modal { root: ReactDOM.Root; settingsManager: SettingsManager; - boards: Board[]; - activeBoardIndex: number; + currentBoardData: Board; + allBoardsData: Board[] | undefined; isEdited: boolean; - onSave: (updatedBoards: Board[], boardIndex: number) => void; + onSave: (updatedBoard: Board) => void; plugin: TaskBoard; constructor( plugin: TaskBoard, - boards: Board[], - activeBoardIndex: number, - onSave: (updatedBoards: Board[], boardIndex: number) => void + currentBoardData: Board, + onSave: (updatedBoard: Board) => void, + allBoardsData?: Board[] ) { super(plugin.app); this.plugin = plugin; - this.boards = boards; - this.activeBoardIndex = activeBoardIndex; - this.isEdited = false; + this.currentBoardData = currentBoardData; this.onSave = onSave; + this.allBoardsData = allBoardsData; + + this.isEdited = false; this.settingsManager = new SettingsManager(plugin); const { contentEl } = this; this.root = ReactDOM.createRoot(contentEl); @@ -996,11 +1038,11 @@ export class BoardConfigureModal extends Modal { { + currentBoardData={this.currentBoardData} + allBoardsData={this.allBoardsData} + onSave={(updatedBoard: Board) => { this.isEdited = false; - this.onSave(updatedBoards, boardIndex); + this.onSave(updatedBoard); this.close(); }} onClose={() => this.close()} diff --git a/src/services/OpenModals.ts b/src/services/OpenModals.ts index eba6a5a3..8ac7dfac 100644 --- a/src/services/OpenModals.ts +++ b/src/services/OpenModals.ts @@ -34,11 +34,10 @@ import { getCurrentLocalDateTimeString } from "src/utils/DateTimeCalculations"; // Function to open the BoardConfigModal export const openBoardConfigModal = ( plugin: TaskBoard, - boards: Board[], - activeBoardIndex: number, - onSave: (updatedBoards: Board[], boardIndex: number) => void, + currentBoardData: Board, + onSave: (updatedBoard: Board) => void, ) => { - new BoardConfigureModal(plugin, boards, activeBoardIndex, onSave).open(); + new BoardConfigureModal(plugin, currentBoardData, onSave).open(); }; // Function to open the BoardConfigModal diff --git a/src/settings/SettingConstructUI.ts b/src/settings/SettingConstructUI.ts index cf3627da..9c22da44 100644 --- a/src/settings/SettingConstructUI.ts +++ b/src/settings/SettingConstructUI.ts @@ -299,6 +299,7 @@ export class SettingsManager { const { scanFilters, openOnStartup, + loadAllBoards, scanMode, tasksCacheFilePath, showModifiedFilesNotice, @@ -603,6 +604,17 @@ export class SettingsManager { renderTasksCachePathSetting(); + // Setting to show/Hide the Header of the task card + new Setting(contentEl) + .setName(t("load-all-boards")) + .setDesc(t("load-all-boards-info")) + .addToggle((toggle) => + toggle.setValue(loadAllBoards).onChange(async (value) => { + this.globalSettings!.loadAllBoards = value; + await this.saveSettings(); + }), + ); + // Setting to show/Hide the Header of the task card new Setting(contentEl) .setName(t("open-board-on-obsidian-startup")) @@ -2544,7 +2556,9 @@ export class SettingsManager { "" + t("note") + " : " + - t("If you are using inline-tasks, then please note that, this plugin dont scan all kinds of format at present. For example, if there are spaces as a seperater (2026 01 01), then this plugin might fail to scan the value, even if its able to apply the date.") + + t( + "If you are using inline-tasks, then please note that, this plugin dont scan all kinds of format at present. For example, if there are spaces as a seperater (2026 01 01), then this plugin might fail to scan the value, even if its able to apply the date.", + ) + "" + "date-fns library formatting guide." + "" + diff --git a/src/types/obsidian-ex.d.ts b/src/types/obsidian-ex.d.ts index 85988681..9dfed3d5 100644 --- a/src/types/obsidian-ex.d.ts +++ b/src/types/obsidian-ex.d.ts @@ -525,6 +525,7 @@ declare module "obsidian" { height: number; tabHeaderInnerIconEl: HTMLElement; tabHeaderInnerTitleEl: HTMLElement; + id: number; } interface MarkdownScrollableEditView extends MarkdownBaseView { diff --git a/src/utils/lang/locale/en.ts b/src/utils/lang/locale/en.ts index 7c9a6607..cceb7e0e 100644 --- a/src/utils/lang/locale/en.ts +++ b/src/utils/lang/locale/en.ts @@ -173,7 +173,7 @@ const en: Lang = { "hide-description": "Hide description", "show-description": "Show description", "open-board-on-obsidian-startup": "Open board on Obsidian startup", - "open-board-on-obsidian-startup-info": "Enable this option if you want to see the task board to be visible, when you close and open Obsidian.", + "open-board-on-obsidian-startup-info": "Enable this option if you want to see the task board to be visible, when you close and open Obsidian. You last viewed board will be opened.", "file": "File", "discard": "Discard", "go-back": "Go back and save", diff --git a/src/views/TaskBoardView.tsx b/src/views/TaskBoardView.tsx index 001f33fa..6574ce23 100644 --- a/src/views/TaskBoardView.tsx +++ b/src/views/TaskBoardView.tsx @@ -12,14 +12,17 @@ import { PENDING_SCAN_FILE_STACK, VIEW_TYPE_TASKBOARD } from "src/interfaces/Con import { openScanVaultModal } from "../services/OpenModals"; import { t } from "src/utils/lang/helper"; import { eventEmitter } from "src/services/EventEmitter"; +import { bugReporterManagerInsatance } from "src/managers/BugReporter"; export class TaskBoardView extends ItemView { plugin: TaskBoard; + leaf: WorkspaceLeaf; // boards: Board[]; root: Root | null = null; constructor(plugin: TaskBoard, leaf: WorkspaceLeaf) { super(leaf); + this.leaf = leaf; this.app = plugin.app; this.plugin = plugin; // this.boards = []; @@ -61,8 +64,10 @@ export class TaskBoardView extends ItemView { if (mandatoryScanSignal) this.highlighgtScanvaultIcon(); - // All boards data should be cumpulsorily loaded. - let allBoardsData = await this.plugin.taskBoardFileManager.getAllBoards(); + // All boards data will be loaded based on user configuration + let allBoardsData; + if (this.plugin.settings.data.loadAllBoards) + allBoardsData = await this.plugin.taskBoardFileManager.getAllBoards(); // Check if a specific .taskboard file was clicked from File Navigator // First check the leaf instance directly (set by monkey patch) @@ -70,13 +75,27 @@ export class TaskBoardView extends ItemView { console.log("TaskBoardView.tsx : clickedFilePath from leaf:", clickedFilePath); - let clickedFileData: Board | null; + let clickedFileData: Board | undefined; if (clickedFilePath && typeof clickedFilePath === 'string' && clickedFilePath.endsWith('.taskboard')) { // User clicked on a specific .taskboard file - load just that file clickedFileData = await this.plugin.taskBoardFileManager.loadBoardUsingPath(clickedFilePath); - this.renderBoard(allBoardsData, clickedFileData); + if (clickedFileData) + this.renderBoard(clickedFileData, allBoardsData); + else + bugReporterManagerInsatance.showNotice(183, `There was an issue with opening the task board file : ${clickedFilePath}`, "clickedFileData is undefined", "TaskBoardView.tsx/onOpen"); } else { - this.renderBoard(allBoardsData); + // This will be the case, if the leaf was already opened before Obsidian app was closed or it was in in-active state and now suddenly brought into active state. + // In this case, we should : + // - Fetch the leaf id from the this.leaf.id + // - Now, from the mapping stored in localStorage, get the filePath for this leaf.id + // - Using this filePath, fetch the boardData. + const leafID = this.leaf?.id; + const filePath = await this.plugin.taskBoardFileManager.getFilepathFromLeafID(String(leafID)); + const lastViewedBoardData = await this.plugin.taskBoardFileManager.loadBoardUsingPath(filePath || ""); + if (lastViewedBoardData) + this.renderBoard(lastViewedBoardData, allBoardsData); + else + bugReporterManagerInsatance.showNotice(183, `There was an issue with opening the task board file : ${filePath}`, "lastViewedBoardData is undefined", "TaskBoardView.tsx/onOpen"); } } @@ -105,14 +124,14 @@ export class TaskBoardView extends ItemView { // } // } - private renderBoard(allBoardsData: Board[], clickedFileData?: Board | null) { + private renderBoard(currentBoardData: Board, allBoardsData?: Board[] | undefined) { this.root = createRoot(this.containerEl.children[1]); this.root.render( , , ); From 4f29b12da678d264ddbd0dd0387c6216e25b4aa4 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Sat, 21 Feb 2026 17:39:30 +0530 Subject: [PATCH 134/236] feat : a new modal to show all boards as cards | Boards Explorer --- src/components/TaskBoardViewContent.tsx | 4 +- src/modals/BoardsExplorer.ts | 163 ++++++++++++++++++++++++ src/services/OpenModals.ts | 17 ++- 3 files changed, 181 insertions(+), 3 deletions(-) create mode 100644 src/modals/BoardsExplorer.ts diff --git a/src/components/TaskBoardViewContent.tsx b/src/components/TaskBoardViewContent.tsx index 570b9dda..209a94b9 100644 --- a/src/components/TaskBoardViewContent.tsx +++ b/src/components/TaskBoardViewContent.tsx @@ -9,7 +9,7 @@ import { taskJsonMerged } from "src/interfaces/TaskItem"; import { App, debounce, Platform, Menu } from "obsidian"; import type TaskBoard from "main"; import { eventEmitter } from "src/services/EventEmitter"; -import { openAddNewTaskModal, openBoardConfigModal, openScanVaultModal, openTaskBoardActionsModal } from "../services/OpenModals"; +import { openAddNewTaskModal, openBoardConfigModal, openScanVaultModal, openTaskBoardActionsModal, openBoardsExplorerModal } from "../services/OpenModals"; import { t } from "src/utils/lang/helper"; import KanbanBoard from "./KanbanView/KanbanBoardView"; import MapView from "./MapView/MapView"; @@ -881,7 +881,7 @@ const TaskBoardViewContent: React.FC<{ plugin: TaskBoard, currentBoardData: Boar ) : ( + {/* On Left-side */} + {allBoardsData && allBoardsData.length > 0 ? ( + <> + {showAllElements ? ( +
    + {allBoardsData.map((board, index) => ( + + ))} +
    ) : ( - +
    + + {!showSearchInput && ( + {boardData?.name} + )} +
    )} + + ) : ( +
    + {!showSearchInput && ( {boardData?.name} )}
    - ) : ( - // Desktop view: Original board titles (only render if allBoardsData is available) - allBoardsData && allBoardsData.length > 0 ? ( -
    - {allBoardsData.map((board, index) => ( - - ))} -
    - ) : ( -
    - ) )} + + {/* On Right-side */}
    -
    = 1500 ? "" : "-hidden"}`}> +
    Date: Sun, 22 Feb 2026 10:32:52 +0530 Subject: [PATCH 142/236] OPTI : use Obsidian callbacks for ItemView to properly handle leaf history and remove use of localStorage for storing the mapping --- src/views/TaskBoardView.tsx | 229 +++++++++++++++++++++++++++--------- 1 file changed, 173 insertions(+), 56 deletions(-) diff --git a/src/views/TaskBoardView.tsx b/src/views/TaskBoardView.tsx index f55df2a7..965f2392 100644 --- a/src/views/TaskBoardView.tsx +++ b/src/views/TaskBoardView.tsx @@ -1,15 +1,15 @@ // src/views/TaskBoardView.tsx -import { ItemView, Platform, WorkspaceLeaf } from "obsidian"; +import { ItemView, Platform, WorkspaceLeaf, ViewStateResult, Notice, Menu } from "obsidian"; import { Root, createRoot } from "react-dom/client"; -import { RefreshIcon, ScanVaultIcon, TaskBoardIcon } from "src/interfaces/Icons"; +import { funnelIcon, RefreshIcon, ScanVaultIcon, TaskBoardIcon } from "src/interfaces/Icons"; import { StrictMode } from "react"; import { Board } from "src/interfaces/BoardConfigs"; import TaskBoardViewContent from "src/components/TaskBoardViewContent"; import type TaskBoard from "../../main"; import { PENDING_SCAN_FILE_STACK, VIEW_TYPE_TASKBOARD } from "src/interfaces/Constants"; -import { openScanVaultModal } from "../services/OpenModals"; +import { openBoardConfigModal, openScanVaultModal } from "../services/OpenModals"; import { t } from "src/utils/lang/helper"; import { eventEmitter } from "src/services/EventEmitter"; import { bugReporterManagerInsatance } from "src/managers/BugReporter"; @@ -17,8 +17,8 @@ import { bugReporterManagerInsatance } from "src/managers/BugReporter"; export class TaskBoardView extends ItemView { plugin: TaskBoard; leaf: WorkspaceLeaf; - // boards: Board[]; root: Root | null = null; + private currentFilePath: string | undefined = undefined; constructor(plugin: TaskBoard, leaf: WorkspaceLeaf) { super(leaf); @@ -37,81 +37,194 @@ export class TaskBoardView extends ItemView { return t("task-board"); } - getSettings() { - return this.plugin.settings; - } + onPaneMenu(menu: Menu, source: "more-options" | "tab-header" | string): void { + if (source === "more-options") { + menu.addItem((item) => { + item.setTitle(t("quick-actions")); + item.setIsLabel(true); + }); + menu.addItem((item) => { + item.setTitle(t("refresh-the-board")); + item.setIcon("rotate-cw"); + item.onClick(async () => { + // refreshBoardButton(); + }); + }); + menu.addItem((item) => { + item.setTitle(t("show-hide-properties")); + item.setIcon("list"); + item.onClick(async () => { + // handlePropertiesBtnClick(event); + }); + }); + menu.addItem((item) => { + item.setTitle(t("open-board-filters-modal")); + item.setIcon(funnelIcon); + item.onClick(async () => { + // handleFilterButtonClick(event); + }); + }); + menu.addItem((item) => { + item.setTitle(t("open-board-configuration-modal")); + item.setIcon("settings"); + item.onClick(async () => { + // openBoardConfigModal(plugin, currentBoardData, (updatedBoard: Board) => { + // // handleUpdateBoards(plugin, updatedBoards, setCurrentBoardData) + // // setCurrentBoardData(updatedBoard); + // this.plugin.taskBoardFileManager.saveBoard(updatedBoard); + // }) + }); + }); + menu.addItem((item) => { + item.setTitle(t("scan-vault-modal")); + item.setIcon(ScanVaultIcon); + item.onClick(async () => { + openScanVaultModal(this.plugin); + }); + }); - async onOpen() { - if (Platform.isMobile) { - this.addAction(RefreshIcon, t("refresh-board-button"), async () => { - const fileStackString = localStorage.getItem(PENDING_SCAN_FILE_STACK); - const fileStack = fileStackString ? JSON.parse(fileStackString) : null; - if (fileStack && fileStack.length > 0) { - await this.plugin.realTimeScanner.processAllUpdatedFiles(); - } - eventEmitter.emit("REFRESH_BOARD"); - }).addClass("taskboardRefreshBtn"); + menu.addItem((item) => { + item.setTitle(t("view-type")); + item.setIsLabel(true); + }); + menu.addItem((item) => { + item.setTitle(t("kanban-view")); + item.setIcon("square-kanban"); + item.onClick(async () => { + eventEmitter.emit("SWITCH_VIEW", 'kanban'); + }); + }); + menu.addItem((item) => { + item.setTitle(t("map-view")); + item.setIcon("network"); + item.onClick(async () => { + eventEmitter.emit("SWITCH_VIEW", 'map'); + }); + }); } + } - const mandatoryScanSignal = localStorage.getItem("manadatoryScan") === "true"; - - if (!Platform.isMobile || mandatoryScanSignal) { - this.addAction(ScanVaultIcon, t("scan-vault-modal"), () => { - openScanVaultModal(this.app, this.plugin); - }).addClass("taskboardScanVaultBtn"); - } + /** + * This is called by Obsidian when the layout is about to be save its state/layout. + * @returns The state data to be stored inside the workspace.json file inside the + * Obsidian's config folder. (.obsidian/workspace.json) + */ + getState() { + // Save the current filePath to the workspace state + return { + ...super.getState(), + ...(this.currentFilePath ? { filePath: this.currentFilePath } : {}), + }; + } - if (mandatoryScanSignal) this.highlighgtScanvaultIcon(); + /** + * This setState is always called after the onOpen function has finished its work by Obsidian. + * If the leaf was saved by Obsidian in its workspace.json file previosly, + * we can get the data in this function through the state param. + * @param state + * @param result + */ + async setState(state: any, result: ViewStateResult): Promise { + const { filePath } = state; + console.log(`Running setState for filepath :${filePath}`); if (this.plugin.settings.data.loadAllBoards) { // All boards data will be loaded based on user configuration let allBoardsData = await this.plugin.taskBoardFileManager.getAllBoards(); + new Notice("Implementation is pending...", 0); } else { - // Here we shall handle essentially three cases of opening this view : - // 1. When the board file is clicked from file-navigator. - // 2. When the leaf changes from in-active state to active state. - // 3. When the plugin ribbon icon is clicked. - const leafID = this.leaf?.id; - console.log("Leaf ID : ", leafID); - + // Check if a specific .taskboard file was clicked from File Navigator const clickedFilePath = (this.leaf as any).taskboardFilePath as string | undefined; - console.log("TaskBoardView.tsx : clickedFilePath from leaf:", clickedFilePath); - let clickedFileData: Board | undefined; if (clickedFilePath && typeof clickedFilePath === 'string' && clickedFilePath.endsWith('.taskboard')) { - // Check if a specific .taskboard file was clicked from File Navigator - // First check the leaf instance directly (set by monkey patch) - clickedFileData = await this.plugin.taskBoardFileManager.loadBoardUsingPath(clickedFilePath); - if (clickedFileData) + // Load and render the clicked file + const clickedFileData = await this.plugin.taskBoardFileManager.loadBoardUsingPath(clickedFilePath); + if (clickedFileData) { + this.currentFilePath = clickedFilePath; this.renderBoard(clickedFileData, undefined); - else + } else { bugReporterManagerInsatance.showNotice(183, `There was an issue with opening the task board file : ${clickedFilePath}`, "clickedFileData is undefined", "TaskBoardView.tsx/onOpen"); - - this.plugin.taskBoardFileManager.setFilepathToLeafID(leafID, clickedFilePath); + } } else { - // First lets check if this leafID already exists in localStorage - const filePath = await this.plugin.taskBoardFileManager.getFilepathFromLeafID(leafID); - if (filePath) { - const lastViewedBoardData = await this.plugin.taskBoardFileManager.loadBoardUsingPath(filePath || ""); - if (lastViewedBoardData) - this.renderBoard(lastViewedBoardData, undefined); - else - bugReporterManagerInsatance.showNotice(184, `There was an issue with opening the task board file : ${filePath}`, "lastViewedBoardData is undefined", "TaskBoardView.tsx/onOpen"); + // In this case, mostly user is opening the leaf from the ribbon icon + // Show last viewed board - // } else if (filePath === undefined) { - // bugReporterManagerInsatance.showNotice(186, `There was some issue while fetching the filepath from localStorage for the following leafID : ${leafID}`, "filePath is undefined", "TaskBoardView.tsx/onOpen"); - } else { - // In this case, mostly user is opening the leaf from the ribbon icon - // Show last viewed board + // Check if the board was already loaded by setState() when workspace was restored + // if (this.currentFilePath) { + if (filePath && typeof filePath === "string") { + // Use the filePath from saved state to load and render the board + const boardData = await this.plugin.taskBoardFileManager.loadBoardUsingPath( + filePath + ); + if (boardData) { + this.currentFilePath = filePath; + this.renderBoard(boardData, undefined); + } else { + bugReporterManagerInsatance.showNotice( + 183, + `There was an issue with opening the task board file : ${filePath}`, + "boardData is undefined", + "TaskBoardView.tsx/setState" + ); + } + } + // } + else { const lastViewedBoardData = await this.plugin.taskBoardFileManager.getLastOpenedBoard(); - if (lastViewedBoardData) + if (lastViewedBoardData) { + // Get the filePath from the registry + const taskBoardFilesRegistry = this.plugin.settings.data.taskBoardFilesRegistry || {}; + const registryEntries = Object.entries(taskBoardFilesRegistry) + .filter(([key]) => isNaN(Number(key))) + .slice(0, 1); + + if (registryEntries.length > 0) { + const [, firstItemFromRegistry] = registryEntries[0]; + if (firstItemFromRegistry?.filePath) { + this.currentFilePath = firstItemFromRegistry.filePath; + } + } + this.renderBoard(lastViewedBoardData, undefined); - else + } else { bugReporterManagerInsatance.showNotice(185, `There was an issue with opening the last viewed board by user`, "lastViewedBoardData is undefined", "TaskBoardView.tsx/onOpen"); + } + } } } + + await super.setState(state, result); + } + + /** + * This function is ran by Obsidian, whenever a new leaf is created or + * an in-active leaf is brough to life. + */ + async onOpen() { + console.log("Running onOpen..."); + if (Platform.isMobile) { + this.addAction(RefreshIcon, t("refresh-board-button"), async () => { + const fileStackString = localStorage.getItem(PENDING_SCAN_FILE_STACK); + const fileStack = fileStackString ? JSON.parse(fileStackString) : null; + + if (fileStack && fileStack.length > 0) { + await this.plugin.realTimeScanner.processAllUpdatedFiles(); + } + eventEmitter.emit("REFRESH_BOARD"); + }).addClass("taskboardRefreshBtn"); + } + + const mandatoryScanSignal = localStorage.getItem("manadatoryScan") === "true"; + + if (!Platform.isMobile || mandatoryScanSignal) { + this.addAction(ScanVaultIcon, t("scan-vault-modal"), () => { + openScanVaultModal(this.plugin); + }).addClass("taskboardScanVaultBtn"); + } + + if (mandatoryScanSignal) this.highlighgtScanvaultIcon(); } async highlighgtScanvaultIcon() { @@ -147,9 +260,13 @@ export class TaskBoardView extends ItemView { plugin={this.plugin} allBoards={allBoardsData} currentBoardData={currentBoardData} + currentLeaf={this.leaf} />, , ); + + // Signal the workspace to save the updated layout state + this.app.workspace.requestSaveLayout(); } async onClose() { From 2e61ca81fb6887a2acff239a495f4885f22d4594 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Sun, 22 Feb 2026 10:33:43 +0530 Subject: [PATCH 143/236] test the latest changes | working --- data.json | 26 +++--- task-board-logs.log | 199 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 213 insertions(+), 12 deletions(-) diff --git a/data.json b/data.json index 261e75b9..9e0bf2a9 100644 --- a/data.json +++ b/data.json @@ -266,20 +266,26 @@ "renderVisibleNodes": false, "edgeType": "default" }, - "taskBoardFilesRegistry": [ - { - "boardId": "3103563481", - "filePath": "Meta/Task_Board/Boards/Time Based Workflow.taskboard" - }, - { + "taskBoardFilesRegistry": { + "2957159294": { "boardId": "2957159294", - "filePath": "Meta/Task_Board/Boards/Tag Based Workflow.taskboard" + "filePath": "Meta/Task_Board/Boards/Tag Based Workflow.taskboard", + "boardName": "Tag Based Workflow", + "boardDescription": "" }, - { + "3103563481": { + "boardId": "3103563481", + "filePath": "Meta/Task_Board/Boards/Time Based Workflow.taskboard", + "boardName": "Time Based Workflow", + "boardDescription": "" + }, + "4271106430": { "boardId": "4271106430", - "filePath": "Meta/Task_Board/Boards/Status Based Workflow.taskboard" + "filePath": "Meta/Task_Board/Boards/Status Based Workflow.taskboard", + "boardName": "Status Based Workflow", + "boardDescription": "" } - ], + }, "searchQuery": "", "dateFormat": "yyyy/MM/dd", "dateTimeFormat": "yyyy-MM-dd'T'HH:mm:ss", diff --git a/task-board-logs.log b/task-board-logs.log index 755355d4..6da42058 100644 --- a/task-board-logs.log +++ b/task-board-logs.log @@ -21,13 +21,208 @@ ## Recent Bug Reports -Timestamp : 2026-02-21T15:56:34 -ID : 183 +Timestamp : 2026-02-21T19:47:00 +ID : 186 +Message : There was some issue while fetching the filepath from localStorage for the following leafID : e993213dc6bf51cd +Context : TaskBoardView.tsx/onOpen +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 + +#### Bug Content +```log +filePath is undefined +``` + +------------- + +Timestamp : 2026-02-21T19:55:50 +ID : 186 +Message : There was some issue while fetching the filepath from localStorage for the following leafID : e993213dc6bf51cd +Context : TaskBoardView.tsx/onOpen +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 + +#### Bug Content +```log +filePath is undefined +``` + +------------- + +Timestamp : 2026-02-21T19:56:18 +ID : 186 +Message : There was some issue while fetching the filepath from localStorage for the following leafID : e993213dc6bf51cd +Context : TaskBoardView.tsx/onOpen +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 + +#### Bug Content +```log +filePath is undefined +``` + +------------- + +Timestamp : 2026-02-21T20:01:46 +ID : 186 +Message : There was some issue while fetching the filepath from localStorage for the following leafID : ffc9c6a52193b699 +Context : TaskBoardView.tsx/onOpen +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 + +#### Bug Content +```log +filePath is undefined +``` + +------------- + +Timestamp : 2026-02-21T20:03:28 +ID : 184 Message : There was an issue with opening the task board file : undefined Context : TaskBoardView.tsx/onOpen Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 + +#### Bug Content +```log +lastViewedBoardData is undefined +``` + +------------- + +Timestamp : 2026-02-21T21:29:39 +ID : 185 +Message : There was an issue with opening the last viewed board by user +Context : TaskBoardView.tsx/onOpen +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 + +#### Bug Content +```log +lastViewedBoardData is undefined +``` + +------------- + +Timestamp : 2026-02-21T21:31:01 +ID : 185 +Message : There was an issue with opening the last viewed board by user +Context : TaskBoardView.tsx/onOpen +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 + +#### Bug Content +```log +lastViewedBoardData is undefined +``` + +------------- + +Timestamp : 2026-02-21T21:47:41 +ID : 185 +Message : There was an issue with opening the last viewed board by user +Context : TaskBoardView.tsx/onOpen +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 + +#### Bug Content +```log +lastViewedBoardData is undefined +``` + +------------- + +Timestamp : 2026-02-21T21:53:14 +ID : 185 +Message : There was an issue with opening the last viewed board by user +Context : TaskBoardView.tsx/onOpen +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 #### Bug Content ```log lastViewedBoardData is undefined ``` + +------------- + +Timestamp : 2026-02-21T22:01:20 +ID : 185 +Message : There was an issue with opening the last viewed board by user +Context : TaskBoardView.tsx/onOpen +Version : 1.9.5 +Version : 1.9.5 +Version : 1.9.5 + +#### Bug Content +```log +lastViewedBoardData is undefined +``` + +------------- + +Timestamp : 2026-02-21T22:15:04 +ID : 142 +Message : +Context : DragDropTasksManager.ts/handleDropEvent +Version : 1.9.5 + +#### Bug Content +```log +No current drag data available for drop operation.currentDragData=null +``` From 78c7289a996ef54aa224adc226825c2674522a35 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Sun, 22 Feb 2026 11:43:29 +0530 Subject: [PATCH 144/236] fix : The version field for every bug report entry is getting duplicated --- src/managers/BugReporter.ts | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/managers/BugReporter.ts b/src/managers/BugReporter.ts index 0ad49018..d2a83dea 100644 --- a/src/managers/BugReporter.ts +++ b/src/managers/BugReporter.ts @@ -136,9 +136,10 @@ class BugReporterManager { const bugEntries: BugReportEntry[] = []; for (const section of bugReportSections) { - if (!section.trim()) continue; + const sectionTrimmed = section.trim(); + if (!sectionTrimmed.trim()) continue; - const parsed = this.parseBugReportEntry(section); + const parsed = this.parseBugReportEntry(sectionTrimmed); if (parsed) { bugEntries.push(parsed); } @@ -179,13 +180,14 @@ class BugReporterManager { } const contextMatch = entryText.match( - /Context\s*:\s*([\s\S]*?)(?=\n#### Bug Content|\n\n|$)/, + /Context\s*:\s*(.+?)(?=\Version|$)/, + // /Context\s*:\s*([\s\S]*?)(?=\n#### Bug Content|\n\n|$)/, ); if (contextMatch) { entry.context = contextMatch[1].trim(); } - // Extract version (for backward compatibility, set to null if not found) + // Extract version (for backward compatibility const versionMatch = entryText.match(/Version\s*:\s*(.+?)(?=\n|$)/); entry.version = versionMatch ? versionMatch[1].trim() @@ -199,10 +201,10 @@ class BugReporterManager { // Validate that all required fields are present if ( - entry.timestamp && - entry.id !== undefined && - entry.message && - entry.context && + entry.timestamp || + entry.id !== undefined || + entry.message || + entry.context || entry.bugContent ) { return entry as BugReportEntry; @@ -223,7 +225,7 @@ class BugReporterManager { ID : ${entry.id} Message : ${entry.message} Context : ${entry.context} -Version : ${newReleaseVersion} +Version : ${entry.version ?? "Older than 1.9.3"} #### Bug Content \`\`\`log From 211f704f3e61d2bc4e00c31d9736c8bf8c630f5f Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Sun, 22 Feb 2026 11:43:37 +0530 Subject: [PATCH 145/236] test the latest changes | working --- task-board-logs.log | 196 +++++--------------------------------------- 1 file changed, 20 insertions(+), 176 deletions(-) diff --git a/task-board-logs.log b/task-board-logs.log index 6da42058..c474101e 100644 --- a/task-board-logs.log +++ b/task-board-logs.log @@ -13,216 +13,60 @@ - **Plugins enabled**: - Plugin Reloader v0.0.3 - Advanced Debug Mode v1.9.1 - - Task Board v1.9.5 - BRAT v1.4.0 - Tasks v7.22.0 - Hover Editor v0.11.28 - Excalidraw v2.18.3 + - Task Board v1.9.5 ## Recent Bug Reports -Timestamp : 2026-02-21T19:47:00 -ID : 186 -Message : There was some issue while fetching the filepath from localStorage for the following leafID : e993213dc6bf51cd -Context : TaskBoardView.tsx/onOpen -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 - -#### Bug Content -```log -filePath is undefined -``` - -------------- - -Timestamp : 2026-02-21T19:55:50 -ID : 186 -Message : There was some issue while fetching the filepath from localStorage for the following leafID : e993213dc6bf51cd -Context : TaskBoardView.tsx/onOpen -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 - -#### Bug Content -```log -filePath is undefined -``` - -------------- - -Timestamp : 2026-02-21T19:56:18 -ID : 186 -Message : There was some issue while fetching the filepath from localStorage for the following leafID : e993213dc6bf51cd -Context : TaskBoardView.tsx/onOpen -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 - -#### Bug Content -```log -filePath is undefined -``` - -------------- - -Timestamp : 2026-02-21T20:01:46 -ID : 186 -Message : There was some issue while fetching the filepath from localStorage for the following leafID : ffc9c6a52193b699 -Context : TaskBoardView.tsx/onOpen -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 - -#### Bug Content -```log -filePath is undefined -``` - -------------- - -Timestamp : 2026-02-21T20:03:28 -ID : 184 -Message : There was an issue with opening the task board file : undefined -Context : TaskBoardView.tsx/onOpen -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 - -#### Bug Content -```log -lastViewedBoardData is undefined -``` - -------------- - -Timestamp : 2026-02-21T21:29:39 -ID : 185 -Message : There was an issue with opening the last viewed board by user -Context : TaskBoardView.tsx/onOpen -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 - -#### Bug Content -```log -lastViewedBoardData is undefined -``` - -------------- - -Timestamp : 2026-02-21T21:31:01 -ID : 185 -Message : There was an issue with opening the last viewed board by user -Context : TaskBoardView.tsx/onOpen -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 - -#### Bug Content -```log -lastViewedBoardData is undefined -``` - -------------- - -Timestamp : 2026-02-21T21:47:41 -ID : 185 -Message : There was an issue with opening the last viewed board by user -Context : TaskBoardView.tsx/onOpen -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 +Timestamp : 2026-02-22T11:01:22 +ID : 149 +Message : +Context : undefined Version : 1.9.5 #### Bug Content ```log -lastViewedBoardData is undefined +There is testing going on. ``` ------------- -Timestamp : 2026-02-21T21:53:14 -ID : 185 -Message : There was an issue with opening the last viewed board by user -Context : TaskBoardView.tsx/onOpen -Version : 1.9.5 -Version : 1.9.5 -Version : 1.9.5 +Timestamp : 2026-02-22T11:27:53 +ID : 149 +Message : +Context : undefined Version : 1.9.5 #### Bug Content ```log -lastViewedBoardData is undefined +There is testing going on. ``` ------------- -Timestamp : 2026-02-21T22:01:20 -ID : 185 -Message : There was an issue with opening the last viewed board by user -Context : TaskBoardView.tsx/onOpen -Version : 1.9.5 -Version : 1.9.5 +Timestamp : 2026-02-22T11:35:29 +ID : 149 +Message : +Context : undefined Version : 1.9.5 #### Bug Content ```log -lastViewedBoardData is undefined +This is a test bug. ``` ------------- -Timestamp : 2026-02-21T22:15:04 -ID : 142 +Timestamp : 2026-02-22T11:42:40 +ID : 149 Message : -Context : DragDropTasksManager.ts/handleDropEvent +Context : OpenModals.ts/openAddNewTaskNoteModal/callback() Version : 1.9.5 #### Bug Content ```log -No current drag data available for drop operation.currentDragData=null +This is a test bug. ``` From d72359aea75c362ad1fb43e4baf66fca0b9d2b58 Mon Sep 17 00:00:00 2001 From: Atmanand Gauns Date: Wed, 18 Mar 2026 19:52:42 +0530 Subject: [PATCH 146/236] HUGE refactoring to rename files and class names to work well with upcoming changes --- .github/copilot-instructions.md | 2 +- main.ts | 2 +- .../FilterConfigModal.ts | 0 .../TaskFilterComponent.ts} | 0 .../TaskFilterModal.ts} | 8 +++--- .../TaskFilterPopover.ts} | 10 +++---- .../index.ts | 10 +++---- src/components/KanbanView/LazyColumn.tsx | 10 +++---- src/components/KanbanView/TaskItem.tsx | 4 +-- src/components/KanbanView/TaskItemV2.tsx | 4 +-- ...Content.tsx => TaskBoardViewContainer.tsx} | 26 +++++++++---------- .../AddOrEditTaskView.tsx | 0 .../TaskBoardView.tsx | 6 ++--- src/services/OpenModals.ts | 2 +- src/settings/SettingSynchronizer.ts | 2 +- .../{BoardFilterer.ts => AdvancedFilterer.ts} | 0 src/utils/algorithms/ColumnSegregator.ts | 2 +- styles.css | 2 +- 18 files changed, 45 insertions(+), 45 deletions(-) rename src/components/{BoardFilters => AdvancedFilterer}/FilterConfigModal.ts (100%) rename src/components/{BoardFilters/ViewTaskFilter.ts => AdvancedFilterer/TaskFilterComponent.ts} (100%) rename src/components/{BoardFilters/ViewTaskFilterModal.ts => AdvancedFilterer/TaskFilterModal.ts} (91%) rename src/components/{BoardFilters/ViewTaskFilterPopover.ts => AdvancedFilterer/TaskFilterPopover.ts} (96%) rename src/components/{BoardFilters => AdvancedFilterer}/index.ts (69%) rename src/components/{TaskBoardViewContent.tsx => TaskBoardViewContainer.tsx} (97%) rename src/{views => obsidian_views}/AddOrEditTaskView.tsx (100%) rename src/{views => obsidian_views}/TaskBoardView.tsx (98%) rename src/utils/algorithms/{BoardFilterer.ts => AdvancedFilterer.ts} (100%) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 9cc1eae0..ba29f2a8 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -95,7 +95,7 @@ npm run dev # Should start watch mode and display "watching for changes..." │ │ ├── Column.tsx # Kanban column component │ │ ├── TaskItem.tsx # Individual task component (26k lines - complex) │ │ ├── KanbanBoard.tsx # Main board component -│ │ ├── TaskBoardViewContent.tsx # Main board content wrapper +│ │ ├── TaskBoardViewContainer.tsx # Main board content wrapper │ │ └── MapView.tsx # Map-based view component │ ├── interfaces/ # TypeScript interfaces (3 files) │ │ ├── TaskItem.ts # Task data structures diff --git a/main.ts b/main.ts index f8cdb306..6fbd2acd 100644 --- a/main.ts +++ b/main.ts @@ -23,7 +23,7 @@ import { openScanVaultModal, } from "src/services/OpenModals"; -import { TaskBoardView } from "./src/views/TaskBoardView"; +import { TaskBoardView } from "./src/obsidian_views/TaskBoardView"; import { RealTimeScanner } from "src/managers/RealTimeScanner"; import VaultScanner, { fileTypeAllowedForScanning, diff --git a/src/components/BoardFilters/FilterConfigModal.ts b/src/components/AdvancedFilterer/FilterConfigModal.ts similarity index 100% rename from src/components/BoardFilters/FilterConfigModal.ts rename to src/components/AdvancedFilterer/FilterConfigModal.ts diff --git a/src/components/BoardFilters/ViewTaskFilter.ts b/src/components/AdvancedFilterer/TaskFilterComponent.ts similarity index 100% rename from src/components/BoardFilters/ViewTaskFilter.ts rename to src/components/AdvancedFilterer/TaskFilterComponent.ts diff --git a/src/components/BoardFilters/ViewTaskFilterModal.ts b/src/components/AdvancedFilterer/TaskFilterModal.ts similarity index 91% rename from src/components/BoardFilters/ViewTaskFilterModal.ts rename to src/components/AdvancedFilterer/TaskFilterModal.ts index 1a4ff197..c729a8e8 100644 --- a/src/components/BoardFilters/ViewTaskFilterModal.ts +++ b/src/components/AdvancedFilterer/TaskFilterModal.ts @@ -2,10 +2,10 @@ import { Modal } from "obsidian"; import type TaskBoard from "main"; import { t } from "src/utils/lang/helper"; import { RootFilterState } from "src/interfaces/BoardConfigs"; -import { TaskFilterComponent } from "./ViewTaskFilter"; +import { TaskFilterComponent } from "./TaskFilterComponent"; import { bugReporterManagerInsatance } from "src/managers/BugReporter"; -export class ViewTaskFilterModal extends Modal { +export class TaskFilterModal extends Modal { private plugin: TaskBoard; public activeBoardIndex?: number; public taskFilterComponent: TaskFilterComponent | null; @@ -70,7 +70,7 @@ export class ViewTaskFilterModal extends Modal { bugReporterManagerInsatance.addToLogs( 114, String(error), - "ViewTaskFilterModal.ts/onClose", + "TaskFilterModal.ts/onClose", ); } } @@ -84,7 +84,7 @@ export class ViewTaskFilterModal extends Modal { bugReporterManagerInsatance.addToLogs( 115, String(error), - "ViewTaskFilterModal.ts/onClose", + "TaskFilterModal.ts/onClose", ); } } diff --git a/src/components/BoardFilters/ViewTaskFilterPopover.ts b/src/components/AdvancedFilterer/TaskFilterPopover.ts similarity index 96% rename from src/components/BoardFilters/ViewTaskFilterPopover.ts rename to src/components/AdvancedFilterer/TaskFilterPopover.ts index e54a688d..d7e769a7 100644 --- a/src/components/BoardFilters/ViewTaskFilterPopover.ts +++ b/src/components/AdvancedFilterer/TaskFilterPopover.ts @@ -1,4 +1,4 @@ -// /src/components/BoardFilters/ViewTaskFilterPopover.ts +// /src/components/BoardFilters/TaskFilterPopover.ts import { App } from "obsidian"; import { CloseableComponent, Component } from "obsidian"; @@ -6,10 +6,10 @@ import { createPopper, Instance as PopperInstance } from "@popperjs/core"; import type TaskBoard from "main"; import { t } from "src/utils/lang/helper"; import { RootFilterState } from "src/interfaces/BoardConfigs"; -import { TaskFilterComponent } from "./ViewTaskFilter"; +import { TaskFilterComponent } from "./TaskFilterComponent"; import { bugReporterManagerInsatance } from "src/managers/BugReporter"; -export class ViewTaskFilterPopover +export class TaskFilterPopover extends Component implements CloseableComponent { @@ -225,7 +225,7 @@ export class ViewTaskFilterPopover bugReporterManagerInsatance.addToLogs( 116, String(error), - "ViewTaskFilterPopover.ts/close", + "TaskFilterPopover.ts/close", ); } } @@ -253,7 +253,7 @@ export class ViewTaskFilterPopover bugReporterManagerInsatance.addToLogs( 117, String(error), - "ViewTaskFilterPopover.ts/close", + "TaskFilterPopover.ts/close", ); } } diff --git a/src/components/BoardFilters/index.ts b/src/components/AdvancedFilterer/index.ts similarity index 69% rename from src/components/BoardFilters/index.ts rename to src/components/AdvancedFilterer/index.ts index 058023fd..9fc139f2 100644 --- a/src/components/BoardFilters/index.ts +++ b/src/components/AdvancedFilterer/index.ts @@ -2,7 +2,7 @@ * This BoardFilters component has been inspired from Bases filter and Task Genius plugin filters. All credits for this component go to the developer of Task Genius plugin. * * Changes made to the original code: - * - Added type safetly at various places. + * - Added type safety at various places. * - This component can be used for both board as well as column. * - A heading for the popover and modal to display the column or board name. * - Input suggestion for various properties such as tags, priority, status, filePath, etc. @@ -10,8 +10,8 @@ * @url https://github.com/Quorafind/Obsidian-Task-Genius/blob/6307b018cae3c1a20e753127faac88492aac9ffc/src/components/features/task/filter/index.ts */ -import { TaskFilterComponent } from "./ViewTaskFilter"; -import { ViewTaskFilterModal } from "./ViewTaskFilterModal"; -import { ViewTaskFilterPopover } from "./ViewTaskFilterPopover"; +import { TaskFilterComponent } from "./TaskFilterComponent"; +import { TaskFilterModal } from "./TaskFilterModal"; +import { TaskFilterPopover } from "./TaskFilterPopover"; -export { TaskFilterComponent, ViewTaskFilterModal, ViewTaskFilterPopover }; +export { TaskFilterComponent, TaskFilterModal, TaskFilterPopover }; diff --git a/src/components/KanbanView/LazyColumn.tsx b/src/components/KanbanView/LazyColumn.tsx index 67a28c85..6b3ce7b5 100644 --- a/src/components/KanbanView/LazyColumn.tsx +++ b/src/components/KanbanView/LazyColumn.tsx @@ -9,12 +9,12 @@ import TaskBoard from 'main'; import { Board, ColumnData, RootFilterState } from 'src/interfaces/BoardConfigs'; import { taskItem } from 'src/interfaces/TaskItem'; import { Menu, Notice, Platform } from 'obsidian'; -import { ViewTaskFilterPopover } from 'src/components/BoardFilters/ViewTaskFilterPopover'; +import { TaskFilterPopover } from 'src/components/AdvancedFilterer/TaskFilterPopover'; import { eventEmitter } from 'src/services/EventEmitter'; -import { ViewTaskFilterModal } from 'src/components/BoardFilters'; +import { TaskFilterModal } from 'src/components/AdvancedFilterer'; import { ConfigureColumnSortingModal } from 'src/modals/ConfigureColumnSortingModal'; import { matchTagsWithWildcards } from 'src/utils/algorithms/ScanningFilterer'; -import { isRootFilterStateEmpty } from 'src/utils/algorithms/BoardFilterer'; +import { isRootFilterStateEmpty } from 'src/utils/algorithms/AdvancedFilterer'; import { dragDropTasksManagerInsatance } from 'src/managers/DragDropTasksManager'; import { taskCardStyleNames } from 'src/interfaces/GlobalSettings'; import TaskItemV2 from './TaskItemV2'; @@ -315,7 +315,7 @@ const LazyColumn: React.FC = ({ const columnIndex = columnData.index - 1; if (Platform.isMobile || Platform.isMacOS) { // If its a mobile platform, then we will open a modal instead of popover. - const filterModal = new ViewTaskFilterModal( + const filterModal = new TaskFilterModal( plugin, true, undefined, boardIndex, columnData.name, columnData.filters ); @@ -347,7 +347,7 @@ const LazyColumn: React.FC = ({ // Create and show filter popover // leafId is undefined for column filters (not tied to a specific leaf) - const popover = new ViewTaskFilterPopover( + const popover = new TaskFilterPopover( plugin, true, // forColumn is true undefined, diff --git a/src/components/KanbanView/TaskItem.tsx b/src/components/KanbanView/TaskItem.tsx index a896365e..788b891e 100644 --- a/src/components/KanbanView/TaskItem.tsx +++ b/src/components/KanbanView/TaskItem.tsx @@ -100,7 +100,7 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin, task, a // if (titleElement && task.title !== "") { // let cleanedTitle = cleanTaskTitleLegacy(task); - // // NOTE : This search method is not working smoothly, hence using the first approach in file TaskBoardViewContent.tsx + // // NOTE : This search method is not working smoothly, hence using the first approach in file TaskBoardViewContainer.tsx // // const searchQuery = plugin.settings.data.searchQuery || ''; // // if (searchQuery) { // // const escapedQuery = searchQuery.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); @@ -184,7 +184,7 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin, task, a // // console.log("renderSubTasks : This useEffect should only run when subTask updates | Calling rendered with:\n", subtaskText); // element.empty(); // Clear previous content - // // NOTE : This search method is not working smoothly, hence using the first approach in file TaskBoardViewContent.tsx + // // NOTE : This search method is not working smoothly, hence using the first approach in file TaskBoardViewContainer.tsx // // const searchQuery = plugin.settings.data.searchQuery || ''; // // if (searchQuery) { // // const escapedQuery = searchQuery.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); diff --git a/src/components/KanbanView/TaskItemV2.tsx b/src/components/KanbanView/TaskItemV2.tsx index 2507dd87..7b479d75 100644 --- a/src/components/KanbanView/TaskItemV2.tsx +++ b/src/components/KanbanView/TaskItemV2.tsx @@ -110,7 +110,7 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, // if (titleElement && task.title !== "") { // let cleanedTitle = cleanTaskTitleLegacy(task); - // // NOTE : This search method is not working smoothly, hence using the first approach in file TaskBoardViewContent.tsx + // // NOTE : This search method is not working smoothly, hence using the first approach in file TaskBoardViewContainer.tsx // // const searchQuery = plugin.settings.data.searchQuery || ''; // // if (searchQuery) { // // const escapedQuery = searchQuery.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); @@ -194,7 +194,7 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, // // console.log("renderSubTasks : This useEffect should only run when subTask updates | Calling rendered with:\n", subtaskText); // element.empty(); // Clear previous content - // // NOTE : This search method is not working smoothly, hence using the first approach in file TaskBoardViewContent.tsx + // // NOTE : This search method is not working smoothly, hence using the first approach in file TaskBoardViewContainer.tsx // // const searchQuery = plugin.settings.data.searchQuery || ''; // // if (searchQuery) { // // const escapedQuery = searchQuery.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); diff --git a/src/components/TaskBoardViewContent.tsx b/src/components/TaskBoardViewContainer.tsx similarity index 97% rename from src/components/TaskBoardViewContent.tsx rename to src/components/TaskBoardViewContainer.tsx index f0d3d614..b9871a10 100644 --- a/src/components/TaskBoardViewContent.tsx +++ b/src/components/TaskBoardViewContainer.tsx @@ -1,4 +1,4 @@ -// src/components/TaskBoardViewContent.tsx +// src/components/TaskBoardViewContainer.tsx import { Board, ColumnData, RootFilterState } from "../interfaces/BoardConfigs"; import { CirclePlus, RefreshCcw, Search, SearchX, Filter, Menu as MenuICon, Settings, EllipsisVertical, List, KanbanSquareIcon, Network, BrickWall, KanbanSquare, SquareKanban, Save, LayoutGridIcon } from 'lucide-react'; @@ -13,15 +13,15 @@ import { openAddNewTaskModal, openBoardConfigModal, openScanVaultModal, openTask import { t } from "src/utils/lang/helper"; import KanbanBoard from "./KanbanView/KanbanBoardView"; import MapView from "./MapView/MapView"; -import { DEFAULT_DATE_FORMAT, VIEW_TYPE_TASKBOARD } from "src/interfaces/Constants"; -import { ViewTaskFilterPopover } from "./BoardFilters/ViewTaskFilterPopover"; -import { advancedFilterer } from "src/utils/algorithms/BoardFilterer"; -import { ViewTaskFilterModal } from 'src/components/BoardFilters'; +import { DEFAULT_DATE_FORMAT } from "src/interfaces/Constants"; +import { TaskFilterPopover } from "./AdvancedFilterer/TaskFilterPopover"; +import { advancedFilterer } from "src/utils/algorithms/AdvancedFilterer"; +import { TaskFilterModal } from 'src/components/AdvancedFilterer'; import { taskPropertiesNames, viewTypeNames } from "src/interfaces/Enums"; import { ScanVaultIcon, funnelIcon } from "src/interfaces/Icons"; import { bugReporterManagerInsatance } from "src/managers/BugReporter"; -const TaskBoardViewContent: React.FC<{ plugin: TaskBoard, currentBoardData: Board, currentLeaf: WorkspaceLeaf, allBoards?: Board[] }> = ({ plugin, currentBoardData, currentLeaf, allBoards }) => { +const TaskBoardViewContainer: React.FC<{ plugin: TaskBoard, currentBoardData: Board, currentLeaf: WorkspaceLeaf, allBoards?: Board[] }> = ({ plugin, currentBoardData, currentLeaf, allBoards }) => { // const [boards, setBoards] = useState(boardConfigs); const [activeBoardIndex, setActiveBoardIndex] = useState(plugin.settings.data.lastViewHistory.boardIndex ?? 0); const [boardData, setCurrentBoardData] = useState(); @@ -36,7 +36,7 @@ const TaskBoardViewContent: React.FC<{ plugin: TaskBoard, currentBoardData: Boar const [showSearchInput, setShowSearchInput] = useState(plugin.settings.data.searchQuery ? true : false); const [searchQuery, setSearchQuery] = useState(plugin.settings.data.searchQuery ?? ""); - const filterPopoverRef = useRef(null); + const filterPopoverRef = useRef(null); const [mapViewDataUpdated, setMapViewDataUpdated] = useState(false); const [viewWidth, setviewWidth] = useState(currentLeaf.width); @@ -92,7 +92,7 @@ const TaskBoardViewContent: React.FC<{ plugin: TaskBoard, currentBoardData: Boar bugReporterManagerInsatance.addToLogs( 131, `No need to worry about this bug, if its appearing on the fresh install.\n${String(error)}`, - "TaskBoardViewContent.tsx/loading boards and tasks useEffect", + "TaskBoardViewContainer.tsx/loading boards and tasks useEffect", ); setFreshInstall(true); } @@ -147,7 +147,7 @@ const TaskBoardViewContent: React.FC<{ plugin: TaskBoard, currentBoardData: Boar const allTasks = await loadTasksAndMerge(plugin, false); setAllTasks(allTasks); } catch (error) { - bugReporterManagerInsatance.showNotice(28, "Error loading tasks on column refresh", String(error), "TaskBoardViewContent.tsx/debouncedRefreshColumn"); + bugReporterManagerInsatance.showNotice(28, "Error loading tasks on column refresh", String(error), "TaskBoardViewContainer.tsx/debouncedRefreshColumn"); } }, 500), [plugin] @@ -282,7 +282,7 @@ const TaskBoardViewContent: React.FC<{ plugin: TaskBoard, currentBoardData: Boar const currentBoardConfig = boardData; if (Platform.isMobile || Platform.isMacOS) { // If its a mobile platform, then we will open a modal instead of popover. - const filterModal = new ViewTaskFilterModal( + const filterModal = new TaskFilterModal( plugin, false, undefined, activeBoardIndex, currentBoardConfig!.name ); @@ -334,7 +334,7 @@ const TaskBoardViewContent: React.FC<{ plugin: TaskBoard, currentBoardData: Boar }; // Create and show popover - const popover = new ViewTaskFilterPopover( + const popover = new TaskFilterPopover( plugin, false, // forColumn = false since this is for board-level filter undefined, @@ -374,7 +374,7 @@ const TaskBoardViewContent: React.FC<{ plugin: TaskBoard, currentBoardData: Boar filterPopoverRef.current = popover; } } catch (error) { - bugReporterManagerInsatance.showNotice(29, "Error showing filter popover", String(error), "TaskBoardViewContent.tsx/handleFilterButtonClick"); + bugReporterManagerInsatance.showNotice(29, "Error showing filter popover", String(error), "TaskBoardViewContainer.tsx/handleFilterButtonClick"); } } @@ -1132,4 +1132,4 @@ const TaskBoardViewContent: React.FC<{ plugin: TaskBoard, currentBoardData: Boar ); }; -export default TaskBoardViewContent; +export default TaskBoardViewContainer; diff --git a/src/views/AddOrEditTaskView.tsx b/src/obsidian_views/AddOrEditTaskView.tsx similarity index 100% rename from src/views/AddOrEditTaskView.tsx rename to src/obsidian_views/AddOrEditTaskView.tsx diff --git a/src/views/TaskBoardView.tsx b/src/obsidian_views/TaskBoardView.tsx similarity index 98% rename from src/views/TaskBoardView.tsx rename to src/obsidian_views/TaskBoardView.tsx index 965f2392..f34e494c 100644 --- a/src/views/TaskBoardView.tsx +++ b/src/obsidian_views/TaskBoardView.tsx @@ -6,10 +6,10 @@ import { funnelIcon, RefreshIcon, ScanVaultIcon, TaskBoardIcon } from "src/inter import { StrictMode } from "react"; import { Board } from "src/interfaces/BoardConfigs"; -import TaskBoardViewContent from "src/components/TaskBoardViewContent"; +import TaskBoardViewContainer from "src/components/TaskBoardViewContainer"; import type TaskBoard from "../../main"; import { PENDING_SCAN_FILE_STACK, VIEW_TYPE_TASKBOARD } from "src/interfaces/Constants"; -import { openBoardConfigModal, openScanVaultModal } from "../services/OpenModals"; +import { openScanVaultModal } from "../services/OpenModals"; import { t } from "src/utils/lang/helper"; import { eventEmitter } from "src/services/EventEmitter"; import { bugReporterManagerInsatance } from "src/managers/BugReporter"; @@ -256,7 +256,7 @@ export class TaskBoardView extends ItemView { this.root = createRoot(this.containerEl.children[1]); this.root.render( - Date: Thu, 19 Mar 2026 19:35:40 +0530 Subject: [PATCH 147/236] fix all the typescript error and refactor a lot of code --- main.ts | 4 +- .../AdvancedFilterer/TaskFilterComponent.ts | 2 - .../AdvancedFilterer/TaskFilterModal.ts | 4 - .../AdvancedFilterer/TaskFilterPopover.ts | 4 - src/components/KanbanView/KanbanBoardView.tsx | 37 +- .../KanbanView/KanbanSwimlanesContainer.tsx | 26 +- src/components/KanbanView/LazyColumn.tsx | 43 +- src/components/MapView/MapView.tsx | 38 +- src/components/MapView/TasksImporterPanel.tsx | 14 +- src/components/TaskBoardViewContainer.tsx | 256 ++++---- .../{KanbanView => TaskCard}/TaskItem.tsx | 14 +- .../{KanbanView => TaskCard}/TaskItemV2.tsx | 20 +- src/interfaces/BoardConfigs.ts | 557 +++++++++--------- src/interfaces/GlobalSettings.ts | 5 - src/managers/DragDropTasksManager.ts | 2 +- src/obsidian_views/TaskBoardView.tsx | 1 - src/services/OpenModals.ts | 22 +- src/utils/ViewUtils.ts | 253 ++++++++ 18 files changed, 815 insertions(+), 487 deletions(-) rename src/components/{KanbanView => TaskCard}/TaskItem.tsx (98%) rename src/components/{KanbanView => TaskCard}/TaskItemV2.tsx (98%) create mode 100644 src/utils/ViewUtils.ts diff --git a/main.ts b/main.ts index 6fbd2acd..303ea62a 100644 --- a/main.ts +++ b/main.ts @@ -1504,13 +1504,13 @@ export default class TaskBoard extends Plugin { ); // Import DEFAULT_BOARDS from BoardConfigs - const { DEFAULT_BOARDS } = + const { DEFAULT_BOARD } = await import("src/interfaces/BoardConfigs"); // Try to create missing default board files const createdCount = await this.taskBoardFileManager.createMissingDefaultBoardFiles( - DEFAULT_BOARDS, + [DEFAULT_BOARD], ); if (createdCount > 0) { diff --git a/src/components/AdvancedFilterer/TaskFilterComponent.ts b/src/components/AdvancedFilterer/TaskFilterComponent.ts index a2eb810b..83b9ca9a 100644 --- a/src/components/AdvancedFilterer/TaskFilterComponent.ts +++ b/src/components/AdvancedFilterer/TaskFilterComponent.ts @@ -76,7 +76,6 @@ export class TaskFilterComponent extends Component { plugin: TaskBoard, app: App, private leafId?: string | undefined, - activeBoardIndex?: number, private initialFilterState?: RootFilterState, ) { super(); @@ -84,7 +83,6 @@ export class TaskFilterComponent extends Component { this.plugin = plugin; this.app = app; this.pluginSettings = plugin.settings; - this.activeBoardIndex = activeBoardIndex; } onload() { diff --git a/src/components/AdvancedFilterer/TaskFilterModal.ts b/src/components/AdvancedFilterer/TaskFilterModal.ts index c729a8e8..3b19167e 100644 --- a/src/components/AdvancedFilterer/TaskFilterModal.ts +++ b/src/components/AdvancedFilterer/TaskFilterModal.ts @@ -7,7 +7,6 @@ import { bugReporterManagerInsatance } from "src/managers/BugReporter"; export class TaskFilterModal extends Modal { private plugin: TaskBoard; - public activeBoardIndex?: number; public taskFilterComponent: TaskFilterComponent | null; private columnOrBoardName?: string; private initialFilterState?: RootFilterState; @@ -19,13 +18,11 @@ export class TaskFilterModal extends Modal { plugin: TaskBoard, forColumn: boolean, private leafId?: string, - activeBoardIndex?: number, columnOrBoardName?: string, initialFilterState?: RootFilterState, ) { super(plugin.app); this.plugin = plugin; - this.activeBoardIndex = activeBoardIndex; this.columnOrBoardName = columnOrBoardName; this.initialFilterState = initialFilterState; @@ -51,7 +48,6 @@ export class TaskFilterModal extends Modal { this.plugin, this.app, this.leafId, - this.activeBoardIndex, this.initialFilterState, ); // Ensure the component is properly loaded diff --git a/src/components/AdvancedFilterer/TaskFilterPopover.ts b/src/components/AdvancedFilterer/TaskFilterPopover.ts index d7e769a7..f1af3c55 100644 --- a/src/components/AdvancedFilterer/TaskFilterPopover.ts +++ b/src/components/AdvancedFilterer/TaskFilterPopover.ts @@ -22,7 +22,6 @@ export class TaskFilterPopover private scrollParent: HTMLElement | Window; private popperInstance: PopperInstance | null = null; public onClose: ((filterState?: RootFilterState) => void) | null = null; - private activeBoardIndex?: number; private columnOrBoardName?: string; private initialFilterState?: RootFilterState; @@ -30,7 +29,6 @@ export class TaskFilterPopover plugin: TaskBoard, forColumn: boolean, private leafId?: string | undefined, - activeBoardIndex?: number, columnOrBoardName?: string, initialFilterState?: RootFilterState, ) { @@ -38,7 +36,6 @@ export class TaskFilterPopover this.plugin = plugin; this.app = plugin.app; this.forColumn = forColumn; - this.activeBoardIndex = activeBoardIndex; this.columnOrBoardName = columnOrBoardName; this.initialFilterState = initialFilterState; this.win = plugin.app.workspace.containerEl.win || window; @@ -90,7 +87,6 @@ export class TaskFilterPopover this.plugin, this.app, this.leafId, - this.activeBoardIndex, this.initialFilterState, ); // Ensure the component is properly loaded diff --git a/src/components/KanbanView/KanbanBoardView.tsx b/src/components/KanbanView/KanbanBoardView.tsx index 46966e6e..aeaa35e4 100644 --- a/src/components/KanbanView/KanbanBoardView.tsx +++ b/src/components/KanbanView/KanbanBoardView.tsx @@ -1,10 +1,8 @@ // src/components/KanbanBoard.tsx -import { Board, ColumnData } from "../../interfaces/BoardConfigs"; +import { Board, ColumnData, View } from "../../interfaces/BoardConfigs"; import React, { memo, useEffect, useMemo, useState } from "react"; import { taskItem, taskJsonMerged } from "src/interfaces/TaskItem"; - -import { App } from "obsidian"; import LazyColumn from "./LazyColumn"; import KanbanSwimlanesContainer from "./KanbanSwimlanesContainer"; import type TaskBoard from "main"; @@ -14,21 +12,25 @@ import { columnSegregator } from "src/utils/algorithms/ColumnSegregator"; interface KanbanBoardProps { plugin: TaskBoard; currentBoardData: Board; - currentBoardIndex: number; + currentView: View; + currentViewIndex: number; filteredAndSearchedTasks: taskJsonMerged; freshInstall: boolean; } -const KanbanBoard: React.FC = ({ plugin, currentBoardData, currentBoardIndex, filteredAndSearchedTasks, freshInstall }) => { +const KanbanBoard: React.FC = ({ plugin, currentBoardData, currentView, currentViewIndex, filteredAndSearchedTasks, freshInstall }) => { const [loading, setLoading] = useState(true); // Check if lazy loading is enabled const ColumnComponent = LazyColumn; // lazyLoadingEnabled ? LazyColumn : Column; + // Get columns from the current view's kanban configuration + const columns = currentView?.kanbanView?.columns || []; + // Second memo: Segregate filtered tasks by column (for Kanban view only) const allTasksArrangedPerColumn = useMemo(() => { - if (currentBoardData && filteredAndSearchedTasks) { - return currentBoardData.columns + if (currentBoardData && currentView && filteredAndSearchedTasks) { + return columns .filter((column) => column.active) .map((column: ColumnData) => columnSegregator(plugin.settings, currentBoardData, column, filteredAndSearchedTasks, (updatedBoardData: Board) => { @@ -40,7 +42,7 @@ const KanbanBoard: React.FC = ({ plugin, currentBoardData, cur ); } return []; - }, [filteredAndSearchedTasks, currentBoardData]); + }, [filteredAndSearchedTasks, currentBoardData, currentView]); useEffect(() => { if (allTasksArrangedPerColumn.flat().length > 0) { @@ -70,27 +72,29 @@ const KanbanBoard: React.FC = ({ plugin, currentBoardData, cur )}
    - ) : currentBoardData?.columns?.length === 0 ? ( + ) : columns?.length === 0 ? (
    - Create columns on this board using the board config modal from top right corner button. + Create columns on this view using the view config modal from top right corner button.
    - ) : currentBoardData?.swimlanes?.enabled ? ( + ) : currentView?.kanbanView?.swimlanes?.enabled ? ( ) : ( - currentBoardData?.columns + columns .filter((column) => column.active) .map((column, index) => ( @@ -104,8 +108,9 @@ const KanbanBoard: React.FC = ({ plugin, currentBoardData, cur const MemoizedColumn = memo<{ plugin: TaskBoard; activeBoardData: Board; + currentView: View; + currentViewIndex: number; columnData: ColumnData; - activeBoardIndex: number; tasksForThisColumn: taskItem[]; Component: typeof LazyColumn; }>(({ Component, ...props }) => { @@ -113,6 +118,8 @@ const MemoizedColumn = memo<{ }, (prevProps, nextProps) => { return ( prevProps.activeBoardData === nextProps.activeBoardData && + prevProps.currentView === nextProps.currentView && + prevProps.currentViewIndex === nextProps.currentViewIndex && prevProps.columnData === nextProps.columnData && prevProps.tasksForThisColumn === nextProps.tasksForThisColumn && prevProps.Component === nextProps.Component diff --git a/src/components/KanbanView/KanbanSwimlanesContainer.tsx b/src/components/KanbanView/KanbanSwimlanesContainer.tsx index 74057e8c..c4b33ab6 100644 --- a/src/components/KanbanView/KanbanSwimlanesContainer.tsx +++ b/src/components/KanbanView/KanbanSwimlanesContainer.tsx @@ -1,7 +1,7 @@ // src/components/KanbanView/KanbanSwimlanesContainer.tsx import React, { useMemo, memo } from 'react'; -import { Board, ColumnData } from 'src/interfaces/BoardConfigs'; +import { Board, ColumnData, View } from 'src/interfaces/BoardConfigs'; import { taskItem } from 'src/interfaces/TaskItem'; import LazyColumn from './LazyColumn'; import type TaskBoard from 'main'; @@ -13,7 +13,8 @@ import { bugReporterManagerInsatance } from 'src/managers/BugReporter'; interface KanbanSwimlanesContainerProps { plugin: TaskBoard; currentBoardData: Board; - currentBoardIndex: number; + currentView: View; + currentViewIndex: number; tasksPerColumn: taskItem[][]; } @@ -27,7 +28,8 @@ interface SwimlaneRow { const KanbanSwimlanesContainer: React.FC = ({ plugin, currentBoardData, - currentBoardIndex, + currentView, + currentViewIndex, tasksPerColumn, }) => { const ColumnComponent = LazyColumn; // lazyLoadingEnabled ? LazyColumn : Column; @@ -42,7 +44,7 @@ const KanbanSwimlanesContainer: React.FC = ({ maxHeight: maxSwimlaneHeight, verticalHeaderUI, minimized - } = currentBoardData.swimlanes; + } = currentView.kanbanView!.swimlanes; const activeColumns = currentBoardData.columns .filter((col) => col.active); @@ -267,8 +269,9 @@ const KanbanSwimlanesContainer: React.FC = ({ key={`header-${column.id}`} plugin={plugin} activeBoardData={currentBoardData} + currentView={currentView} + currentViewIndex={currentViewIndex} columnData={column} - activeBoardIndex={currentBoardIndex} tasksForThisColumn={tasksPerColumn?.[colIndex] || []} Component={ColumnComponent} headerOnly={true} @@ -308,12 +311,13 @@ const KanbanSwimlanesContainer: React.FC = ({ key={`${swimlane.swimlaneValue}-${column.id}`} plugin={plugin} activeBoardData={currentBoardData} + currentView={currentView} + currentViewIndex={currentViewIndex} columnData={column} - activeBoardIndex={currentBoardIndex} tasksForThisColumn={swimlane.tasks[colIndex] || []} Component={ColumnComponent} - swimlaneData={swimlaneData} hideColumnHeader={true} + swimlaneData={swimlaneData} /> ); })} @@ -349,8 +353,9 @@ const KanbanSwimlanesContainer: React.FC = ({ key={`${swimlane.swimlaneValue}-${column.id}`} plugin={plugin} activeBoardData={currentBoardData} + currentView={currentView} + currentViewIndex={currentViewIndex} columnData={column} - activeBoardIndex={currentBoardIndex} tasksForThisColumn={swimlane.tasks[colIndex] || []} Component={ColumnComponent} hideColumnHeader={true} @@ -459,8 +464,9 @@ function getPropertyValues( const MemoizedSwimlanColumn = memo<{ plugin: TaskBoard; activeBoardData: Board; + currentView: View; + currentViewIndex: number; columnData: ColumnData; - activeBoardIndex: number; tasksForThisColumn: taskItem[]; Component: typeof LazyColumn; swimlaneData?: { property: string, value: string }; @@ -471,6 +477,8 @@ const MemoizedSwimlanColumn = memo<{ }, (prevProps, nextProps) => { return ( prevProps.activeBoardData === nextProps.activeBoardData && + prevProps.currentView === nextProps.currentView && + prevProps.currentViewIndex === nextProps.currentViewIndex && prevProps.columnData === nextProps.columnData && prevProps.tasksForThisColumn === nextProps.tasksForThisColumn && prevProps.Component === nextProps.Component && diff --git a/src/components/KanbanView/LazyColumn.tsx b/src/components/KanbanView/LazyColumn.tsx index 6b3ce7b5..75f8cf18 100644 --- a/src/components/KanbanView/LazyColumn.tsx +++ b/src/components/KanbanView/LazyColumn.tsx @@ -3,10 +3,10 @@ import React, { memo, useMemo, useState, useEffect, useRef, useCallback } from 'react'; import { CSSProperties } from 'react'; -import TaskItem, { swimlaneDataProp } from './TaskItem'; +import TaskItem, { swimlaneDataProp } from '../TaskCard/TaskItem'; import { t } from 'src/utils/lang/helper'; import TaskBoard from 'main'; -import { Board, ColumnData, RootFilterState } from 'src/interfaces/BoardConfigs'; +import { Board, ColumnData, RootFilterState, View } from 'src/interfaces/BoardConfigs'; import { taskItem } from 'src/interfaces/TaskItem'; import { Menu, Notice, Platform } from 'obsidian'; import { TaskFilterPopover } from 'src/components/AdvancedFilterer/TaskFilterPopover'; @@ -17,7 +17,7 @@ import { matchTagsWithWildcards } from 'src/utils/algorithms/ScanningFilterer'; import { isRootFilterStateEmpty } from 'src/utils/algorithms/AdvancedFilterer'; import { dragDropTasksManagerInsatance } from 'src/managers/DragDropTasksManager'; import { taskCardStyleNames } from 'src/interfaces/GlobalSettings'; -import TaskItemV2 from './TaskItemV2'; +import TaskItemV2 from '../TaskCard/TaskItemV2'; import { AlertOctagon } from 'lucide-react'; import { bugReporterManagerInsatance } from 'src/managers/BugReporter'; @@ -28,7 +28,8 @@ type CustomCSSProperties = CSSProperties & { export interface LazyColumnProps { plugin: TaskBoard; activeBoardData: Board; - activeBoardIndex: number; + currentView: View; + currentViewIndex: number; columnData: ColumnData; // columnIndex: number; tasksForThisColumn: taskItem[]; @@ -41,15 +42,16 @@ export interface LazyColumnProps { const LazyColumn: React.FC = ({ plugin, activeBoardData, + currentView, + currentViewIndex, columnData, - activeBoardIndex, tasksForThisColumn, swimlaneData, hideColumnHeader = false, headerOnly = false, }) => { // console.log("Column Data :", columnData); - if (!headerOnly && activeBoardData?.hideEmptyColumns && (tasksForThisColumn === undefined || tasksForThisColumn?.length === 0)) { + if (!headerOnly && currentView.kanbanView && currentView.kanbanView.hideEmptyColumns && (tasksForThisColumn === undefined || tasksForThisColumn?.length === 0)) { return null; // Don't render the column if it has no tasks and empty columns are hidden } @@ -58,6 +60,8 @@ const LazyColumn: React.FC = ({ const loadMoreCount = 10; const scrollThresholdPercent = 80; + const [currentViewData, setCurrentViewData] = useState(currentView); + // State for managing visible tasks const [visibleTaskCount, setVisibleTaskCount] = useState(initialTaskCount); const tasksContainerRef = useRef(null); @@ -239,7 +243,7 @@ const LazyColumn: React.FC = ({ if (columnIndex !== -1) { let newBoardData = activeBoardData; - newBoardData.columns[columnIndex].minimized = !newBoardData.columns[columnIndex].minimized; + newBoardData.views[currentViewIndex].kanbanView!.columns[columnIndex].minimized = !newBoardData.views[currentViewIndex].kanbanView!.columns[columnIndex].minimized; plugin.taskBoardFileManager.saveBoard(newBoardData); eventEmitter.emit('REFRESH_BOARD'); @@ -277,14 +281,14 @@ const LazyColumn: React.FC = ({ // Update the column configuration in the board data // if (activeBoardData.index !== -1) { - const columnIndex = activeBoardData.columns.findIndex( + const columnIndex = activeBoardData.views[currentViewIndex].kanbanView!.columns.findIndex( (col: ColumnData) => col.id === columnData.id ); if (columnIndex !== -1) { // Update the column configuration let newBoardData = activeBoardData; - newBoardData.columns[columnIndex] = updatedColumnConfiguration; + newBoardData.views[currentViewIndex].kanbanView!.columns[columnIndex] = updatedColumnConfiguration; plugin.taskBoardFileManager.saveBoard(newBoardData); } @@ -316,7 +320,7 @@ const LazyColumn: React.FC = ({ if (Platform.isMobile || Platform.isMacOS) { // If its a mobile platform, then we will open a modal instead of popover. const filterModal = new TaskFilterModal( - plugin, true, undefined, boardIndex, columnData.name, columnData.filters + plugin, true, undefined, columnData.name, columnData.filters ); // Set the close callback - mainly used for handling cancel actions @@ -325,7 +329,7 @@ const LazyColumn: React.FC = ({ if (columnIndex !== -1) { // Update the column filters let newBoardData = activeBoardData; - newBoardData.columns[columnIndex].filters = filterState; + newBoardData.views[currentViewIndex].kanbanView!.columns[columnIndex].filters = filterState; plugin.taskBoardFileManager.saveBoard(newBoardData); @@ -351,7 +355,6 @@ const LazyColumn: React.FC = ({ plugin, true, // forColumn is true undefined, - boardIndex, columnData.name, columnData.filters ); @@ -362,7 +365,7 @@ const LazyColumn: React.FC = ({ if (columnIndex !== -1) { // Update the column filters let newBoardData = activeBoardData; - newBoardData.columns[columnIndex].filters = filterState; + newBoardData.views[currentViewIndex].kanbanView!.columns[columnIndex].filters = filterState; plugin.taskBoardFileManager.saveBoard(newBoardData); @@ -405,7 +408,7 @@ const LazyColumn: React.FC = ({ if (columnIndex !== -1) { // Set the active property to false let newBoardData = activeBoardData; - newBoardData.columns[columnIndex].active = false; + newBoardData.views[currentViewIndex].kanbanView!.columns[columnIndex].active = false; plugin.taskBoardFileManager.saveBoard(newBoardData); @@ -883,8 +886,8 @@ const LazyColumn: React.FC = ({ dataAttributeIndex={i} plugin={plugin} task={task} - activeBoardSettings={activeBoardData} - activeBoardIndex={activeBoardIndex} + activeViewData={currentViewData} + activeViewIndex={currentViewIndex} columnIndex={columnData.index} swimlaneData={swimlaneData} /> @@ -929,8 +932,8 @@ const MemoizedTaskItem = memo<{ dataAttributeIndex: number; plugin: TaskBoard; task: taskItem; - activeBoardSettings: Board; - activeBoardIndex: number; + activeViewData: View; + activeViewIndex: number; columnIndex?: number; swimlaneData?: swimlaneDataProp; }>(({ Component, ...props }) => { @@ -939,8 +942,8 @@ const MemoizedTaskItem = memo<{ return ( prevProps.dataAttributeIndex === nextProps.dataAttributeIndex && prevProps.task === nextProps.task && - prevProps.activeBoardSettings === nextProps.activeBoardSettings && - prevProps.activeBoardIndex === nextProps.activeBoardIndex && + prevProps.activeViewData === nextProps.activeViewData && + prevProps.activeViewIndex === nextProps.activeViewIndex && prevProps.columnIndex === nextProps.columnIndex && prevProps.swimlaneData === nextProps.swimlaneData ); diff --git a/src/components/MapView/MapView.tsx b/src/components/MapView/MapView.tsx index cc252b4e..9048cea8 100644 --- a/src/components/MapView/MapView.tsx +++ b/src/components/MapView/MapView.tsx @@ -19,10 +19,9 @@ import { import { taskItem, UpdateTaskEventData, taskJsonMerged } from 'src/interfaces/TaskItem'; import TaskBoard from 'main'; import ResizableNodeSelected from './ResizableNodeSelected'; -import TaskItem from '../KanbanView/TaskItem'; +import TaskItem from '../TaskCard/TaskItem'; import { updateTaskInFile } from 'src/utils/taskLine/TaskLineUtils'; import { debounce, Menu, Notice, Platform } from 'obsidian'; -import { NODE_POSITIONS_STORAGE_KEY, NODE_SIZE_STORAGE_KEY, VIEWPORT_STORAGE_KEY } from 'src/interfaces/Constants'; import { sanitizeDependsOn } from 'src/utils/taskLine/TaskContentFormatter'; import { t } from 'src/utils/lang/helper'; import { MapViewMinimap } from './MapViewMinimap'; @@ -33,12 +32,13 @@ import { TasksImporterPanel } from './TasksImporterPanel'; import { isTaskNotePresentInTags, updateFrontmatterInMarkdownFile } from 'src/utils/taskNote/TaskNoteUtils'; import { isTaskCompleted } from 'src/utils/CheckBoxUtils'; import { bugReporterManagerInsatance } from 'src/managers/BugReporter'; -import { Board, nodeDataType, nodePositionWidth, viewPortType } from 'src/interfaces/BoardConfigs'; +import { Board, View, nodeDataType, nodePositionWidth, viewPortType } from 'src/interfaces/BoardConfigs'; type MapViewProps = { plugin: TaskBoard; activeBoardData: Board; - activeBoardIndex: number; + currentView: View; + currentViewIndex: number; filteredTasks: taskJsonMerged; focusOnTaskId?: string; }; @@ -50,7 +50,7 @@ const nodeTypes = { const MapView: React.FC = ({ - plugin, activeBoardData, activeBoardIndex, filteredTasks, focusOnTaskId + plugin, activeBoardData, currentView, currentViewIndex, filteredTasks, focusOnTaskId }) => { plugin.settings.data.lastViewHistory.taskId = ""; // Clear the taskId after focusing once const mapViewSettings = plugin.settings.data.mapView; @@ -97,10 +97,14 @@ const MapView: React.FC = ({ if (!mapDataUpdated.current) return; let newBoardData = activeBoardData; - newBoardData.mapView = { - viewPortData: viewport, - nodesData: allNodesData.current, - }; + // Update the currentView's mapView data + const viewIndex = newBoardData.views.findIndex(v => v.viewId === currentView.viewId); + if (viewIndex >= 0) { + newBoardData.views[viewIndex].mapView = { + viewPortData: viewport, + nodesData: allNodesData.current, + }; + } plugin.taskBoardFileManager.saveBoard(newBoardData); emitMapDataUpdatedSignal(false); @@ -108,7 +112,7 @@ const MapView: React.FC = ({ eventEmitter.on("SAVE_MAP", saveMapDataListener); return () => eventEmitter.off("SAVE_MAP", saveMapDataListener); - }, [activeBoardData, viewport]); + }, [activeBoardData, currentView, viewport]); const emitMapDataUpdatedSignal = (flag: boolean) => { console.log("emitMapDataUpdatedSignal called....\nflag : ", mapDataUpdated.current); @@ -126,7 +130,7 @@ const MapView: React.FC = ({ // Load positions from the active board data const loadAllNodesData = () => { try { - const list = activeBoardData?.mapView?.nodesData ? activeBoardData.mapView.nodesData : {}; + const list = currentView?.mapView?.nodesData ? currentView.mapView.nodesData : {}; // const map: Record = {}; // list.forEach(item => { // if (item && typeof item.key === 'string') { @@ -143,7 +147,7 @@ const MapView: React.FC = ({ // Load viewport from the active board data const loadViewport = (): viewPortType => { try { - const vp = activeBoardData?.mapView?.viewPortData; + const vp = currentView?.mapView?.viewPortData; if (vp && typeof vp === 'object') { return { x: Number.isFinite(vp.x) ? vp.x : 10, @@ -277,8 +281,8 @@ const MapView: React.FC = ({ dataAttributeIndex={0} plugin={plugin} task={task} - activeBoardSettings={activeBoardData} - activeBoardIndex={activeBoardIndex} + activeViewData={currentView} + activeViewIndex={currentViewIndex} /> }, position: { @@ -874,7 +878,8 @@ const MapView: React.FC = ({ plugin={plugin} allTasksArranged={[allTasksFlattened]} activeBoardSettings={activeBoardData} - activeBoardIndex={activeBoardIndex} + activeViewData={currentView} + activeViewIndex={currentViewIndex} isVisible={isImporterPanelVisible} onClose={() => setIsImporterPanelVisible(false)} /> @@ -1017,7 +1022,8 @@ const MapView: React.FC = ({ plugin={plugin} allTasksArranged={[allTasksFlattened]} activeBoardSettings={activeBoardData} - activeBoardIndex={activeBoardIndex} + activeViewData={currentView} + activeViewIndex={currentViewIndex} isVisible={isImporterPanelVisible} onClose={() => setIsImporterPanelVisible(false)} /> diff --git a/src/components/MapView/TasksImporterPanel.tsx b/src/components/MapView/TasksImporterPanel.tsx index ff589a8b..7b59715c 100644 --- a/src/components/MapView/TasksImporterPanel.tsx +++ b/src/components/MapView/TasksImporterPanel.tsx @@ -3,9 +3,9 @@ import React, { useState, useEffect, useMemo, useRef, useCallback } from 'react'; import { X, ChevronDown } from 'lucide-react'; import { taskItem } from 'src/interfaces/TaskItem'; -import TaskItem from '../KanbanView/TaskItem'; +import TaskItem from '../TaskCard/TaskItem'; import TaskBoard from 'main'; -import { Board } from 'src/interfaces/BoardConfigs'; +import { Board, View } from 'src/interfaces/BoardConfigs'; import { t } from 'src/utils/lang/helper'; import { eventEmitter } from 'src/services/EventEmitter'; import { applyIdToTaskItem } from 'src/utils/TaskItemUtils'; @@ -15,7 +15,8 @@ interface TasksImporterPanelProps { plugin: TaskBoard; allTasksArranged: taskItem[][]; activeBoardSettings: Board; - activeBoardIndex: number; + activeViewData: View; + activeViewIndex: number; isVisible: boolean; onClose: () => void; } @@ -24,7 +25,8 @@ export const TasksImporterPanel: React.FC = ({ plugin, allTasksArranged, activeBoardSettings, - activeBoardIndex, + activeViewData, + activeViewIndex, isVisible, onClose }) => { @@ -217,8 +219,8 @@ export const TasksImporterPanel: React.FC = ({ key={task.id} plugin={plugin} task={task} - activeBoardSettings={activeBoardSettings} - activeBoardIndex={activeBoardIndex} + activeViewData={activeViewData} + activeViewIndex={activeViewIndex} dataAttributeIndex={0} // TODO : No need of this data in this case. />
    diff --git a/src/components/TaskBoardViewContainer.tsx b/src/components/TaskBoardViewContainer.tsx index b9871a10..d1bbac62 100644 --- a/src/components/TaskBoardViewContainer.tsx +++ b/src/components/TaskBoardViewContainer.tsx @@ -1,6 +1,6 @@ // src/components/TaskBoardViewContainer.tsx -import { Board, ColumnData, RootFilterState } from "../interfaces/BoardConfigs"; +import { Board, ColumnData, RootFilterState, View } from "../interfaces/BoardConfigs"; import { CirclePlus, RefreshCcw, Search, SearchX, Filter, Menu as MenuICon, Settings, EllipsisVertical, List, KanbanSquareIcon, Network, BrickWall, KanbanSquare, SquareKanban, Save, LayoutGridIcon } from 'lucide-react'; import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { loadTasksAndMerge } from "src/utils/JsonFileOperations"; @@ -9,7 +9,7 @@ import { taskJsonMerged } from "src/interfaces/TaskItem"; import { App, debounce, Platform, Menu, WorkspaceLeaf } from "obsidian"; import type TaskBoard from "main"; import { eventEmitter } from "src/services/EventEmitter"; -import { openAddNewTaskModal, openBoardConfigModal, openScanVaultModal, openTaskBoardActionsModal, openBoardsExplorerModal } from "../services/OpenModals"; +import { openAddNewTaskModal, openBoardConfigModal, openScanVaultModal, openBoardsExplorerModal } from "../services/OpenModals"; import { t } from "src/utils/lang/helper"; import KanbanBoard from "./KanbanView/KanbanBoardView"; import MapView from "./MapView/MapView"; @@ -20,15 +20,43 @@ import { TaskFilterModal } from 'src/components/AdvancedFilterer'; import { taskPropertiesNames, viewTypeNames } from "src/interfaces/Enums"; import { ScanVaultIcon, funnelIcon } from "src/interfaces/Icons"; import { bugReporterManagerInsatance } from "src/managers/BugReporter"; +import { getViewById, getViewIndex } from "src/utils/ViewUtils"; -const TaskBoardViewContainer: React.FC<{ plugin: TaskBoard, currentBoardData: Board, currentLeaf: WorkspaceLeaf, allBoards?: Board[] }> = ({ plugin, currentBoardData, currentLeaf, allBoards }) => { +const TaskBoardViewContainer: React.FC<{ plugin: TaskBoard, currentBoardData: Board, currentLeaf: WorkspaceLeaf }> = ({ plugin, currentBoardData, currentLeaf }) => { // const [boards, setBoards] = useState(boardConfigs); - const [activeBoardIndex, setActiveBoardIndex] = useState(plugin.settings.data.lastViewHistory.boardIndex ?? 0); - const [boardData, setCurrentBoardData] = useState(); - const [allBoardsData, setAllBoardsData] = useState(allBoards); + const [boardData, setCurrentBoardData] = useState(currentBoardData); const [allTasks, setAllTasks] = useState(); const [filteredTasks, setFilteredTasks] = useState(null); - const [viewType, setViewType] = useState(plugin.settings.data.lastViewHistory.viewedType || viewTypeNames.kanban); + // Track current view by ID. Initialize to the first view if available + const [currentViewIndex, setCurrentViewIndex] = useState(0); + const [currentView, setCurrentView] = useState(() => { + const initialBoard = currentBoardData; + if (initialBoard?.views?.length > 0) { + const lastViewIndex = getViewIndex(initialBoard, initialBoard.lastViewId); + if (lastViewIndex !== -1) { + setCurrentViewIndex(lastViewIndex); + return initialBoard.views[lastViewIndex]; + } + + return initialBoard.views[0]; + } + return null; + }); + + // Display a message that no views are present inside this board. Stop here only instead of moving with the rest of the code which is dependent on the views. + if (!currentView) { + return ( +
    +
    +

    {t("no-views-in-board")}

    + +
    +
    + ); + } const [refreshCount, setRefreshCount] = useState(0); const [loading, setLoading] = useState(true); @@ -46,6 +74,14 @@ const TaskBoardViewContainer: React.FC<{ plugin: TaskBoard, currentBoardData: Bo const [sidebarAnimating, setSidebarAnimating] = useState(false); const [editorModified, setEditorModified] = useState(plugin.editorModified); + // // Derive current view from board data and currentViewId + // const currentView: View | undefined = useMemo(() => { + // if (boardData) { + // return getViewById(boardData, currentViewId); + // } + // return undefined; + // }, [boardData, currentViewId]); + useEffect(() => { const handleResize = () => { const taskBoardLeaf = currentLeaf; @@ -73,9 +109,16 @@ const TaskBoardViewContainer: React.FC<{ plugin: TaskBoard, currentBoardData: Bo // if (currentBoardData) { setCurrentBoardData(currentBoardData); - // Get index of the new board from the registry based on the board id. - const indexOfNewBoard = plugin.taskBoardFileManager.getBoardIndexFromRegistry(currentBoardData.id);; - const registryLength = Object.keys(plugin.settings.data.taskBoardFilesRegistry || {}).length; setActiveBoardIndex(indexOfNewBoard ?? registryLength); + // // Get index of the new board from the registry based on the board id. + // const indexOfNewBoard = plugin.taskBoardFileManager.getBoardIndexFromRegistry(currentBoardData.id);; + // const registryLength = Object.keys(plugin.settings.data.taskBoardFilesRegistry || {}).length; setActiveBoardIndex(indexOfNewBoard ?? registryLength); + + // // When board changes, automatically select the first view if available + // if (currentBoardData?.views?.length > 0) { + // const firstViewId = currentBoardData.views[0].viewId; + // setCurrentViewId(firstViewId); + // plugin.settings.data.lastViewHistory.currentViewId = firstViewId; + // } // } else { // const data = await plugin.taskBoardFileManager.loadBoardUsingIndex(activeBoardIndex); // if (!data) throw "Board data not found."; @@ -105,19 +148,19 @@ const TaskBoardViewContainer: React.FC<{ plugin: TaskBoard, currentBoardData: Bo const filteredAndSearchedTasks = useMemo(() => { if (allTasks && boardData) { const currentBoard = boardData; - const boardFilter = currentBoard.boardFilter; + const viewFilter = currentBoard.views?.find((view) => view.viewId === currentView.viewId)?.viewFilter; const dateFormat = plugin.settings.data.dateFormat || DEFAULT_DATE_FORMAT; // Apply board filters to tasks const boardFilteredTasks = { ...allTasks, - Pending: advancedFilterer(allTasks.Pending, boardFilter, dateFormat), - Completed: advancedFilterer(allTasks.Completed, boardFilter, dateFormat), + Pending: advancedFilterer(allTasks.Pending, viewFilter, dateFormat), + Completed: advancedFilterer(allTasks.Completed, viewFilter, dateFormat), }; let newBoardData = currentBoard; // Update task count in settings - newBoardData.taskCount = { + newBoardData.views[currentViewIndex].taskCount = { pending: boardFilteredTasks.Pending.length, completed: boardFilteredTasks.Completed.length, }; @@ -177,10 +220,11 @@ const TaskBoardViewContainer: React.FC<{ plugin: TaskBoard, currentBoardData: Bo }, []); useEffect(() => { - const refreshView = (viewType: string) => { - setViewType(viewType); - plugin.settings.data.lastViewHistory.viewedType = viewType; - plugin.saveSettings(); + const refreshView = (viewId: string) => { + setCurrentView(getViewById(boardData, viewId)!); + currentBoardData!.lastViewId = viewId; + // plugin.settings.data.lastViewHistory.currentViewId = viewId; + // plugin.saveSettings(); }; eventEmitter.on("SWITCH_VIEW", refreshView); return () => eventEmitter.off("SWITCH_VIEW", refreshView); @@ -211,10 +255,10 @@ const TaskBoardViewContainer: React.FC<{ plugin: TaskBoard, currentBoardData: Bo openAddNewTaskModal(plugin); } - function handleOpenTaskBoardActionsModal() { - if (boardData) - openTaskBoardActionsModal(plugin, boardData); - } + // function handleOpenTaskBoardActionsModal() { + // if (boardData) + // openTaskBoardActionsModal(plugin, boardData); + // } function handleSearchButtonClick() { if (showSearchInput) { @@ -283,16 +327,16 @@ const TaskBoardViewContainer: React.FC<{ plugin: TaskBoard, currentBoardData: Bo if (Platform.isMobile || Platform.isMacOS) { // If its a mobile platform, then we will open a modal instead of popover. const filterModal = new TaskFilterModal( - plugin, false, undefined, activeBoardIndex, currentBoardConfig!.name + plugin, false, undefined, currentBoardConfig!.name ); // Set initial filter state - if (currentBoardConfig!.boardFilter) { + if (currentBoardConfig!.views[currentViewIndex].viewFilter) { setTimeout(() => { // Use type assertion to resolve non-null issues // const filterState = filterModal.liveFilterState as RootFilterState; if (filterModal.taskFilterComponent) { - filterModal.taskFilterComponent.loadFilterState(currentBoardConfig!.boardFilter); + filterModal.taskFilterComponent.loadFilterState(currentBoardConfig!.views[currentViewIndex].viewFilter); } }, 100); } @@ -302,11 +346,11 @@ const TaskBoardViewContainer: React.FC<{ plugin: TaskBoard, currentBoardData: Bo if (filterState) { // Save the filter state to the board const updatedcurrentBoardData = boardData; - updatedcurrentBoardData!.boardFilter = filterState; + updatedcurrentBoardData!.views[currentViewIndex].viewFilter = filterState; setCurrentBoardData(updatedcurrentBoardData); // Persist to settings - boardData!.boardFilter = filterState; + boardData!.views[currentViewIndex].viewFilter = filterState; await plugin.saveSettings(); // Refresh the board view @@ -338,16 +382,15 @@ const TaskBoardViewContainer: React.FC<{ plugin: TaskBoard, currentBoardData: Bo plugin, false, // forColumn = false since this is for board-level filter undefined, - activeBoardIndex, boardData?.name || "Board", ); // Load existing filter state if available - if (currentBoardConfig!.boardFilter) { + if (currentBoardConfig!.views[currentViewIndex].viewFilter) { // Wait for component to be created and loaded setTimeout(() => { if (popover.taskFilterComponent) { - popover.taskFilterComponent.loadFilterState(currentBoardConfig!.boardFilter!); + popover.taskFilterComponent.loadFilterState(currentBoardConfig!.views[currentViewIndex].viewFilter!); } }, 100); } @@ -357,11 +400,11 @@ const TaskBoardViewContainer: React.FC<{ plugin: TaskBoard, currentBoardData: Bo if (filterState) { // Save the filter state to the board const updatedcurrentBoardData = boardData; - updatedcurrentBoardData!.boardFilter = filterState; + updatedcurrentBoardData!.views[currentViewIndex].viewFilter = filterState; setCurrentBoardData(updatedcurrentBoardData); // Persist to settings - boardData!.boardFilter = filterState; + boardData!.views[currentViewIndex].viewFilter = filterState; await plugin.saveSettings(); // Refresh the board view @@ -665,27 +708,20 @@ const TaskBoardViewContainer: React.FC<{ plugin: TaskBoard, currentBoardData: Bo ); } - function handleBoardSelection(index: number) { - if (index !== activeBoardIndex) { + function handleViewSelect(index: number) { + if (index !== currentViewIndex) { setSearchQuery(""); plugin.settings.data.searchQuery = ""; - plugin.settings.data.lastViewHistory.boardIndex = index; - setActiveBoardIndex(index); + setCurrentViewIndex(index); setTimeout(() => { eventEmitter.emit("REFRESH_BOARD"); - plugin.saveSettings(); + // plugin.saveSettings(); }, 100); - } closeBoardSidebar(); // Close sidebar after selection } function toggleBoardSidebar() { - // Only allow opening sidebar if allBoardsData is available - if (!allBoardsData || allBoardsData.length === 0) { - return; - } - if (showBoardSidebar) { closeBoardSidebar(); } else { @@ -738,7 +774,7 @@ const TaskBoardViewContainer: React.FC<{ plugin: TaskBoard, currentBoardData: Bo item.setTitle(t("open-board-configuration-modal")); item.setIcon("settings"); item.onClick(async () => { - openBoardConfigModal(plugin, currentBoardData, (updatedBoard: Board) => { + openBoardConfigModal(plugin, currentBoardData, currentViewIndex, (updatedBoard: Board) => { // handleUpdateBoards(plugin, updatedBoards, setCurrentBoardData) setCurrentBoardData(updatedBoard); plugin.taskBoardFileManager.saveBoard(updatedBoard); @@ -780,40 +816,45 @@ const TaskBoardViewContainer: React.FC<{ plugin: TaskBoard, currentBoardData: Bo ); } - function handleViewChangeDropdownClick(event: React.MouseEvent) { - const viewMenu = new Menu(); - - setTimeout(() => { - eventEmitter.emit("SAVE_MAP"); - }, 200) - - viewMenu.addItem((item) => { - item.setTitle(t("kanban-view")); - item.setIcon("square-kanban"); - item.onClick(async () => { - const newViewType = viewTypeNames.kanban; - setViewType(newViewType); - plugin.settings.data.lastViewHistory.viewedType = newViewType; - plugin.saveSettings(); - }); - }); - - viewMenu.addItem((item) => { - item.setTitle(t("map-view")); - item.setIcon("network"); - item.onClick(async () => { - const newViewType = viewTypeNames.map; - setViewType(newViewType); - plugin.settings.data.lastViewHistory.viewedType = newViewType; - plugin.saveSettings(); - }); - }); + // DEPRECATED: This function is no longer used since we have moved view switching to the header menu for better discoverability. We can remove this in future iterations. + // function handleViewChangeDropdownClick(event: React.MouseEvent) { + // const viewMenu = new Menu(); + + // setTimeout(() => { + // eventEmitter.emit("SAVE_MAP"); + // }, 200) + + // // Show all available views in the current board + // if (boardData && boardData.views && boardData.views.length > 0) { + // boardData.views.forEach((view) => { + // viewMenu.addItem((item) => { + // item.setTitle(view.viewName); + // const icon = view.viewType === viewTypeNames.kanban ? "square-kanban" : "network"; + // item.setIcon(icon); + // item.onClick(async () => { + // setCurrentViewId(view.viewId); + // plugin.settings.data.lastViewHistory.currentViewId = view.viewId; + // plugin.saveSettings(); + // eventEmitter.emit("SWITCH_VIEW", view.viewId); + // }); + // // Mark current view as checked + // if (view.viewId === currentViewId) { + // item.setChecked(true); + // } + // }); + // }); + // } else { + // viewMenu.addItem((item) => { + // item.setTitle(t("no-views-available")); + // item.setDisabled(true); + // }); + // } - // Use native event if available (React event has nativeEvent property) - viewMenu.showAtMouseEvent( - (event instanceof MouseEvent ? event : event.nativeEvent) - ); - } + // // Use native event if available (React event has nativeEvent property) + // viewMenu.showAtMouseEvent( + // (event instanceof MouseEvent ? event : event.nativeEvent) + // ); + // } // useEffect(() => { // const taskBoardLeaf = plugin.app.workspace.getLeavesOfType(VIEW_TYPE_TASKBOARD)[0]; @@ -837,7 +878,7 @@ const TaskBoardViewContainer: React.FC<{ plugin: TaskBoard, currentBoardData: Bo }, [showBoardSidebar]); const viewTypeIconComponent = () => { - switch (viewType) { + switch (boardData.views[currentViewIndex].viewType) { case viewTypeNames.kanban: return ; case viewTypeNames.map: @@ -851,17 +892,17 @@ const TaskBoardViewContainer: React.FC<{ plugin: TaskBoard, currentBoardData: Bo
    {/* On Left-side */} - {allBoardsData && allBoardsData.length > 0 ? ( + {boardData.views && boardData.views.length > 0 ? ( <> {showAllElements ? (
    - {allBoardsData.map((board, index) => ( + {boardData.views.map((view, index) => ( ))}
    @@ -961,7 +1002,7 @@ const TaskBoardViewContainer: React.FC<{ plugin: TaskBoard, currentBoardData: Bo className={`ConfigureBtn ${(isMobileView || Platform.isMobile) ? "taskBoardViewHeaderHideElements" : ""}`} aria-label={t("board-configure-button")} onClick={() => - openBoardConfigModal(plugin, currentBoardData, (updatedBoard: Board) => { + openBoardConfigModal(plugin, currentBoardData, currentViewIndex, (updatedBoard: Board) => { // handleUpdateBoards(plugin, updatedBoards, setCurrentBoardData) setCurrentBoardData(updatedBoard); plugin.taskBoardFileManager.saveBoard(updatedBoard); @@ -975,17 +1016,17 @@ const TaskBoardViewContainer: React.FC<{ plugin: TaskBoard, currentBoardData: Bo */} -
    handleViewChangeDropdownClick(e)} >
    {viewTypeIconComponent()}
    -
    {t(viewType)}
    -
    +
    {currentView?.viewName || "Unknown"}
    +
    */} - {viewType === viewTypeNames.map && ( + {currentView && currentView.viewType === viewTypeNames.map && (
    - {/* Mobile board sidebar overlay (only render if allBoardsData is available) */} + {/* View change panel on left side */} { - allBoardsData && allBoardsData.length > 0 && !showAllElements && showBoardSidebar && ( + boardData.views && boardData.views.length > 0 && !showAllElements && showBoardSidebar && (
    - {allBoardsData.map((board, index) => ( + {boardData.views.map((view, index) => (
    handleBoardSelection(index)} + className={`boardSidebarCard ${index === currentViewIndex ? 'boardSidebarCard--active' : ''}`} + onClick={() => handleViewSelect(index)} >
    - {board.name} + {view.viewName}
    - {board?.description} + {view?.description}
    - {(board?.taskCount ? board.taskCount.completed : 0)} / {board?.taskCount ? board?.taskCount.pending + board?.taskCount?.completed : 0} + {(view.taskCount.completed)} / {view.taskCount.pending + view.taskCount?.completed}
    @@ -1057,7 +1098,7 @@ const TaskBoardViewContainer: React.FC<{ plugin: TaskBoard, currentBoardData: Bo
    @@ -903,16 +945,17 @@ const TaskBoardViewContent: React.FC<{ app: App; plugin: TaskBoard; boardConfigs */} - +
    + {viewTypeIconComponent()} +
    +
    {t(viewType)}
    +
    @@ -982,66 +1009,67 @@ const TaskBoardViewContent: React.FC<{ plugin: TaskBoard, allBoards: Board[], cl