Skip to content

Commit

Permalink
feat(dev): refactor settings (#100)
Browse files Browse the repository at this point in the history
* feat(dev): refactor settings

* fix: use the new settings framework for default templates & misc

* fix: use the new settings framework for templates source

* fix: remove unnecessary code

* fix: generic

* fix: bug fix

* fix: failing unit test build
  • Loading branch information
nishantwrp committed Sep 29, 2024
1 parent 4a0b645 commit b1c80fc
Show file tree
Hide file tree
Showing 15 changed files with 272 additions and 109 deletions.
3 changes: 2 additions & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ module.exports = {
moduleFileExtensions: ["ts", "js", "json", "node"],
moduleNameMapper: {
"^@templates/(.*)$": "<rootDir>/src/$1",
"^api$": "<rootDir>/tests/mock-joplin-api.ts"
"^api$": "<rootDir>/tests/mock-joplin-api.ts",
"^api/types$": "<rootDir>/api/types.ts"
},
globalSetup: "./tests/jest-setup.js",
collectCoverageFrom: ["src/**/*.{js,ts}"]
Expand Down
3 changes: 2 additions & 1 deletion src/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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) {
Expand Down
83 changes: 20 additions & 63 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,79 +1,36 @@
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";
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,
Expand Down Expand Up @@ -102,7 +59,7 @@ joplin.plugins.register({
await performActionWithParsedTemplate(action, template);
}

const getNotebookDefaultTemplatesDisplayData = async (settings: DefaultTemplatesConfigSetting): Promise<NotebookDefaultTemplatesDisplayData[]> => {
const getNotebookDefaultTemplatesDisplayData = async (settings: DefaultTemplatesConfig): Promise<NotebookDefaultTemplatesDisplayData[]> => {
const getDisplayDataForNotebook = async (notebookId: string, defaultTemplateNoteId: string | null, defaultTemplateTodoId: string | null): Promise<NotebookDefaultTemplatesDisplayData | null> => {
const promiseGroup = new PromiseGroup();
promiseGroup.set("notebook", getFolderFromId(notebookId));
Expand All @@ -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 {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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!`);
}
}));
Expand All @@ -228,15 +185,15 @@ 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) {
defaultTemplate = await getTemplateFromId(defaultTemplatesConfig[currentFolderId].defaultNoteTemplateId);
}

if (defaultTemplate === null) {
defaultTemplate = await getTemplateFromId(await joplin.settings.value("defaultNoteTemplateId"));
defaultTemplate = await getTemplateFromId(await DefaultNoteTemplateIdSetting.get());
}

if (defaultTemplate) {
Expand All @@ -252,15 +209,15 @@ 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) {
defaultTemplate = await getTemplateFromId(defaultTemplatesConfig[currentFolderId].defaultTodoTemplateId);
}

if (defaultTemplate === null) {
defaultTemplate = await getTemplateFromId(await joplin.settings.value("defaultTodoTemplateId"));
defaultTemplate = await getTemplateFromId(await DefaultTodoTemplateIdSetting.get());
}

if (defaultTemplate) {
Expand Down
11 changes: 11 additions & 0 deletions src/settings/applyTagsWhileInserting.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { SettingItemType } from "api/types";
import { createSimpleSetting } from "./base";

export const ApplyTagsWhileInsertingSetting = createSimpleSetting<boolean>("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"
});
46 changes: 46 additions & 0 deletions src/settings/base.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import joplin from "api";
import { SettingItem } from "api/types";

export interface PluginSetting<T> {
id: string;
manifest: SettingItem;

get(): Promise<T>;
set(newValue: T): Promise<void>;
}

export const createSimpleSetting = <T>(id: string, manifest: SettingItem): PluginSetting<T> => {
return class {
static id = id;
static manifest = manifest;

static async get(): Promise<T> {
return await joplin.settings.value(id);
}

static async set(newValue: T): Promise<void> {
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 = <T>(id: string, manifest: SettingItem, valueMap: Record<string, T>, defaultValue: T): PluginSetting<T> => {
return class {
static id = id;
static manifest = manifest;

static async get(): Promise<T> {
const value: string = await joplin.settings.value(id);
return value in valueMap ? valueMap[value] : defaultValue;
}

static async set(newValue: T): Promise<void> {
const potentialValues = Object.entries(valueMap).filter((entry) => entry[1] === newValue);
await joplin.settings.setValue(id, potentialValues.length ? potentialValues[0][0] : "" );
}
}
}
9 changes: 9 additions & 0 deletions src/settings/defaultNoteTemplateId.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { SettingItemType } from "api/types";
import { createSimpleSetting } from "./base";

export const DefaultNoteTemplateIdSetting = createSimpleSetting<string | null>("defaultNoteTemplateId", {
public: false,
type: SettingItemType.String,
value: null,
label: "Default note template ID"
});
71 changes: 71 additions & 0 deletions src/settings/defaultTemplatesConfig.ts
Original file line number Diff line number Diff line change
@@ -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<DefaultTemplatesConfig> {
const defaultTemplatesConfig: DefaultTemplatesConfig | null = await joplin.settings.value(DefaultTemplatesConfigSetting.id);
return defaultTemplatesConfig ? defaultTemplatesConfig : {};
}

static async set(newValue: DefaultTemplatesConfig): Promise<void> {
await joplin.settings.setValue(DefaultTemplatesConfigSetting.id, newValue);
}

static async setDefaultTempalte(notebookId: string, templateId: string, defaultType: DefaultType): Promise<void> {
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<void> {
const defaultTemplatesConfig = await this.get();
delete defaultTemplatesConfig[notebookId];
await this.set(defaultTemplatesConfig);
}
}
9 changes: 9 additions & 0 deletions src/settings/defaultTodoTemplateId.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { SettingItemType } from "api/types";
import { createSimpleSetting } from "./base";

export const DefaultTodoTemplateIdSetting = createSimpleSetting<string | null>("defaultTodoTemplateId", {
public: false,
type: SettingItemType.String,
value: null,
label: "Default to-do template ID"
});
16 changes: 16 additions & 0 deletions src/settings/global/base.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import joplin from "api";

export interface GlobalSetting<T> {
id: string;
get(): Promise<T>;
}

export const createGlobalSetting = <T>(id: string): GlobalSetting<T> => {
return class {
static id = id;

static async get(): Promise<T> {
return await joplin.settings.globalValue(id);
}
}
}
9 changes: 9 additions & 0 deletions src/settings/global/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { createGlobalSetting } from "./base";

export const LocaleGlobalSetting = createGlobalSetting<string>("locale");

export const DateFormatGlobalSetting = createGlobalSetting<string>("dateFormat");

export const TimeFormatGlobalSetting = createGlobalSetting<string>("timeFormat");

export const ProfileDirGlobalSetting = createGlobalSetting<string>("profileDir");
9 changes: 9 additions & 0 deletions src/settings/index.ts
Original file line number Diff line number Diff line change
@@ -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";
Loading

0 comments on commit b1c80fc

Please sign in to comment.