Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 43 additions & 26 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ import {
DEFAULT_SETTINGS
} from "./settings";
import {CustomSortPluginAPI} from "./custom-sort-plugin";
import { UndeferHandler } from './utils/UndeferHandler';

const PLUGIN_ID = 'custom-sort' // same as in manifest.json

Expand Down Expand Up @@ -230,6 +231,39 @@ export default class CustomSortPlugin
return fileExplorerOrError
}

patchFileExplorerWhenAvailable() {
const fileExplorerLeaf = this.app.workspace.getLeavesOfType('file-explorer')[0] as FileExplorerLeaf
// Called under one of these 2 conditions:
// - File Explorer has not been loaded yet.
// - File Explorer had been loaded in its defer state, but then it was closed before it had
// a chance to be fully loaded.
const waitAndPatch = () => {
const patchRef = this.app.workspace.on('active-leaf-change', leaf => {
if (leaf?.view.getViewType() == 'file-explorer') {
this.patchFileExplorer(leaf as FileExplorerLeaf)
this.app.workspace.offref(patchRef)
this.app.workspace.offref(cancelRef)
}
})
const cancelRef = this.app.workspace.on('custom-sort:plugin-unload', () => {
this.app.workspace.offref(patchRef)
this.app.workspace.offref(cancelRef)
})
}

if (!fileExplorerLeaf) {
waitAndPatch()
} else if (fileExplorerLeaf.isDeferred) {
new UndeferHandler(
fileExplorerLeaf,
this.patchFileExplorer.bind(this),
waitAndPatch
)
} else {
this.patchFileExplorer(fileExplorerLeaf)
}
}

// For the idea of monkey-patching credits go to https://github.com/nothingislost/obsidian-bartender
patchFileExplorer(patchableFileExplorer: FileExplorerLeaf): FileExplorerLeaf|undefined {
let plugin = this;
Expand All @@ -250,21 +284,14 @@ export default class CustomSortPlugin
const sortingData = plugin.determineAndPrepareSortingDataForFolder(folder)

if (sortingData.sortSpec) {
if (!plugin.customSortAppliedAtLeastOnce) {
plugin.customSortAppliedAtLeastOnce = true
setTimeout(() => {
plugin.setRibbonIconToEnabled.apply(plugin)
plugin.showNotice('Custom sort APPLIED.');
plugin.updateStatusBar()
})
}
return getSortedFolderItems.call(this, folder, sortingData.sortSpec, plugin.createProcessingContextForSorting(sortingData.sortingAndGroupingStats))
} else {
return old.call(this, ...args);
}
};
}
})
patchableFileExplorer.view.requestSort()
return patchableFileExplorer
} else {
return undefined
Expand Down Expand Up @@ -295,8 +322,7 @@ export default class CustomSortPlugin
this.settings.suspended = !enabled;
this.saveSettings()

let fileExplorerOrError: FileExplorerLeafOrError = this.checkFileExplorerIsAvailableAndPatchable(!this.settings.suspended)
const fileExplorer = fileExplorerOrError.v ? this.patchFileExplorer(fileExplorerOrError.v) : undefined
const fileExplorer = this.getFileExplorer().v

if (this.settings.suspended) {
this.showNotice('Custom sort OFF');
Expand All @@ -311,17 +337,9 @@ export default class CustomSortPlugin
if (fileExplorer) {
this.customSortAppliedAtLeastOnce = false
fileExplorer.view.requestSort();
} else {
if (Platform.isDesktop) {
this.showNotice('Custom sort File Explorer view problem. See console for detailed message.')
} else { // No console access on mobile
this.showNotice(`Custom sort File Explorer view problem - is it visible?`
+ ` Can't apply custom sorting when the File Explorer was not displayed at least once.`)
}
setIcon(this.ribbonIconEl, ICON_SORT_SUSPENDED_GENERAL_ERROR)
this.settings.suspended = true
this.saveSettings()
}
this.setRibbonIconToEnabled()
this.showNotice('Custom sort APPLIED.')
} else {
setIcon(this.ribbonIconEl, ICON_SORT_SUSPENDED_SYNTAX_ERROR)
this.settings.suspended = true
Expand Down Expand Up @@ -614,12 +632,9 @@ export default class CustomSortPlugin
}

initialize() {
const plugin = this
this.app.workspace.onLayoutReady(() => {
setTimeout(() => {
plugin.delayedApplicationOfCustomSorting.apply(this)
},
plugin.settings.delayForInitialApplication)
this.patchFileExplorerWhenAvailable()
this.switchPluginStateTo(!this.settings.suspended)
})
}

Expand Down Expand Up @@ -683,14 +698,16 @@ export default class CustomSortPlugin
}

onunload() {
// Trigger "plugin-unload" event
this.app.workspace.trigger('custom-sort:plugin-unload');
}

onUserEnable() {
}

updateStatusBar() {
if (this.statusBarItemEl) {
let status = (!this.settings.suspended && this.customSortAppliedAtLeastOnce) ? 'ON' : 'OFF'
let status = !this.settings.suspended ? 'ON' : 'OFF'
this.statusBarItemEl.setText(`Custom sort:${status}`)
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/types/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,9 @@ declare module 'obsidian' {
interface MenuItem {
setSubmenu: () => Menu;
}

interface Workspace {
// Augmented event, triggered after unloading the plugin
on(name: 'custom-sort:plugin-unload', callback: () => unknown, ctx?: any): EventRef;
}
}
58 changes: 58 additions & 0 deletions src/utils/UndeferHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { App, Component, View, WorkspaceLeaf } from 'obsidian';

export class UndeferHandler extends Component {
app: App;
leaf: WorkspaceLeaf;
view: View;
callback?: (leaf: WorkspaceLeaf) => unknown;
// Called when the deferred leaf is detached before being fully loaded
onLeafDetach?: () => unknown;

constructor(leaf: WorkspaceLeaf, callback: (leaf: WorkspaceLeaf) => unknown, onLeafDetach?: () => unknown) {
super();
this.app = leaf.view.app;
this.leaf = leaf;
this.view = leaf.view;
this.callback = callback;
this.onLeafDetach = onLeafDetach;

this.view.addChild(this);
}

onload(): void {
// Run the callback immediately if the leaf is not deferred
if (!this.leaf.isDeferred) {
this.view.removeChild(this);
return;
}

// Detach the handler once the plugin has been disabled/unistalled
this.registerEvent(this.app.workspace.on('custom-sort:plugin-unload', () => this.detach()));
}

onunload(): void {
if (this.callback) this.runCallback();
}

// Detach the handler without invoking the callback
detach(): void {
delete this.callback;
this.view.removeChild(this);
}

private async runCallback(): Promise<void> {
// Run the callback after the actual view has been loaded
await sleep(0);
// Do not run the callback if the deferred view was unloaded
// because of being closed
if (
!this.leaf.isDeferred &&
this.leaf.parent &&
this.leaf.view.getViewType() !== 'empty'
) {
this.callback?.(this.leaf);
} else {
this.onLeafDetach?.();
}
}
}
Loading