aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/apis/hypothesis/HypothesisUtils.ts149
-rw-r--r--src/client/views/MainView.tsx6
-rw-r--r--src/client/views/collections/CollectionSubView.tsx10
-rw-r--r--src/client/views/linking/LinkMenuItem.tsx3
-rw-r--r--src/client/views/nodes/DocumentLinksButton.tsx33
-rw-r--r--src/server/index.ts1
6 files changed, 105 insertions, 97 deletions
diff --git a/src/client/apis/hypothesis/HypothesisUtils.ts b/src/client/apis/hypothesis/HypothesisUtils.ts
index de068c7d4..2a192c8e1 100644
--- a/src/client/apis/hypothesis/HypothesisUtils.ts
+++ b/src/client/apis/hypothesis/HypothesisUtils.ts
@@ -3,28 +3,31 @@ 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 { Docs } 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 { simulateMouseClick, Utils } from "../../../Utils";
import { DocumentView } from "../../views/nodes/DocumentView";
+import { Id } from "../../../fields/FieldSymbols";
export namespace Hypothesis {
- // Retrieve a WebDocument with the given url exists, create and return a new
+ /**
+ * Retrieve a WebDocument with the given url, prioritizing results that are on screen.
+ * If none exist, create and return a new WebDocument.
+ */
export const getSourceWebDoc = async (uri: string) => {
const result = await findWebDoc(uri);
console.log(result ? "existing doc found" : "existing doc NOT found");
return result || Docs.Create.WebDocument(uri, { title: uri, _nativeWidth: 850, _nativeHeight: 962, _width: 400, UseCors: true }); // create and return a new Web doc with given uri if no matching docs are found
};
- // Search for a WebDocument whose url field matches the given uri, return undefined if not found
+
+ /**
+ * Search for a WebDocument 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;
if (currentDoc && Cast(currentDoc.data, WebField)?.url.href === uri) return currentDoc; // always check first whether the currently selected doc is the annotation's source, only use Search otherwise
@@ -36,7 +39,6 @@ export namespace Hypothesis {
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);
uri === Cast(doc.data, WebField)?.url.href && results.push(doc); // TODO check visited sites history?
});
}));
@@ -45,7 +47,36 @@ export namespace Hypothesis {
return onScreenResults.length ? onScreenResults[0] : (results.length ? results[0] : undefined); // prioritize results that are currently on the screen
};
- // Ask Hypothes.is client to edit an annotation to add a Dash hyperlink
+ /**
+ * 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 = StrCast(e.detail.uri).split("#annotations:")[0]; // clean hypothes.is URLs that reference a specific annotation
+ const sourceDoc: Doc = await getSourceWebDoc(annotationUri);
+
+ if (!DocumentLinksButton.StartLink || sourceDoc === DocumentLinksButton.StartLink) { // start new link if there were none already started, or if the old startLink came from the same web document (prevent links to itself)
+ runInAction(() => {
+ DocumentLinksButton.AnnotationId = annotationId;
+ DocumentLinksButton.AnnotationUri = annotationUri;
+ DocumentLinksButton.StartLink = sourceDoc;
+ });
+ } else { // if a link has already been started, complete the link to sourceDoc
+ runInAction(() => {
+ DocumentLinksButton.AnnotationId = annotationId;
+ DocumentLinksButton.AnnotationUri = annotationUri;
+ });
+ const endLinkView = DocumentManager.Instance.getFirstDocumentView(sourceDoc);
+ const rect = document.body.getBoundingClientRect();
+ const x = rect.x + rect.width / 2;
+ const y = 250;
+ DocumentLinksButton.finishLinkClick(x, y, DocumentLinksButton.StartLink, sourceDoc, false, endLinkView);
+ }
+ };
+
+ /**
+ * Send message to 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
@@ -53,14 +84,13 @@ export namespace Hypothesis {
var success = false;
const onSuccess = action(() => {
- console.log("EDIT SUCCESS");
+ console.log("Edit success!!");
success = true;
clearTimeout(interval);
DocumentLinksButton.invisibleWebDoc = undefined;
document.removeEventListener("editSuccess", onSuccess);
});
- console.log("send addLink");
const newHyperlink = `[${title}\n](${url})`;
const interval = setInterval(() => // keep trying to edit until annotations have loaded and editing is successful
!success && document.dispatchEvent(new CustomEvent<{ newHyperlink: string, id: string }>("addLink", {
@@ -73,22 +103,58 @@ export namespace Hypothesis {
clearInterval(interval);
DocumentLinksButton.invisibleWebDoc = undefined;
}
- }), 12000); // give up if no success after 12s
+ }), 10000); // give up if no success after 10s
+ document.addEventListener("editSuccess", onSuccess);
+ };
+
+ /**
+ * Send message Hypothes.is client request to edit an annotation to find and delete the target Dash hyperlink
+ */
+ export const deleteLink = async (linkDoc: Doc, sourceDoc: Doc, destinationDoc: Doc) => {
+ if (Cast(destinationDoc.data, WebField)?.url.href !== StrCast(linkDoc.annotationUri)) return; // check that the destinationDoc is a WebDocument containing the target annotation
+
+ !DocumentManager.Instance.getFirstDocumentView(destinationDoc) && runInAction(() => DocumentLinksButton.invisibleWebDoc = destinationDoc); // see note in makeLink
+
+ var success = false;
+ const onSuccess = action(() => {
+ console.log("Edit success!");
+ success = true;
+ clearTimeout(interval);
+ DocumentLinksButton.invisibleWebDoc = undefined;
+ document.removeEventListener("editSuccess", onSuccess);
+ });
+
+ const annotationId = StrCast(linkDoc.annotationId);
+ const linkUrl = Utils.prepend("/doc/" + sourceDoc[Id]);
+ const interval = setInterval(() => {// keep trying to edit until annotations have loaded and editing is successful
+ !success && document.dispatchEvent(new CustomEvent<{ targetUrl: string, id: string }>("deleteLink", {
+ detail: { targetUrl: linkUrl, id: annotationId },
+ bubbles: true
+ }));
+ }, 300);
+ setTimeout(action(() => {
+ if (!success) {
+ clearInterval(interval);
+ DocumentLinksButton.invisibleWebDoc = undefined;
+ }
+ }), 10000); // give up if no success after 10s
document.addEventListener("editSuccess", onSuccess);
};
+ /**
+ * Send message to Hypothes.is client to scroll to an annotation when it loads
+ */
export const scrollToAnnotation = (annotationId: string, target: Doc) => {
var success = false;
const onSuccess = () => {
- console.log("scroll success!!");
+ 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
@@ -101,57 +167,4 @@ export namespace Hypothesis {
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 = StrCast(e.detail.uri).split("#annotations:")[0]; // clean hypothes.is URLs that reference a specific annotation
- const isStart = (e.detail.isLinkStart === "true");
- 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
- 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 = 60;
- TaskCompletionBox.popupY = 60;
- TaskCompletionBox.taskCompleted = true;
-
- if (LinkDescriptionPopup.showDescriptions === "ON" || !LinkDescriptionPopup.showDescriptions) {
- LinkDescriptionPopup.popupX = 60;
- LinkDescriptionPopup.popupY = 93;
- LinkDescriptionPopup.descriptionPopup = true;
- }
- setTimeout(action(() => { TaskCompletionBox.taskCompleted = false; }), 2500);
- }
- });
- }
- };
} \ No newline at end of file
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 3cb4fbb17..8f9b9e0a0 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -880,14 +880,14 @@ export class MainView extends React.Component {
var success = false;
const onSuccess = () => {
- console.log("EDIT SUCCESS");
success = true;
clearTimeout(interval);
document.removeEventListener("editSuccess", onSuccess);
};
- const interval = setInterval(() => { // keep trying to click until annotations have loaded and editing is successful
- console.log("clicked");
+ // For some reason, Hypothes.is annotations don't load until a click is registered on the page,
+ // so we keep simulating clicks until annotations have loaded and editing is successful
+ const interval = setInterval(() => {
!success && simulateMouseClick(ele, 50, 50, 50, 50);
}, 500);
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 4d9ed358b..e045b351f 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -403,7 +403,6 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
}
if (uriList) {
const existingWebDoc = await Hypothesis.findWebDoc(uriList);
-
if (existingWebDoc) {
const alias = Doc.MakeAlias(existingWebDoc);
alias.x = options.x;
@@ -413,16 +412,17 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
alias._width = 400;
this.addDocument(alias);
} else {
- const cleanedUri = uriList.split("#annotations:")[0]; // clean hypothes.is URLs that reference a specific annotation (eg. https://en.wikipedia.org/wiki/Cartoon#annotations:t7qAeNbCEeqfG5972KR2Ig)
- this.addDocument(Docs.Create.WebDocument(uriList, {
+ const newDoc = Docs.Create.WebDocument(uriList, {
...options,
- title: cleanedUri,
+ title: uriList.split("#annotations:")[0],
_width: 400,
_height: 315,
_nativeWidth: 850,
_nativeHeight: 962,
UseCors: true
- }));
+ });
+ newDoc.data = new WebField(uriList.split("#annotations:")[0]); // clean hypothes.is URLs that reference a specific annotation (eg. https://en.wikipedia.org/wiki/Cartoon#annotations:t7qAeNbCEeqfG5972KR2Ig)
+ this.addDocument(newDoc);
}
return;
}
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index 17c2f42bf..de9e25f48 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -173,8 +173,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
@undoBatch
@action
deleteLink = (): void => {
- this.props.linkDoc.linksToAnnotation && Hypothesis.deleteLink(StrCast(this.props.linkDoc.annotationId), Utils.prepend("/doc/" + this.props.sourceDoc[Id])); // delete hyperlink in annotation
- this.props.linkDoc.linksToAnnotation && console.log("annotationId", this.props.linkDoc.annotationId);
+ this.props.linkDoc.linksToAnnotation && Hypothesis.deleteLink(this.props.linkDoc, this.props.sourceDoc, this.props.destinationDoc);
LinkManager.Instance.deleteLink(this.props.linkDoc);
runInAction(() => {
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index e8f7c8e9f..1e6f663d3 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -114,15 +114,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
} else if (DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document) {
const sourceDoc = DocumentLinksButton.StartLink;
const targetDoc = this.props.View.props.Document;
- const linkDoc = DocUtils.MakeLink({ doc: sourceDoc }, { doc: targetDoc }, DocumentLinksButton.AnnotationId ? "hypothes.is annotation" : "long drag");
-
- // currently possible to drag to complete links to annotations
- if (DocumentLinksButton.AnnotationId && DocumentLinksButton.AnnotationUri) {
- Doc.GetProto(linkDoc as Doc).linksToAnnotation = true;
- Doc.GetProto(linkDoc as Doc).annotationId = DocumentLinksButton.AnnotationId;
- Doc.GetProto(linkDoc as Doc).annotationUri = DocumentLinksButton.AnnotationUri;
- Hypothesis.makeLink(StrCast(targetDoc.title), Utils.prepend("/doc/" + targetDoc[Id]), DocumentLinksButton.AnnotationId, sourceDoc); // update and link placeholder annotation
- }
+ const linkDoc = DocUtils.MakeLink({ doc: sourceDoc }, { doc: targetDoc }, "long drag");
LinkManager.currentLink = linkDoc;
@@ -148,24 +140,29 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
}
})));
}
- finishLinkClick = undoBatch(action((screenX: number, screenY: number) => {
- if (DocumentLinksButton.StartLink === this.props.View.props.Document) {
+
+ public static finishLinkClick = undoBatch(action((screenX: number, screenY: number, startLink: Doc, endLink: Doc, startIsAnnotation: boolean, endLinkView?: DocumentView,) => {
+ if (startLink === endLink) {
DocumentLinksButton.StartLink = undefined;
DocumentLinksButton.AnnotationId = undefined;
DocumentLinksButton.AnnotationUri = undefined;
- } else if (!this.props.StartLink && DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document) {
- const linkDoc = DocUtils.MakeLink({ doc: DocumentLinksButton.StartLink }, { doc: this.props.View.props.Document }, DocumentLinksButton.AnnotationId ? "hypothes.is annotation" : "long drag");
+ //!this.props.StartLink
+ } else if (startLink !== endLink) {
+ const linkDoc = DocUtils.MakeLink({ doc: startLink }, { doc: endLink }, DocumentLinksButton.AnnotationId ? "hypothes.is annotation" : "long drag");
// this notifies any of the subviews that a document is made so that they can make finer-grained hyperlinks (). see note above in onLInkButtonMoved
- DocumentLinksButton.StartLink._link = this.props.View._link = linkDoc;
- setTimeout(action(() => DocumentLinksButton.StartLink!._link = this.props.View._link = undefined), 0);
+ if (endLinkView) {
+ startLink._link = endLinkView._link = linkDoc;
+ setTimeout(action(() => startLink._link = endLinkView._link = undefined), 0);
+ }
LinkManager.currentLink = linkDoc;
if (DocumentLinksButton.AnnotationId && DocumentLinksButton.AnnotationUri) { // if linking from a Hypothes.is annotation
- const targetDoc = this.props.View.props.Document;
Doc.GetProto(linkDoc as Doc).linksToAnnotation = true;
Doc.GetProto(linkDoc as Doc).annotationId = DocumentLinksButton.AnnotationId;
Doc.GetProto(linkDoc as Doc).annotationUri = DocumentLinksButton.AnnotationUri;
- Hypothesis.makeLink(StrCast(targetDoc.title), Utils.prepend("/doc/" + targetDoc[Id]), DocumentLinksButton.AnnotationId, DocumentLinksButton.StartLink); // edit annotation to add a Dash hyperlink to the linked doc
+ const dashHyperlink = Utils.prepend("/doc/" + (startIsAnnotation ? endLink[Id] : startLink[Id]));
+ Hypothesis.makeLink(StrCast(startIsAnnotation ? endLink.title : startLink.title), dashHyperlink, DocumentLinksButton.AnnotationId,
+ (startIsAnnotation ? startLink : endLink)); // edit annotation to add a Dash hyperlink to the linked doc
}
if (linkDoc) {
@@ -247,7 +244,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
border: DocumentLinksButton.StartLink ? "" : "none"
}}
onPointerDown={DocumentLinksButton.StartLink ? this.completeLink : emptyFunction}
- onClick={e => DocumentLinksButton.StartLink ? this.finishLinkClick(e.screenX, e.screenY) : emptyFunction} /> : (null)
+ onClick={e => DocumentLinksButton.StartLink ? DocumentLinksButton.finishLinkClick(e.screenX, e.screenY, DocumentLinksButton.StartLink, this.props.View.props.Document, true, this.props.View) : emptyFunction} /> : (null)
}
{
DocumentLinksButton.StartLink === this.props.View.props.Document && this.props.InMenu && this.props.StartLink ? <div className={"documentLinksButton-startLink"}
diff --git a/src/server/index.ts b/src/server/index.ts
index 40fc5222f..97a90825d 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -8,7 +8,6 @@ import DeleteManager from "./ApiManagers/DeleteManager";
import DownloadManager from './ApiManagers/DownloadManager';
import GeneralGoogleManager from "./ApiManagers/GeneralGoogleManager";
import GooglePhotosManager from "./ApiManagers/GooglePhotosManager";
-import HypothesisManager from "./ApiManagers/HypothesisManager";
import PDFManager from "./ApiManagers/PDFManager";
import { SearchManager } from './ApiManagers/SearchManager';
import SessionManager from "./ApiManagers/SessionManager";