diff --git a/jest.config.js b/jest.config.js index a084558..861a582 100644 --- a/jest.config.js +++ b/jest.config.js @@ -5,7 +5,8 @@ module.exports = { moduleFileExtensions: ["ts", "js", "json", "node"], moduleNameMapper: { "^@templates/(.*)$": "/src/$1", - "^api$": "/tests/mock-joplin-api.ts" + "^api$": "/tests/mock-joplin-api.ts", + "^api/types$": "/api/types.ts" }, globalSetup: "./tests/jest-setup.js", collectCoverageFrom: ["src/**/*.{js,ts}"] diff --git a/src/actions.ts b/src/actions.ts index 2064ee7..975619d 100644 --- a/src/actions.ts +++ b/src/actions.ts @@ -2,6 +2,7 @@ import joplin from "api"; import { NewNote } from "./parser"; import { getSelectedFolder } from "./utils/folders"; import { applyTagToNote, getAnyTagWithTitle } from "./utils/tags"; +import { ApplyTagsWhileInsertingSetting } from "./settings"; export enum TemplateAction { NewNote = "newNote", @@ -12,7 +13,7 @@ export enum TemplateAction { const performInsertTextAction = async (template: NewNote) => { await joplin.commands.execute("insertText", template.body); - const applyTags = await joplin.settings.value("applyTagsWhileInserting") + const applyTags = await ApplyTagsWhileInsertingSetting.get() if (applyTags) { const noteId = (await joplin.workspace.selectedNote()).id; for (const tag of template.tags) { diff --git a/src/index.ts b/src/index.ts index 14019ac..259a4a3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,9 +1,9 @@ import joplin from "api"; -import { MenuItemLocation, SettingItemType } from "api/types"; +import { MenuItemLocation } from "api/types"; import { Parser } from "./parser"; import { DateAndTimeUtils } from "./utils/dateAndTime"; import { getFolderFromId, getSelectedFolder, getUserFolderSelection, Folder } from "./utils/folders"; -import { clearDefaultTemplates, getNotebookDefaultTemplatesConfig, getUserDefaultTemplateTypeSelection, setDefaultTemplate, DefaultTemplatesConfigSetting } from "./utils/defaultTemplates"; +import { getUserDefaultTemplateTypeSelection, setDefaultTemplate } from "./utils/defaultTemplates"; import { getTemplateFromId, getUserTemplateSelection, Note } from "./utils/templates"; import { setDefaultTemplatesView, DefaultTemplatesDisplayData, NotebookDefaultTemplatesDisplayData } from "./views/defaultTemplates"; import { TemplateAction, performAction } from "./actions"; @@ -11,69 +11,26 @@ import { loadLegacyTemplates } from "./legacyTemplates"; import * as open from "open"; import { Logger } from "./logger"; import { PromiseGroup } from "./utils/promises"; +import { PluginSettingsRegistry, DefaultNoteTemplateIdSetting, DefaultTodoTemplateIdSetting, DefaultTemplatesConfigSetting } from "./settings"; +import { LocaleGlobalSetting, DateFormatGlobalSetting, TimeFormatGlobalSetting, ProfileDirGlobalSetting } from "./settings/global"; +import { DefaultTemplatesConfig } from "./settings/defaultTemplatesConfig"; const DOCUMENTATION_URL = "https://github.com/joplin/plugin-templates#readme"; joplin.plugins.register({ onStart: async function() { // Register setting section - await joplin.settings.registerSection("templatesPlugin", { - label: "Templates", - }); - - - // Register all settings - await joplin.settings.registerSettings({ - "defaultNoteTemplateId": { - public: false, - type: SettingItemType.String, - value: null, - label: "Default note template ID" - }, - "defaultTodoTemplateId": { - public: false, - type: SettingItemType.String, - value: null, - label: "Default to-do template ID" - }, - "defaultTemplatesConfig": { - public: false, - type: SettingItemType.Object, - value: null, - label: "Default templates config" - }, - "applyTagsWhileInserting": { - public: true, - type: SettingItemType.Bool, - value: true, - label: "Apply tags while inserting template", - description: "Apply tags using 'template_tags' variable while inserting template to notes/to-dos.", - section: "templatesPlugin" - }, - "templatesSource": { - public: true, - type: SettingItemType.String, - isEnum: true, - value: "tag", - options: { - "tag": "Tag", - "notebook": "Notebook" - }, - label: "Are templates set with tags or stored in a notebook?", - description: "If set to 'Tag', any note/to-do with a 'template' tag is considered a template. If set to 'Notebook', any note/todo stored in a notebook titled 'Templates' is considered a template.", - section: "templatesPlugin" - }, - }); + await PluginSettingsRegistry.registerSettings(); // Global variables const joplinGlobalApis = new PromiseGroup(); joplinGlobalApis.set("dialogViewHandle", joplin.views.dialogs.create("dialog")); - joplinGlobalApis.set("userLocale", joplin.settings.globalValue("locale")); - joplinGlobalApis.set("userDateFormat", joplin.settings.globalValue("dateFormat")); - joplinGlobalApis.set("userTimeFormat", joplin.settings.globalValue("timeFormat")); - joplinGlobalApis.set("profileDir", joplin.settings.globalValue("profileDir")); + joplinGlobalApis.set("userLocale", LocaleGlobalSetting.get()); + joplinGlobalApis.set("userDateFormat", DateFormatGlobalSetting.get()); + joplinGlobalApis.set("userTimeFormat", TimeFormatGlobalSetting.get()); + joplinGlobalApis.set("profileDir", ProfileDirGlobalSetting.get()); const { dialogViewHandle, userLocale, userDateFormat, @@ -102,7 +59,7 @@ joplin.plugins.register({ await performActionWithParsedTemplate(action, template); } - const getNotebookDefaultTemplatesDisplayData = async (settings: DefaultTemplatesConfigSetting): Promise => { + const getNotebookDefaultTemplatesDisplayData = async (settings: DefaultTemplatesConfig): Promise => { const getDisplayDataForNotebook = async (notebookId: string, defaultTemplateNoteId: string | null, defaultTemplateTodoId: string | null): Promise => { const promiseGroup = new PromiseGroup(); promiseGroup.set("notebook", getFolderFromId(notebookId)); @@ -112,7 +69,7 @@ joplin.plugins.register({ if (notebook === null || (noteTemplate === null && todoTemplate === null)) { // Async remove of the obsolete config - clearDefaultTemplates(notebookId); + DefaultTemplatesConfigSetting.clearDefaultTemplates(notebookId); return null; } return { @@ -162,9 +119,9 @@ joplin.plugins.register({ name: "showDefaultTemplates", label: "Show default templates", execute: async () => { - const noteTemplate = await getTemplateFromId(await joplin.settings.value("defaultNoteTemplateId")); - const todoTemplate = await getTemplateFromId(await joplin.settings.value("defaultTodoTemplateId")); - const defaultTemplatesConfig = await getNotebookDefaultTemplatesConfig(); + const noteTemplate = await getTemplateFromId(await DefaultNoteTemplateIdSetting.get()); + const todoTemplate = await getTemplateFromId(await DefaultTodoTemplateIdSetting.get()); + const defaultTemplatesConfig = await DefaultTemplatesConfigSetting.get(); const globalDefaultTemplates: DefaultTemplatesDisplayData = { defaultNoteTemplateTitle: noteTemplate ? noteTemplate.title : null, @@ -217,7 +174,7 @@ joplin.plugins.register({ const folder: Folder | null = JSON.parse(await getUserFolderSelection() || "null"); if (folder === null) return; - await clearDefaultTemplates(folder.id); + await DefaultTemplatesConfigSetting.clearDefaultTemplates(folder.id); await joplin.views.dialogs.showMessageBox(`Default templates for "${folder.title}" cleared successfully!`); } })); @@ -228,7 +185,7 @@ joplin.plugins.register({ execute: async () => { let defaultTemplate: Note | null = null; - const defaultTemplatesConfig = await getNotebookDefaultTemplatesConfig(); + const defaultTemplatesConfig = await DefaultTemplatesConfigSetting.get(); const currentFolderId = await getSelectedFolder(); if (currentFolderId in defaultTemplatesConfig) { @@ -236,7 +193,7 @@ joplin.plugins.register({ } if (defaultTemplate === null) { - defaultTemplate = await getTemplateFromId(await joplin.settings.value("defaultNoteTemplateId")); + defaultTemplate = await getTemplateFromId(await DefaultNoteTemplateIdSetting.get()); } if (defaultTemplate) { @@ -252,7 +209,7 @@ joplin.plugins.register({ execute: async () => { let defaultTemplate: Note | null = null; - const defaultTemplatesConfig = await getNotebookDefaultTemplatesConfig(); + const defaultTemplatesConfig = await DefaultTemplatesConfigSetting.get(); const currentFolderId = await getSelectedFolder(); if (currentFolderId in defaultTemplatesConfig) { @@ -260,7 +217,7 @@ joplin.plugins.register({ } if (defaultTemplate === null) { - defaultTemplate = await getTemplateFromId(await joplin.settings.value("defaultTodoTemplateId")); + defaultTemplate = await getTemplateFromId(await DefaultTodoTemplateIdSetting.get()); } if (defaultTemplate) { diff --git a/src/settings/applyTagsWhileInserting.ts b/src/settings/applyTagsWhileInserting.ts new file mode 100644 index 0000000..90ec00c --- /dev/null +++ b/src/settings/applyTagsWhileInserting.ts @@ -0,0 +1,11 @@ +import { SettingItemType } from "api/types"; +import { createSimpleSetting } from "./base"; + +export const ApplyTagsWhileInsertingSetting = createSimpleSetting("applyTagsWhileInserting", { + public: true, + type: SettingItemType.Bool, + value: true, + label: "Apply tags while inserting template", + description: "Apply tags using 'template_tags' variable while inserting template to notes/to-dos.", + section: "templatesPlugin" +}); diff --git a/src/settings/base.ts b/src/settings/base.ts new file mode 100644 index 0000000..3ed0b32 --- /dev/null +++ b/src/settings/base.ts @@ -0,0 +1,46 @@ +import joplin from "api"; +import { SettingItem } from "api/types"; + +export interface PluginSetting { + id: string; + manifest: SettingItem; + + get(): Promise; + set(newValue: T): Promise; +} + +export const createSimpleSetting = (id: string, manifest: SettingItem): PluginSetting => { + return class { + static id = id; + static manifest = manifest; + + static async get(): Promise { + return await joplin.settings.value(id); + } + + static async set(newValue: T): Promise { + await joplin.settings.setValue(id, newValue); + } + } +} + +/** + * This considers that the original setting is of type `string`. On `set` if no original value + * can be traced back, the setting is set to an empty string. + */ +export const createMappedSetting = (id: string, manifest: SettingItem, valueMap: Record, defaultValue: T): PluginSetting => { + return class { + static id = id; + static manifest = manifest; + + static async get(): Promise { + const value: string = await joplin.settings.value(id); + return value in valueMap ? valueMap[value] : defaultValue; + } + + static async set(newValue: T): Promise { + const potentialValues = Object.entries(valueMap).filter((entry) => entry[1] === newValue); + await joplin.settings.setValue(id, potentialValues.length ? potentialValues[0][0] : "" ); + } + } +} diff --git a/src/settings/defaultNoteTemplateId.ts b/src/settings/defaultNoteTemplateId.ts new file mode 100644 index 0000000..0409465 --- /dev/null +++ b/src/settings/defaultNoteTemplateId.ts @@ -0,0 +1,9 @@ +import { SettingItemType } from "api/types"; +import { createSimpleSetting } from "./base"; + +export const DefaultNoteTemplateIdSetting = createSimpleSetting("defaultNoteTemplateId", { + public: false, + type: SettingItemType.String, + value: null, + label: "Default note template ID" +}); diff --git a/src/settings/defaultTemplatesConfig.ts b/src/settings/defaultTemplatesConfig.ts new file mode 100644 index 0000000..7e7b1d8 --- /dev/null +++ b/src/settings/defaultTemplatesConfig.ts @@ -0,0 +1,71 @@ +import joplin from "api"; +import { SettingItemType } from "api/types"; + +export interface DefaultTemplatesConfig { + [notebookId: string]: { + defaultNoteTemplateId: string | null; + defaultTodoTemplateId: string | null; + } +} + +enum DefaultType { + Both, + Note, + Todo, +} + +export const DefaultTemplatesConfigSetting = class { + static readonly DefaultType = DefaultType; + + static id = "defaultTemplatesConfig"; + + static manifest = { + public: false, + type: SettingItemType.Object, + value: null, + label: "Default templates config" + }; + + static async get(): Promise { + const defaultTemplatesConfig: DefaultTemplatesConfig | null = await joplin.settings.value(DefaultTemplatesConfigSetting.id); + return defaultTemplatesConfig ? defaultTemplatesConfig : {}; + } + + static async set(newValue: DefaultTemplatesConfig): Promise { + await joplin.settings.setValue(DefaultTemplatesConfigSetting.id, newValue); + } + + static async setDefaultTempalte(notebookId: string, templateId: string, defaultType: DefaultType): Promise { + const defaultTemplatesConfig = await this.get(); + + if (!(notebookId in defaultTemplatesConfig)) { + defaultTemplatesConfig[notebookId] = { + defaultNoteTemplateId: null, + defaultTodoTemplateId: null + }; + } + + switch (defaultType) { + case DefaultType.Note: + defaultTemplatesConfig[notebookId].defaultNoteTemplateId = templateId; + break; + case DefaultType.Todo: + defaultTemplatesConfig[notebookId].defaultTodoTemplateId = templateId; + break; + case DefaultType.Both: + defaultTemplatesConfig[notebookId].defaultNoteTemplateId = templateId; + defaultTemplatesConfig[notebookId].defaultTodoTemplateId = templateId; + break; + default: + break; + } + + await this.set(defaultTemplatesConfig); + } + + static async clearDefaultTemplates(notebookId: string): Promise { + const defaultTemplatesConfig = await this.get(); + delete defaultTemplatesConfig[notebookId]; + await this.set(defaultTemplatesConfig); + } +} diff --git a/src/settings/defaultTodoTemplateId.ts b/src/settings/defaultTodoTemplateId.ts new file mode 100644 index 0000000..3c6bfcc --- /dev/null +++ b/src/settings/defaultTodoTemplateId.ts @@ -0,0 +1,9 @@ +import { SettingItemType } from "api/types"; +import { createSimpleSetting } from "./base"; + +export const DefaultTodoTemplateIdSetting = createSimpleSetting("defaultTodoTemplateId", { + public: false, + type: SettingItemType.String, + value: null, + label: "Default to-do template ID" +}); diff --git a/src/settings/global/base.ts b/src/settings/global/base.ts new file mode 100644 index 0000000..19c4584 --- /dev/null +++ b/src/settings/global/base.ts @@ -0,0 +1,16 @@ +import joplin from "api"; + +export interface GlobalSetting { + id: string; + get(): Promise; +} + +export const createGlobalSetting = (id: string): GlobalSetting => { + return class { + static id = id; + + static async get(): Promise { + return await joplin.settings.globalValue(id); + } + } +} diff --git a/src/settings/global/index.ts b/src/settings/global/index.ts new file mode 100644 index 0000000..14ab0c8 --- /dev/null +++ b/src/settings/global/index.ts @@ -0,0 +1,9 @@ +import { createGlobalSetting } from "./base"; + +export const LocaleGlobalSetting = createGlobalSetting("locale"); + +export const DateFormatGlobalSetting = createGlobalSetting("dateFormat"); + +export const TimeFormatGlobalSetting = createGlobalSetting("timeFormat"); + +export const ProfileDirGlobalSetting = createGlobalSetting("profileDir"); diff --git a/src/settings/index.ts b/src/settings/index.ts new file mode 100644 index 0000000..9735b75 --- /dev/null +++ b/src/settings/index.ts @@ -0,0 +1,9 @@ +// Export all individual settings +export { ApplyTagsWhileInsertingSetting } from "./applyTagsWhileInserting"; +export { DefaultNoteTemplateIdSetting } from "./defaultNoteTemplateId"; +export { DefaultTemplatesConfigSetting } from "./defaultTemplatesConfig"; +export { DefaultTodoTemplateIdSetting } from "./defaultTodoTemplateId"; +export { TemplatesSourceSetting } from "./templatesSource"; + +// Export registry +export { PluginSettingsRegistry } from "./registry"; diff --git a/src/settings/registry.ts b/src/settings/registry.ts new file mode 100644 index 0000000..f465be6 --- /dev/null +++ b/src/settings/registry.ts @@ -0,0 +1,34 @@ +import joplin from "api"; +import { SettingItem } from "api/types"; + +import { ApplyTagsWhileInsertingSetting } from "./applyTagsWhileInserting"; +import { DefaultNoteTemplateIdSetting } from "./defaultNoteTemplateId"; +import { DefaultTemplatesConfigSetting } from "./defaultTemplatesConfig"; +import { DefaultTodoTemplateIdSetting } from "./defaultTodoTemplateId"; +import { TemplatesSourceSetting } from "./templatesSource"; + +import { PluginSetting } from "./base"; + +export class PluginSettingsRegistry { + private static allPluginSettings: PluginSetting[] = [ + ApplyTagsWhileInsertingSetting, + DefaultNoteTemplateIdSetting, + DefaultTemplatesConfigSetting, + DefaultTodoTemplateIdSetting, + TemplatesSourceSetting, + ]; + + public static async registerSettings(): Promise { + // Register setting section + await joplin.settings.registerSection("templatesPlugin", { + label: "Templates", + }); + + // Register all settings + const settingsManifest: Record = this.allPluginSettings.reduce((manifest, setting) => { + manifest[setting.id] = setting.manifest; + return manifest; + }, {}); + await joplin.settings.registerSettings(settingsManifest); + } +} diff --git a/src/settings/templatesSource.ts b/src/settings/templatesSource.ts new file mode 100644 index 0000000..de0077d --- /dev/null +++ b/src/settings/templatesSource.ts @@ -0,0 +1,21 @@ +import { SettingItemType } from "api/types"; +import { createMappedSetting } from "./base"; + +export enum TemplatesSource { + Tag, + Notebook, +} + +export const TemplatesSourceSetting = createMappedSetting("templatesSource", { + public: true, + type: SettingItemType.String, + isEnum: true, + value: "tag", + options: { + "tag": "Tag", + "notebook": "Notebook" + }, + label: "Are templates set with tags or stored in a notebook?", + description: "If set to 'Tag', any note/to-do with a 'template' tag is considered a template. If set to 'Notebook', any note/todo stored in a notebook titled 'Templates' is considered a template.", + section: "templatesPlugin" +}, { "tag": TemplatesSource.Tag, "notebook": TemplatesSource.Notebook }, TemplatesSource.Tag); diff --git a/src/utils/defaultTemplates.ts b/src/utils/defaultTemplates.ts index d82a24d..05887af 100644 --- a/src/utils/defaultTemplates.ts +++ b/src/utils/defaultTemplates.ts @@ -1,11 +1,5 @@ import joplin from "api"; - -export interface DefaultTemplatesConfigSetting { - [notebookId: string]: { - defaultNoteTemplateId: string | null; - defaultTodoTemplateId: string | null; - } -} +import { DefaultTemplatesConfigSetting, DefaultNoteTemplateIdSetting, DefaultTodoTemplateIdSetting } from "../settings"; export enum DefaultTemplateType { Both, @@ -45,57 +39,30 @@ export const setDefaultTemplate = async (notebookId: string | null, templateId: // Global default switch (defaultType) { case DefaultTemplateType.Note: - await joplin.settings.setValue("defaultNoteTemplateId", templateId); + await DefaultNoteTemplateIdSetting.set(templateId); break; case DefaultTemplateType.Todo: - await joplin.settings.setValue("defaultTodoTemplateId", templateId); + await DefaultTodoTemplateIdSetting.set(templateId); break; case DefaultTemplateType.Both: - await joplin.settings.setValue("defaultNoteTemplateId", templateId); - await joplin.settings.setValue("defaultTodoTemplateId", templateId); + await DefaultNoteTemplateIdSetting.set(templateId); + await DefaultTodoTemplateIdSetting.set(templateId); break; default: break; } } else { // Notebook specific default - let defaultTemplatesConfig: DefaultTemplatesConfigSetting | null = await joplin.settings.value("defaultTemplatesConfig"); - if (defaultTemplatesConfig === null) defaultTemplatesConfig = {}; - - if (!(notebookId in defaultTemplatesConfig)) { - defaultTemplatesConfig[notebookId] = { - defaultNoteTemplateId: null, - defaultTodoTemplateId: null - }; - } - switch (defaultType) { case DefaultTemplateType.Note: - defaultTemplatesConfig[notebookId].defaultNoteTemplateId = templateId; + await DefaultTemplatesConfigSetting.setDefaultTempalte(notebookId, templateId, DefaultTemplatesConfigSetting.DefaultType.Note); break; case DefaultTemplateType.Todo: - defaultTemplatesConfig[notebookId].defaultTodoTemplateId = templateId; + await DefaultTemplatesConfigSetting.setDefaultTempalte(notebookId, templateId, DefaultTemplatesConfigSetting.DefaultType.Todo); break; case DefaultTemplateType.Both: - defaultTemplatesConfig[notebookId].defaultNoteTemplateId = templateId; - defaultTemplatesConfig[notebookId].defaultTodoTemplateId = templateId; - break; - default: + await DefaultTemplatesConfigSetting.setDefaultTempalte(notebookId, templateId, DefaultTemplatesConfigSetting.DefaultType.Both); break; } - - await joplin.settings.setValue("defaultTemplatesConfig", defaultTemplatesConfig); } } - -export const getNotebookDefaultTemplatesConfig = async (): Promise => { - let defaultTemplatesConfig: DefaultTemplatesConfigSetting | null = await joplin.settings.value("defaultTemplatesConfig"); - if (defaultTemplatesConfig === null) defaultTemplatesConfig = {}; - return defaultTemplatesConfig; -} - -export const clearDefaultTemplates = async (notebookId: string): Promise => { - const defaultTemplatesConfig = await getNotebookDefaultTemplatesConfig(); - delete defaultTemplatesConfig[notebookId]; - await joplin.settings.setValue("defaultTemplatesConfig", defaultTemplatesConfig); -} diff --git a/src/utils/templates.ts b/src/utils/templates.ts index ce20cae..0bbb270 100644 --- a/src/utils/templates.ts +++ b/src/utils/templates.ts @@ -1,6 +1,8 @@ import joplin from "api"; import { getAllNotesInFolder } from "./folders"; import { getAllNotesWithTag, getAllTagsWithTitle } from "./tags"; +import { TemplatesSourceSetting, TemplatesSource } from "../settings/templatesSource"; +import { LocaleGlobalSetting } from "../settings/global"; export interface Note { id: string; @@ -27,9 +29,9 @@ const removeDuplicateTemplates = (templates: Note[]) => { const getAllTemplates = async () => { let templates: Note[] = []; - const templatesSource = await joplin.settings.value("templatesSource"); + const templatesSource = await TemplatesSourceSetting.get(); - if (templatesSource == "tag"){ + if (templatesSource == TemplatesSource.Tag) { const templateTags = await getAllTagsWithTitle("template"); for (const tag of templateTags) { @@ -41,7 +43,7 @@ const getAllTemplates = async () => { templates = removeDuplicateTemplates(templates); - let userLocale: string = await joplin.settings.globalValue("locale"); + let userLocale: string = await LocaleGlobalSetting.get(); userLocale = userLocale.split("_").join("-"); templates.sort((a, b) => {