diff --git a/src/constants.ts b/src/constants.ts index d97f6084..9f322257 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,9 +1,9 @@ export const SCHEDULING_INFO_REGEX = - /^---\n((?:.*\n)*)sr-due: (.+)\nsr-interval: (\d+)\nsr-ease: (\d+)\n((?:.*\n)?)---/; + /^---\n((?:.*\n)*)sr-due: (.+)\nsr-interval: ([\d\.]+)\nsr-ease: ([\d\.]+)\n((?:.*\n)?)---/; export const YAML_FRONT_MATTER_REGEX = /^---\n((?:.*\n)*?)---/; -export const MULTI_SCHEDULING_EXTRACTOR = /!([\d-]+),(\d+),(\d+)/gm; -export const LEGACY_SCHEDULING_EXTRACTOR = //gm; +export const MULTI_SCHEDULING_EXTRACTOR = /!([\d-]+),([\d\.]+),([\d\.]+)/gm; +export const LEGACY_SCHEDULING_EXTRACTOR = //gm; export const IMAGE_FORMATS = [ "jpg", diff --git a/src/flashcard-modal.tsx b/src/flashcard-modal.tsx index 98b36e8b..b40a21ac 100644 --- a/src/flashcard-modal.tsx +++ b/src/flashcard-modal.tsx @@ -490,7 +490,9 @@ export class FlashcardModal extends Modal { return; } - const dueString: string = due.format("YYYY-MM-DD"); + const dueString: string = due.format("YYYY-MM-DD-HH-mm"); + + // const dueString: string = due.format("YYYY-MM-DD"); let fileText: string = await this.app.vault.read(this.currentCard.note); const replacementRegex = new RegExp(escapeRegexString(this.currentCard.cardText), "gm"); diff --git a/src/lang/locale/en.ts b/src/lang/locale/en.ts index e6e01eda..dfbbfdc1 100644 --- a/src/lang/locale/en.ts +++ b/src/lang/locale/en.ts @@ -123,6 +123,10 @@ export default { EASY_BONUS_DESC: "The easy bonus allows you to set the difference in intervals between answering Good and Easy on a flashcard/note (minimum = 100%).", EASY_BONUS_MIN_WARNING: "The easy bonus must be at least 100.", + REVIEW_BEFORE_DUE: "Review Flashnotes Before Due", + REVIEW_BEFORE_DUE_DESC: + "The review before due allows you to review flashnote before due.", + REVIEW_BEFORE_DUE_MIN_WARNING: "The review before due must be at least 0.", MAX_INTERVAL: "Maximum Interval", MAX_INTERVAL_DESC: "Allows you to place an upper limit on the interval (default = 100 years).", MAX_INTERVAL_MIN_WARNING: "The maximum interval must be at least 1 day.", diff --git a/src/main.ts b/src/main.ts index 5704b1d7..81922602 100644 --- a/src/main.ts +++ b/src/main.ts @@ -275,7 +275,7 @@ export default class SRPlugin extends Plugin { }; const now = window.moment(Date.now()); - const todayDate: string = now.format("YYYY-MM-DD"); + const todayDate: string = now.format("YYYY-MM-DD-HH-mm"); // clear bury list if we've changed dates if (todayDate !== this.data.buryDate) { this.data.buryDate = todayDate; @@ -364,7 +364,7 @@ export default class SRPlugin extends Plugin { } const dueUnix: number = window - .moment(frontmatter["sr-due"], ["YYYY-MM-DD", "DD-MM-YYYY", "ddd MMM DD YYYY"]) + .moment(frontmatter["sr-due"], ["YYYY-MM-DD-HH-mm", "YYYY-MM-DD", "DD-MM-YYYY", "ddd MMM DD YYYY"]) .valueOf(); for (const matchedNoteTag of matchedNoteTags) { @@ -511,7 +511,7 @@ export default class SRPlugin extends Plugin { delayBeforeReview = now - window - .moment(frontmatter["sr-due"], ["YYYY-MM-DD", "DD-MM-YYYY", "ddd MMM DD YYYY"]) + .moment(frontmatter["sr-due"], ["YYYY-MM-DD-HH-mm", "YYYY-MM-DD", "DD-MM-YYYY", "ddd MMM DD YYYY"]) .valueOf(); } @@ -527,7 +527,7 @@ export default class SRPlugin extends Plugin { ease = schedObj.ease; const due = window.moment(now + interval * 24 * 3600 * 1000); - const dueString: string = due.format("YYYY-MM-DD"); + const dueString: string = due.format("YYYY-MM-DD-HH-mm"); // check if scheduling info exists if (SCHEDULING_INFO_REGEX.test(fileText)) { @@ -815,7 +815,7 @@ export default class SRPlugin extends Plugin { this.deckTree.insertFlashcard([...deckPath], cardObj); } else if (i < scheduling.length) { const dueUnix: number = window - .moment(scheduling[i][1], ["YYYY-MM-DD", "DD-MM-YYYY"]) + .moment(scheduling[i][1], ["YYYY-MM-DD-HH-mm", "YYYY-MM-DD", "DD-MM-YYYY"]) .valueOf(); const nDays: number = Math.ceil((dueUnix - now) / (24 * 3600 * 1000)); if (!Object.prototype.hasOwnProperty.call(this.dueDatesFlashcards, nDays)) { @@ -847,7 +847,7 @@ export default class SRPlugin extends Plugin { continue; } - if (dueUnix <= now) { + if (dueUnix <= now + this.data.settings.reviewBeforeDue * 24 * 3600 * 1000) { cardObj.interval = interval; cardObj.ease = ease; cardObj.delayBeforeReview = now - dueUnix; diff --git a/src/scheduling.ts b/src/scheduling.ts index 8fdf2562..765c4386 100644 --- a/src/scheduling.ts +++ b/src/scheduling.ts @@ -68,6 +68,9 @@ export function schedule( // replaces random fuzz with load balancing over the fuzz interval if (dueDates !== undefined) { + // conserve the decimal part + const interval_decimal = interval - Math.round(interval); + interval = Math.round(interval); if (!Object.prototype.hasOwnProperty.call(dueDates, interval)) { dueDates[interval] = 0; @@ -94,8 +97,11 @@ export function schedule( } dueDates[interval]++; - } + // add the decimal part + interval = interval + interval_decimal; + } + interval = Math.min(interval, settingsObj.maximumInterval); return { interval: Math.round(interval * 10) / 10, ease }; diff --git a/src/settings.ts b/src/settings.ts index dc361c01..51b0cb42 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -39,6 +39,7 @@ export interface SRSettings { easyBonus: number; maximumInterval: number; maxLinkFactor: number; + reviewBeforeDue: number; // logging showDebugMessages: boolean; } @@ -80,6 +81,7 @@ export const DEFAULT_SETTINGS: SRSettings = { easyBonus: 1.3, maximumInterval: 36525, maxLinkFactor: 1.0, + reviewBeforeDue: 0.0, // logging showDebugMessages: false, }; @@ -656,6 +658,44 @@ export class SRSettingTab extends PluginSettingTab { }); }); + + new Setting(containerEl) + .setName(t("REVIEW_BEFORE_DUE")) + .setDesc(t("REVIEW_BEFORE_DUE_DESC")) + .addText((text) => + text + .setValue((this.plugin.data.settings.reviewBeforeDue).toString()) + .onChange((value) => { + applySettingsUpdate(async () => { + const numValue: number = Number.parseFloat(value); + if (!isNaN(numValue)) { + if (numValue < 0.0) { + new Notice(t("REVIEW_BEFORE_DUE_MIN_WARNING")); + text.setValue( + (this.plugin.data.settings.reviewBeforeDue * 100).toString() + ); + return; + } + + this.plugin.data.settings.reviewBeforeDue = Math.floor(numValue * 100) / 100; + await this.plugin.savePluginData(); + } else { + new Notice(t("VALID_NUMBER_WARNING")); + } + }); + }) + ) + .addExtraButton((button) => { + button + .setIcon("reset") + .setTooltip(t("RESET_DEFAULT")) + .onClick(async () => { + this.plugin.data.settings.reviewBeforeDue = DEFAULT_SETTINGS.reviewBeforeDue; + await this.plugin.savePluginData(); + this.display(); + }); + }); + new Setting(containerEl) .setName(t("MAX_INTERVAL")) .setDesc(t("MAX_INTERVAL_DESC"))