diff --git a/CHANGELOG.md b/CHANGELOG.md index cb4f94f7..1fcfbd40 120000 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +1 @@ -docs/docs/changelog.md \ No newline at end of file +docs/docs/changelog.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 54cb9a6d..f6af8f1f 120000 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1 +1 @@ -docs/docs/en/contributing.md \ No newline at end of file +docs/docs/en/contributing.md diff --git a/src/constants.ts b/src/constants.ts index 2b8a094f..4d663350 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -2,10 +2,16 @@ // https://github.com/st3v3nmw/obsidian-spaced-repetition/issues/776 export const SCHEDULING_INFO_REGEX = /^---\r?\n((?:.*\r?\n)*)sr-due: (.+)\r?\nsr-interval: (\d+)\r?\nsr-ease: (\d+)\r?\n((?:.*\r?\n)?)---/; + +export const SCHEDULING_INFO_BLOCK = /---\s(sr-due:[^>\n]+)\s(sr-interval:[^>\n]+)\s(sr-ease:[^>\n]+)\s---/g; +export const SCHEDULING_INFO_TEXT = /(sr-due:[^>\n]+)\s(sr-interval:[^>\n]+)\s(sr-ease:[^>\n]+)\s/g + export const YAML_FRONT_MATTER_REGEX = /^---\r?\n((?:.*\r?\n)*?)---/; export const MULTI_SCHEDULING_EXTRACTOR = /!([\d-]+),(\d+),(\d+)/gm; export const LEGACY_SCHEDULING_EXTRACTOR = //gm; +export const SCHEDULING_EXTRACTOR = /[\s]*/g; + export const OBSIDIAN_TAG_AT_STARTOFLINE_REGEX = /^#[^\s#]+/gi; // https://help.obsidian.md/Linking+notes+and+files/Internal+links#Link+to+a+block+in+a+note diff --git a/src/delete-scheduling-data.ts b/src/delete-scheduling-data.ts new file mode 100644 index 00000000..e3b78b6e --- /dev/null +++ b/src/delete-scheduling-data.ts @@ -0,0 +1,26 @@ +import { TFile, Vault } from "obsidian"; + +import { SCHEDULING_EXTRACTOR, SCHEDULING_INFO_BLOCK, SCHEDULING_INFO_TEXT } from "src/constants"; + +/** + * Modifies a file to remove scheduling data. + * @param vault - The Obsidian vault instance. + * @param file - The file to modify. + * @returns - A promise that resolves to the modified file content. + */ +function modifyFile(vault: Vault, file: TFile): Promise { + return vault.process(file, (data) => { + return data.replace(SCHEDULING_INFO_BLOCK, "").replace(SCHEDULING_INFO_TEXT, "").replace(SCHEDULING_EXTRACTOR, "").trim(); + }); +} + +/** + * Deletes all scheduling data from all markdown files in the vault. + */ +export function deleteSchedulingData() { + const files = this.app.vault.getMarkdownFiles(); + + for (const file of files) { + modifyFile(this.app.vault, file); + } +} diff --git a/src/gui/confirmation-modal.tsx b/src/gui/confirmation-modal.tsx new file mode 100644 index 00000000..a3da0ad8 --- /dev/null +++ b/src/gui/confirmation-modal.tsx @@ -0,0 +1,54 @@ +import { App, Modal, Notice, Setting } from "obsidian"; + +import { t } from "src/lang/helpers"; + +/** + * A reusable confirmation modal. + */ +export class ConfirmationModal extends Modal { + /** + * Creates a confirmation modal. + * @param app - The Obsidian app instance. + * @param title - Title of the modal. + * @param message - Body message of the modal. + * @param confirmationMessage - Notice message to display upon confirmation. + * @param onConfirm - Callback function to execute upon confirmation. + */ + constructor( + app: App, + title: string, + message: string, + confirmationMessage?: string, + onConfirm?: () => void, + ) { + super(app); + + this.setTitle(title); + this.titleEl.addClass("modal-header"); + + this.setContent(message); + this.contentEl.addClass("modal-content"); + + new Setting(this.contentEl) + .setClass("modal-button-container") + .addButton((button) => + button + .setButtonText(t("CONFIRM")) + .setClass("mod-warning") + .onClick(() => { + if (onConfirm) { + onConfirm(); + } + if (confirmationMessage) { + new Notice(confirmationMessage); + } + this.close(); + }), + ) + .addButton((button) => + button.setButtonText(t("CANCEL")).onClick(() => { + this.close(); + }), + ); + } +} diff --git a/src/gui/settings.tsx b/src/gui/settings.tsx index 20dc2092..419dd93b 100644 --- a/src/gui/settings.tsx +++ b/src/gui/settings.tsx @@ -1,5 +1,7 @@ import { App, Notice, PluginSettingTab, Setting } from "obsidian"; +import { deleteSchedulingData } from "src/delete-scheduling-data"; +import { ConfirmationModal } from "src/gui/confirmation-modal"; import { StatisticsView } from "src/gui/statistics"; import { createTabs, TabStructure } from "src/gui/tabs"; import { t } from "src/lang/helpers"; @@ -1040,6 +1042,25 @@ export class SRSettingTab extends PluginSettingTab { await this.plugin.savePluginData(); }), ); + + containerEl.createEl("h3", { text: t("DELETE_SCHEDULING_DATA") }); + new Setting(containerEl) + .setName(t("DELETE_SCHEDULING_DATA")) + .setDesc(t("DELETE_SCHEDULING_DATA_IN_NOTES_AND_FLASHCARDS")) + .addButton((button) => + button + .setButtonText(t("DELETE")) + .setClass("mod-warning") + .onClick(async () => { + new ConfirmationModal( + this.plugin.app, + t("DELETE_SCHEDULING_DATA"), + t("CONFIRM_SCHEDULING_DATA_DELETION"), + t("SCHEDULING_DATA_HAS_BEEN_DELETED"), + deleteSchedulingData, + ).open(); + }), + ); } private async tabHelp(containerEl: HTMLElement): Promise { diff --git a/src/lang/locale/en.ts b/src/lang/locale/en.ts index 5f9f0aed..3108bca8 100644 --- a/src/lang/locale/en.ts +++ b/src/lang/locale/en.ts @@ -194,6 +194,15 @@ export default { EXPERIMENTAL: "Experimental", HELP: "Help", STORE_IN_NOTES: "In the notes", + DELETE_SCHEDULING_DATA: "Delete Scheduling Data", + DELETE_SCHEDULING_DATA_IN_NOTES_AND_FLASHCARDS: + "Delete scheduling data from all notes and flashcards.", + DELETE: "Delete", + CONFIRM_SCHEDULING_DATA_DELETION: + "Are you sure you want to delete all scheduling data from your notes and flashcards? This action cannot be undone.", + CONFIRM: "Confirm", + SCHEDULING_DATA_HAS_BEEN_DELETED: + "Scheduling data has been deleted from all notes and flashcards.", // sidebar.ts NOTES_REVIEW_QUEUE: "Notes Review Queue",