167 lines
4.3 KiB
TypeScript
167 lines
4.3 KiB
TypeScript
import { MessageHelper } from "zotero-plugin-toolkit";
|
|
import { config } from "../../package.json";
|
|
import { getNoteLinkParams } from "./link";
|
|
import type { handlers } from "../extras/relationWorker";
|
|
|
|
function closeRelationServer() {
|
|
if (addon.data.relation.server) {
|
|
addon.data.relation.server.destroy();
|
|
addon.data.relation.server = undefined;
|
|
}
|
|
}
|
|
|
|
async function getRelationServer(): Promise<MessageHelper<typeof handlers>> {
|
|
if (!addon.data.relation.server) {
|
|
const worker = new Worker(
|
|
`chrome://${config.addonRef}/content/scripts/relationWorker.js`,
|
|
{ name: "relationWorker" },
|
|
);
|
|
const server = new MessageHelper<typeof handlers>({
|
|
canBeDestroyed: false,
|
|
dev: __env__ === "development",
|
|
name: "relationWorkerMain",
|
|
target: worker,
|
|
handlers: {},
|
|
});
|
|
server.start();
|
|
await server.exec("_ping");
|
|
addon.data.relation.server = server;
|
|
}
|
|
|
|
return addon.data.relation.server!;
|
|
}
|
|
|
|
export { getRelationServer, closeRelationServer };
|
|
|
|
export {
|
|
updateNoteLinkRelation,
|
|
getNoteLinkInboundRelation,
|
|
getNoteLinkOutboundRelation,
|
|
linkAnnotationToTarget,
|
|
getLinkTargetByAnnotation,
|
|
getAnnotationByLinkTarget,
|
|
};
|
|
|
|
async function updateNoteLinkRelation(noteID: number) {
|
|
ztoolkit.log("updateNoteLinkRelation", noteID);
|
|
const note = Zotero.Items.get(noteID);
|
|
const affectedNoteIDs = new Set([noteID]);
|
|
const fromLibID = note.libraryID;
|
|
const fromKey = note.key;
|
|
const lines = await addon.api.note.getLinesInNote(note);
|
|
const linkToData: LinkModel[] = [];
|
|
for (let i = 0; i < lines.length; i++) {
|
|
const linkMatches = lines[i].match(/href="zotero:\/\/note\/[^"]+"/g);
|
|
if (!linkMatches) {
|
|
continue;
|
|
}
|
|
for (const match of linkMatches) {
|
|
const link = decodeHTMLEntities(match.slice(6, -1));
|
|
const { noteItem, libraryID, noteKey, lineIndex, sectionName } =
|
|
getNoteLinkParams(link);
|
|
if (noteItem && noteItem.isNote() && noteItem.id !== note.id) {
|
|
affectedNoteIDs.add(noteItem.id);
|
|
linkToData.push({
|
|
fromLibID,
|
|
fromKey,
|
|
toLibID: libraryID,
|
|
toKey: noteKey!,
|
|
fromLine: i,
|
|
toLine: lineIndex ?? null,
|
|
toSection: sectionName ?? null,
|
|
url: link,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
const result = await (
|
|
await getRelationServer()
|
|
).proxy.rebuildLinkForNote(fromLibID, fromKey, linkToData);
|
|
|
|
for (const link of result.oldOutboundLinks as LinkModel[]) {
|
|
const item = Zotero.Items.getByLibraryAndKey(link.toLibID, link.toKey);
|
|
if (!item) {
|
|
continue;
|
|
}
|
|
affectedNoteIDs.add(item.id);
|
|
}
|
|
Zotero.Notifier.trigger(
|
|
// @ts-ignore
|
|
"updateBNRelation",
|
|
"item",
|
|
Array.from(affectedNoteIDs),
|
|
{},
|
|
true,
|
|
);
|
|
}
|
|
|
|
async function getNoteLinkOutboundRelation(
|
|
noteID: number,
|
|
): Promise<LinkModel[]> {
|
|
const note = Zotero.Items.get(noteID);
|
|
const fromLibID = note.libraryID;
|
|
const fromKey = note.key;
|
|
return await (
|
|
await getRelationServer()
|
|
).proxy.getOutboundLinks(fromLibID, fromKey);
|
|
}
|
|
|
|
async function getNoteLinkInboundRelation(
|
|
noteID: number,
|
|
): Promise<LinkModel[]> {
|
|
const note = Zotero.Items.get(noteID);
|
|
const toLibID = note.libraryID;
|
|
const toKey = note.key;
|
|
return await (
|
|
await getRelationServer()
|
|
).proxy.getInboundLinks(toLibID, toKey);
|
|
}
|
|
|
|
function decodeHTMLEntities(text: string) {
|
|
return text
|
|
.replace(/&/g, "&")
|
|
.replace(/</g, "<")
|
|
.replace(/>/g, ">");
|
|
}
|
|
|
|
interface LinkModel {
|
|
fromLibID: number;
|
|
fromKey: string;
|
|
toLibID: number;
|
|
toKey: string;
|
|
fromLine: number;
|
|
toLine: number | null;
|
|
toSection: string | null;
|
|
url: string;
|
|
}
|
|
|
|
async function linkAnnotationToTarget(model: AnnotationModel) {
|
|
return await (await getRelationServer()).proxy.linkAnnotationToTarget(model);
|
|
}
|
|
|
|
async function getLinkTargetByAnnotation(
|
|
fromLibID: number,
|
|
fromKey: string,
|
|
): Promise<AnnotationModel | undefined> {
|
|
return await (
|
|
await getRelationServer()
|
|
).proxy.getLinkTargetByAnnotation(fromLibID, fromKey);
|
|
}
|
|
|
|
async function getAnnotationByLinkTarget(
|
|
toLibID: number,
|
|
toKey: string,
|
|
): Promise<AnnotationModel | undefined> {
|
|
return await (
|
|
await getRelationServer()
|
|
).proxy.getAnnotationByLinkTarget(toLibID, toKey);
|
|
}
|
|
|
|
interface AnnotationModel {
|
|
fromLibID: number;
|
|
fromKey: string;
|
|
toLibID: number;
|
|
toKey: string;
|
|
url: string;
|
|
}
|