gtritchie_bulk-properties/src/deselect-all.ts
Gary Ritchie 353372c921
Add metadata cache warning after large bulk operations (#44)
* Add metadata cache warning modal for large bulk operations

When a bulk operation modifies more than 75 notes, show a modal
explaining that Obsidian's metadata cache may take several minutes
to re-index. Includes a "Don't show again" toggle persisted via a
new showLargeOperationWarning setting, re-enableable from the
settings tab. Covers bulk update, bulk delete, deselect all, and
remove selection property entry points.

Also simplifies deselectAll and removeSelectionProperty signatures
to take the plugin instance, deriving app and selectionProperty
from it.

* Pass captured selectionProperty through to doRemove

Restore the original data flow where selectionProperty is captured
once at command start and threaded through, rather than re-read
from plugin.settings inside doRemove. The signature simplification
in the prior commit introduced a subtle re-read that could
mismatch if the setting changed between confirmation and execution.

* Remove cache warning from file deletion, lower threshold to 25

File deletion reconciles in the metadata cache much faster than
property edits (entry removal vs YAML re-parse), so the warning
would be misleading. Also lower LARGE_OPERATION_THRESHOLD from 75
to 25.

* Document scale expectations and cache warning in README

* Avoid hardcoding threshold number in README
2026-04-12 09:09:44 -06:00

54 lines
1.5 KiB
TypeScript

import {Notice} from "obsidian";
import type BulkPropertiesPlugin from "./main";
import {confirmDeselectAll} from "./confirm-modal";
import {getSelectedFiles} from "./files";
import {
shouldWarnLargeOperation,
showLargeOperationNotice,
} from "./large-operation-notice";
import {withProgress} from "./progress";
export async function deselectAll(
plugin: BulkPropertiesPlugin,
): Promise<void> {
const {app} = plugin;
const selectionProperty = plugin.settings.selectionProperty;
const files = getSelectedFiles(app, selectionProperty);
if (files.length === 0) {
new Notice("No notes are selected");
return;
}
const confirmed = await confirmDeselectAll(app, files.length);
if (!confirmed) return;
const result = await withProgress(
files,
"Deselecting",
async (file) => {
await app.fileManager.processFrontMatter(
file,
(fm: Record<string, unknown>) => {
fm[selectionProperty] = false;
},
);
},
);
const {succeeded, failed, cancelled, total} = result;
let msg: string;
if (cancelled) {
msg = `Deselected ${succeeded} of ${total} note${total === 1 ? "" : "s"} (cancelled)`;
} else if (failed.length === 0) {
msg = `Deselected ${succeeded} note${succeeded === 1 ? "" : "s"}`;
} else {
msg = `Deselected ${succeeded} note${succeeded === 1 ? "" : "s"}, failed on ${failed.length}: ${failed.join(", ")}`;
}
if (shouldWarnLargeOperation(plugin, succeeded)) {
showLargeOperationNotice(plugin, succeeded, msg);
} else {
new Notice(msg);
}
}