chore: remove type casting; adds type validation

This commit is contained in:
Aleix Soler 2025-04-23 12:57:06 +02:00
parent 3fb95380be
commit 287d6bb918
5 changed files with 74 additions and 64 deletions

13
main.ts
View file

@ -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);

View file

@ -165,7 +165,7 @@ export class StatusService {
*/
public async updateNoteStatuses(newStatuses: string[], file?: TFile): Promise<void> {
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<void> {
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<void> {
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);

View file

@ -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);
});
}

View file

@ -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;
}

View file

@ -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<void> {
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) {