Files
ob2qualkard/src/main.ts

133 lines
4.5 KiB
TypeScript
Raw Normal View History

2026-08-05 21:11:46 -03:00
// *************************************************************************
// * (C)opyright 2026 by Ruben Carlo Benante *
// * *
// * This program is free software; you can redistribute it and/or modify *
// * it under the terms of the GNU General Public License as published by *
// * the Free Software Foundation, either version 3 of the License, or *
// * (at your option) any later version. *
// * *
// * This program is distributed in the hope that it will be useful, *
// * but WITHOUT ANY WARRANTY; without even the implied warranty of *
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
// * GNU General Public License for more details. *
// * *
// * You should have received a copy of the GNU General Public License *
// * along with this program. If not, see http://www.gnu.org/licenses/. *
// * *
// * Contact author at: *
// * Ruben Carlo Benante *
// * rcb@beco.cc *
// *************************************************************************
import { Editor, Notice, Plugin } from 'obsidian';
import { parseQualcardBlocks, selectBlockToPush } from './card';
import { getProjectIdByName, createTask } from './kanboard';
2026-08-05 20:29:39 -03:00
import {
2026-08-05 21:11:46 -03:00
Ob2qualkardSettings,
2026-08-05 20:29:39 -03:00
DEFAULT_SETTINGS,
2026-08-05 21:11:46 -03:00
Ob2qualkardSettingTab,
2026-08-05 20:29:39 -03:00
} from './settings';
2026-08-05 21:11:46 -03:00
export default class Ob2qualkardPlugin extends Plugin {
settings!: Ob2qualkardSettings;
// Board name -> project id, resolved once per session (cleared on save).
private boardCache = new Map<string, number>();
2026-08-05 20:29:39 -03:00
async onload() {
await this.loadSettings();
2026-08-05 21:11:46 -03:00
this.addSettingTab(new Ob2qualkardSettingTab(this.app, this));
2026-08-05 20:29:39 -03:00
this.addCommand({
2026-08-05 21:11:46 -03:00
id: 'push-note-to-qualkard',
name: 'Push this note to QualKard',
editorCallback: (editor) => void this.pushNote(editor),
2026-08-05 20:29:39 -03:00
});
2026-08-05 21:11:46 -03:00
this.registerEvent(
this.app.workspace.on('editor-menu', (menu, editor) => {
menu.addItem((item) =>
item
.setTitle('Push this note to QualKard')
.setIcon('upload')
.onClick(() => void this.pushNote(editor)),
);
}),
2026-08-05 20:29:39 -03:00
);
}
async loadSettings() {
this.settings = Object.assign(
{},
DEFAULT_SETTINGS,
2026-08-05 21:11:46 -03:00
(await this.loadData()) as Partial<Ob2qualkardSettings>,
2026-08-05 20:29:39 -03:00
);
}
async saveSettings() {
await this.saveData(this.settings);
2026-08-05 21:11:46 -03:00
// The board name may have changed; re-resolve its id on the next push.
this.boardCache.clear();
2026-08-05 20:29:39 -03:00
}
2026-08-05 21:11:46 -03:00
// Parse the note, pick the card to push (sole block, or the one under the
// cursor), validate it, then create the Kanboard task. One-way, no dedup.
private async pushNote(editor: Editor) {
const sel = selectBlockToPush(
parseQualcardBlocks(editor.getValue()),
editor.getCursor().line,
);
if (sel.reason === 'none') {
new Notice('No qualcard block in this note');
return;
}
if (sel.reason === 'ambiguous') {
new Notice(
'Several qualcard blocks; place the cursor inside the one to push',
);
return;
}
const block = sel.block;
if (!block || !block.front) {
new Notice('This qualcard block has no prompt (front)');
return;
}
if (!block.hasAnswer) {
new Notice('This qualcard block has no answer, so it was skipped');
return;
}
if (!this.settings.apiToken || !this.settings.board) {
new Notice(
'Set the API token and study board in settings first',
);
return;
}
try {
const projectId = await this.resolveBoard();
if (projectId === null) {
new Notice(`Board "${this.settings.board}" not found`);
return;
}
const taskId = await createTask(
this.settings,
projectId,
block.front,
block.back,
);
new Notice(`Card created (task #${taskId})`);
} catch (e) {
const msg = e instanceof Error ? e.message : 'push failed';
console.error('ob2qualkard: push failed', e);
new Notice(msg);
}
2026-08-05 20:29:39 -03:00
}
2026-08-05 21:11:46 -03:00
private async resolveBoard(): Promise<number | null> {
const name = this.settings.board;
const cached = this.boardCache.get(name);
if (cached !== undefined) return cached;
const id = await getProjectIdByName(this.settings, name);
if (id !== null) this.boardCache.set(name, id);
return id;
2026-08-05 20:29:39 -03:00
}
}