Skip to content

Commit

Permalink
implement done bucket support for kanban
Browse files Browse the repository at this point in the history
  • Loading branch information
Heiss committed Jul 20, 2024
1 parent 259acff commit 82046ae
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 5 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ The experience with this plugin is a lot like the excellent plugins for todoist:
- [Ultimate Todoist Sync for Obsidian Plugin](https://github.com/HeroBlackInk/ultimate-todoist-sync-for-obsidian)
- [Obsidian Todoist Plugin](https://github.com/jamiebrynes7/obsidian-todoist-plugin)

Supported Vikunja API: v0.24.1

## Features

Every feature prioritizes the Obsidian side. If a task is updated in both systems, the Obsidian task will be the one who
Expand Down
6 changes: 6 additions & 0 deletions src/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ export default class Commands {
foundProblem = true;
}

if (this.plugin.settings.availableViews.length === 0) {
new Notice("Vikunja Plugin: No views found. Please configure the plugin before using it.");
if (this.plugin.settings.debugging) console.log("Vikunja Plugin: No views found. Please configure the plugin before using it.");
foundProblem = true;
}

if (foundProblem) {
new Notice(
"Vikunja Plugin: Found problems. Please fix them in settings before using the plugin."
Expand Down
50 changes: 47 additions & 3 deletions src/settings/mainSetting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {App, Notice, PluginSettingTab, Setting} from "obsidian";
import VikunjaPlugin from "../../main";
import {Projects} from "../vikunja/projects";
import {backendToFindTasks, chooseOutputFile, supportedTasksPluginsFormat} from "../enums";
import {ModelsProject} from "../../vikunja_sdk";
import {ModelsProject, ModelsProjectView} from "../../vikunja_sdk";
import {appHasDailyNotesPluginLoaded} from "obsidian-daily-notes-interface";

export interface VikunjaPluginSettings {
Expand All @@ -26,7 +26,10 @@ export interface VikunjaPluginSettings {
cronInterval: number,
updateOnStartup: boolean,
updateOnCursorMovement: boolean,
pullTasksOnlyFromDefaultProject: boolean
pullTasksOnlyFromDefaultProject: boolean,
availableViews: ModelsProjectView[],
selectedView: number,
selectBucketForDoneTasks: number,
}

export const DEFAULT_SETTINGS: VikunjaPluginSettings = {
Expand All @@ -50,7 +53,10 @@ export const DEFAULT_SETTINGS: VikunjaPluginSettings = {
cronInterval: 500,
updateOnStartup: false,
updateOnCursorMovement: false,
pullTasksOnlyFromDefaultProject: false
pullTasksOnlyFromDefaultProject: false,
availableViews: [],
selectedView: 0,
selectBucketForDoneTasks: 0,
}

export class MainSetting extends PluginSettingTab {
Expand Down Expand Up @@ -468,11 +474,49 @@ export class MainSetting extends PluginSettingTab {
dropdown.onChange(async (value: string) => {
this.plugin.settings.defaultVikunjaProject = parseInt(value);
if (this.plugin.settings.debugging) console.log(`SettingsTab: Selected Vikunja project:`, this.plugin.settings.defaultVikunjaProject);

this.plugin.settings.availableViews = await this.projectsApi.getViewsByProjectId(this.plugin.settings.defaultVikunjaProject);
if (this.plugin.settings.debugging) console.log(`SettingsTab: Available views:`, this.plugin.settings.availableViews);

if (this.plugin.settings.availableViews.length === 1) {
const id = this.plugin.settings.availableViews[0].id;
if (id === undefined) throw new Error("View id is undefined");
this.plugin.settings.selectedView = id;
this.plugin.settings.selectBucketForDoneTasks = await this.projectsApi.getDoneBucketIdFromKanbanView(this.plugin.settings.defaultVikunjaProject);
if (this.plugin.settings.debugging) console.log(`SettingsTab: Done bucket set to:`, this.plugin.settings.selectBucketForDoneTasks);
}
await this.plugin.saveSettings();
this.display();
});
}
)

if (this.plugin.settings.availableViews.length > 1) {
new Setting(containerEl)
.setName("Select bucket")
.setDesc("Because vikunja does not move done tasks to the correct bucket, you have to select the bucket where the done tasks are placed, so this plugin can do it for you.")
.addDropdown(dropdown => {
let i = 0;
for (const view of this.plugin.settings.availableViews) {
if (view.id === undefined || view.title === undefined) {
throw new Error("View id or title is undefined");
}
dropdown.addOption((i++).toString(), view.title);
}

dropdown.setValue(this.plugin.settings.selectedView.toString());

dropdown.onChange(async (value: string) => {
this.plugin.settings.selectedView = parseInt(value);
if (this.plugin.settings.debugging) console.log(`SettingsTab: Selected Vikunja bucket:`, this.plugin.settings.selectedView);

this.plugin.settings.selectBucketForDoneTasks = await this.projectsApi.getDoneBucketIdFromKanbanView(this.plugin.settings.defaultVikunjaProject);
if (this.plugin.settings.debugging) console.log(`SettingsTab: Done bucket set to:`, this.plugin.settings.selectBucketForDoneTasks);
await this.plugin.saveSettings();
});
});
}

new Setting(containerEl)
.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.")
Expand Down
71 changes: 70 additions & 1 deletion src/vikunja/projects.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
import {App} from "obsidian";
import VikunjaPlugin from "../../main";
import {Configuration, ModelsTask, ProjectApi} from "../../vikunja_sdk";
import {
Configuration,
ModelsBucket,
ModelsProjectView,
ModelsProjectViewKind,
ModelsTask,
ProjectApi,
ProjectsIdViewsViewBucketsPutRequest,
ProjectsProjectViewsGetRequest,
ProjectsProjectViewsIdPostRequest
} from "../../vikunja_sdk";

class Projects {
plugin: VikunjaPlugin;
Expand All @@ -26,6 +36,65 @@ class Projects {
async getProjectById(id: number): Promise<ModelsTask> {
return this.projectsApi.projectsIdGet({id: id});
}

async getViewsByProjectId(id: number): Promise<ModelsProjectView[]> {
const params: ProjectsProjectViewsGetRequest = {
project: id
};
const views = await this.projectsApi.projectsProjectViewsGet(params);
if(this.plugin.settings.debugging) console.log("Found views", views);

// @ts-ignore
const kanbanViews = views.filter(view => view.viewKind === "kanban");
if (this.plugin.settings.debugging) console.log("Found kanban views", kanbanViews);

return kanbanViews;
}

// Get the done bucket id from the Kanban view
// If no done bucket is found, create a new one, call it Done
async getDoneBucketIdFromKanbanView(id: number): Promise<number> {
const params: ProjectsProjectViewsGetRequest = {
project: id
};
const views = await this.projectsApi.projectsProjectViewsGet(params);
if (this.plugin.settings.debugging) console.log("Found views", views);

const kanbanView = views.find(view => view.id === this.plugin.settings.selectedView);
if (!kanbanView) throw new Error("No Kanban view found");

let done_bucket = kanbanView.doneBucketId;
if (done_bucket === undefined) {
throw new Error("No done bucket id found");
}

if (done_bucket === 0) {
if (kanbanView.id === undefined) throw new Error("No Kanban view id found");

// create new bucket, so we can set this as done bucket
const bucket_done: ModelsBucket = {
title: "Done",
};
const params: ProjectsIdViewsViewBucketsPutRequest = {
id: id,
view: kanbanView.id,
bucket: bucket_done,

}
const bucket = await this.projectsApi.projectsIdViewsViewBucketsPut(params);
kanbanView.doneBucketId = bucket.id;
const paramsView: ProjectsProjectViewsIdPostRequest = {
project: id,
id: kanbanView.id,
view: kanbanView
}
const kanbanViewUpdated = await this.projectsApi.projectsProjectViewsIdPost(paramsView);
if (this.plugin.settings.debugging) console.log("Updated Kanban view", kanbanViewUpdated);
if (kanbanViewUpdated.doneBucketId === undefined) throw new Error("No done bucket id found");
done_bucket = kanbanViewUpdated.doneBucketId;
}
return done_bucket;
}
}

export {Projects};
15 changes: 14 additions & 1 deletion src/vikunja/tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
ModelsTask,
ProjectsIdTasksPutRequest,
TaskApi,
TasksAllGetRequest,
TasksIdDeleteRequest,
TasksIdGetRequest,
TasksIdPostRequest,
Expand Down Expand Up @@ -37,6 +36,9 @@ class Tasks {
async updateTask(task: ModelsTask): Promise<ModelsTask> {
if (!task.id) throw new Error("TasksApi: Task id is not defined");
if (this.plugin.settings.debugging) console.log("TasksApi: Updating task", task.id, task);
if (task.done) {
task.bucketId = this.plugin.settings.selectBucketForDoneTasks;
}
const param: TasksIdPostRequest = {id: task.id, task: task};
return this.tasksApi.tasksIdPost(param);
}
Expand All @@ -55,6 +57,10 @@ class Tasks {
// filepath could be an issue, because this information is dropped right before calling this method right now
// Another problem is, that it cannot track moved tasks in the vault

if (task.done) {
task.bucketId = this.plugin.settings.selectBucketForDoneTasks;
}

const param: ProjectsIdTasksPutRequest = {
id: task.projectId,
task: task
Expand Down Expand Up @@ -141,6 +147,7 @@ class Tasks {

async updateProjectsIdInVikunja(tasks: ModelsTask[], projectId: number) {
if (this.plugin.settings.debugging) console.log("TasksApi: Updating project id in tasks", projectId);
// FIXME there is a bulkPost in tasksApi, use it instead of update any task separately
return await Promise.all(tasks.map(task => this.updateProjectIdInVikunja(task, projectId)));
}

Expand All @@ -152,6 +159,12 @@ class Tasks {
await this.updateTask(task);
}

async updateBucketInVikunja(task: ModelsTask, bucketId: number) {
if (!task.id) throw new Error("TasksApi: Task id is not defined");
if (this.plugin.settings.debugging) console.log("TasksApi: Updating bucket in task", task.id, bucketId);

}

private async updateLabelsInVikunja(task: ModelsTask) {
try {
await this.addLabelToTask(task);
Expand Down

0 comments on commit 82046ae

Please sign in to comment.