From 2bb79c4209435e3046fd83e056e09fc4634ee1b2 Mon Sep 17 00:00:00 2001 From: Panagiotis Papadopoulos Date: Sat, 8 Feb 2025 11:55:16 +0100 Subject: [PATCH] feat: "friendly number handling" for note erasure timeouts --- .../options/other/note_erasure_timeout.ts | 88 +++++++++++++++++-- src/routes/api/options.ts | 1 + src/services/options_init.ts | 1 + src/services/options_interface.ts | 1 + 4 files changed, 82 insertions(+), 9 deletions(-) diff --git a/src/public/app/widgets/type_widgets/options/other/note_erasure_timeout.ts b/src/public/app/widgets/type_widgets/options/other/note_erasure_timeout.ts index 876cf36d3..6b187a825 100644 --- a/src/public/app/widgets/type_widgets/options/other/note_erasure_timeout.ts +++ b/src/public/app/widgets/type_widgets/options/other/note_erasure_timeout.ts @@ -11,26 +11,62 @@ const TPL = `

${t("note_erasure_timeout.note_erasure_description")}

- - + +
+ + + +

${t("note_erasure_timeout.manual_erasing_description")}

- + `; export default class NoteErasureTimeoutOptions extends OptionsWidget { - private $eraseEntitiesAfterTimeInSeconds!: JQuery; - private $eraseDeletedNotesButton!: JQuery; + private $eraseEntitiesAfterTime!: JQuery; + private $eraseEntitiesAfterTimeScale!: JQuery; + private $eraseDeletedNotesButton!: JQuery; + private eraseEntitiesAfterTimeInSeconds!: string | number; doRender() { this.$widget = $(TPL); - this.$eraseEntitiesAfterTimeInSeconds = this.$widget.find(".erase-entities-after-time-in-seconds"); - this.$eraseEntitiesAfterTimeInSeconds.on("change", () => this.updateOption("eraseEntitiesAfterTimeInSeconds", this.$eraseEntitiesAfterTimeInSeconds.val())); + this.$eraseEntitiesAfterTime = this.$widget.find("#erase-entities-after-time"); + this.$eraseEntitiesAfterTimeScale = this.$widget.find("#erase-entities-after-time-scale"); - this.$eraseDeletedNotesButton = this.$widget.find(".erase-deleted-notes-now-button"); + this.$eraseEntitiesAfterTime.on("change", () => { + const time = this.$eraseEntitiesAfterTime.val(); + const timeScale = this.$eraseEntitiesAfterTimeScale.val(); + + if (!this.handleTimeValidation() || typeof timeScale !== "string" || !time) return; + + this.eraseEntitiesAfterTimeInSeconds = this.convertTime(time, timeScale).toOption(); + this.updateOption("eraseEntitiesAfterTimeInSeconds", this.eraseEntitiesAfterTimeInSeconds); + + }); + + this.$eraseEntitiesAfterTimeScale.on("change", () => { + + const timeScale = this.$eraseEntitiesAfterTimeScale.val(); + + if (!this.handleTimeValidation() || typeof timeScale !== "string") return; + + //calculate the new displayed value + const displayedTime = this.convertTime(this.eraseEntitiesAfterTimeInSeconds, timeScale).toDisplay(); + + this.updateOption("eraseEntitiesAfterTimeScale", timeScale); + this.$eraseEntitiesAfterTime.val(displayedTime).trigger("change"); + + }); + + this.$eraseDeletedNotesButton = this.$widget.find("#erase-deleted-notes-now-button"); this.$eraseDeletedNotesButton.on("click", () => { server.post("notes/erase-deleted-notes-now").then(() => { toastService.showMessage(t("note_erasure_timeout.deleted_notes_erased")); @@ -39,6 +75,40 @@ export default class NoteErasureTimeoutOptions extends OptionsWidget { } async optionsLoaded(options: OptionMap) { - this.$eraseEntitiesAfterTimeInSeconds.val(options.eraseEntitiesAfterTimeInSeconds); + this.eraseEntitiesAfterTimeInSeconds = options.eraseEntitiesAfterTimeInSeconds; + + const displayedTime = this.convertTime(options.eraseEntitiesAfterTimeInSeconds, options.eraseEntitiesAfterTimeScale).toDisplay(); + this.$eraseEntitiesAfterTime.val(displayedTime); + this.$eraseEntitiesAfterTimeScale.val(options.eraseEntitiesAfterTimeScale); } + + + convertTime(time: string | number, timeScale: string | number) { + + const value = typeof time === "number" ? time : parseInt(time); + if (Number.isNaN(value)) { + throw new Error(`Time needs to be a valid integer, but received: ${time}`); + } + + const operand = typeof timeScale === "number" ? timeScale : parseInt(timeScale); + if (Number.isNaN(operand) || operand < 1) { + throw new Error(`TimeScale needs to be a valid integer >= 1, but received: ${timeScale}`); + } + + return { + toOption: () => Math.ceil(value * operand), + toDisplay: () => Math.ceil(value / operand), + } + + } + + handleTimeValidation() { + if (this.$eraseEntitiesAfterTime.is(":invalid")) { + // TriliumNextTODO: i18n + toastService.showMessage("The entered time value is not a valid number."); + return false + } + return true; + } + } diff --git a/src/routes/api/options.ts b/src/routes/api/options.ts index 257344cab..14cb7ec42 100644 --- a/src/routes/api/options.ts +++ b/src/routes/api/options.ts @@ -12,6 +12,7 @@ import type { OptionNames } from "../../services/options_interface.js"; // options allowed to be updated directly in the Options dialog const ALLOWED_OPTIONS = new Set([ "eraseEntitiesAfterTimeInSeconds", + "eraseEntitiesAfterTimeScale", "protectedSessionTimeout", "revisionSnapshotTimeInterval", "revisionSnapshotNumberLimit", diff --git a/src/services/options_init.ts b/src/services/options_init.ts index dedd1a896..bb962835d 100644 --- a/src/services/options_init.ts +++ b/src/services/options_init.ts @@ -105,6 +105,7 @@ const defaultOptions: DefaultOption[] = [ { name: "rightPaneVisible", value: "true", isSynced: false }, { name: "nativeTitleBarVisible", value: "false", isSynced: false }, { name: "eraseEntitiesAfterTimeInSeconds", value: "604800", isSynced: true }, // default is 7 days + { name: "eraseEntitiesAfterTimeScale", value: "86400", isSynced: true }, // default 86400 seconds = Day { name: "hideArchivedNotes_main", value: "false", isSynced: false }, { name: "debugModeEnabled", value: "false", isSynced: false }, { name: "headingStyle", value: "underline", isSynced: true }, diff --git a/src/services/options_interface.ts b/src/services/options_interface.ts index 2632b7b2e..f3a92383b 100644 --- a/src/services/options_interface.ts +++ b/src/services/options_interface.ts @@ -61,6 +61,7 @@ export interface OptionDefinitions extends KeyboardShortcutsOptions