Fix linter issues for the events folder

This commit is contained in:
Lost Paul 2025-08-08 09:37:35 +02:00
parent d59f9dc631
commit 8adf6186ac
13 changed files with 440 additions and 215 deletions

View file

@ -139,18 +139,23 @@ export default [
'no-empty-function': 'warn',
'complexity': [
'warn',
10
15
],
'max-len': [
'warn', {
code: 100
code: 100,
ignoreComments: true,
}
],
'no-inline-comments': 'warn',
'no-magic-numbers': [
'warn', {
ignore: [0, 1],
enforceConst: true
enforceConst: true,
ignoreTypeIndexes: true,
ignoreEnums: true,
ignoreNumericLiteralTypes: true,
ignoreClassFieldInitialValues: true,
}
],
}

View file

@ -1,20 +1,20 @@
export class CustomEventEmitter {
private events: { [key: string]: Array<(data?: any) => void> } = {};
private events: { [key: string]: Array<(data?: unknown) => void> } = {};
on(event: string, listener: (data?: any) => void) {
on(event: string, listener: (data?: unknown) => void): void {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(listener);
}
off(event: string, listener: (data?: any) => void) {
off(event: string, listener: (data?: unknown) => void): void {
if (!this.events[event]) return;
this.events[event] = this.events[event].filter((l) => l !== listener);
}
emit(event: string, data?: any) {
emit(event: string, data?: unknown): void {
if (!this.events[event]) return;
this.events[event].forEach((listener) => listener(data));

View file

@ -1,9 +1,28 @@
import type FolderNotesPlugin from 'src/main';
import type { Listener, Events, ApiInterface, DeferInterface, ListenerRef, EventDispatcherInterface } from 'front-matter-plugin-api-provider';
import { getDefer } from 'front-matter-plugin-api-provider';
import type { App } from 'obsidian';
import { TFile, TFolder } from 'obsidian';
import {
type Listener,
type Events,
type ApiInterface,
type DeferInterface,
type ListenerRef,
type EventDispatcherInterface,
getDefer,
} from 'front-matter-plugin-api-provider';
import { type App, TFile, TFolder } from 'obsidian';
import { getFolder, getFolderNote } from 'src/functions/folderNoteFunctions';
interface UpdateData {
id: string;
result: boolean;
path: string;
pathOnly: boolean;
breadcrumb?: HTMLElement;
}
interface WrappedUpdateData {
data: UpdateData;
}
export class FrontMatterTitlePluginHandler {
plugin: FolderNotesPlugin;
app: App;
@ -16,7 +35,7 @@ export class FrontMatterTitlePluginHandler {
this.plugin = plugin;
this.app = plugin.app;
(async () => {
(async (): Promise<void> => {
this.deffer = getDefer(this.app);
if (this.deffer.isPluginReady()) {
this.api = this.deffer.getApi();
@ -35,8 +54,8 @@ export class FrontMatterTitlePluginHandler {
}
const event: Listener<Events, 'manager:update'> = {
name: 'manager:update',
cb: (data) => {
this.fmptUpdateFileName(data as any, true);
cb: (data): void => {
this.fmptUpdateFileName(data as unknown as UpdateData, true);
},
};
// Keep ref to remove listener
@ -51,20 +70,18 @@ export class FrontMatterTitlePluginHandler {
}
})();
}
deleteEvent() {
deleteEvent(): void {
if (this.eventRef) {
this.dispatcher.removeListener(this.eventRef);
}
}
async fmptUpdateFileName(data: {
id: string;
result: boolean;
path: string;
pathOnly: boolean;
breadcrumb?: HTMLElement;
}, isEvent: boolean) {
if ((data as any).data) data = (data as any).data;
const file = this.app.vault.getAbstractFileByPath(data.path);
async fmptUpdateFileName(data: UpdateData, isEvent: boolean): Promise<void> {
const hasNestedData = 'data' in (data as unknown as Record<string, unknown>);
const actualData: UpdateData = hasNestedData
? (data as unknown as WrappedUpdateData).data
: data;
const file = this.app.vault.getAbstractFileByPath(actualData.path);
if (!(file instanceof TFile)) { return; }
const resolver = this.api?.getResolverFactory()?.createResolver('#feature-id#');
@ -75,11 +92,11 @@ export class FrontMatterTitlePluginHandler {
const folderNote = getFolderNote(this.plugin, folder.path);
if (!folderNote) { return; }
if (folderNote !== file) { return; }
if (!data.pathOnly) {
if (!actualData.pathOnly) {
this.plugin.changeFolderNameInExplorer(folder, newName);
}
const { breadcrumb } = data;
const { breadcrumb } = actualData;
if (breadcrumb) {
this.plugin.changeFolderNameInPath(folder, newName, breadcrumb);
}
@ -98,15 +115,12 @@ export class FrontMatterTitlePluginHandler {
}
async fmptUpdateFolderName(data: {
id: string;
result: boolean;
path: string;
pathOnly: boolean;
breadcrumb?: HTMLElement;
}, replacePath: boolean) {
if ((data as any).data) data = (data as any).data;
const folder = this.app.vault.getAbstractFileByPath(data.path);
async fmptUpdateFolderName(data: UpdateData, _replacePath: boolean): Promise<void> {
const hasNestedData = 'data' in (data as unknown as Record<string, unknown>);
const actualData: UpdateData = hasNestedData
? (data as unknown as WrappedUpdateData).data
: data;
const folder = this.app.vault.getAbstractFileByPath(actualData.path);
if (!(folder instanceof TFolder)) { return; }
const folderNote = getFolderNote(this.plugin, folder.path);
if (!folderNote) { return; }
@ -115,11 +129,11 @@ export class FrontMatterTitlePluginHandler {
const newName = resolver?.resolve(folderNote?.path ?? '');
if (!newName) return;
if (!data.pathOnly) {
if (!actualData.pathOnly) {
this.plugin.changeFolderNameInExplorer(folder, newName);
}
const { breadcrumb } = data;
const { breadcrumb } = actualData;
if (breadcrumb) {
this.plugin.changeFolderNameInPath(folder, newName, breadcrumb);
}

View file

@ -7,7 +7,7 @@ import { updateCSSClassesForFolder } from 'src/functions/styleFunctions';
let fileExplorerMutationObserver: MutationObserver | null = null;
export function registerFileExplorerObserver(plugin: FolderNotesPlugin) {
export function registerFileExplorerObserver(plugin: FolderNotesPlugin): void {
// Run once on initial layout
plugin.app.workspace.onLayoutReady(() => {
initializeFolderNoteFeatures(plugin);
@ -30,19 +30,19 @@ export function registerFileExplorerObserver(plugin: FolderNotesPlugin) {
);
}
export function unregisterFileExplorerObserver() {
export function unregisterFileExplorerObserver(): void {
if (fileExplorerMutationObserver) {
fileExplorerMutationObserver.disconnect();
fileExplorerMutationObserver = null;
}
}
function initializeFolderNoteFeatures(plugin: FolderNotesPlugin) {
function initializeFolderNoteFeatures(plugin: FolderNotesPlugin): void {
initializeAllFolderTitles(plugin);
observeFolderTitleMutations(plugin);
}
function initializeBreadcrumbs(plugin: FolderNotesPlugin) {
function initializeBreadcrumbs(plugin: FolderNotesPlugin): void {
const titleContainers = document.querySelectorAll('.view-header-title-container');
if (!titleContainers.length) return;
titleContainers.forEach((container) => {
@ -55,7 +55,7 @@ function initializeBreadcrumbs(plugin: FolderNotesPlugin) {
* Observes the File Explorer for newly added folder elements and applies plugin logic (e.g., styles, event listeners)
* automatically when folders are created, expanded, or when the File Explorer view is reopened.
*/
function observeFolderTitleMutations(plugin: FolderNotesPlugin) {
function observeFolderTitleMutations(plugin: FolderNotesPlugin): void {
if (fileExplorerMutationObserver) {
fileExplorerMutationObserver.disconnect();
}
@ -71,7 +71,7 @@ function observeFolderTitleMutations(plugin: FolderNotesPlugin) {
fileExplorerMutationObserver.observe(document, { childList: true, subtree: true });
}
function initializeAllFolderTitles(plugin: FolderNotesPlugin) {
function initializeAllFolderTitles(plugin: FolderNotesPlugin): void {
const allTitles = document.querySelectorAll('.nav-folder-title-content');
for (const title of Array.from(allTitles)) {
const folderTitle = title as HTMLElement;
@ -83,7 +83,7 @@ function initializeAllFolderTitles(plugin: FolderNotesPlugin) {
}
}
function processAddedFolders(node: HTMLElement, plugin: FolderNotesPlugin) {
function processAddedFolders(node: HTMLElement, plugin: FolderNotesPlugin): void {
const titles: HTMLElement[] = [];
if (node.matches('.nav-folder-title-content')) {
titles.push(node);
@ -95,6 +95,7 @@ function processAddedFolders(node: HTMLElement, plugin: FolderNotesPlugin) {
titles.forEach((folderTitle) => {
const folderEl = folderTitle.closest('.nav-folder-title');
const folderPath = folderEl?.getAttribute('data-path') || '';
const RETRY_TIMEOUT = 50;
if (!folderEl || !folderPath) {
setTimeout(() => {
const retryFolderEl = folderTitle.closest('.nav-folder-title');
@ -102,14 +103,18 @@ function processAddedFolders(node: HTMLElement, plugin: FolderNotesPlugin) {
if (retryFolderEl && retryFolderPath) {
setupFolderTitle(folderTitle, plugin, retryFolderPath);
}
}, 50);
}, RETRY_TIMEOUT);
return;
}
setupFolderTitle(folderTitle, plugin, folderPath);
});
}
async function setupFolderTitle(folderTitle: HTMLElement, plugin: FolderNotesPlugin, folderPath: string) {
async function setupFolderTitle(
folderTitle: HTMLElement,
plugin: FolderNotesPlugin,
folderPath: string,
): Promise<void> {
if (folderTitle.dataset.initialized === 'true') return;
if (!folderPath) return;
@ -117,7 +122,10 @@ async function setupFolderTitle(folderTitle: HTMLElement, plugin: FolderNotesPlu
await updateCSSClassesForFolder(folderPath, plugin);
if (plugin.settings.frontMatterTitle.enabled) {
plugin.fmtpHandler?.fmptUpdateFolderName({ id: '', result: false, path: folderPath, pathOnly: false }, false);
plugin.fmtpHandler?.fmptUpdateFolderName(
{ id: '', result: false, path: folderPath, pathOnly: false },
false,
);
}
if (Platform.isMobile && plugin.settings.disableOpenFolderNoteOnClick) return;
@ -150,21 +158,24 @@ async function setupFolderTitle(folderTitle: HTMLElement, plugin: FolderNotesPlu
});
}
async function updateFolderNamesInPath(plugin: FolderNotesPlugin, titleContainer: HTMLElement) {
async function updateFolderNamesInPath(
plugin: FolderNotesPlugin,
titleContainer: HTMLElement,
): Promise<void> {
const headers = titleContainer.querySelectorAll('span.view-header-breadcrumb');
let path = '';
const TRAILING_SLASH_LENGTH = 1;
headers.forEach(async (breadcrumb: HTMLElement) => {
path += breadcrumb.getAttribute('old-name') ?? (breadcrumb as HTMLElement).innerText.trim();
path += '/';
const folderPath = path.slice(0, -1);
const folderPath = path.slice(0, -TRAILING_SLASH_LENGTH);
const excludedFolder = getExcludedFolder(plugin, folderPath, true);
if (excludedFolder?.disableFolderNote) return;
const folderNote = getFolderNote(plugin, folderPath);
if (!folderNote) return;
if (folderNote) breadcrumb.classList.add('has-folder-note');
breadcrumb?.setAttribute('data-path', path.slice(0, -1));
breadcrumb?.setAttribute('data-path', path.slice(0, -TRAILING_SLASH_LENGTH));
if (!breadcrumb.onclick) {
breadcrumb.addEventListener('click', (e) => {
handleViewHeaderClick(e as MouseEvent, plugin);
@ -172,7 +183,10 @@ async function updateFolderNamesInPath(plugin: FolderNotesPlugin, titleContainer
}
if (plugin.settings.frontMatterTitle.enabled) {
plugin.fmtpHandler?.fmptUpdateFolderName({ id: '', result: false, path: folderPath, pathOnly: true, breadcrumb: breadcrumb }, true);
plugin.fmtpHandler?.fmptUpdateFolderName(
{ id: '', result: false, path: folderPath, pathOnly: true, breadcrumb: breadcrumb },
true,
);
}
});
}
@ -180,10 +194,14 @@ async function updateFolderNamesInPath(plugin: FolderNotesPlugin, titleContainer
// Schedules a callback to run when the browser is idle, or after a timeout as a fallback.
// - callback: The function to execute when idle or after the timeout.
// - options: Optional object with a 'timeout' property (in milliseconds).
function scheduleIdle(callback: () => void, options?: { timeout: number }) {
function scheduleIdle(callback: () => void, options?: { timeout: number }): void {
const DEFAULT_IDLE_TIMEOUT = 200;
if ('requestIdleCallback' in window) {
(window as any).requestIdleCallback(callback, options);
const windowWithIdle = window as Window & {
requestIdleCallback: (callback: () => void, options?: { timeout: number }) => void
};
windowWithIdle.requestIdleCallback(callback, options);
} else {
setTimeout(callback, options?.timeout || 200);
setTimeout(callback, options?.timeout || DEFAULT_IDLE_TIMEOUT);
}
}

View file

@ -1,6 +1,5 @@
import type FolderNotesPlugin from 'src/main';
import type { App } from 'obsidian';
import { EditableFileView, TFolder } from 'obsidian';
import { EditableFileView, TFolder, type App } from 'obsidian';
import { getFolder, getFolderNote } from 'src/functions/folderNoteFunctions';
export class TabManager {
plugin: FolderNotesPlugin;
@ -10,19 +9,18 @@ export class TabManager {
this.app = plugin.app;
}
resetTabs() {
resetTabs(): void {
if (!this.isEnabled()) return;
this.app.workspace.iterateAllLeaves((leaf) => {
if (!(leaf.view instanceof EditableFileView)) return;
const file = leaf.view?.file;
if (!file) return;
// @ts-ignore
leaf.tabHeaderInnerTitleEl.setText(file.basename);
});
}
updateTabs() {
updateTabs(): void {
if (!this.isEnabled()) return;
this.app.workspace.iterateAllLeaves((leaf) => {
if (!(leaf.view instanceof EditableFileView)) return;
@ -30,12 +28,11 @@ export class TabManager {
if (!file) return;
const folder = getFolder(this.plugin, file);
if (!folder) return;
// @ts-ignore
leaf.tabHeaderInnerTitleEl.setText(folder.name);
});
}
updateTab(folderPath: string) {
updateTab(folderPath: string): void {
if (!this.isEnabled()) return;
const folder = this.app.vault.getAbstractFileByPath(folderPath);
@ -49,13 +46,12 @@ export class TabManager {
const file = leaf.view?.file;
if (!file) return;
if (file.path === folderNote.path) {
// @ts-ignore
leaf.tabHeaderInnerTitleEl.setText(folder.name);
}
});
}
isEnabled() {
isEnabled(): boolean {
if (this.plugin.settings.folderNoteName === '{{folder_name}}') return false;
return this.plugin.settings.tabManagerEnabled;
}

View file

@ -1,10 +1,18 @@
import { Keymap, Platform } from 'obsidian';
import { Keymap, Platform, type TFile } from 'obsidian';
import type FolderNotesPlugin from 'src/main';
import { openFolderNote, createFolderNote, getFolderNote } from 'src/functions/folderNoteFunctions';
import { getExcludedFolder } from 'src/ExcludeFolders/functions/folderFunctions';
import { addCSSClassToFileExplorerEl, removeCSSClassFromFileExplorerEL } from 'src/functions/styleFunctions';
import {
addCSSClassToFileExplorerEl,
removeCSSClassFromFileExplorerEL,
} from 'src/functions/styleFunctions';
export async function handleViewHeaderClick(event: MouseEvent, plugin: FolderNotesPlugin) {
export async function handleViewHeaderClick(
event: MouseEvent,
plugin: FolderNotesPlugin,
): Promise<void> {
if (!plugin.settings.openFolderNoteOnClickInPath) return;
event.stopImmediatePropagation();
event.preventDefault();
@ -13,37 +21,61 @@ export async function handleViewHeaderClick(event: MouseEvent, plugin: FolderNot
const folderPath = event.target.getAttribute('data-path');
if (!folderPath) { return; }
const excludedFolder = getExcludedFolder(plugin, folderPath, true);
if (excludedFolder?.disableFolderNote) {
event.target.onclick = null;
event.target.click();
return;
} else if (excludedFolder?.enableCollapsing || plugin.settings.enableCollapsing) {
event.target.onclick = null;
event.target.click();
}
if (await isExcludedFolder(event, plugin, folderPath)) return;
const folderNote = getFolderNote(plugin, folderPath);
if (folderNote) {
await openFolderNote(plugin, folderNote, event).then(async () => {
// @ts-ignore
const fileExplorerPlugin = plugin.app.internalPlugins.getEnabledPluginById('file-explorer');
if (fileExplorerPlugin && Platform.isMobile && plugin.settings.openSidebar.mobile) {
setTimeout(() => { fileExplorerPlugin.revealInFolder(folderNote); }, 200);
} else if (fileExplorerPlugin && Platform.isDesktop && plugin.settings.openSidebar.desktop) {
fileExplorerPlugin.revealInFolder(folderNote);
}
});
await openFolderNote(plugin, folderNote, event).then(() =>
handleFolderNoteReveal(plugin, folderNote),
);
return;
} else if (event.altKey || Keymap.isModEvent(event) === 'tab') {
const usedCtrl = Platform.isMacOS ? event.metaKey : event.ctrlKey;
if ((plugin.settings.altKey && event.altKey) || (usedCtrl && Keymap.isModEvent(event) === 'tab')) {
await createFolderNote(plugin, folderPath, true, undefined, true);
addCSSClassToFileExplorerEl(folderPath, 'has-folder-note', false, plugin);
removeCSSClassFromFileExplorerEL(folderPath, 'has-not-folder-note', false, plugin);
return;
}
if (await handleFolderNoteCreation(event, plugin, folderPath)) return;
}
event.target.onclick = null;
event.target.click();
(event.target as HTMLElement).onclick = null;
(event.target as HTMLElement).click();
}
async function isExcludedFolder(
event: MouseEvent,
plugin: FolderNotesPlugin,
folderPath: string,
): Promise<boolean> {
const excludedFolder = getExcludedFolder(plugin, folderPath, true);
if (excludedFolder?.disableFolderNote) {
(event.target as HTMLElement).onclick = null;
(event.target as HTMLElement).click();
return true;
} else if (excludedFolder?.enableCollapsing || plugin.settings.enableCollapsing) {
(event.target as HTMLElement).onclick = null;
(event.target as HTMLElement).click();
}
return false;
}
async function handleFolderNoteReveal(plugin: FolderNotesPlugin, folderNote: TFile): Promise<void> {
const fileExplorerPlugin = plugin.app.internalPlugins.getEnabledPluginById('file-explorer');
if (fileExplorerPlugin && Platform.isMobile && plugin.settings.openSidebar.mobile) {
const OPEN_SIDEBAR_DELAY = 200;
setTimeout(() => { fileExplorerPlugin.revealInFolder(folderNote); }, OPEN_SIDEBAR_DELAY);
} else if (fileExplorerPlugin && Platform.isDesktop && plugin.settings.openSidebar.desktop) {
fileExplorerPlugin.revealInFolder(folderNote);
}
}
async function handleFolderNoteCreation(
event: MouseEvent,
plugin: FolderNotesPlugin,
folderPath: string,
): Promise<boolean> {
const usedCtrl = Platform.isMacOS ? event.metaKey : event.ctrlKey;
if ((plugin.settings.altKey && event.altKey) ||
(usedCtrl && Keymap.isModEvent(event) === 'tab')) {
await createFolderNote(plugin, folderPath, true, undefined, true);
addCSSClassToFileExplorerEl(folderPath, 'has-folder-note', false, plugin);
removeCSSClassFromFileExplorerEL(folderPath, 'has-not-folder-note', false, plugin);
return true;
}
return false;
}

View file

@ -1,11 +1,18 @@
import type { TAbstractFile } from 'obsidian';
import { TFolder, TFile } from 'obsidian';
import { TFolder, TFile, type TAbstractFile } from 'obsidian';
import type FolderNotesPlugin from 'src/main';
import { createFolderNote, getFolder, getFolderNote, turnIntoFolderNote } from 'src/functions/folderNoteFunctions';
import {
createFolderNote,
getFolder,
getFolderNote,
turnIntoFolderNote,
} from 'src/functions/folderNoteFunctions';
import { getExcludedFolder } from 'src/ExcludeFolders/functions/folderFunctions';
import { removeCSSClassFromFileExplorerEL, addCSSClassToFileExplorerEl } from 'src/functions/styleFunctions';
import {
removeCSSClassFromFileExplorerEL,
addCSSClassToFileExplorerEl,
} from 'src/functions/styleFunctions';
export async function handleCreate(file: TAbstractFile, plugin: FolderNotesPlugin) {
export async function handleCreate(file: TAbstractFile, plugin: FolderNotesPlugin): Promise<void> {
if (!plugin.app.workspace.layoutReady) return;
const folder = file.parent;
@ -24,7 +31,7 @@ export async function handleCreate(file: TAbstractFile, plugin: FolderNotesPlugi
}
}
async function handleFileCreation(file: TFile, plugin: FolderNotesPlugin) {
async function handleFileCreation(file: TFile, plugin: FolderNotesPlugin): Promise<void> {
const folder = getFolder(plugin, file);
if (!(folder instanceof TFolder) && plugin.settings.autoCreateForFiles) {
@ -51,7 +58,7 @@ async function handleFileCreation(file: TFile, plugin: FolderNotesPlugin) {
}
}
async function handleFolderCreation(folder: TFolder, plugin: FolderNotesPlugin) {
async function handleFolderCreation(folder: TFolder, plugin: FolderNotesPlugin): Promise<void> {
let openFile = plugin.settings.autoCreateFocusFiles;
const attachmentFolderPath = plugin.app.vault.getConfig('attachmentFolderPath') as string;

View file

@ -1,11 +1,14 @@
import type { TAbstractFile } from 'obsidian';
import { TFolder, TFile } from 'obsidian';
import { TFolder, TFile, type TAbstractFile } from 'obsidian';
import type FolderNotesPlugin from 'src/main';
import { getFolderNote, getFolder, deleteFolderNote } from 'src/functions/folderNoteFunctions';
import { removeCSSClassFromFileExplorerEL, addCSSClassToFileExplorerEl, hideFolderNoteInFileExplorer } from 'src/functions/styleFunctions';
import {
removeCSSClassFromFileExplorerEL,
addCSSClassToFileExplorerEl,
hideFolderNoteInFileExplorer,
} from 'src/functions/styleFunctions';
import { getFolderPathFromString } from 'src/functions/utils';
export function handleDelete(file: TAbstractFile, plugin: FolderNotesPlugin) {
export function handleDelete(file: TAbstractFile, plugin: FolderNotesPlugin): void {
const folder = plugin.app.vault.getAbstractFileByPath(getFolderPathFromString(file.path));
if (folder instanceof TFolder) {
if (plugin.isEmptyFolderNoteFolder(folder) && getFolderNote(plugin, folder.path)) {
@ -16,13 +19,15 @@ export function handleDelete(file: TAbstractFile, plugin: FolderNotesPlugin) {
}
if (file instanceof TFile) {
const folder = getFolder(plugin, file);
if (!folder) { return; }
const folderNote = getFolderNote(plugin, folder.path);
const folderNoteFolder = getFolder(plugin, file);
if (!folderNoteFolder) { return; }
const folderNote = getFolderNote(plugin, folderNoteFolder.path);
if (folderNote) { return; }
removeCSSClassFromFileExplorerEL(folder.path, 'has-folder-note', false, plugin);
removeCSSClassFromFileExplorerEL(folder.path, 'only-has-folder-note', true, plugin);
hideFolderNoteInFileExplorer(folder.path, plugin);
removeCSSClassFromFileExplorerEL(folderNoteFolder.path, 'has-folder-note', false, plugin);
removeCSSClassFromFileExplorerEL(
folderNoteFolder.path, 'only-has-folder-note', true, plugin,
);
hideFolderNoteInFileExplorer(folderNoteFolder.path, plugin);
}
if (!(file instanceof TFolder)) { return; }

View file

@ -1,14 +1,29 @@
import type { TAbstractFile } from 'obsidian';
import { TFile, TFolder, Notice } from 'obsidian';
import { TFile, TFolder, Notice, type TAbstractFile } from 'obsidian';
import type FolderNotesPlugin from 'src/main';
import { extractFolderName, getFolderNote, getFolderNoteFolder } from '../functions/folderNoteFunctions';
import { getExcludedFolder, addExcludedFolder, updateExcludedFolder, deleteExcludedFolder, getDetachedFolder } from '../ExcludeFolders/functions/folderFunctions';
import {
extractFolderName, getFolderNote, getFolderNoteFolder,
} from '../functions/folderNoteFunctions';
import {
getExcludedFolder, addExcludedFolder,
updateExcludedFolder, deleteExcludedFolder, getDetachedFolder,
} from '../ExcludeFolders/functions/folderFunctions';
import { ExcludedFolder } from 'src/ExcludeFolders/ExcludeFolder';
import { removeCSSClassFromFileExplorerEL, addCSSClassToFileExplorerEl, markFileAsFolderNote, unmarkFileAsFolderNote, unmarkFolderAsFolderNote, markFolderWithFolderNoteClasses, hideFolderNoteInFileExplorer, removeActiveFolder, setActiveFolder } from 'src/functions/styleFunctions';
import { getFolderPathFromString, removeExtension, getFileNameFromPathString } from 'src/functions/utils';
import {
removeCSSClassFromFileExplorerEL, addCSSClassToFileExplorerEl,
markFileAsFolderNote, unmarkFileAsFolderNote,
unmarkFolderAsFolderNote, markFolderWithFolderNoteClasses,
hideFolderNoteInFileExplorer, removeActiveFolder, setActiveFolder,
} from 'src/functions/styleFunctions';
import {
getFolderPathFromString, removeExtension, getFileNameFromPathString,
} from 'src/functions/utils';
export function handleRename(file: TAbstractFile, oldPath: string, plugin: FolderNotesPlugin) {
const folder = file.parent;
export function handleRename(
file: TAbstractFile,
oldPath: string,
plugin: FolderNotesPlugin,
): void {
let folder = file.parent;
const oldFolder = plugin.app.vault.getAbstractFileByPath(getFolderPathFromString(oldPath));
if (folder instanceof TFolder) {
@ -28,19 +43,22 @@ export function handleRename(file: TAbstractFile, oldPath: string, plugin: Folde
}
if (file instanceof TFolder) {
const folder = file;
folder = file;
plugin.tabManager.updateTab(folder.path);
updateExcludedFolderPath(folder, oldPath, plugin);
if (isFolderRename(folder, oldPath)) {
return handleFolderRename(folder, oldPath, plugin);
handleFolderRename(folder, oldPath, plugin);
return;
}
return handleFolderMove(folder, oldPath, plugin);
} else if (file instanceof TFile) {
if (isFileRename(file, oldPath)) {
return fmptUpdateFileName(file, oldPath, plugin);
handleFileRename(file, oldPath, plugin);
return;
}
return handleFileMove(file, oldPath, plugin);
handleFileMove(file, oldPath, plugin);
return;
}
}
@ -64,7 +82,7 @@ function isFolderRename(folder: TFolder, oldPath: string): boolean {
return oldParent === newParent && oldName !== newName;
}
export function handleFolderMove(file: TFolder, oldPath: string, plugin: FolderNotesPlugin) {
export function handleFolderMove(file: TFolder, oldPath: string, plugin: FolderNotesPlugin): void {
if (plugin.settings.storageLocation === 'insideFolder') { return; }
if (!plugin.settings.syncMove) { return; }
const folderNote = getFolderNote(plugin, oldPath, plugin.settings.storageLocation);
@ -82,60 +100,43 @@ export function handleFolderMove(file: TFolder, oldPath: string, plugin: FolderN
plugin.app.fileManager.renameFile(folderNote, newPath);
}
export async function handleFileMove(file: TFile, oldPath: string, plugin: FolderNotesPlugin) {
const folderName = extractFolderName(plugin.settings.folderNoteName, file.basename) || file.basename;
const oldFileName = removeExtension(getFileNameFromPathString(oldPath));
const newFolder = getFolderNoteFolder(plugin, file, file.basename);
let excludedFolder = getExcludedFolder(plugin, newFolder?.path || '', true);
const oldFolder = getFolderNoteFolder(plugin, oldPath, oldFileName);
const folderNote = getFolderNote(plugin, oldPath, plugin.settings.storageLocation, file);
// eslint-disable-next-line complexity
export async function handleFileMove(
file: TFile,
oldPath: string,
plugin: FolderNotesPlugin,
): Promise<void> {
const { folderName, oldFileName, newFolder, excludedFolder, oldFolder, folderNote } = getArgs(
plugin, file, oldPath,
);
const isFileNowFolderNoteInNewFolder = folderName === newFolder?.name;
const isFileMovedFromOldFolderNote = oldFolder && oldFolder.name === oldFileName && newFolder?.path !== oldFolder.path;
const isFolderNoteInNewFolder = folderName === newFolder?.name;
const fileMovedFromOldFolderNote = oldFolder && oldFolder.name === oldFileName
&& newFolder?.path !== oldFolder.path;
// this is for turning files into folder notes for folders that already have a folder note
// e.g. Turn into folder note for "Folder name"
const isFileNowFolderNoteWithExistingNote = folderName === newFolder?.name && folderNote;
const isFileWithExistingNote = folderName === newFolder?.name && folderNote;
if (isFileNowFolderNoteWithExistingNote) {
let excludedFolderExisted = true;
let disabledSync = false;
if (!excludedFolder) {
excludedFolderExisted = false;
excludedFolder = new ExcludedFolder(oldFolder?.path || '', plugin.settings.excludeFolders.length, undefined, plugin);
addExcludedFolder(plugin, excludedFolder);
} else if (!excludedFolder.disableSync) {
disabledSync = false;
excludedFolder.disableSync = true;
updateExcludedFolder(plugin, excludedFolder, excludedFolder);
}
return plugin.app.fileManager.renameFile(file, oldPath).then(() => {
if (!excludedFolder) { return; }
if (!excludedFolderExisted) {
deleteExcludedFolder(plugin, excludedFolder);
} else if (!disabledSync) {
excludedFolder.disableSync = false;
updateExcludedFolder(plugin, excludedFolder, excludedFolder);
}
});
} else if (isFileNowFolderNoteInNewFolder) {
if (!excludedFolder?.disableFolderNote) {
markFileAsFolderNote(file, plugin);
if (newFolder instanceof TFolder) {
markFolderWithFolderNoteClasses(newFolder, plugin);
if (plugin.app.workspace.getActiveFile()?.path === file.path) {
removeActiveFolder(plugin);
setActiveFolder(newFolder.path, plugin);
}
}
if (oldFolder instanceof TFolder) {
hideFolderNoteInFileExplorer(oldFolder.path, plugin);
unmarkFolderAsFolderNote(oldFolder, plugin);
if (isFileWithExistingNote) {
renameExistingFolderNote(
file, oldPath, plugin, excludedFolder, oldFolder,
);
} else if (isFolderNoteInNewFolder) {
if (excludedFolder?.disableFolderNote) { return; }
markFileAsFolderNote(file, plugin);
if (newFolder instanceof TFolder) {
markFolderWithFolderNoteClasses(newFolder, plugin);
if (plugin.app.workspace.getActiveFile()?.path === file.path) {
removeActiveFolder(plugin);
setActiveFolder(newFolder.path, plugin);
}
}
} else if (isFileMovedFromOldFolderNote) {
if (oldFolder instanceof TFolder) {
hideFolderNoteInFileExplorer(oldFolder.path, plugin);
unmarkFolderAsFolderNote(oldFolder, plugin);
}
} else if (fileMovedFromOldFolderNote) {
unmarkFileAsFolderNote(file, plugin);
if (oldFolder instanceof TFolder) {
removeActiveFolder(plugin);
@ -145,9 +146,75 @@ export async function handleFileMove(file: TFile, oldPath: string, plugin: Folde
}
}
export async function handleFolderRename(file: TFolder, oldPath: string, plugin: FolderNotesPlugin) {
function getArgs(plugin: FolderNotesPlugin, file: TFile, oldPath: string): {
folderName: string;
oldFileName: string;
newFolder: TAbstractFile | null;
excludedFolder: ExcludedFolder | undefined;
oldFolder: TAbstractFile | null;
folderNote: TFile | null | undefined;
} {
const folderName = extractFolderName(plugin.settings.folderNoteName, file.basename)
|| file.basename;
const oldFileName = removeExtension(getFileNameFromPathString(oldPath));
const newFolder = getFolderNoteFolder(plugin, file, file.basename);
let excludedFolder = getExcludedFolder(plugin, newFolder?.path || '', true);
const oldFolder = getFolderNoteFolder(plugin, oldPath, oldFileName);
const folderNote = getFolderNote(plugin, oldPath, plugin.settings.storageLocation, file);
return {
folderName,
oldFileName,
newFolder,
excludedFolder,
oldFolder,
folderNote,
};
}
function renameExistingFolderNote(
file: TFile, oldPath: string, plugin: FolderNotesPlugin,
excludedFolder: ExcludedFolder | undefined,
oldFolder: TAbstractFile | null,
): void {
let excludedFolderExisted = true;
let disabledSync = false;
if (!excludedFolder) {
excludedFolderExisted = false;
excludedFolder = new ExcludedFolder(
oldFolder?.path || '',
plugin.settings.excludeFolders.length,
undefined,
plugin,
);
addExcludedFolder(plugin, excludedFolder);
} else if (!excludedFolder.disableSync) {
disabledSync = false;
excludedFolder.disableSync = true;
updateExcludedFolder(plugin, excludedFolder, excludedFolder);
}
plugin.app.fileManager.renameFile(file, oldPath).then(() => {
if (!excludedFolder) { return; }
if (!excludedFolderExisted) {
deleteExcludedFolder(plugin, excludedFolder);
} else if (!disabledSync) {
excludedFolder.disableSync = false;
updateExcludedFolder(plugin, excludedFolder, excludedFolder);
}
});
}
export async function handleFolderRename(
file: TFolder, oldPath: string, plugin: FolderNotesPlugin,
): Promise<void> {
const fileName = plugin.settings.folderNoteName.replace('{{folder_name}}', file.name);
const oldFileName = plugin.settings.folderNoteName.replace('{{folder_name}}', getFileNameFromPathString(oldPath));
const oldFileName = plugin.settings.folderNoteName
.replace('{{folder_name}}', getFileNameFromPathString(oldPath));
if (fileName === oldFileName) { return; }
@ -183,50 +250,62 @@ export async function handleFolderRename(file: TFolder, oldPath: string, plugin:
plugin.app.fileManager.renameFile(folderNote, newPath);
}
export async function fmptUpdateFileName(file: TFile, oldPath: string, plugin: FolderNotesPlugin) {
// eslint-disable-next-line complexity
export async function handleFileRename(
file: TFile,
oldPath: string,
plugin: FolderNotesPlugin,
): Promise<void> {
const oldFileName = removeExtension(getFileNameFromPathString(oldPath));
const newFileName = file.basename;
if (oldFileName === newFileName) { return; }
const oldFolder = getFolderNoteFolder(plugin, oldPath, oldFileName);
const folderName = extractFolderName(plugin.settings.folderNoteName, file.basename) || file.basename;
const oldFolderName = extractFolderName(plugin.settings.folderNoteName, oldFileName) || oldFileName;
const folderName = extractFolderName(plugin.settings.folderNoteName, file.basename)
|| file.basename;
const oldFolderName = extractFolderName(plugin.settings.folderNoteName, oldFileName)
|| oldFileName;
const newFolder = getFolderNoteFolder(plugin, file, file.basename);
const excludedFolder = getExcludedFolder(plugin, newFolder?.path || '', true);
const detachedExcludedFolder = getDetachedFolder(plugin, newFolder?.path || '');
const folderNote = getFolderNote(plugin, oldPath, plugin.settings.storageLocation, file);
if (!excludedFolder?.disableFolderNote && folderName === newFolder?.name && !detachedExcludedFolder) {
addCSSClassToFileExplorerEl(file.path, 'is-folder-note', false, plugin);
addCSSClassToFileExplorerEl(newFolder.path, 'has-folder-note', false, plugin);
return;
} else if (excludedFolder?.disableFolderNote || (folderName !== newFolder?.name)) {
removeCSSClassFromFileExplorerEL(file.path, 'is-folder-note', false, plugin);
removeCSSClassFromFileExplorerEL(newFolder?.path || '', 'has-folder-note', false, plugin);
}
if (excludedFolder?.disableSync || !plugin.settings.syncFolderName) { return; }
if (folderName === newFolder?.name) {
addCSSClassToFileExplorerEl(file.path, 'is-folder-note', false, plugin);
removeCSSClassFromFileExplorerEL(oldFolder?.path, 'has-folder-note', false, plugin);
addCSSClassToFileExplorerEl(newFolder.path, 'has-folder-note', false, plugin);
// Handle folder note creation
if (shouldCreateFolderNote(excludedFolder, folderName, newFolder, detachedExcludedFolder)) {
if (newFolder) {
handleFolderNoteCreation(file, newFolder, plugin);
}
return;
}
// file matched folder name before rename
// file hasnt moved just renamed
// Need to rename the folder
if (!oldFolder) return;
if (oldFolderName === oldFolder.name && newFolder?.path === oldFolder.path) {
return renameFolderOnFileRename(file, oldPath, oldFolder, plugin);
} else if (folderNote && oldFolderName === oldFolder.name) {
return renameFolderOnFileRename(file, oldPath, oldFolder, plugin);
// Handle folder note removal
if (shouldRemoveFolderNoteClasses(excludedFolder, folderName, newFolder)) {
handleFolderNoteRemoval(file, newFolder, plugin);
}
// Early return if sync is disabled
if (excludedFolder?.disableSync || !plugin.settings.syncFolderName) {
return;
}
// Handle same folder rename
if (folderName === newFolder?.name && newFolder) {
handleSameFolderRename(file, newFolder, oldFolder, plugin);
return;
}
// Handle folder rename on file rename
if (shouldRenameFolderOnFileRename(oldFolderName, oldFolder, newFolder, folderNote)) {
return renameFolderOnFileRename(file, oldPath, oldFolder!, plugin);
}
}
async function renameFolderOnFileRename(file: TFile, oldPath: string, oldFolder: TAbstractFile, plugin: FolderNotesPlugin) {
async function renameFolderOnFileRename(
file: TFile,
oldPath: string,
oldFolder: TAbstractFile,
plugin: FolderNotesPlugin,
): Promise<void> {
const newFolderName = extractFolderName(plugin.settings.folderNoteName, file.basename);
if (!newFolderName) {
removeCSSClassFromFileExplorerEL(oldFolder.path, 'has-folder-note', false, plugin);
@ -256,12 +335,17 @@ async function renameFolderOnFileRename(file: TFile, oldPath: string, oldFolder:
if (plugin.app.vault.getAbstractFileByPath(newFolderPath)) {
await plugin.app.fileManager.renameFile(file, oldPath);
return new Notice('A folder with the same name already exists');
new Notice('A folder with the same name already exists');
return;
}
plugin.app.fileManager.renameFile(oldFolder, newFolderPath);
}
function updateExcludedFolderPath(folder: TFolder, oldPath: string, plugin: FolderNotesPlugin) {
function updateExcludedFolderPath(
folder: TFolder,
oldPath: string,
plugin: FolderNotesPlugin,
): void {
const excludedFolders = plugin.settings.excludeFolders.filter(
(excludedFolder) => excludedFolder.path?.includes(oldPath),
);
@ -282,3 +366,68 @@ function updateExcludedFolderPath(folder: TFolder, oldPath: string, plugin: Fold
});
plugin.saveSettings();
}
function shouldCreateFolderNote(
excludedFolder: ExcludedFolder | undefined,
folderName: string,
newFolder: TAbstractFile | null,
detachedExcludedFolder: ExcludedFolder | undefined,
): boolean {
return !excludedFolder?.disableFolderNote
&& folderName === (newFolder as TFolder)?.name
&& !detachedExcludedFolder;
}
function shouldRemoveFolderNoteClasses(
excludedFolder: ExcludedFolder | undefined,
folderName: string,
newFolder: TAbstractFile | null,
): boolean {
return excludedFolder?.disableFolderNote || (folderName !== (newFolder as TFolder)?.name);
}
function handleFolderNoteCreation(
file: TFile,
newFolder: TAbstractFile,
plugin: FolderNotesPlugin,
): void {
addCSSClassToFileExplorerEl(file.path, 'is-folder-note', false, plugin);
addCSSClassToFileExplorerEl(newFolder.path, 'has-folder-note', false, plugin);
}
function handleFolderNoteRemoval(
file: TFile,
newFolder: TAbstractFile | null,
plugin: FolderNotesPlugin,
): void {
removeCSSClassFromFileExplorerEL(file.path, 'is-folder-note', false, plugin);
removeCSSClassFromFileExplorerEL(newFolder?.path || '', 'has-folder-note', false, plugin);
}
function handleSameFolderRename(
file: TFile,
newFolder: TAbstractFile,
oldFolder: TAbstractFile | null,
plugin: FolderNotesPlugin,
): void {
addCSSClassToFileExplorerEl(file.path, 'is-folder-note', false, plugin);
removeCSSClassFromFileExplorerEL(oldFolder?.path, 'has-folder-note', false, plugin);
addCSSClassToFileExplorerEl(newFolder.path, 'has-folder-note', false, plugin);
}
function shouldRenameFolderOnFileRename(
oldFolderName: string,
oldFolder: TAbstractFile | null,
newFolder: TAbstractFile | null,
folderNote: TFile | null | undefined,
): boolean {
if (!oldFolder) return false;
const oldFolderAsFolder = oldFolder as TFolder;
const newFolderAsFolder = newFolder as TFolder;
return (oldFolderName === oldFolderAsFolder.name
&& newFolderAsFolder?.path === oldFolderAsFolder.path)
|| (folderNote !== null && oldFolderName === oldFolderAsFolder.name);
}

View file

@ -203,7 +203,7 @@ export default class FolderNotesPlugin extends Plugin {
// @ts-ignore
const editMode = view.editMode ?? view.sourceMode ?? this.app.workspace.activeEditor?.editMode;
// eslint-disable-next-line
const plugin = this;
if (!editMode) { return; }
@ -308,7 +308,7 @@ export default class FolderNotesPlugin extends Plugin {
openFolderNote(this, folderNote, evt);
}
handleOverviewBlock(source: string, el: HTMLElement, ctx: MarkdownPostProcessorContext) {
handleOverviewBlock(source: string, el: HTMLElement, ctx: MarkdownPostProcessorContext): void {
const observer = new MutationObserver(() => {
const editButton = el.parentElement?.childNodes.item(1);
if (editButton) {
@ -316,7 +316,10 @@ export default class FolderNotesPlugin extends Plugin {
e.stopImmediatePropagation();
e.preventDefault();
e.stopPropagation();
new FolderOverviewSettings(this.app, this, parseYaml(source), ctx, el, this.settings.defaultOverview).open();
new FolderOverviewSettings(
this.app, this, parseYaml(source),
ctx, el, this.settings.defaultOverview,
).open();
}, { capture: true });
}
});
@ -329,11 +332,11 @@ export default class FolderNotesPlugin extends Plugin {
try {
if (this.app.workspace.layoutReady) {
const folderOverview = new FolderOverview(this, ctx, source, el, this.settings.defaultOverview);
folderOverview.create(this, parseYaml(source), el, ctx);
folderOverview.create(this, el, ctx);
} else {
this.app.workspace.onLayoutReady(() => {
const folderOverview = new FolderOverview(this, ctx, source, el, this.settings.defaultOverview);
folderOverview.create(this, parseYaml(source), el, ctx);
folderOverview.create(this, el, ctx);
});
}
} catch (e) {

@ -1 +1 @@
Subproject commit 07825d6f187a9ddf2c9b29066eb0c06aa8b8fee2
Subproject commit 5323f27922aff0165dca78770f24a7d8a99de7c0

View file

@ -1,7 +1,4 @@
// Credits go to Liam's Periodic Notes Plugin: https://github.com/liamcain/obsidian-periodic-notes and https://github.com/SilentVoid13/Templater
import type { TAbstractFile } from 'obsidian';
import { TFile } from 'obsidian';
import { type TAbstractFile, TFile } from 'obsidian';
import { TextInputSuggest } from './Suggest';
import type FolderNotesPlugin from '../main';
export enum FileSuggestMode {

View file

@ -1,5 +1,4 @@
import type { App } from 'obsidian';
import { TFile, WorkspaceLeaf } from 'obsidian';
import { TFile, WorkspaceLeaf, type App } from 'obsidian';
import type FolderNotesPlugin from './main';
export async function applyTemplate(