diff --git a/main.ts b/main.ts index 96b592a..fa6f11a 100644 --- a/main.ts +++ b/main.ts @@ -1,5 +1,5 @@ import {Notice, Plugin} from 'obsidian'; -import {DEFAULT_SETTINGS, SettingTab, VikunjaPluginSettings} from "./src/settings/settingTab"; +import {DEFAULT_SETTINGS, MainSetting, VikunjaPluginSettings} from "./src/settings/mainSetting"; import {Tasks} from "./src/vikunja/tasks"; import {Processor} from "./src/processing/processor"; import {UserUser} from "./vikunja_sdk"; @@ -63,7 +63,7 @@ export default class VikunjaPlugin extends Plugin { this.setupCommands(); // This adds a settings tab so the user can configure various aspects of the plugin - this.addSettingTab(new SettingTab(this.app, this)); + this.addSettingTab(new MainSetting(this.app, this)); this.registerDomEvent(document, 'keyup', this.handleUpDownEvent.bind(this)); this.registerDomEvent(document, 'click', this.handleClickEvent.bind(this)); diff --git a/src/modals/mainModal.ts b/src/modals/mainModal.ts deleted file mode 100644 index 9666447..0000000 --- a/src/modals/mainModal.ts +++ /dev/null @@ -1,17 +0,0 @@ -import {App, Modal} from "obsidian"; - -export class MainModal extends Modal { - constructor(app: App) { - super(app); - } - - onOpen() { - const {contentEl} = this; - contentEl.setText('Woah!'); - } - - onClose() { - const {contentEl} = this; - contentEl.empty(); - } -} diff --git a/src/processing/syncLabels.ts b/src/processing/syncLabels.ts index 88f550d..ce9f475 100644 --- a/src/processing/syncLabels.ts +++ b/src/processing/syncLabels.ts @@ -17,7 +17,6 @@ class SyncLabels implements IAutomatonSteps { await this.removeLabelsInVikunjaIfNotInVault(localTasks, vikunjaTasks); const localTasksWithLabels: PluginTask[] = await this.createLabels(localTasks); - // FIXME there is still a bug with previously added labels... something is empty in devlog for (const task of localTasksWithLabels) { if (!task.task) throw new Error("Task is not defined"); if (!task.task.id) throw new Error("Task id is not defined"); @@ -26,11 +25,7 @@ class SyncLabels implements IAutomatonSteps { for (const label of task.task.labels) { if (this.plugin.settings.debugging) console.log("Step SyncLabels: Adding label to task ", taskId, " in Vikunja", label); - try { - await this.plugin.labelsApi.addLabelToTask(taskId, label); - } catch (e) { - console.error("Error adding label to task", e); - } + await this.plugin.labelsApi.addLabelToTask(taskId, label); } } @@ -43,16 +38,19 @@ class SyncLabels implements IAutomatonSteps { if (this.plugin.settings.debugging) console.log("Step SyncLabels: Not deleting labels in vikunja if ID not found in vault"); return; } + // FIXME This call will be placed everytime for every task. It should be cached or optimized away. const allLabels = await this.plugin.labelsApi.getLabels(); const usedLabels = localTasks.flatMap(task => task.task.labels ?? []); + let foundLabels = new Map(); for (const label of allLabels) { - if (usedLabels.find(usedLabel => usedLabel.title === label.title)) { + if (!label.id) throw new Error("Label id is not defined"); + if (usedLabels.find(usedLabel => usedLabel.title === label.title) && foundLabels.get(label.id) === undefined) { + foundLabels.set(label.id, label); continue; } if (this.plugin.settings.debugging) console.log("Step SyncLabels: Deleting label in vikunja", label); - if (!label.id) throw new Error("Label id is not defined"); await this.plugin.labelsApi.deleteLabel(label.id); } } @@ -64,7 +62,7 @@ class SyncLabels implements IAutomatonSteps { if (!task.task) throw new Error("Task is not defined"); if (!task.task.labels) return task; - task.task.labels = await this.plugin.labelsApi.getAndCreateLabels(task.task.labels); + task.task.labels = await this.plugin.labelsApi.getOrCreateLabels(task.task.labels); if (this.plugin.settings.debugging) console.log("Step SyncLabels: Preparing labels for local tasks for vikunja update", task); return task; } diff --git a/src/settings/settingTab.ts b/src/settings/mainSetting.ts similarity index 94% rename from src/settings/settingTab.ts rename to src/settings/mainSetting.ts index 937bf2d..3dce633 100644 --- a/src/settings/settingTab.ts +++ b/src/settings/mainSetting.ts @@ -25,7 +25,8 @@ export interface VikunjaPluginSettings { enableCron: boolean, cronInterval: number, updateOnStartup: boolean, - updateOnCursorMovement: boolean + updateOnCursorMovement: boolean, + pullTasksOnlyFromDefaultProject: boolean } export const DEFAULT_SETTINGS: VikunjaPluginSettings = { @@ -48,10 +49,11 @@ export const DEFAULT_SETTINGS: VikunjaPluginSettings = { enableCron: false, cronInterval: 500, updateOnStartup: false, - updateOnCursorMovement: false + updateOnCursorMovement: false, + pullTasksOnlyFromDefaultProject: false } -export class SettingTab extends PluginSettingTab { +export class MainSetting extends PluginSettingTab { plugin: VikunjaPlugin; projectsApi: Projects; projects: ModelsProject[] = []; @@ -409,12 +411,44 @@ export class SettingTab extends PluginSettingTab { } if (this.projects.length === 0) { - new Setting(containerEl).setName("Loading projects...").setDesc("Please wait until the projects are loaded. If this message still there after a few seconds, please check your Vikunja Host and Access Token. Enable debugging and check the console for more information."); + new Setting(containerEl) + .setName("Loading projects...") + .setDesc("Please wait until the projects are loaded. If this message still there after a few seconds, please check your Vikunja Host and Access Token. Enable debugging and check the console for more information."); this.loadApi().then(_r => { }); return; } + + new Setting(containerEl) + .setHeading() + .setName("Updates: Obsidian <-> Vikunja") + + new Setting(containerEl) + .setDesc("This plugin prioritizes changes in Obsidian over Vikunja. This means, that if you make changes in both systems, the changes in Obsidian will be used over the one in Vikunja. To prevent data loss, do not make any changes in your markdown files without Obsidian."); + + new Setting(containerEl) + .setName("Check for updates on startup") + .setDesc("This will check for changes in Vault and Vikunja and update the tasks vice versa, but prioritize the changes in Obsidian. Useful, if you want to use Vikunja, but do not make any changes directly on the markdown files while obsidian is closed.") + .addToggle(toggle => + toggle + .setValue(this.plugin.settings.updateOnStartup) + .onChange(async (value: boolean) => { + this.plugin.settings.updateOnStartup = value; + await this.plugin.saveSettings(); + })); + + new Setting(containerEl) + .setName("Check for updates on cursor movement") + .setDesc("This will check for changes only on cursors last line in Vault. Useful, if you want to reduce the load on your system and faster updates.") + .addToggle(toggle => + toggle + .setValue(this.plugin.settings.updateOnCursorMovement) + .onChange(async (value: boolean) => { + this.plugin.settings.updateOnCursorMovement = value; + await this.plugin.saveSettings(); + })); + new Setting(containerEl) .setName("Select default project") .setDesc("This project will be used to place new tasks created by this plugin.") @@ -439,46 +473,27 @@ export class SettingTab extends PluginSettingTab { }); } ) - new Setting(containerEl) - .setName("Move all tasks to selected default project") - .setDesc("This will move all tasks from Vault to the selected default project in Vikunja. This will not delete any tasks in Vikunja, but only move them to the selected project. This helps, if you make a wrong decision in the past. This does not create any tasks in Vikunja.") - .addButton(button => button - .setButtonText("Move all tasks") - .onClick(async () => { - await this.plugin.commands.moveAllTasksToDefaultProject(); - } - )); - - - new Setting(containerEl) - .setHeading() - .setName("Updates: Obsidian <-> Vikunja") new Setting(containerEl) - .setDesc("This plugin prioritizes changes in Obsidian over Vikunja. This means, that if you make changes in both systems, the changes in Obsidian will be used over the one in Vikunja. To prevent data loss, do not make any changes in your markdown files without Obsidian."); - - new Setting(containerEl) - .setName("Check for updates on startup") - .setDesc("This will check for changes in Vault and Vikunja and update the tasks vice versa, but prioritize the changes in Obsidian. Useful, if you want to use Vikunja, but do not make any changes directly on the markdown files while obsidian is closed.") + .setName("Pull tasks only from default project") + .setDesc("If enabled, only tasks from the default project will be pulled from Vikunja. Useful, if you use Vikunja with several apps or different projects and Obsidian is only one of them. Beware: If you select that labels should be deleted in vikunja, if not found in vault, this will sync all labels regardless of projects.") .addToggle(toggle => toggle - .setValue(this.plugin.settings.updateOnStartup) + .setValue(this.plugin.settings.pullTasksOnlyFromDefaultProject) .onChange(async (value: boolean) => { - this.plugin.settings.updateOnStartup = value; + this.plugin.settings.pullTasksOnlyFromDefaultProject = value; await this.plugin.saveSettings(); })); new Setting(containerEl) - .setName("Check for updates on cursor movement") - .setDesc("This will check for changes only on cursors last line in Vault. Useful, if you want to reduce the load on your system and faster updates.") - .addToggle(toggle => - toggle - .setValue(this.plugin.settings.updateOnCursorMovement) - .onChange(async (value: boolean) => { - this.plugin.settings.updateOnCursorMovement = value; - await this.plugin.saveSettings(); - })); - + .setName("Move all tasks to selected default project") + .setDesc("This will move all tasks from Vault to the selected default project in Vikunja. This will not delete any tasks in Vikunja, but only move them to the selected project. This helps, if you make a wrong decision in the past. This does not create any tasks in Vikunja.") + .addButton(button => button + .setButtonText("Move all tasks") + .onClick(async () => { + await this.plugin.commands.moveAllTasksToDefaultProject(); + } + )); } diff --git a/src/vikunja/labels.ts b/src/vikunja/labels.ts index 3d1f5ce..28ab3f0 100644 --- a/src/vikunja/labels.ts +++ b/src/vikunja/labels.ts @@ -38,7 +38,11 @@ class Label { task: id, label: modelLabel }; - await this.labelsApi.tasksTaskLabelsPut(params); + try { + await this.labelsApi.tasksTaskLabelsPut(params); + } catch (e) { + console.error("Error adding label to task", e); + } } init() { @@ -77,12 +81,13 @@ class Label { return Promise.all(labels.map(label => this.createLabel(label))); } - async deleteLabel(labelId: number): Promise { + async deleteLabel(labelId: number) { if (this.plugin.settings.debugging) console.log("LabelsAPI: Deleting label", labelId); const param: LabelsIdDeleteRequest = { id: labelId, }; - return await this.labelsApi.labelsIdDelete(param); + await this.labelsApi.labelsIdDelete(param); + if (this.plugin.settings.debugging) console.log("LabelsAPI: Deleted label", labelId); } async updateLabel(label: ModelsLabel): Promise { @@ -94,26 +99,27 @@ class Label { return this.labelsApi.labelsIdPut(param); } - async getAndCreateLabels(labels: ModelsLabel[]): Promise { + async getOrCreateLabels(labels: ModelsLabel[]): Promise { if (this.plugin.settings.debugging) console.log("LabelsAPI: Get or create labels", labels); // FIXME This call will be placed everytime for every task. It should be cached or optimized away. const allLabels = await this.getLabels(); - let createdLabels: ModelsLabel[] = []; + let createdLabels = new Map(); for (const label of labels) { const vikunjaLabel = allLabels.find(l => l.title === label.title); - if (vikunjaLabel) { - createdLabels.push(vikunjaLabel); + if (vikunjaLabel !== undefined && vikunjaLabel.id !== undefined && createdLabels.get(vikunjaLabel.id) === undefined) { + createdLabels.set(vikunjaLabel.id, vikunjaLabel); continue; } if (this.plugin.settings.debugging) console.log("LabelsAPI: Create label in vikunja", label); const createdLabel = await this.createLabel(label); - createdLabels.push(createdLabel); + if (!createdLabel.id) throw new Error("Label id for freshly created Label is not defined"); + createdLabels.set(createdLabel.id, createdLabel); } if (this.plugin.settings.debugging) console.log("LabelsAPI: Created labels", createdLabels); - return createdLabels; + return Array.from(createdLabels.values()); } async deleteLabels(labels: ModelsLabel[]) { diff --git a/src/vikunja/tasks.ts b/src/vikunja/tasks.ts index 15c1e38..6b59231 100644 --- a/src/vikunja/tasks.ts +++ b/src/vikunja/tasks.ts @@ -5,6 +5,7 @@ import { ModelsTask, ProjectsIdTasksPutRequest, TaskApi, + TasksAllGetRequest, TasksIdDeleteRequest, TasksIdGetRequest, TasksIdPostRequest,