From c0714a92d547b68ba3afdee08e75e5c401572df2 Mon Sep 17 00:00:00 2001
From: Panagiotis Papadopoulos
Date: Sun, 16 Feb 2025 13:46:20 +0100
Subject: [PATCH 01/24] feat(time_selector): add time_selector options widget
---
.../type_widgets/options/time_selector.ts | 126 ++++++++++++++++++
1 file changed, 126 insertions(+)
create mode 100644 src/public/app/widgets/type_widgets/options/time_selector.ts
diff --git a/src/public/app/widgets/type_widgets/options/time_selector.ts b/src/public/app/widgets/type_widgets/options/time_selector.ts
new file mode 100644
index 000000000..49c149f75
--- /dev/null
+++ b/src/public/app/widgets/type_widgets/options/time_selector.ts
@@ -0,0 +1,126 @@
+import OptionsWidget from "./options_widget.js";
+import toastService from "../../../services/toast.js";
+import { t } from "../../../services/i18n.js";
+import type { OptionDefinitions, OptionMap } from "../../../../../services/options_interface.js";
+
+
+type TimeSelectorConstructor = {
+ widgetId: string;
+ widgetLabelId: string;
+ optionValueId: keyof OptionDefinitions;
+ optionTimeScaleId: keyof OptionDefinitions;
+};
+
+
+const TPL = (options: Pick) => `
+
+
+
+`;
+
+export default class TimeSelector extends OptionsWidget {
+
+ private $timeValueInput!: JQuery;
+ private $timeScaleSelect!: JQuery;
+ private internalTimeInSeconds!: string | number;
+ private widgetId: string;
+ private widgetLabelId: string;
+ private optionValueId: keyof OptionDefinitions;
+ private optionTimeScaleId: keyof OptionDefinitions;
+
+ constructor(options: TimeSelectorConstructor) {
+ super();
+ this.widgetId = options.widgetId;
+ this.widgetLabelId = options.widgetLabelId;
+ this.optionValueId = options.optionValueId;
+ this.optionTimeScaleId = options.optionTimeScaleId;
+ }
+
+ doRender() {
+ this.$widget = $(TPL({
+ widgetId: this.widgetId,
+ widgetLabelId: this.widgetLabelId
+ }));
+
+ this.$timeValueInput = this.$widget.find(`#${this.widgetId}`);
+ this.$timeScaleSelect = this.$widget.find(`#${this.widgetId}-time-scale`);
+
+ this.$timeValueInput.on("change", () => {
+ const time = this.$timeValueInput.val();
+ const timeScale = this.$timeScaleSelect.val();
+
+ if (!this.handleTimeValidation() || typeof timeScale !== "string" || !time) return;
+
+ this.internalTimeInSeconds = this.convertTime(time, timeScale).toOption();
+ this.updateOption(this.optionValueId, this.internalTimeInSeconds);
+
+ });
+
+ this.$timeScaleSelect.on("change", () => {
+
+ const timeScale = this.$timeScaleSelect.val();
+
+ if (!this.handleTimeValidation() || typeof timeScale !== "string") return;
+
+ //calculate the new displayed value
+ const displayedTime = this.convertTime(this.internalTimeInSeconds, timeScale).toDisplay();
+
+ this.updateOption(this.optionTimeScaleId, timeScale);
+ this.$timeValueInput.val(displayedTime).trigger("change");
+
+ });
+
+ }
+
+ async optionsLoaded(options: OptionMap) {
+ this.internalTimeInSeconds = options[this.optionValueId];
+ const displayedTime = this.convertTime(options[this.optionValueId], options[this.optionTimeScaleId]).toDisplay();
+ this.$timeValueInput.val(displayedTime);
+ this.$timeScaleSelect.val(options[this.optionTimeScaleId]);
+ }
+
+
+ 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.$timeValueInput.is(":invalid")) {
+ // TriliumNextTODO: i18n
+ toastService.showMessage("The entered time value is not a valid number.");
+ return false
+ }
+ return true;
+ }
+
+}
\ No newline at end of file
From e0e530b2195a45698c765f7006a9646920b1fa5b Mon Sep 17 00:00:00 2001
From: Panagiotis Papadopoulos
Date: Sun, 16 Feb 2025 13:56:05 +0100
Subject: [PATCH 02/24] feat(time_selector): use time_selector in
note_erasure_timeout
---
.../options/other/note_erasure_timeout.ts | 107 +++---------------
1 file changed, 17 insertions(+), 90 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 99161f9a9..f06274c03 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
@@ -3,76 +3,40 @@ import server from "../../../../services/server.js";
import toastService from "../../../../services/toast.js";
import { t } from "../../../../services/i18n.js";
import type { OptionMap } from "../../../../../../services/options_interface.js";
+import TimeSelector from "../time_selector.js";
const TPL = `
${t("note_erasure_timeout.note_erasure_timeout_title")}
-
${t("note_erasure_timeout.note_erasure_description")}
+`;
-
-
+const TPL2 = `
${t("note_erasure_timeout.manual_erasing_description")}
-
-
-
`;
-export default class NoteErasureTimeoutOptions extends OptionsWidget {
+export default class NoteErasureTimeoutOptions extends TimeSelector {
- private $eraseEntitiesAfterTime!: JQuery;
- private $eraseEntitiesAfterTimeScale!: JQuery;
private $eraseDeletedNotesButton!: JQuery;
- private eraseEntitiesAfterTimeInSeconds!: string | number;
+
+ constructor() {
+ super( {
+ widgetId: "erase-entities-after",
+ widgetLabelId: "note_erasure_timeout.erase_notes_after",
+ optionValueId: "eraseEntitiesAfterTimeInSeconds",
+ optionTimeScaleId: "eraseEntitiesAfterTimeScale"
+ })
+ super.doRender();
+
+ }
doRender() {
- this.$widget = $(TPL);
- this.$eraseEntitiesAfterTime = this.$widget.find("#erase-entities-after-time");
- this.$eraseEntitiesAfterTimeScale = this.$widget.find("#erase-entities-after-time-scale");
- 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.$widget = $(TPL).append(this.$widget).append(TPL2);
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"));
@@ -80,41 +44,4 @@ export default class NoteErasureTimeoutOptions extends OptionsWidget {
});
}
- async optionsLoaded(options: OptionMap) {
- 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;
- }
-
}
From f672054441bc45042ed814357286a824f0321a25 Mon Sep 17 00:00:00 2001
From: Panagiotis Papadopoulos
Date: Sun, 16 Feb 2025 14:00:28 +0100
Subject: [PATCH 03/24] feat(time_selector): use time_selector in
attachment_erasure_timeout
---
.../other/attachment_erasure_timeout.ts | 29 ++++++++++---------
src/routes/api/options.ts | 1 +
src/services/options_init.ts | 3 +-
src/services/options_interface.ts | 1 +
4 files changed, 19 insertions(+), 15 deletions(-)
diff --git a/src/public/app/widgets/type_widgets/options/other/attachment_erasure_timeout.ts b/src/public/app/widgets/type_widgets/options/other/attachment_erasure_timeout.ts
index 48ce0cb0d..c384dce10 100644
--- a/src/public/app/widgets/type_widgets/options/other/attachment_erasure_timeout.ts
+++ b/src/public/app/widgets/type_widgets/options/other/attachment_erasure_timeout.ts
@@ -2,33 +2,37 @@ import OptionsWidget from "../options_widget.js";
import server from "../../../../services/server.js";
import toastService from "../../../../services/toast.js";
import { t } from "../../../../services/i18n.js";
-import type { OptionMap } from "../../../../../../services/options_interface.js";
+import TimeSelector from "../time_selector.js";
const TPL = `
${t("attachment_erasure_timeout.attachment_erasure_timeout")}
${t("attachment_erasure_timeout.attachment_auto_deletion_description")}
+`;
-
-
-
-
-
+const TPL2 = `
${t("attachment_erasure_timeout.manual_erasing_description")}
`;
-export default class AttachmentErasureTimeoutOptions extends OptionsWidget {
+export default class AttachmentErasureTimeoutOptions extends TimeSelector {
- private $eraseUnusedAttachmentsAfterTimeInSeconds!: JQuery;
private $eraseUnusedAttachmentsNowButton!: JQuery;
+ constructor() {
+ super({
+ widgetId: "erase-unused-attachments-after",
+ widgetLabelId: "attachment_erasure_timeout.erase_attachments_after_x_seconds",
+ optionValueId: "eraseUnusedAttachmentsAfterSeconds",
+ optionTimeScaleId: "eraseUnusedAttachmentsAfterTimeScale"
+ })
+ super.doRender()
+ }
+
doRender() {
- this.$widget = $(TPL);
- this.$eraseUnusedAttachmentsAfterTimeInSeconds = this.$widget.find(".erase-unused-attachments-after-time-in-seconds");
- this.$eraseUnusedAttachmentsAfterTimeInSeconds.on("change", () => this.updateOption("eraseUnusedAttachmentsAfterSeconds", this.$eraseUnusedAttachmentsAfterTimeInSeconds.val()));
+ this.$widget = $(TPL).append(this.$widget).append(TPL2);
this.$eraseUnusedAttachmentsNowButton = this.$widget.find(".erase-unused-attachments-now-button");
this.$eraseUnusedAttachmentsNowButton.on("click", () => {
@@ -38,7 +42,4 @@ export default class AttachmentErasureTimeoutOptions extends OptionsWidget {
});
}
- async optionsLoaded(options: OptionMap) {
- this.$eraseUnusedAttachmentsAfterTimeInSeconds.val(options.eraseUnusedAttachmentsAfterSeconds);
- }
}
diff --git a/src/routes/api/options.ts b/src/routes/api/options.ts
index 14cb7ec42..33cd5fa8c 100644
--- a/src/routes/api/options.ts
+++ b/src/routes/api/options.ts
@@ -61,6 +61,7 @@ const ALLOWED_OPTIONS = new Set([
"checkForUpdates",
"disableTray",
"eraseUnusedAttachmentsAfterSeconds",
+ "eraseUnusedAttachmentsAfterTimeScale",
"disableTray",
"customSearchEngineName",
"customSearchEngineUrl",
diff --git a/src/services/options_init.ts b/src/services/options_init.ts
index bb962835d..3bb87703e 100644
--- a/src/services/options_init.ts
+++ b/src/services/options_init.ts
@@ -122,7 +122,8 @@ const defaultOptions: DefaultOption[] = [
{ name: "highlightsList", value: '["bold","italic","underline","color","bgColor"]', isSynced: true },
{ name: "checkForUpdates", value: "true", isSynced: true },
{ name: "disableTray", value: "false", isSynced: false },
- { name: "eraseUnusedAttachmentsAfterSeconds", value: "2592000", isSynced: true },
+ { name: "eraseUnusedAttachmentsAfterSeconds", value: "2592000", isSynced: true }, // default 30 days
+ { name: "eraseUnusedAttachmentsAfterTimeScale", value: "86400", isSynced: true }, // default 86400 seconds = Day
{ name: "customSearchEngineName", value: "DuckDuckGo", isSynced: true },
{ name: "customSearchEngineUrl", value: "https://duckduckgo.com/?q={keyword}", isSynced: true },
{ name: "promotedAttributesOpenInRibbon", value: "true", isSynced: true },
diff --git a/src/services/options_interface.ts b/src/services/options_interface.ts
index f3a92383b..fb234240c 100644
--- a/src/services/options_interface.ts
+++ b/src/services/options_interface.ts
@@ -67,6 +67,7 @@ export interface OptionDefinitions extends KeyboardShortcutsOptions
Date: Sun, 16 Feb 2025 14:10:13 +0100
Subject: [PATCH 04/24] i18n(time_selector): translate invalid_input message
---
src/public/app/widgets/type_widgets/options/time_selector.ts | 3 +--
src/public/translations/en/translation.json | 3 +++
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/public/app/widgets/type_widgets/options/time_selector.ts b/src/public/app/widgets/type_widgets/options/time_selector.ts
index 49c149f75..d364cb095 100644
--- a/src/public/app/widgets/type_widgets/options/time_selector.ts
+++ b/src/public/app/widgets/type_widgets/options/time_selector.ts
@@ -116,8 +116,7 @@ export default class TimeSelector extends OptionsWidget {
handleTimeValidation() {
if (this.$timeValueInput.is(":invalid")) {
- // TriliumNextTODO: i18n
- toastService.showMessage("The entered time value is not a valid number.");
+ toastService.showMessage(t("time_selector.invalid_input"));
return false
}
return true;
diff --git a/src/public/translations/en/translation.json b/src/public/translations/en/translation.json
index 392ea89ae..636b3732a 100644
--- a/src/public/translations/en/translation.json
+++ b/src/public/translations/en/translation.json
@@ -1655,5 +1655,8 @@
"minutes": "Minutes",
"hours": "Hours",
"days": "Days"
+ },
+ "time_selector": {
+ "invalid_input": "The entered time value is not a valid number."
}
}
From 793b0c9fe89dcfc057c0d152b3ff12bff5838cc6 Mon Sep 17 00:00:00 2001
From: Panagiotis Papadopoulos
Date: Sun, 16 Feb 2025 15:27:18 +0100
Subject: [PATCH 05/24] feat(time_selector): add possibility to omit time
scales
---
.../type_widgets/options/time_selector.ts | 18 ++++++++++++------
1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/src/public/app/widgets/type_widgets/options/time_selector.ts b/src/public/app/widgets/type_widgets/options/time_selector.ts
index d364cb095..1bd413022 100644
--- a/src/public/app/widgets/type_widgets/options/time_selector.ts
+++ b/src/public/app/widgets/type_widgets/options/time_selector.ts
@@ -9,19 +9,22 @@ type TimeSelectorConstructor = {
widgetLabelId: string;
optionValueId: keyof OptionDefinitions;
optionTimeScaleId: keyof OptionDefinitions;
+ includedTimeScales?: Set;
};
+type TimeSelectorScale = "seconds" | "minutes" | "hours" | "days";
-const TPL = (options: Pick) => `
+
+const TPL = (options: Omit) => `
@@ -42,6 +45,7 @@ export default class TimeSelector extends OptionsWidget {
private widgetLabelId: string;
private optionValueId: keyof OptionDefinitions;
private optionTimeScaleId: keyof OptionDefinitions;
+ private includedTimeScales: Set;
constructor(options: TimeSelectorConstructor) {
super();
@@ -49,12 +53,14 @@ export default class TimeSelector extends OptionsWidget {
this.widgetLabelId = options.widgetLabelId;
this.optionValueId = options.optionValueId;
this.optionTimeScaleId = options.optionTimeScaleId;
+ this.includedTimeScales = (!options.includedTimeScales) ? new Set(["seconds", "minutes", "hours", "days"]) : options.includedTimeScales;
}
doRender() {
this.$widget = $(TPL({
widgetId: this.widgetId,
- widgetLabelId: this.widgetLabelId
+ widgetLabelId: this.widgetLabelId,
+ includedTimeScales: this.includedTimeScales,
}));
this.$timeValueInput = this.$widget.find(`#${this.widgetId}`);
From 10ba4672020219ce526c1eec7c23bc83ac44f10f Mon Sep 17 00:00:00 2001
From: Panagiotis Papadopoulos
Date: Sun, 16 Feb 2025 18:13:52 +0100
Subject: [PATCH 06/24] chore(prettier): run prettier on time_selector related
files
---
.../other/attachment_erasure_timeout.ts | 6 ++--
.../options/other/note_erasure_timeout.ts | 8 ++---
.../type_widgets/options/time_selector.ts | 35 +++++++------------
3 files changed, 17 insertions(+), 32 deletions(-)
diff --git a/src/public/app/widgets/type_widgets/options/other/attachment_erasure_timeout.ts b/src/public/app/widgets/type_widgets/options/other/attachment_erasure_timeout.ts
index c384dce10..65cd61ce6 100644
--- a/src/public/app/widgets/type_widgets/options/other/attachment_erasure_timeout.ts
+++ b/src/public/app/widgets/type_widgets/options/other/attachment_erasure_timeout.ts
@@ -18,7 +18,6 @@ const TPL2 = `
`;
export default class AttachmentErasureTimeoutOptions extends TimeSelector {
-
private $eraseUnusedAttachmentsNowButton!: JQuery;
constructor() {
@@ -27,8 +26,8 @@ export default class AttachmentErasureTimeoutOptions extends TimeSelector {
widgetLabelId: "attachment_erasure_timeout.erase_attachments_after_x_seconds",
optionValueId: "eraseUnusedAttachmentsAfterSeconds",
optionTimeScaleId: "eraseUnusedAttachmentsAfterTimeScale"
- })
- super.doRender()
+ });
+ super.doRender();
}
doRender() {
@@ -41,5 +40,4 @@ export default class AttachmentErasureTimeoutOptions extends TimeSelector {
});
});
}
-
}
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 f06274c03..d71a2c370 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
@@ -17,22 +17,19 @@ const TPL2 = `
`;
export default class NoteErasureTimeoutOptions extends TimeSelector {
-
private $eraseDeletedNotesButton!: JQuery;
constructor() {
- super( {
+ super({
widgetId: "erase-entities-after",
widgetLabelId: "note_erasure_timeout.erase_notes_after",
optionValueId: "eraseEntitiesAfterTimeInSeconds",
optionTimeScaleId: "eraseEntitiesAfterTimeScale"
- })
+ });
super.doRender();
-
}
doRender() {
-
this.$widget = $(TPL).append(this.$widget).append(TPL2);
this.$eraseDeletedNotesButton = this.$widget.find("#erase-deleted-notes-now-button");
@@ -43,5 +40,4 @@ export default class NoteErasureTimeoutOptions extends TimeSelector {
});
});
}
-
}
diff --git a/src/public/app/widgets/type_widgets/options/time_selector.ts b/src/public/app/widgets/type_widgets/options/time_selector.ts
index 1bd413022..dc95b0a77 100644
--- a/src/public/app/widgets/type_widgets/options/time_selector.ts
+++ b/src/public/app/widgets/type_widgets/options/time_selector.ts
@@ -3,7 +3,6 @@ import toastService from "../../../services/toast.js";
import { t } from "../../../services/i18n.js";
import type { OptionDefinitions, OptionMap } from "../../../../../services/options_interface.js";
-
type TimeSelectorConstructor = {
widgetId: string;
widgetLabelId: string;
@@ -14,7 +13,6 @@ type TimeSelectorConstructor = {
type TimeSelectorScale = "seconds" | "minutes" | "hours" | "days";
-
const TPL = (options: Omit) => `
-
-
Installation
-
-
-
Desktop application
-
-
-
-
-
-
-
-
- Fedora
-
- |
-
-
-
-
-
Self-hosted server
-
-
-
diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Installation.html b/src/public/app/doc_notes/en/User Guide/User Guide/Installation.html
deleted file mode 100644
index 058f84feb..000000000
--- a/src/public/app/doc_notes/en/User Guide/User Guide/Installation.html
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
-