1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
|
import { StrCast, Cast } from "../../../fields/Types";
import { SearchUtil } from "../../util/SearchUtil";
import { action, runInAction } from "mobx";
import { Doc, Opt } from "../../../fields/Doc";
import { DocumentType } from "../../documents/DocumentTypes";
import { Docs, DocUtils } from "../../documents/Documents";
import { SelectionManager } from "../../util/SelectionManager";
import { WebField } from "../../../fields/URLField";
import { DocumentManager } from "../../util/DocumentManager";
import { DocumentLinksButton } from "../../views/nodes/DocumentLinksButton";
import { LinkManager } from "../../util/LinkManager";
import { TaskCompletionBox } from "../../views/nodes/TaskCompletedBox";
import { Utils, simulateMouseClick } from "../../../Utils";
import { LinkDescriptionPopup } from "../../views/nodes/LinkDescriptionPopup";
import { Id } from "../../../fields/FieldSymbols";
import { DocumentView } from "../../views/nodes/DocumentView";
export namespace Hypothesis {
export const getSourceWebDoc = async (uri: string) => {
const result = await findWebDoc(uri);
return result || Docs.Create.WebDocument(uri, { _nativeWidth: 850, _nativeHeight: 962, _width: 600, UseCors: true }); // create and return a new Web doc with given uri if no matching docs are found
};
// Search for a web Doc whose url field matches the given uri, return undefined if not found
export const findWebDoc = async (uri: string) => {
const currentDoc = SelectionManager.SelectedDocuments().length && SelectionManager.SelectedDocuments()[0].props.Document;
currentDoc && console.log(Cast(currentDoc.data, WebField)?.url.href === uri, uri, Cast(currentDoc.data, WebField)?.url.href);
if (currentDoc && Cast(currentDoc.data, WebField)?.url.href === uri) return currentDoc; // always check first whether the current doc is the source, only resort to Search otherwise
const results: Doc[] = [];
await SearchUtil.Search("web", true).then(action(async (res: SearchUtil.DocSearchResult) => {
const docs = await Promise.all(res.docs.map(async doc => (await Cast(doc.extendsDoc, Doc)) || doc));
const filteredDocs = docs.filter(doc =>
doc.author === Doc.CurrentUserEmail && doc.type === DocumentType.WEB && doc.data
);
filteredDocs.forEach(doc => console.log("web docs:", doc.title, Cast(doc.data, WebField)?.url.href));
filteredDocs.forEach(doc => { uri === Cast(doc.data, WebField)?.url.href && results.push(doc); }); // TODO check history? imperfect matches?
}));
results.forEach(doc => console.log(doc.title, StrCast(doc.data)));
return results.length ? results[0] : undefined;
};
// Ask Hypothes.is client to edit an annotation to add a Dash hyperlink
export const makeLink = async (title: string, url: string, annotationId: string, annotationSourceDoc: Doc) => {
// if the annotation's source webpage isn't currently loaded in Dash, we're not able to access and edit the annotation from the client
// so we're loading the webpage and its annotations invisibly in a WebBox in MainView.tsx, until the editing is done
!DocumentManager.Instance.getFirstDocumentView(annotationSourceDoc) && runInAction(() => DocumentLinksButton.invisibleWebDoc = annotationSourceDoc);
var success = false;
const onSuccess = action(() => {
console.log("EDITSUCCESS");
clearTimeout(interval);
DocumentLinksButton.invisibleWebDoc = undefined;
document.removeEventListener("editSuccess", onSuccess);
success = true;
});
console.log("SEND addLink");
const newHyperlink = `[${title}\n](${url})`;
const interval = setInterval(() => // keep trying to scroll every 250ms until annotations have loaded and editing is successful
!success && document.dispatchEvent(new CustomEvent<{ newHyperlink: string, id: string }>("addLink", {
detail: { newHyperlink: newHyperlink, id: annotationId },
bubbles: true
})), 300);
setTimeout(action(() => {
if (!success) {
clearInterval(interval);
DocumentLinksButton.invisibleWebDoc = undefined;
}
}), 15000); // give up if no success after 15s
document.addEventListener("editSuccess", onSuccess);
};
export const scrollToAnnotation = (annotationId: string, target: Doc) => {
var success = false;
const onSuccess = () => {
console.log("scroll success!!");
document.removeEventListener('scrollSuccess', onSuccess);
clearInterval(interval);
success = true;
};
const interval = setInterval(() => { // keep trying to scroll every 250ms until annotations have loaded and scrolling is successful
console.log("send scroll");
document.dispatchEvent(new CustomEvent('scrollToAnnotation', {
detail: annotationId,
bubbles: true
}));
const targetView: Opt<DocumentView> = DocumentManager.Instance.getFirstDocumentView(target);
const position = targetView?.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
targetView && position && simulateMouseClick(targetView.ContentDiv!, position[0], position[1], position[0], position[1], false);
}, 300);
document.addEventListener('scrollSuccess', onSuccess); // listen for success message from client
setTimeout(() => !success && clearInterval(interval), 10000); // give up if no success after 10s
};
// Send Hypothes.is client request to edit an annotation to find and remove a dash hyperlink
export const deleteLink = async (annotationId: string, linkUrl: string) => {
document.dispatchEvent(new CustomEvent<{ targetUrl: string, id: string }>("deleteLink", {
detail: { targetUrl: linkUrl, id: annotationId },
bubbles: true
}));
};
// listen for event from Hypothes.is plugin to link an annotation to Dash
export const linkListener = async (e: any) => {
const annotationId: string = e.detail.id;
const annotationUri: string = e.detail.uri;
const sourceDoc: Doc = await getSourceWebDoc(annotationUri);
if (!DocumentLinksButton.StartLink) { // start link if there were none already started
runInAction(() => {
DocumentLinksButton.AnnotationId = annotationId;
DocumentLinksButton.AnnotationUri = annotationUri;
DocumentLinksButton.StartLink = sourceDoc;
});
} else if (!Doc.AreProtosEqual(sourceDoc, DocumentLinksButton.StartLink)) { // if a link has already been started, complete the link to the sourceDoc
console.log("completing link", sourceDoc.title);
runInAction(() => {
DocumentLinksButton.AnnotationId = annotationId;
DocumentLinksButton.AnnotationUri = annotationUri;
});
const linkDoc = DocUtils.MakeLink({ doc: DocumentLinksButton.StartLink }, { doc: sourceDoc }, DocumentLinksButton.AnnotationId ? "hypothes.is annotation" : "long drag");
LinkManager.currentLink = linkDoc;
Doc.GetProto(linkDoc as Doc).linksToAnnotation = true;
Doc.GetProto(linkDoc as Doc).annotationId = DocumentLinksButton.AnnotationId;
Doc.GetProto(linkDoc as Doc).annotationUri = DocumentLinksButton.AnnotationUri;
makeLink(StrCast(DocumentLinksButton.StartLink.title), Utils.prepend("/doc/" + DocumentLinksButton.StartLink[Id]), StrCast(DocumentLinksButton.AnnotationId), sourceDoc); // update and link placeholder annotation
runInAction(() => {
if (linkDoc) {
TaskCompletionBox.textDisplayed = "Link Created";
TaskCompletionBox.popupX = screenX;
TaskCompletionBox.popupY = screenY - 133;
TaskCompletionBox.taskCompleted = true;
if (LinkDescriptionPopup.showDescriptions === "ON" || !LinkDescriptionPopup.showDescriptions) {
LinkDescriptionPopup.popupX = screenX;
LinkDescriptionPopup.popupY = screenY - 100;
LinkDescriptionPopup.descriptionPopup = true;
}
setTimeout(action(() => { TaskCompletionBox.taskCompleted = false; }), 2500);
}
});
}
};
}
|