diff --git a/src/api.ts b/src/api.ts index 7c08c82..631eb15 100644 --- a/src/api.ts +++ b/src/api.ts @@ -71,8 +71,11 @@ import { getLinesInNote, } from "./utils/note"; import { + getAnnotationByLinkTarget, + getLinkTargetByAnnotation, getNoteLinkInboundRelation, getNoteLinkOutboundRelation, + linkAnnotationToTarget, updateNoteLinkRelation, } from "./utils/relation"; @@ -162,6 +165,9 @@ const relation = { getNoteLinkInboundRelation, getNoteLinkOutboundRelation, updateNoteLinkRelation, + linkAnnotationToTarget, + getLinkTargetByAnnotation, + getAnnotationByLinkTarget, }; export default { diff --git a/src/extras/relationWorker.ts b/src/extras/relationWorker.ts index 834efe7..8dfd1a1 100644 --- a/src/extras/relationWorker.ts +++ b/src/extras/relationWorker.ts @@ -2,10 +2,12 @@ import Dexie from "dexie"; const db = new Dexie("BN_Two_Way_Relation") as Dexie & { link: Dexie.Table; + annotation: Dexie.Table; }; -db.version(1).stores({ +db.version(2).stores({ link: "++id, fromLibID, fromKey, toLibID, toKey, fromLine, toLine, toSection, url", + annotation: "++id, fromLibID, fromKey, toLibID, toKey, url", }); console.log("Using Dexie v" + Dexie.semVer, db); @@ -31,9 +33,9 @@ async function rebuildLinkForNote( const collection = db.link.where({ fromLibID, fromKey }); const oldOutboundLinks = await collection.toArray(); - collection.delete().then((deleteCount) => { + await collection.delete().then((deleteCount) => { console.log("Deleted " + deleteCount + " objects"); - bulkAddLink(links); + return bulkAddLink(links); }); return { oldOutboundLinks, @@ -50,6 +52,27 @@ async function getInboundLinks(toLibID: number, toKey: string) { return db.link.where({ toLibID, toKey }).toArray(); } +async function linkAnnotationToTarget(model: AnnotationModel) { + console.log("linkAnnotationToTarget", model); + const collection = db.annotation.where({ + fromLibID: model.fromLibID, + fromKey: model.fromKey, + }); + await collection.delete().then(() => { + return db.annotation.add(model); + }); +} + +async function getLinkTargetByAnnotation(fromLibID: number, fromKey: string) { + console.log("getLinkTargetByAnnotation", fromLibID, fromKey); + return db.annotation.get({ fromLibID, fromKey }); +} + +async function getAnnotationByLinkTarget(toLibID: number, toKey: string) { + console.log("getAnnotationByLinkTarget", toLibID, toKey); + return db.annotation.get({ toLibID, toKey }); +} + interface LinkModel { fromLibID: number; fromKey: string; @@ -61,6 +84,14 @@ interface LinkModel { url: string; } +interface AnnotationModel { + fromLibID: number; + fromKey: string; + toLibID: number; + toKey: string; + url: string; +} + // Handle messages from the main thread and send responses back for await onmessage = async (event) => { const { type, jobID, data } = event.data; @@ -105,6 +136,27 @@ onmessage = async (event) => { result: await getInboundLinks(data.toLibID, data.toKey), }); break; + case "linkAnnotationToTarget": + postMessage({ + type, + jobID, + result: await linkAnnotationToTarget(data), + }); + break; + case "getLinkTargetByAnnotation": + postMessage({ + type, + jobID, + result: await getLinkTargetByAnnotation(data.fromLibID, data.fromKey), + }); + break; + case "getAnnotationByLinkTarget": + postMessage({ + type, + jobID, + result: await getAnnotationByLinkTarget(data.toLibID, data.toKey), + }); + break; default: break; } diff --git a/src/modules/reader.ts b/src/modules/reader.ts index 3c459c7..0615f85 100644 --- a/src/modules/reader.ts +++ b/src/modules/reader.ts @@ -23,7 +23,7 @@ export function registerReaderAnnotationButton() { createNoteFromAnnotation( reader._item.libraryID, annotationData.id, - (e as MouseEvent).shiftKey ? "builtin" : "auto", + (e as MouseEvent).shiftKey ? "window" : "tab", ); e.preventDefault(); }, @@ -54,7 +54,7 @@ export function registerReaderAnnotationButton() { async function createNoteFromAnnotation( libraryID: number, itemKey: string, - openMode: "builtin" | "auto" = "auto", + openMode: "window" | "tab" | undefined, ) { const annotationItem = Zotero.Items.getByLibraryAndKey( libraryID, @@ -63,15 +63,27 @@ async function createNoteFromAnnotation( if (!annotationItem) { return; } + + // Check if the annotation has a note link tag const annotationTags = annotationItem.getTags().map((_) => _.tag); const linkRegex = new RegExp("^zotero://note/(.*)$"); for (const tag of annotationTags) { if (linkRegex.test(tag)) { const linkParams = getNoteLinkParams(tag); if (linkParams.noteItem) { - addon.hooks.onOpenNote(linkParams.noteItem.id, openMode, { + addon.hooks.onOpenNote(linkParams.noteItem.id, openMode || "tab", { lineIndex: linkParams.lineIndex || undefined, }); + // Remove deprecated link tag and create a link in IndexedDB + await addon.api.relation.linkAnnotationToTarget({ + fromLibID: annotationItem.libraryID, + fromKey: annotationItem.key, + toLibID: linkParams.libraryID, + toKey: linkParams.noteKey!, + url: tag, + }); + annotationItem.removeTag(tag); + await annotationItem.saveTx(); return; } else { annotationItem.removeTag(tag); @@ -80,17 +92,31 @@ async function createNoteFromAnnotation( } } + const linkTarget = await addon.api.relation.getLinkTargetByAnnotation( + annotationItem.libraryID, + annotationItem.key, + ); + if (linkTarget) { + const targetItem = Zotero.Items.getByLibraryAndKey( + linkTarget.toLibID, + linkTarget.toKey, + ); + if (targetItem) + addon.hooks.onOpenNote(targetItem.id, openMode || "tab", {}); + return; + } + const note: Zotero.Item = new Zotero.Item("note"); note.libraryID = annotationItem.libraryID; note.parentID = annotationItem.parentItem!.parentID; await note.saveTx(); - const renderredTemplate = await addon.api.template.runTemplate( + const renderedTemplate = await addon.api.template.runTemplate( "[QuickNoteV5]", "annotationItem, topItem, noteItem", [annotationItem, annotationItem.parentItem!.parentItem, note], ); - await addLineToNote(note, renderredTemplate); + await addLineToNote(note, renderedTemplate); const tags = annotationItem.getTags(); for (const tag of tags) { @@ -98,8 +124,13 @@ async function createNoteFromAnnotation( } await note.saveTx(); - ZoteroPane.openNoteWindow(note.id); + await addon.api.relation.linkAnnotationToTarget({ + fromLibID: annotationItem.libraryID, + fromKey: annotationItem.key, + toLibID: note.libraryID, + toKey: note.key, + url: addon.api.convert.note2link(note, { ignore: true })!, + }); - annotationItem.addTag(getNoteLink(note)!); - await annotationItem.saveTx(); + addon.hooks.onOpenNote(note.id, "builtin", {}); } diff --git a/src/utils/relation.ts b/src/utils/relation.ts index de7880f..23e5b47 100644 --- a/src/utils/relation.ts +++ b/src/utils/relation.ts @@ -6,6 +6,9 @@ export { getNoteLinkInboundRelation, getNoteLinkOutboundRelation, closeRelationWorker, + linkAnnotationToTarget, + getLinkTargetByAnnotation, + getAnnotationByLinkTarget, }; function closeRelationWorker() { @@ -158,3 +161,38 @@ interface LinkModel { toSection: string | null; url: string; } + +async function linkAnnotationToTarget(model: AnnotationModel): Promise { + return executeRelationWorker({ + type: "linkAnnotationToTarget", + data: model, + }); +} + +async function getLinkTargetByAnnotation( + fromLibID: number, + fromKey: string, +): Promise { + return executeRelationWorker({ + type: "getLinkTargetByAnnotation", + data: { fromLibID, fromKey }, + }); +} + +async function getAnnotationByLinkTarget( + toLibID: number, + toKey: string, +): Promise { + return executeRelationWorker({ + type: "getAnnotationByLinkTarget", + data: { toLibID, toKey }, + }); +} + +interface AnnotationModel { + fromLibID: number; + fromKey: string; + toLibID: number; + toKey: string; + url: string; +}