From 287d6bb9181ccad8259db2562fdc8900c401ea1b Mon Sep 17 00:00:00 2001 From: Aleix Soler Date: Wed, 23 Apr 2025 12:57:06 +0200 Subject: [PATCH] chore: remove type casting; adds type validation --- main.ts | 13 +++- services/status-service.ts | 6 +- ui/components/status-pane-view.ts | 97 ++++++++++++------------- ui/integrations/explorer-integration.ts | 10 +-- ui/menus/status-context-menu.ts | 12 ++- 5 files changed, 74 insertions(+), 64 deletions(-) diff --git a/main.ts b/main.ts index 89be540..35fdb9c 100644 --- a/main.ts +++ b/main.ts @@ -311,7 +311,12 @@ export default class NoteStatus extends Plugin { // Multiple files selection menu this.registerEvent( this.app.workspace.on('files-menu', (menu, files) => { - const mdFiles = files.filter(file => file instanceof TFile && file.extension === 'md') as TFile[]; + const mdFiles: TFile[] = []; + for (const file of files) { + if (file instanceof TFile && file.extension === 'md') { + mdFiles.push(file); + } + } if (mdFiles.length > 0) { menu.addItem((item) => item @@ -465,12 +470,12 @@ export default class NoteStatus extends Plugin { checkNoteStatus(): void { try { const activeFile = this.app.workspace.getActiveFile(); - if (!activeFile || activeFile.extension !== 'md') { + if (!activeFile || !(activeFile instanceof TFile) || activeFile.extension !== 'md') { this.statusBar.update(['unknown']); this.statusDropdown.update(['unknown']); return; } - + const statuses = this.statusService.getFileStatuses(activeFile); this.statusBar.update(statuses); this.statusDropdown.update(statuses); @@ -491,7 +496,7 @@ export default class NoteStatus extends Plugin { getCurrentStatuses(): string[] { try { const activeFile = this.app.workspace.getActiveFile(); - if (!activeFile || activeFile.extension !== 'md') { + if (!activeFile || !(activeFile instanceof TFile) || activeFile.extension !== 'md') { return ['unknown']; } return this.statusService.getFileStatuses(activeFile); diff --git a/services/status-service.ts b/services/status-service.ts index 2baa882..23dea6a 100644 --- a/services/status-service.ts +++ b/services/status-service.ts @@ -165,7 +165,7 @@ export class StatusService { */ public async updateNoteStatuses(newStatuses: string[], file?: TFile): Promise { const targetFile = file || this.app.workspace.getActiveFile(); - if (!targetFile || targetFile.extension !== 'md') return; + if (!targetFile || !(targetFile instanceof TFile) || targetFile.extension !== 'md') return; // Use processFrontMatter instead of manual read/modify await this.app.fileManager.processFrontMatter(targetFile, (frontmatter) => { @@ -261,7 +261,7 @@ export class StatusService { */ public async addNoteStatus(statusToAdd: string, file?: TFile): Promise { const targetFile = file || this.app.workspace.getActiveFile(); - if (!targetFile || targetFile.extension !== 'md') return; + if (!targetFile || !(targetFile instanceof TFile) || targetFile.extension !== 'md') return; const currentStatuses = this.getFileStatuses(targetFile); @@ -285,7 +285,7 @@ export class StatusService { */ public async removeNoteStatus(statusToRemove: string, file?: TFile): Promise { const targetFile = file || this.app.workspace.getActiveFile(); - if (!targetFile || targetFile.extension !== 'md') return; + if (!targetFile || !(targetFile instanceof TFile) || targetFile.extension !== 'md') return; const currentStatuses = this.getFileStatuses(targetFile); const newStatuses = currentStatuses.filter(status => status !== statusToRemove); diff --git a/ui/components/status-pane-view.ts b/ui/components/status-pane-view.ts index 36bdef5..f045a6e 100644 --- a/ui/components/status-pane-view.ts +++ b/ui/components/status-pane-view.ts @@ -1,7 +1,6 @@ import { TFile, WorkspaceLeaf, View, Menu, Notice, setIcon } from 'obsidian'; import { NoteStatusSettings } from '../../models/types'; import { StatusService } from '../../services/status-service'; -import { ICONS } from '../../constants/icons'; import NoteStatus from 'main'; /** @@ -159,96 +158,96 @@ export class StatusPaneView extends View { private renderStatusGroup(container: HTMLElement, status: string, files: TFile[]): void { const groupEl = container.createDiv({ cls: 'status-group nav-folder' }); const titleEl = groupEl.createDiv({ cls: 'nav-folder-title' }); - + // Create a container for the collapse button and title const collapseContainer = titleEl.createDiv({ cls: 'collapse-indicator' }); setIcon(collapseContainer, 'chevron-down'); - + // Create a container for the title content const titleContentContainer = titleEl.createDiv({ cls: 'nav-folder-title-content' }); - + const statusIcon = this.statusService.getStatusIcon(status); titleContentContainer.createSpan({ - text: `${status} ${statusIcon} (${files.length})`, - cls: `status-${status}` + text: `${status} ${statusIcon} (${files.length})`, + cls: `status-${status}` }); - + // Handle collapsing/expanding behavior titleEl.style.cursor = 'pointer'; const isCollapsed = this.settings.collapsedStatuses[status] ?? false; - + if (isCollapsed) { - groupEl.addClass('is-collapsed'); - collapseContainer.empty(); - setIcon(collapseContainer, 'chevron-right'); - } - - titleEl.addEventListener('click', (e) => { - e.preventDefault(); - const isCurrentlyCollapsed = groupEl.hasClass('is-collapsed'); - - // Toggle the collapsed state - if (isCurrentlyCollapsed) { - groupEl.removeClass('is-collapsed'); - collapseContainer.empty(); - setIcon(collapseContainer, 'chevron-down'); - } else { groupEl.addClass('is-collapsed'); collapseContainer.empty(); setIcon(collapseContainer, 'chevron-right'); - } - - // Update the settings - this.settings.collapsedStatuses[status] = !isCurrentlyCollapsed; - - // Trigger settings save - window.dispatchEvent(new CustomEvent('note-status:settings-changed')); + } + + titleEl.addEventListener('click', (e) => { + e.preventDefault(); + const isCurrentlyCollapsed = groupEl.hasClass('is-collapsed'); + + // Toggle the collapsed state + if (isCurrentlyCollapsed) { + groupEl.removeClass('is-collapsed'); + collapseContainer.empty(); + setIcon(collapseContainer, 'chevron-down'); + } else { + groupEl.addClass('is-collapsed'); + collapseContainer.empty(); + setIcon(collapseContainer, 'chevron-right'); + } + + // Update the settings + this.settings.collapsedStatuses[status] = !isCurrentlyCollapsed; + + // Trigger settings save + window.dispatchEvent(new CustomEvent('note-status:settings-changed')); }); - + // Create and populate child elements const childrenEl = groupEl.createDiv({ cls: 'nav-folder-children' }); - + // Sort files by name files.sort((a, b) => a.basename.localeCompare(b.basename)); - + // Create file list items files.forEach(file => { - this.createFileListItem(childrenEl, file, status); + this.createFileListItem(childrenEl, file, status); }); } private createFileListItem(container: HTMLElement, file: TFile, status: string): void { const fileEl = container.createDiv({ cls: 'nav-file' }); const fileTitleEl = fileEl.createDiv({ cls: 'nav-file-title' }); - + // Add file icon if in standard view if (!this.settings.compactView) { - const fileIcon = fileTitleEl.createDiv({ cls: 'nav-file-icon' }); - setIcon(fileIcon, 'file'); + const fileIcon = fileTitleEl.createDiv({ cls: 'nav-file-icon' }); + setIcon(fileIcon, 'file'); } - + // Add file name fileTitleEl.createSpan({ - text: file.basename, - cls: 'nav-file-title-content' + text: file.basename, + cls: 'nav-file-title-content' }); - + // Add status indicator fileTitleEl.createSpan({ - cls: `note-status-icon nav-file-tag status-${status}`, - text: this.statusService.getStatusIcon(status) + cls: `note-status-icon nav-file-tag status-${status}`, + text: this.statusService.getStatusIcon(status) }); - + // Add click handler to open the file fileEl.addEventListener('click', (e) => { - e.preventDefault(); - this.app.workspace.openLinkText(file.path, file.path, true); + e.preventDefault(); + this.app.workspace.openLinkText(file.path, file.path, true); }); - + // Add context menu fileEl.addEventListener('contextmenu', (e) => { - e.preventDefault(); - this.showFileContextMenu(e, file); + e.preventDefault(); + this.showFileContextMenu(e, file); }); } diff --git a/ui/integrations/explorer-integration.ts b/ui/integrations/explorer-integration.ts index fef3d4a..d1652fc 100644 --- a/ui/integrations/explorer-integration.ts +++ b/ui/integrations/explorer-integration.ts @@ -329,20 +329,20 @@ export class ExplorerIntegration { public getSelectedFiles(): TFile[] { const fileExplorer = this.app.workspace.getLeavesOfType('file-explorer')[0]; if (!fileExplorer || !fileExplorer.view) return []; - + const fileExplorerView = fileExplorer.view as FileExplorerView; if (!fileExplorerView.fileItems) return []; - + const selectedFiles: TFile[] = []; Object.entries(fileExplorerView.fileItems).forEach(([_, item]) => { if (item.el?.classList.contains('is-selected') && - item.file instanceof TFile && - item.file.extension === 'md') { + item.file && item.file instanceof TFile && + item.file.extension === 'md') { selectedFiles.push(item.file); } }); - + return selectedFiles; } diff --git a/ui/menus/status-context-menu.ts b/ui/menus/status-context-menu.ts index abdb33c..f7ce72a 100644 --- a/ui/menus/status-context-menu.ts +++ b/ui/menus/status-context-menu.ts @@ -35,13 +35,13 @@ export class StatusContextMenu { public showForFiles(files: TFile[], position?: { x: number; y: number }): void { if (files.length === 0) return; - // Para un solo archivo, mostrar dropdown directamente + // For a single file, show dropdown directly if (files.length === 1) { this.showSingleFileDropdown(files[0], position); return; } - // Para mĂșltiples archivos, mostrar menĂș primero + // For multiple files, show menu first this.showMultipleFilesMenu(files, position); } @@ -116,6 +116,11 @@ export class StatusContextMenu { * Shows a context menu for a single file */ public showForFile(file: TFile, event: MouseEvent): void { + // Ensure file is a valid TFile instance before proceeding + if (!(file instanceof TFile) || file.extension !== 'md') { + return; + } + const position = { x: event.clientX, y: event.clientY }; this.statusDropdown.openStatusDropdown({ @@ -133,7 +138,8 @@ export class StatusContextMenu { * Handle status update for a specific file */ private async handleStatusUpdateForFile(file: TFile, statuses: string[]): Promise { - if (statuses.length === 0) return; + // Add instanceof check for safety + if (!(file instanceof TFile) || file.extension !== 'md' || statuses.length === 0) return; // Update the file with the selected status if (this.settings.useMultipleStatuses) {