mirror of
https://github.com/devonthesofa/obsidian-note-status.git
synced 2026-07-22 12:30:24 +00:00
215 lines
5 KiB
TypeScript
215 lines
5 KiB
TypeScript
import eventBus from "core/eventBus";
|
|
import {
|
|
Menu,
|
|
Notice,
|
|
Plugin,
|
|
TAbstractFile,
|
|
TFile,
|
|
TFolder,
|
|
WorkspaceLeaf,
|
|
} from "obsidian";
|
|
import {
|
|
BaseNoteStatusService,
|
|
MultipleNoteStatusService,
|
|
NoteStatusService,
|
|
} from "@/core/noteStatusService";
|
|
import settingsService from "@/core/settingsService";
|
|
|
|
export class ContextMenuIntegration {
|
|
private static instance: ContextMenuIntegration | null = null;
|
|
private plugin: Plugin;
|
|
private currentLeaf: WorkspaceLeaf | null = null;
|
|
private noteStatusService:
|
|
| NoteStatusService
|
|
| MultipleNoteStatusService
|
|
| null = null;
|
|
|
|
constructor(plugin: Plugin) {
|
|
if (ContextMenuIntegration.instance) {
|
|
throw new Error("The context menu instance is already created");
|
|
}
|
|
this.plugin = plugin;
|
|
ContextMenuIntegration.instance = this;
|
|
}
|
|
|
|
async integrate() {
|
|
this.plugin.registerEvent(
|
|
this.plugin.app.workspace.on(
|
|
"files-menu",
|
|
(
|
|
menu: Menu,
|
|
files: TAbstractFile[],
|
|
source: string,
|
|
leaf?: WorkspaceLeaf,
|
|
) => {
|
|
menu.addItem((item) => {
|
|
item.setTitle("Change note status")
|
|
.setIcon("rotate-ccw") // Lucide icon
|
|
.onClick(async () => {
|
|
const tFiles = files.filter(
|
|
(f): f is TFile => f instanceof TFile,
|
|
);
|
|
if (!tFiles.length) {
|
|
new Notice(
|
|
"Select at least one file to change its status.",
|
|
);
|
|
return;
|
|
}
|
|
|
|
this.openMultipleFilesStatusesModal(tFiles);
|
|
});
|
|
});
|
|
},
|
|
),
|
|
);
|
|
this.plugin.registerEvent(
|
|
this.plugin.app.workspace.on("file-menu", (menu, file) => {
|
|
if (file instanceof TFile) {
|
|
menu.addItem((item) => {
|
|
item.setTitle("Change note state")
|
|
.setIcon("rotate-ccw")
|
|
.onClick(async () => {
|
|
this.openSingleFileStatusesModal(file);
|
|
});
|
|
});
|
|
} else if (file instanceof TFolder) {
|
|
this.addFolderStatusActions(menu, file);
|
|
}
|
|
}),
|
|
);
|
|
|
|
this.plugin.registerEvent(
|
|
this.plugin.app.workspace.on(
|
|
"editor-menu",
|
|
(menu, editor, view) => {
|
|
menu.addItem((item) => {
|
|
item.setTitle("Change note status")
|
|
.setIcon("rotate-ccw") // Lucide icon
|
|
.onClick(async () => {
|
|
if (
|
|
!view.file ||
|
|
!(view.file instanceof TFile)
|
|
) {
|
|
new Notice(
|
|
"The selected item is not a valid file.",
|
|
);
|
|
return;
|
|
}
|
|
|
|
this.openSingleFileStatusesModal(view.file);
|
|
});
|
|
});
|
|
},
|
|
),
|
|
);
|
|
}
|
|
|
|
private openMultipleFilesStatusesModal(tFiles: TFile[]) {
|
|
const multipleStatusService = new MultipleNoteStatusService(tFiles);
|
|
multipleStatusService.populateStatuses();
|
|
eventBus.publish("triggered-open-modal", {
|
|
statusService: multipleStatusService,
|
|
});
|
|
}
|
|
private openSingleFileStatusesModal(tFile: TFile) {
|
|
const noteStatusService = new NoteStatusService(tFile);
|
|
noteStatusService.populateStatuses();
|
|
eventBus.publish("triggered-open-modal", {
|
|
statusService: noteStatusService,
|
|
});
|
|
}
|
|
|
|
private addFolderStatusActions(menu: Menu, folder: TFolder) {
|
|
const availableStatuses =
|
|
BaseNoteStatusService.getAllAvailableStatuses();
|
|
|
|
if (!availableStatuses.length) {
|
|
menu.addItem((item) => {
|
|
item.setTitle("Apply note status (no statuses available)")
|
|
.setIcon("alert-triangle")
|
|
.setDisabled(true);
|
|
});
|
|
return;
|
|
}
|
|
|
|
menu.addItem((item) => {
|
|
item.setTitle("Apply note status to folder")
|
|
.setIcon("folder-sync")
|
|
.onClick(() => {
|
|
this.openFolderStatusModal(folder, false);
|
|
});
|
|
});
|
|
|
|
if (settingsService.settings.applyStatusRecursivelyToSubfolders) {
|
|
menu.addItem((item) => {
|
|
item.setTitle("Apply note status to folder and subfolders")
|
|
.setIcon("git-merge")
|
|
.onClick(() => {
|
|
this.openFolderStatusModal(folder, true);
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
private openFolderStatusModal(
|
|
folder: TFolder,
|
|
includeSubfolders: boolean,
|
|
): void {
|
|
const files = this.getFilesFromFolder(folder, includeSubfolders);
|
|
|
|
if (!files.length) {
|
|
new Notice("This folder does not contain any files.");
|
|
return;
|
|
}
|
|
|
|
const warningThreshold = 50;
|
|
if (files.length >= warningThreshold) {
|
|
new Notice(
|
|
`This folder contains ${files.length} files. Applying status changes may take a while.`,
|
|
8000,
|
|
);
|
|
}
|
|
|
|
const multiStatusService = new MultipleNoteStatusService(files);
|
|
multiStatusService.populateStatuses();
|
|
|
|
eventBus.publish("triggered-open-modal", {
|
|
statusService: multiStatusService,
|
|
});
|
|
}
|
|
|
|
private getFilesFromFolder(
|
|
folder: TFolder,
|
|
includeSubfolders: boolean,
|
|
): TFile[] {
|
|
const files: TFile[] = [];
|
|
const queue: TFolder[] = [folder];
|
|
|
|
while (queue.length) {
|
|
const current = queue.shift();
|
|
if (!current) continue;
|
|
|
|
current.children.forEach((child) => {
|
|
if (child instanceof TFile) {
|
|
files.push(child);
|
|
} else if (includeSubfolders && child instanceof TFolder) {
|
|
queue.push(child);
|
|
}
|
|
});
|
|
|
|
if (!includeSubfolders) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return files;
|
|
}
|
|
|
|
destroy() {
|
|
this.currentLeaf = null;
|
|
this.noteStatusService = null;
|
|
ContextMenuIntegration.instance = null;
|
|
}
|
|
}
|
|
|
|
export default ContextMenuIntegration;
|