#74 - Integration with Bookmarks core plugin and support for indirect drag & drop arrangement

- singificants refactoring, compilable, basic functions work
This commit is contained in:
SebastianMC 2023-05-05 20:49:08 +02:00
parent 62a91db3de
commit d32e71f064
5 changed files with 184 additions and 123 deletions

View File

@ -25,8 +25,7 @@ import {
} from "./custom-sort-types"; } from "./custom-sort-types";
import {isDefined} from "../utils/utils"; import {isDefined} from "../utils/utils";
import { import {
Bookmarks_PluginInstance, BookmarksPluginInterface
determineBookmarkOrder
} from "../utils/BookmarksCorePluginSignature"; } from "../utils/BookmarksCorePluginSignature";
export interface ProcessingContext { export interface ProcessingContext {
@ -34,10 +33,7 @@ export interface ProcessingContext {
plugin?: Plugin // to hand over the access to App instance to the sorting engine plugin?: Plugin // to hand over the access to App instance to the sorting engine
_mCache?: MetadataCache _mCache?: MetadataCache
starredPluginInstance?: Starred_PluginInstance starredPluginInstance?: Starred_PluginInstance
bookmarksPlugin: { bookmarksPluginInstance?: BookmarksPluginInterface,
instance?: Bookmarks_PluginInstance,
groupNameForSorting?: string
}
iconFolderPluginInstance?: ObsidianIconFolder_PluginInstance iconFolderPluginInstance?: ObsidianIconFolder_PluginInstance
} }
@ -364,8 +360,8 @@ export const determineSortingGroup = function (entry: TFile | TFolder, spec: Cus
} }
break break
case CustomSortGroupType.BookmarkedOnly: case CustomSortGroupType.BookmarkedOnly:
if (ctx?.bookmarksPlugin?.instance) { if (ctx?.bookmarksPluginInstance) {
const bookmarkOrder: number | undefined = determineBookmarkOrder(entry.path, ctx.bookmarksPlugin?.instance, ctx.bookmarksPlugin?.groupNameForSorting) const bookmarkOrder: number | undefined = ctx?.bookmarksPluginInstance.determineBookmarkOrder(entry.path)
if (bookmarkOrder) { // safe ==> orders intentionally start from 1 if (bookmarkOrder) { // safe ==> orders intentionally start from 1
determined = true determined = true
bookmarkedIdx = bookmarkOrder bookmarkedIdx = bookmarkOrder
@ -537,7 +533,7 @@ export const determineFolderDatesIfNeeded = (folderItems: Array<FolderItemForSor
// Order by bookmarks order can be applied independently of grouping by bookmarked status // Order by bookmarks order can be applied independently of grouping by bookmarked status
// This function determines the bookmarked order if the sorting criteria (of group or entire folder) requires it // This function determines the bookmarked order if the sorting criteria (of group or entire folder) requires it
export const determineBookmarksOrderIfNeeded = (folderItems: Array<FolderItemForSorting>, sortingSpec: CustomSortSpec, plugin: Bookmarks_PluginInstance, bookmarksGroup?: string) => { export const determineBookmarksOrderIfNeeded = (folderItems: Array<FolderItemForSorting>, sortingSpec: CustomSortSpec, plugin: BookmarksPluginInterface) => {
if (!plugin) return if (!plugin) return
folderItems.forEach((item) => { folderItems.forEach((item) => {
@ -551,7 +547,7 @@ export const determineBookmarksOrderIfNeeded = (folderItems: Array<FolderItemFor
} }
} }
if (folderDefaultSortRequiresBookmarksOrder || groupSortRequiresBookmarksOrder) { if (folderDefaultSortRequiresBookmarksOrder || groupSortRequiresBookmarksOrder) {
item.bookmarkedIdx = determineBookmarkOrder(item.path, plugin, bookmarksGroup) item.bookmarkedIdx = plugin.determineBookmarkOrder(item.path)
} }
}) })
} }
@ -573,8 +569,8 @@ export const folderSort = function (sortingSpec: CustomSortSpec, ctx: Processing
// Finally, for advanced sorting by modified date, for some folders the modified date has to be determined // Finally, for advanced sorting by modified date, for some folders the modified date has to be determined
determineFolderDatesIfNeeded(folderItems, sortingSpec) determineFolderDatesIfNeeded(folderItems, sortingSpec)
if (ctx.bookmarksPlugin?.instance) { if (ctx.bookmarksPluginInstance) {
determineBookmarksOrderIfNeeded(folderItems, sortingSpec, ctx.bookmarksPlugin.instance, ctx.bookmarksPlugin.groupNameForSorting) determineBookmarksOrderIfNeeded(folderItems, sortingSpec, ctx.bookmarksPluginInstance)
} }
const comparator: SorterFn = getComparator(sortingSpec, fileExplorer.sortOrder) const comparator: SorterFn = getComparator(sortingSpec, fileExplorer.sortOrder)
@ -605,8 +601,8 @@ export const sortFolderItemsForBookmarking = function (items: Array<TAbstractFil
// Finally, for advanced sorting by modified date, for some folders the modified date has to be determined // Finally, for advanced sorting by modified date, for some folders the modified date has to be determined
determineFolderDatesIfNeeded(folderItems, sortingSpec) determineFolderDatesIfNeeded(folderItems, sortingSpec)
if (ctx.bookmarksPlugin?.instance) { if (ctx.bookmarksPluginInstance) {
determineBookmarksOrderIfNeeded(folderItems, sortingSpec, ctx.bookmarksPlugin.instance, ctx.bookmarksPlugin.groupNameForSorting) determineBookmarksOrderIfNeeded(folderItems, sortingSpec, ctx.bookmarksPluginInstance)
} }
const comparator: SorterFn = getComparator(sortingSpec, uiSortOrder) const comparator: SorterFn = getComparator(sortingSpec, uiSortOrder)

View File

@ -36,11 +36,7 @@ import {
} from "./custom-sort/icons"; } from "./custom-sort/icons";
import {getStarredPlugin} from "./utils/StarredPluginSignature"; import {getStarredPlugin} from "./utils/StarredPluginSignature";
import { import {
getBookmarksPlugin, getBookmarksPlugin
bookmarkFolderItem,
saveDataAndUpdateBookmarkViews,
bookmarkSiblings,
updateSortingBookmarksAfterItemRename
} from "./utils/BookmarksCorePluginSignature"; } from "./utils/BookmarksCorePluginSignature";
import {getIconFolderPlugin} from "./utils/ObsidianIconFolderPluginSignature"; import {getIconFolderPlugin} from "./utils/ObsidianIconFolderPluginSignature";
import {lastPathComponent} from "./utils/utils"; import {lastPathComponent} from "./utils/utils";
@ -98,7 +94,7 @@ export default class CustomSortPlugin extends Plugin {
} }
readAndParseSortingSpec() { readAndParseSortingSpec() {
const mCache: MetadataCache = this.app.metadataCache const mCache: MetadataCache = app.metadataCache
let failed: boolean = false let failed: boolean = false
let anySortingSpecFound: boolean = false let anySortingSpecFound: boolean = false
let errorMessage: string | null = null let errorMessage: string | null = null
@ -118,7 +114,7 @@ export default class CustomSortPlugin extends Plugin {
console.log(this.sortSpecCache) console.log(this.sortSpecCache)
} }
Vault.recurseChildren(this.app.vault.getRoot(), (file: TAbstractFile) => { Vault.recurseChildren(app.vault.getRoot(), (file: TAbstractFile) => {
if (failed) return if (failed) return
if (file instanceof TFile) { if (file instanceof TFile) {
const aFile: TFile = file as TFile const aFile: TFile = file as TFile
@ -283,7 +279,7 @@ export default class CustomSortPlugin extends Plugin {
this.ribbonIconStateInaccurate = true this.ribbonIconStateInaccurate = true
} }
this.addSettingTab(new CustomSortSettingTab(this.app, this)); this.addSettingTab(new CustomSortSettingTab(app, this));
this.registerEventHandlers() this.registerEventHandlers()
@ -296,7 +292,7 @@ export default class CustomSortPlugin extends Plugin {
const plugin: CustomSortPlugin = this const plugin: CustomSortPlugin = this
this.registerEvent( this.registerEvent(
// Keep in mind: this event is triggered once after app starts and then after each modification of _any_ metadata // Keep in mind: this event is triggered once after app starts and then after each modification of _any_ metadata
this.app.metadataCache.on("resolved", () => { app.metadataCache.on("resolved", () => {
if (!this.settings.suspended) { if (!this.settings.suspended) {
if (!this.initialAutoOrManualSortingTriggered) { if (!this.initialAutoOrManualSortingTriggered) {
this.readAndParseSortingSpec() this.readAndParseSortingSpec()
@ -323,17 +319,17 @@ export default class CustomSortPlugin extends Plugin {
); );
this.registerEvent( this.registerEvent(
this.app.workspace.on("file-menu", (menu: Menu, file: TAbstractFile, source: string, leaf?: WorkspaceLeaf) => { app.workspace.on("file-menu", (menu: Menu, file: TAbstractFile, source: string, leaf?: WorkspaceLeaf) => {
const bookmarkThisMenuItem = (item: MenuItem) => { const bookmarkThisMenuItem = (item: MenuItem) => {
// TODO: if already bookmarked in the 'custom sort' group (or its descendants) don't show // TODO: if already bookmarked in the 'custom sort' group (or its descendants) don't show
item.setTitle('Custom sort: bookmark for sorting.'); item.setTitle('Custom sort: bookmark for sorting.');
item.setIcon('hashtag'); item.setIcon('hashtag');
item.onClick(() => { item.onClick(() => {
const bookmarksPlugin = getBookmarksPlugin(plugin.app) const bookmarksPlugin = getBookmarksPlugin(plugin.settings.bookmarksGroupToConsumeAsOrderingReference)
console.log(`custom-sort: bookmark this clicked ${source} and the leaf is`) console.log(`custom-sort: bookmark this clicked ${source} and the leaf is`)
if (bookmarksPlugin) { if (bookmarksPlugin) {
bookmarkFolderItem(file, bookmarksPlugin, plugin.settings.bookmarksGroupToConsumeAsOrderingReference) bookmarksPlugin.bookmarkFolderItem(file)
saveDataAndUpdateBookmarkViews(bookmarksPlugin, plugin.app) bookmarksPlugin.saveDataAndUpdateBookmarkViews(true)
} }
}); });
}; };
@ -342,11 +338,11 @@ export default class CustomSortPlugin extends Plugin {
item.setIcon('hashtag'); item.setIcon('hashtag');
item.onClick(() => { item.onClick(() => {
console.log(`custom-sort: bookmark all siblings clicked ${source}`) console.log(`custom-sort: bookmark all siblings clicked ${source}`)
const bookmarksPlugin = getBookmarksPlugin(plugin.app) const bookmarksPlugin = getBookmarksPlugin(plugin.settings.bookmarksGroupToConsumeAsOrderingReference)
if (bookmarksPlugin) { if (bookmarksPlugin) {
const orderedChildren: Array<TAbstractFile> = plugin.orderedFolderItemsForBookmarking(file.parent) const orderedChildren: Array<TAbstractFile> = plugin.orderedFolderItemsForBookmarking(file.parent)
bookmarkSiblings(orderedChildren, bookmarksPlugin, plugin.settings.bookmarksGroupToConsumeAsOrderingReference) bookmarksPlugin.bookmarkSiblings(orderedChildren)
saveDataAndUpdateBookmarkViews(bookmarksPlugin, plugin.app) bookmarksPlugin.saveDataAndUpdateBookmarkViews(true)
} }
}); });
}; };
@ -357,13 +353,21 @@ export default class CustomSortPlugin extends Plugin {
) )
this.registerEvent( this.registerEvent(
this.app.vault.on("rename", (file: TAbstractFile, oldPath: string) => { app.vault.on("rename", (file: TAbstractFile, oldPath: string) => {
const bookmarksPlugin = getBookmarksPlugin(plugin.app) const bookmarksPlugin = getBookmarksPlugin(plugin.settings.bookmarksGroupToConsumeAsOrderingReference)
if (bookmarksPlugin) { if (bookmarksPlugin) {
updateSortingBookmarksAfterItemRename(bookmarksPlugin, file, oldPath, plugin.settings.bookmarksGroupToConsumeAsOrderingReference) bookmarksPlugin.updateSortingBookmarksAfterItemRenamed(file, oldPath)
bookmarksPlugin.saveDataAndUpdateBookmarkViews(true)
} }
}) })
) )
app.vault.on("delete", (file: TAbstractFile) => {
const bookmarksPlugin = getBookmarksPlugin(plugin.settings.bookmarksGroupToConsumeAsOrderingReference)
if (bookmarksPlugin) {
bookmarksPlugin.updateSortingBookmarksAfterItemDeleted(file)
bookmarksPlugin.saveDataAndUpdateBookmarkViews(true)
}
})
} }
registerCommands() { registerCommands() {
@ -385,7 +389,7 @@ export default class CustomSortPlugin extends Plugin {
} }
initialize() { initialize() {
this.app.workspace.onLayoutReady(() => { app.workspace.onLayoutReady(() => {
this.fileExplorerFolderPatched = this.patchFileExplorerFolder(); this.fileExplorerFolderPatched = this.patchFileExplorerFolder();
}) })
} }
@ -404,13 +408,10 @@ export default class CustomSortPlugin extends Plugin {
createProcessingContextForSorting(): ProcessingContext { createProcessingContextForSorting(): ProcessingContext {
const ctx: ProcessingContext = { const ctx: ProcessingContext = {
_mCache: this.app.metadataCache, _mCache: app.metadataCache,
starredPluginInstance: getStarredPlugin(this.app), starredPluginInstance: getStarredPlugin(),
bookmarksPlugin: { bookmarksPluginInstance: getBookmarksPlugin(this.settings.bookmarksGroupToConsumeAsOrderingReference),
instance: this.settings.automaticBookmarksIntegration ? getBookmarksPlugin(this.app) : undefined, iconFolderPluginInstance: getIconFolderPlugin(),
groupNameForSorting: this.settings.bookmarksGroupToConsumeAsOrderingReference
},
iconFolderPluginInstance: getIconFolderPlugin(this.app),
plugin: this plugin: this
} }
return ctx return ctx
@ -469,7 +470,7 @@ export default class CustomSortPlugin extends Plugin {
// Credits go to https://github.com/nothingislost/obsidian-bartender // Credits go to https://github.com/nothingislost/obsidian-bartender
getFileExplorer(): FileExplorerView | undefined { getFileExplorer(): FileExplorerView | undefined {
let fileExplorer: FileExplorerView | undefined = this.app.workspace.getLeavesOfType("file-explorer")?.first() let fileExplorer: FileExplorerView | undefined = app.workspace.getLeavesOfType("file-explorer")?.first()
?.view as unknown as FileExplorerView; ?.view as unknown as FileExplorerView;
return fileExplorer; return fileExplorer;
} }
@ -639,10 +640,4 @@ class CustomSortSettingTab extends PluginSettingTab {
// TODO: remove console.log (many places added) // TODO: remove console.log (many places added)
// Invoke 'onItemsChanged' consciously for the bookmarks plugin
// TODO: on delete - delete from bkmrks
// TODO: ctx menu 'show in bookmarks' instead of 'bookmrk this' // TODO: ctx menu 'show in bookmarks' instead of 'bookmrk this'
// TODO: new items bkmrkd in the top instead of in the bottom. Or maybe at actual location (???)

View File

@ -1,4 +1,4 @@
import {App, InstalledPlugin, Plugin, PluginInstance, TAbstractFile, TFolder} from "obsidian"; import {InstalledPlugin, PluginInstance, TAbstractFile, TFile, TFolder} from "obsidian";
import {extractParentFolderPath, lastPathComponent} from "./utils"; import {extractParentFolderPath, lastPathComponent} from "./utils";
const BookmarksPlugin_getBookmarks_methodName = 'getBookmarks' const BookmarksPlugin_getBookmarks_methodName = 'getBookmarks'
@ -46,25 +46,131 @@ export interface OrderedBookmarkedItem {
group: boolean group: boolean
path: BookmarkedItemPath path: BookmarkedItemPath
order: number order: number
pathOfBookmarkGroupsMatches: boolean
} }
interface OrderedBookmarks { interface OrderedBookmarks {
[key: BookmarkedItemPath]: OrderedBookmarkedItem [key: BookmarkedItemPath]: OrderedBookmarkedItem
} }
export interface Bookmarks_PluginInstance extends PluginInstance { interface Bookmarks_PluginInstance extends PluginInstance {
[BookmarksPlugin_getBookmarks_methodName]: () => Array<BookmarkedItem> | undefined [BookmarksPlugin_getBookmarks_methodName]: () => Array<BookmarkedItem> | undefined
[BookmarksPlugin_items_collectionName]: Array<BookmarkedItem> [BookmarksPlugin_items_collectionName]: Array<BookmarkedItem>
saveData(): void saveData(): void
onItemsChanged(saveData: boolean): void onItemsChanged(saveData: boolean): void
} }
export interface BookmarksPluginInterface {
determineBookmarkOrder(path: string): number|undefined
bookmarkFolderItem(item: TAbstractFile): void
saveDataAndUpdateBookmarkViews(updateBookmarkViews: boolean): void
bookmarkSiblings(siblings: Array<TAbstractFile>, inTheTop?: boolean): void
updateSortingBookmarksAfterItemRenamed(renamedItem: TAbstractFile, oldPath: string): void
updateSortingBookmarksAfterItemDeleted(deletedItem: TAbstractFile): void
}
class BookmarksPluginWrapper implements BookmarksPluginInterface {
plugin: Bookmarks_PluginInstance|undefined
groupNameForSorting: string|undefined
constructor () {
}
// Result:
// undefined ==> item not found in bookmarks
// > 0 ==> item found in bookmarks at returned position
// Intentionally not returning 0 to allow simple syntax of processing the result
determineBookmarkOrder = (path: string): number | undefined => {
if (!bookmarksCache) {
bookmarksCache = getOrderedBookmarks(this.plugin!, this.groupNameForSorting)
bookmarksCacheTimestamp = Date.now()
}
const bookmarkedItemPosition: number | undefined = bookmarksCache?.[path]?.order
return (bookmarkedItemPosition !== undefined && bookmarkedItemPosition >= 0) ? (bookmarkedItemPosition + 1) : undefined
}
bookmarkFolderItem = (item: TAbstractFile) => {
this.bookmarkSiblings([item], true)
}
saveDataAndUpdateBookmarkViews = (updateBookmarkViews: boolean = true) => {
this.plugin!.onItemsChanged(true)
if (updateBookmarkViews) {
const bookmarksLeafs = app.workspace.getLeavesOfType('bookmarks')
bookmarksLeafs?.forEach((leaf) => {
(leaf.view as any)?.update?.()
})
}
}
bookmarkSiblings = (siblings: Array<TAbstractFile>, inTheTop?: boolean) => {
console.log('In this.bookmarksSiblings()')
if (siblings.length === 0) return // for sanity
const bookmarksContainer: BookmarkedParentFolder|undefined = findGroupForItemPathInBookmarks(
siblings[0].path,
CreateIfMissing,
this.plugin!,
this.groupNameForSorting
)
if (bookmarksContainer) { // for sanity, the group should be always created if missing
siblings.forEach((aSibling) => {
const siblingName = lastPathComponent(aSibling.path)
if (!bookmarksContainer.items.find((it) =>
((it.type === 'folder' || it.type === 'file') && it.path === aSibling.path) ||
(it.type === 'group' && it.title === siblingName))) {
const newEntry: BookmarkedItem = (aSibling instanceof TFolder) ? createBookmarkGroupEntry(siblingName) : createBookmarkFileEntry(aSibling.path);
if (inTheTop) {
bookmarksContainer.items.unshift(newEntry)
} else {
bookmarksContainer.items.push(newEntry)
}
}
});
}
}
updateSortingBookmarksAfterItemRenamed = (renamedItem: TAbstractFile, oldPath: string): void => {
updateSortingBookmarksAfterItemRenamed(this.plugin!, renamedItem, oldPath, this.groupNameForSorting)
}
updateSortingBookmarksAfterItemDeleted = (deletedItem: TAbstractFile): void => {
updateSortingBookmarksAfterItemDeleted(this.plugin!, deletedItem, this.groupNameForSorting)
}
}
export const BookmarksCorePluginId: string = 'bookmarks'
export const getBookmarksPlugin = (bookmarksGroupName?: string): BookmarksPluginInterface | undefined => {
invalidateExpiredBookmarksCache()
const installedBookmarksPlugin: InstalledPlugin | undefined = app?.internalPlugins?.getPluginById(BookmarksCorePluginId)
console.log(installedBookmarksPlugin)
const bookmarks = (installedBookmarksPlugin?.instance as any) ?.['getBookmarks']()
console.log(bookmarks)
if (installedBookmarksPlugin && installedBookmarksPlugin.enabled && installedBookmarksPlugin.instance) {
const bookmarksPluginInstance: Bookmarks_PluginInstance = installedBookmarksPlugin.instance as Bookmarks_PluginInstance
// defensive programming, in case Obsidian changes its internal APIs
if (typeof bookmarksPluginInstance?.[BookmarksPlugin_getBookmarks_methodName] === 'function') {
bookmarksPlugin.plugin = bookmarksPluginInstance
bookmarksPlugin.groupNameForSorting = bookmarksGroupName
return bookmarksPlugin
}
}
}
// cache can outlive the wrapper instances
let bookmarksCache: OrderedBookmarks | undefined = undefined let bookmarksCache: OrderedBookmarks | undefined = undefined
let bookmarksCacheTimestamp: number | undefined = undefined let bookmarksCacheTimestamp: number | undefined = undefined
const bookmarksPlugin: BookmarksPluginWrapper = new BookmarksPluginWrapper()
const CacheExpirationMilis = 1000 // One second seems to be reasonable const CacheExpirationMilis = 1000 // One second seems to be reasonable
export const invalidateExpiredBookmarksCache = (force?: boolean): void => { const invalidateExpiredBookmarksCache = (force?: boolean): void => {
if (bookmarksCache) { if (bookmarksCache) {
let flush: boolean = true let flush: boolean = true
if (!force && !!bookmarksCacheTimestamp) { if (!force && !!bookmarksCacheTimestamp) {
@ -79,23 +185,6 @@ export const invalidateExpiredBookmarksCache = (force?: boolean): void => {
} }
} }
export const BookmarksCorePluginId: string = 'bookmarks'
export const getBookmarksPlugin = (app?: App): Bookmarks_PluginInstance | undefined => {
invalidateExpiredBookmarksCache()
const bookmarksPlugin: InstalledPlugin | undefined = app?.internalPlugins?.getPluginById(BookmarksCorePluginId)
console.log(bookmarksPlugin)
const bookmarks = (bookmarksPlugin?.instance as any) ?.['getBookmarks']()
console.log(bookmarks)
if (bookmarksPlugin && bookmarksPlugin.enabled && bookmarksPlugin.instance) {
const bookmarksPluginInstance: Bookmarks_PluginInstance = bookmarksPlugin.instance as Bookmarks_PluginInstance
// defensive programming, in case Obsidian changes its internal APIs
if (typeof bookmarksPluginInstance?.[BookmarksPlugin_getBookmarks_methodName] === 'function') {
return bookmarksPluginInstance
}
}
}
type TraverseCallback = (item: BookmarkedItem, parentsGroupsPath: string) => boolean | void type TraverseCallback = (item: BookmarkedItem, parentsGroupsPath: string) => boolean | void
const traverseBookmarksCollection = (items: Array<BookmarkedItem>, callback: TraverseCallback) => { const traverseBookmarksCollection = (items: Array<BookmarkedItem>, callback: TraverseCallback) => {
@ -132,14 +221,18 @@ const getOrderedBookmarks = (plugin: Bookmarks_PluginInstance, bookmarksGroupNam
const pathOfGroup: string = `${parentGroupsPath}${parentGroupsPath?'/':''}${item.title}` const pathOfGroup: string = `${parentGroupsPath}${parentGroupsPath?'/':''}${item.title}`
const path = isGroup ? pathOfGroup : (item as BookmarkWithPath).path const path = isGroup ? pathOfGroup : (item as BookmarkWithPath).path
// Consume only the first occurrence of a path in bookmarks, even if many duplicates can exist // Consume only the first occurrence of a path in bookmarks, even if many duplicates can exist
// TODO: consume the occurrence at correct folders (groups) location resembling the original structure with highest prio
// and only if not found, consider any (first) occurrence
const alreadyConsumed = orderedBookmarks[path] const alreadyConsumed = orderedBookmarks[path]
if (!alreadyConsumed) { const pathOfBookmarkGroupsMatches: boolean = true // TODO: !!! with fresh head determine the condition to check here
if (!alreadyConsumed || (pathOfBookmarkGroupsMatches && !alreadyConsumed.pathOfBookmarkGroupsMatches)) {
orderedBookmarks[path] = { orderedBookmarks[path] = {
path: path, path: path,
order: order++, order: order++,
file: isFile, file: isFile,
folder: isFile, folder: isFile,
group: isGroup group: isGroup,
pathOfBookmarkGroupsMatches: pathOfBookmarkGroupsMatches
} }
} }
} }
@ -150,23 +243,6 @@ const getOrderedBookmarks = (plugin: Bookmarks_PluginInstance, bookmarksGroupNam
} }
} }
// Result:
// undefined ==> item not found in bookmarks
// > 0 ==> item found in bookmarks at returned position
// Intentionally not returning 0 to allow simple syntax of processing the result
export const determineBookmarkOrder = (path: string, plugin: Bookmarks_PluginInstance, bookmarksGroup?: string): number | undefined => {
if (!bookmarksCache) {
bookmarksCache = getOrderedBookmarks(plugin, bookmarksGroup)
bookmarksCacheTimestamp = Date.now()
}
const bookmarkedItemPosition: number | undefined = bookmarksCache?.[path]?.order
return (bookmarkedItemPosition !== undefined && bookmarkedItemPosition >= 0) ? (bookmarkedItemPosition + 1) : undefined
}
// EXPERIMENTAL - operates on internal structures of core Bookmarks plugin
const createBookmarkFileEntry = (path: string): BookmarkedFile => { const createBookmarkFileEntry = (path: string): BookmarkedFile => {
// Artificial subpath added intentionally to prevent Bookmarks context menu from finding this item in bookmarks // Artificial subpath added intentionally to prevent Bookmarks context menu from finding this item in bookmarks
// and - in turn - allow bookmarking it by the user for regular (non sorting) purposes // and - in turn - allow bookmarking it by the user for regular (non sorting) purposes
@ -177,10 +253,6 @@ const createBookmarkGroupEntry = (title: string): BookmarkedGroup => {
return { type: "group", ctime: Date.now(), items: [], title: title } return { type: "group", ctime: Date.now(), items: [], title: title }
} }
export const bookmarkFolderItem = (item: TAbstractFile, plugin: Bookmarks_PluginInstance, bookmarksGroup?: string) => {
bookmarkSiblings([item], plugin, bookmarksGroup)
}
interface BookmarkedParentFolder { interface BookmarkedParentFolder {
group?: BookmarkedGroup // undefined when the item is at root level of bookmarks group?: BookmarkedGroup // undefined when the item is at root level of bookmarks
items: Array<BookmarkedItem> // reference to group.items or to root collection of bookmarks items: Array<BookmarkedItem> // reference to group.items or to root collection of bookmarks
@ -229,33 +301,9 @@ const findGroupForItemPathInBookmarks = (itemPath: string, createIfMissing: bool
const CreateIfMissing = true const CreateIfMissing = true
const DontCreateIfMissing = false const DontCreateIfMissing = false
export const bookmarkSiblings = (siblings: Array<TAbstractFile>, plugin: Bookmarks_PluginInstance, bookmarksGroup?: string) => {
if (siblings.length === 0) return // for sanity
const bookmarksContainer: BookmarkedParentFolder|undefined = findGroupForItemPathInBookmarks(siblings[0].path, CreateIfMissing, plugin, bookmarksGroup)
if (bookmarksContainer) { // for sanity, the group should be always created if missing const updateSortingBookmarksAfterItemRenamed = (plugin: Bookmarks_PluginInstance, renamedItem: TAbstractFile, oldPath: string, bookmarksGroup?: string) => {
siblings.forEach((aSibling) => {
const siblingName = lastPathComponent(aSibling.path)
if (!bookmarksContainer.items.find((it) =>
((it.type === 'folder' || it.type === 'file') && it.path === aSibling.path) ||
(it.type === 'group' && it.title === siblingName))) {
const newEntry: BookmarkedItem = (aSibling instanceof TFolder) ? createBookmarkGroupEntry(siblingName) : createBookmarkFileEntry(aSibling.path)
bookmarksContainer.items.push(newEntry)
}
});
}
}
export const saveDataAndUpdateBookmarkViews = (plugin: Bookmarks_PluginInstance, app: App) => {
plugin.onItemsChanged(true)
const bookmarksLeafs = app.workspace.getLeavesOfType('bookmarks')
bookmarksLeafs?.forEach((leaf) => {
(leaf.view as any)?.update?.()
})
}
export const updateSortingBookmarksAfterItemRename = (plugin: Bookmarks_PluginInstance, renamedItem: TAbstractFile, oldPath: string, bookmarksGroup?: string) => {
if (renamedItem.path === oldPath) return; // sanity if (renamedItem.path === oldPath) return; // sanity
@ -302,6 +350,29 @@ export const updateSortingBookmarksAfterItemRename = (plugin: Bookmarks_PluginIn
} }
console.log(`Folder renamel from ${oldPath} to ${renamedItem.path}`) console.log(`Folder renamel from ${oldPath} to ${renamedItem.path}`)
}
plugin.onItemsChanged(true)
const updateSortingBookmarksAfterItemDeleted = (plugin: Bookmarks_PluginInstance, deletedItem: TAbstractFile, bookmarksGroup?: string) => {
// Obsidian automatically deletes all bookmark instances of a file, nothing to be done here
if (deletedItem instanceof TFile) return;
let items = plugin[BookmarksPlugin_items_collectionName]
const aFolder: boolean = deletedItem instanceof TFolder
const aFile: boolean = !aFolder
const originalContainer: BookmarkedParentFolder|undefined = findGroupForItemPathInBookmarks(deletedItem.path, DontCreateIfMissing, plugin, bookmarksGroup)
if (!originalContainer) return;
const item: BookmarkedItem|undefined = originalContainer.items.find((it) => {
if (aFolder && it.type === 'group' && it.title === deletedItem.name) return true;
if (aFile && it.type === 'file' && it.path === deletedItem.path) return true;
})
if (!item) return;
originalContainer.items.remove(item)
console.log(`Folder deletel ${deletedItem.path}`)
} }

View File

@ -1,5 +1,4 @@
import {App, CommunityPlugin, TAbstractFile, TFile, TFolder} from "obsidian"; import {CommunityPlugin, TAbstractFile} from "obsidian";
import {Starred_PluginInstance} from "./StarredPluginSignature";
// For https://github.com/FlorianWoelki/obsidian-icon-folder // For https://github.com/FlorianWoelki/obsidian-icon-folder
@ -19,7 +18,7 @@ export interface ObsidianIconFolder_PluginInstance extends CommunityPlugin {
// https://github.com/FlorianWoelki/obsidian-icon-folder/blob/fd9c7df1486744450cec3d7ee9cee2b34d008e56/manifest.json#L2 // https://github.com/FlorianWoelki/obsidian-icon-folder/blob/fd9c7df1486744450cec3d7ee9cee2b34d008e56/manifest.json#L2
export const ObsidianIconFolderPluginId: string = 'obsidian-icon-folder' export const ObsidianIconFolderPluginId: string = 'obsidian-icon-folder'
export const getIconFolderPlugin = (app?: App): ObsidianIconFolder_PluginInstance | undefined => { export const getIconFolderPlugin = (): ObsidianIconFolder_PluginInstance | undefined => {
const iconFolderPlugin: CommunityPlugin | undefined = app?.plugins?.plugins?.[ObsidianIconFolderPluginId] const iconFolderPlugin: CommunityPlugin | undefined = app?.plugins?.plugins?.[ObsidianIconFolderPluginId]
if (iconFolderPlugin && iconFolderPlugin._loaded && app?.plugins?.enabledPlugins?.has(ObsidianIconFolderPluginId)) { if (iconFolderPlugin && iconFolderPlugin._loaded && app?.plugins?.enabledPlugins?.has(ObsidianIconFolderPluginId)) {
const iconFolderPluginInstance: ObsidianIconFolder_PluginInstance = iconFolderPlugin as ObsidianIconFolder_PluginInstance const iconFolderPluginInstance: ObsidianIconFolder_PluginInstance = iconFolderPlugin as ObsidianIconFolder_PluginInstance

View File

@ -1,4 +1,4 @@
import {App, InstalledPlugin, PluginInstance, TAbstractFile, TFile, TFolder} from "obsidian"; import {InstalledPlugin, PluginInstance, TAbstractFile, TFile, TFolder} from "obsidian";
export const StarredPlugin_findStarredFile_methodName = 'findStarredFile' export const StarredPlugin_findStarredFile_methodName = 'findStarredFile'
@ -12,7 +12,7 @@ export interface Starred_PluginInstance extends PluginInstance {
export const StarredCorePluginId: string = 'starred' export const StarredCorePluginId: string = 'starred'
export const getStarredPlugin = (app?: App): Starred_PluginInstance | undefined => { export const getStarredPlugin = (): Starred_PluginInstance | undefined => {
const starredPlugin: InstalledPlugin | undefined = app?.internalPlugins?.getPluginById(StarredCorePluginId) const starredPlugin: InstalledPlugin | undefined = app?.internalPlugins?.getPluginById(StarredCorePluginId)
if (starredPlugin && starredPlugin.enabled && starredPlugin.instance) { if (starredPlugin && starredPlugin.enabled && starredPlugin.instance) {
const starredPluginInstance: Starred_PluginInstance = starredPlugin.instance as Starred_PluginInstance const starredPluginInstance: Starred_PluginInstance = starredPlugin.instance as Starred_PluginInstance