Skip to content

Commit

Permalink
Add advanced notices
Browse files Browse the repository at this point in the history
  • Loading branch information
tansongchen committed Sep 1, 2022
1 parent 89e75c8 commit d25c568
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 63 deletions.
38 changes: 23 additions & 15 deletions main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ export default class AnkiSynchronizer extends Plugin {
this.noteTypeState.set(parseInt(key), noteTypeState[key]);
}
}
await this.configureUI();
this.configureUI();
console.log(locale.onLoad);
}

async configureUI() {
configureUI() {
// Add import note types command
this.addCommand({
id: 'import',
Expand All @@ -58,8 +58,8 @@ export default class AnkiSynchronizer extends Plugin {
}

// Save data to local file
async save() {
await this.saveData({
save() {
return this.saveData({
version: version,
settings: this.settings,
noteState: Object.fromEntries(this.noteState),
Expand All @@ -73,39 +73,42 @@ export default class AnkiSynchronizer extends Plugin {
}

// Retrieve template information from Obsidian core plugin "Templates"
async getTemplatePath() {
getTemplatePath() {
const templatesPlugin = (this.app as any).internalPlugins?.plugins['templates'];
if (!templatesPlugin?.enabled) {
new Notice(locale.templatesNotEnabledNotice);
return undefined;
throw new Error("Cannot get template path!");
}
return normalizePath(templatesPlugin.instance.options.folder);
}

async importNoteTypes() {
new Notice(locale.importStartNotice);
const templatesPath = await this.getTemplatePath();
if (!templatesPath) return;
const templatesPath = this.getTemplatePath();
this.noteTypeState.setTemplatePath(templatesPath);
const noteTypesAndIds = await this.anki.noteTypesAndIds();
if (noteTypesAndIds instanceof AnkiError || noteTypesAndIds instanceof Error) return;
if (noteTypesAndIds instanceof AnkiError) {
new Notice(locale.importFaliureNotice);
return;
}
const noteTypes = Object.keys(noteTypesAndIds);
const noteTypeFields = await this.anki.multi<{ modelName: string }, string[]>('modelFieldNames', noteTypes.map(s => ({ modelName: s })));
if (noteTypeFields instanceof AnkiError || noteTypeFields instanceof Error) return;
if (noteTypeFields instanceof AnkiError) {
new Notice(locale.importFaliureNotice);
return;
}
const state = new Map<number, NoteTypeDigest>(noteTypes.map((name, index) => [noteTypesAndIds[name], {
name: name,
fieldNames: noteTypeFields[index]
}]));
console.log('Note type data retrieved from Anki:');
console.log(state);
console.log(`Retrieved note type data from Anki`, state);
await this.noteTypeState.change(state);
await this.save();
new Notice(locale.importSuccessNotice);
}

async synchronize() {
const templatesPath = await this.getTemplatePath();
if (!templatesPath) return;
const templatesPath = this.getTemplatePath();
new Notice(locale.synchronizeStartNotice);
const allFiles = this.app.vault.getMarkdownFiles();
const state = new Map<number, [NoteDigest, Note]>();
Expand All @@ -117,7 +120,12 @@ export default class AnkiSynchronizer extends Plugin {
const note = Note.validateNote((file as TAbstractFile).path, content, this.noteTypeState);
if (!note) continue;
if (note.nid === 0) { // new file
await this.noteState.handleAddNote(note);
const nid = await this.noteState.handleAddNote(note);
if (nid === undefined) {
new Notice(locale.synchronizeAddNoteFaliureNotice(file.basename));
continue;
}
note.nid = nid;
this.app.vault.modify(file, note.dump());
}
state.set(note.nid, [note.digest(), note]);
Expand Down
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "obsidian-anki-synchronizer",
"name": "Anki Synchronizer",
"version": "0.0.5",
"version": "0.0.6",
"minAppVersion": "0.14.0",
"description": "This is a plugin for exporting Obsidian contents to Anki.",
"author": "Songchen Tan",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "obsidian-anki-synchronizer",
"version": "0.0.5",
"version": "0.0.6",
"description": "This is a plugin for exporting Obsidian contents to Anki.",
"main": "main.js",
"scripts": {
Expand Down
12 changes: 3 additions & 9 deletions src/anki.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,7 @@ interface Response<R = null> {
result: R
}

export class AnkiError {
error: string

constructor(error: string) {
this.error = error
}
}
export class AnkiError extends Error {}

export interface Note {
deckName: string,
Expand All @@ -35,7 +29,7 @@ export interface Note {
class Anki {
private port = 8765;

async invoke<R = null, P = undefined>(action: string, params: P): Promise<R | Error | AnkiError> {
async invoke<R = null, P = undefined>(action: string, params: P): Promise<R | AnkiError> {
type requestType = Request<P>;
type responseType = Response<R>;
const request = {
Expand All @@ -51,7 +45,7 @@ class Anki {
return data.result;
} catch (error) {
new Notice(locale.synchronizeAnkiConnectUnavailableNotice);
return new Error("Bad Anki Connection");
throw error;
}
}

Expand Down
15 changes: 15 additions & 0 deletions src/lang.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,15 @@ interface Locale {
importCommandName: string,
importStartNotice: string,
importSuccessNotice: string,
importFaliureNotice: string,
synchronizeStartNotice: string,
synchronizeSuccessNotice: string,
synchronizeBadAnkiConnectNotice: string,
synchronizeAnkiConnectUnavailableNotice: string,
synchronizeAddNoteFaliureNotice: (filename: string) => string,
synchronizeChangeDeckFaliureNotice: (filename: string) => string,
synchronizeUpdateFieldsFaliureNotice: (filename: string) => string,
synchronizeUpdateTagsFaliureNotice: (filename: string) => string,
settingTabHeader: string,
settingRenderName: string,
settingRenderDescription: string,
Expand All @@ -25,10 +30,15 @@ const en: Locale = {
importCommandName: 'Import Note Types',
importStartNotice: 'Importing note types from Anki...',
importSuccessNotice: 'Successfully imported note types from Anki!',
importFaliureNotice: 'Cannot import note types from Anki!',
synchronizeStartNotice: 'Synchronizing to Anki...',
synchronizeSuccessNotice: 'Successfully synchronized to Anki!',
synchronizeBadAnkiConnectNotice: `Bad version of AnkiConnect`,
synchronizeAnkiConnectUnavailableNotice: `Anki is not opened or AnkiConnect is not installed!`,
synchronizeAddNoteFaliureNotice: (s) => `Cannot add note for ${s}`,
synchronizeChangeDeckFaliureNotice: (filename: string) => `Cannot change deck for ${filename}`,
synchronizeUpdateFieldsFaliureNotice: (filename: string) => `Cannot update fields for ${filename}`,
synchronizeUpdateTagsFaliureNotice: (filename: string) => `Cannot update tags for ${filename}`,
settingTabHeader: 'Anki Synchronizer Settings',
settingRenderName: 'Render',
settingRenderDescription: 'Whether to render markdown before importing to Anki or not.',
Expand All @@ -42,10 +52,15 @@ const zh_cn: Locale = {
importCommandName: '导入笔记类型',
importStartNotice: '正在从 Anki 导入笔记类型……',
importSuccessNotice: '已成功为 Anki 导入笔记类型!',
importFaliureNotice: '无法从 Anki 导入笔记类型!',
synchronizeStartNotice: '正在与 Anki 同步笔记……',
synchronizeSuccessNotice: '已成功与 Anki 同步笔记!',
synchronizeBadAnkiConnectNotice: 'Anki Connect 版本不匹配!',
synchronizeAnkiConnectUnavailableNotice: 'Anki 未打开或 Anki Connect 未安装!',
synchronizeAddNoteFaliureNotice: (s) => `无法向 Anki 添加笔记 ${s}`,
synchronizeChangeDeckFaliureNotice: (filename: string) => `无法改变 ${filename} 的牌组`,
synchronizeUpdateFieldsFaliureNotice: (filename: string) => `无法更新 ${filename} 的字段`,
synchronizeUpdateTagsFaliureNotice: (filename: string) => `无法更新 ${filename} 的标签`,
settingTabHeader: 'Anki 同步设置',
settingRenderName: '渲染',
settingRenderDescription: '是否在导入时将 Markdown 渲染为 HTML'
Expand Down
79 changes: 43 additions & 36 deletions src/state.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import AnkiSynchronizer from 'main';
import { Notice, TFile } from 'obsidian';
import Note, { FrontMatter } from 'src/note';
import Anki, { AnkiError } from './anki';
import Anki from './anki';
import Formatter from './format';
import locale from './lang';

abstract class State<K, V, I = undefined> extends Map<K, V> {
protected plugin: AnkiSynchronizer;
Expand Down Expand Up @@ -69,12 +70,10 @@ export class NoteTypeState extends State<number, NoteTypeDigest> {
value.fieldNames.map(x => pseudoFields[x] = '\n\n');
const templateNote = new Note(templatePath, value.name, pseudoFrontMatter, pseudoFields);
const maybeTemplate = this.plugin.app.vault.getAbstractFileByPath(templatePath);
if (maybeTemplate === null) {
this.plugin.app.vault.create(templatePath, templateNote.dump())
} else if (maybeTemplate instanceof TFile) {
this.plugin.app.vault.modify(maybeTemplate as TFile, templateNote.dump());
if (maybeTemplate !== null) {
await this.plugin.app.vault.modify(maybeTemplate as TFile, templateNote.dump());
} else {
new Notice("Bad template type");
await this.plugin.app.vault.create(templatePath, templateNote.dump());
}
console.log(`Created template ${templatePath}`);
}
Expand Down Expand Up @@ -114,38 +113,44 @@ export class NoteState extends State<number, NoteDigest, Note> {
return;
}
const { cards } = notesInfoResponse[0];
console.log(`Changing deck for ${note.title()}`);
console.log(cards, deck);
const changeDeckResponse = await this.anki.changeDeck(cards, deck);
if (changeDeckResponse instanceof AnkiError) {
console.log(changeDeckResponse, ', try creating');
await this.anki.createDeck(deck);
await this.anki.changeDeck(cards, deck);
} else if (changeDeckResponse instanceof Error) {
return;
console.log(`Changing deck for ${note.title()}`, deck);
let changeDeckResponse = await this.anki.changeDeck(cards, deck);
if (changeDeckResponse === null) return;

// if the supposed deck does not exist, create it
if (changeDeckResponse.message.contains('deck was not found')) {
console.log(changeDeckResponse.message, ', try creating');
const createDeckResponse = await this.anki.createDeck(deck);
if (createDeckResponse === null) {
changeDeckResponse = await this.anki.changeDeck(cards, deck);
if (changeDeckResponse === null) return;
}
}

new Notice(locale.synchronizeChangeDeckFaliureNotice(note.title()));
}

async updateFields(key: number, current: NoteDigest, value: NoteDigest, note: Note) {
const fields = this.formatter.format(note.fields);
console.log(`Updating fields for ${note.title()}`)
console.log(note.nid, fields);
await this.anki.updateFields(note.nid, fields);
console.log(`Updating fields for ${note.title()}`, fields);
const updateFieldsResponse = await this.anki.updateFields(note.nid, fields);
if (updateFieldsResponse === null) return;
new Notice(locale.synchronizeUpdateFieldsFaliureNotice(note.title()));
}

async updateTags(key: number, current: NoteDigest, nextValue: NoteDigest, note: Note) {
const tagsToAdd = note.tags.filter(x => !current.tags.contains(x));
const tagsToRemove = current.tags.filter(x => !note.tags.contains(x));
let addTagsResponse = null, removeTagsResponse = null;
if (tagsToAdd.length) {
console.log(`Adding tags for ${note.title()}`);
console.log(tagsToAdd);
await this.anki.addTagsToNotes([note.nid], tagsToAdd);
console.log(`Adding tags for ${note.title()}`, tagsToAdd);
addTagsResponse = await this.anki.addTagsToNotes([note.nid], tagsToAdd);
}
if (tagsToRemove.length) {
console.log(`Removing tags for ${note.title()}`);
console.log(tagsToRemove);
await this.anki.removeTagsFromNotes([note.nid], tagsToRemove);
console.log(`Removing tags for ${note.title()}`, tagsToRemove);
removeTagsResponse = await this.anki.removeTagsFromNotes([note.nid], tagsToRemove);
}
if (addTagsResponse || removeTagsResponse) new Notice(locale.synchronizeUpdateTagsFaliureNotice(note.title()));
}

delete(key: number) {
Expand All @@ -160,20 +165,22 @@ export class NoteState extends State<number, NoteDigest, Note> {
fields: this.formatter.format(note.fields),
tags: note.tags
};
console.log(`Adding note for ${note.title()}`);
console.log(ankiNote);
console.log(`Adding note for ${note.title()}`, ankiNote);
let idOrError = await this.anki.addNote(ankiNote);
if (idOrError instanceof AnkiError) {
// if the supposed deck does not exist, create it
console.log(idOrError.error, ', try creating');
await this.anki.createDeck(ankiNote.deckName);
idOrError = await this.anki.addNote(ankiNote);
if (typeof idOrError !== 'number') {
return;
if (typeof idOrError === 'number') {
return idOrError;
}

// if the supposed deck does not exist, create it
if (idOrError.message.contains('deck was not found')) {
console.log(idOrError.message, ', try creating');
const nullOrError = await this.anki.createDeck(ankiNote.deckName);
if (nullOrError === null) {
idOrError = await this.anki.addNote(ankiNote);
if (typeof idOrError === 'number') {
return idOrError;
}
}
} else if (idOrError instanceof Error) {
return;
}
note.nid = idOrError;
}
}
3 changes: 2 additions & 1 deletion versions.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
"0.0.2": "0.14.0",
"0.0.3": "0.14.0",
"0.0.4": "0.14.0",
"0.0.5": "0.14.0"
"0.0.5": "0.14.0",
"0.0.6": "0.14.0"
}

0 comments on commit d25c568

Please sign in to comment.