diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/apis/hypothesis/HypothesisUtils.ts | 111 | ||||
-rw-r--r-- | src/client/views/MainView.tsx | 43 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/MarqueeView.tsx | 2 | ||||
-rw-r--r-- | src/client/views/linking/LinkMenuItem.tsx | 7 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentLinksButton.tsx | 38 |
5 files changed, 103 insertions, 98 deletions
diff --git a/src/client/apis/hypothesis/HypothesisUtils.ts b/src/client/apis/hypothesis/HypothesisUtils.ts index 855964bf6..5c6e4d31d 100644 --- a/src/client/apis/hypothesis/HypothesisUtils.ts +++ b/src/client/apis/hypothesis/HypothesisUtils.ts @@ -1,15 +1,45 @@ import { StrCast, Cast } from "../../../fields/Types"; import { SearchUtil } from "../../util/SearchUtil"; -import { action } from "mobx"; +import { action, runInAction } from "mobx"; import { Doc } from "../../../fields/Doc"; import { DocumentType } from "../../documents/DocumentTypes"; -import { Docs } from "../../documents/Documents"; +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 } from "../../../Utils"; +import { LinkDescriptionPopup } from "../../views/nodes/LinkDescriptionPopup"; +import { Id } from "../../../fields/FieldSymbols"; export namespace Hypothesis { + // Return web doc with the given uri, or create and create a new doc with the given uri + export const getSourceWebDoc = async (uri: string) => { + const currentDoc = SelectionManager.SelectedDocuments()[0].props.Document; + console.log(Cast(currentDoc.data, WebField)?.url.href === uri, uri, Cast(currentDoc.data, WebField)?.url.href); + if (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] : 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 + }; + // Send Hypothes.is client request to edit an annotation to add a Dash hyperlink export const makeLink = async (title: string, url: string, annotationId: string) => { + console.log("SEND addLink"); const newHyperlink = `[${title}\n](${url})`; document.dispatchEvent(new CustomEvent<{ newHyperlink: string, id: string }>("addLink", { detail: { newHyperlink: newHyperlink, id: annotationId }, @@ -25,22 +55,56 @@ export namespace Hypothesis { })); }; - // Construct an URL which will automatically scroll the web page to a specific annotation's position - export const makeAnnotationUrl = (annotationId: string, baseUrl: string) => { - console.log("baseUrl", baseUrl, annotationId); - return `${baseUrl}#annotations:${annotationId}`; - }; + // 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; - // Extract username from Hypothe.is's userId format - export const extractUsername = (userid: string) => { - const regex = new RegExp('(?<=\:)(.*?)(?=\@)/'); - return regex.exec(userid)![0]; + 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)); // 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); + } + }); + } }; - // Return corres - export const getSourceWebDoc = async (uri: string) => { + // Return web doc with the given uri, or create and create a new doc with the given uri + export const getSourceWebDocView = async (uri: string) => { const currentDoc = SelectionManager.SelectedDocuments()[0].props.Document; - if (StrCast(currentDoc.data) === uri) return currentDoc; // always check first whether the current doc is the source, only resort to Search otherwise + console.log(Cast(currentDoc.data, WebField)?.url.href === uri, uri, Cast(currentDoc.data, WebField)?.url.href); + if (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) => { @@ -48,12 +112,23 @@ export namespace Hypothesis { const filteredDocs = docs.filter(doc => doc.author === Doc.CurrentUserEmail && doc.type === DocumentType.WEB && doc.data ); - filteredDocs.forEach(doc => { uri === StrCast(doc.data) && results.push(doc); }); // TODO check history? imperfect matches? + 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))); + results.forEach(doc => { + const docView = DocumentManager.Instance.getFirstDocumentView(doc); + if (docView) { + console.log(doc.title, StrCast(doc.data)); + return docView; + } + }); + + return undefined; + }; - return results.length ? results[0] : Docs.Create.WebDocument(uri, { _nativeWidth: 850, _nativeHeight: 962, _width: 600 }); // create and return a new Web doc with given uri if no matching docs are found + export const createInvisibleDoc = (uri: string) => { + const newDoc = Docs.Create.WebDocument(uri, { _nativeWidth: 0, _nativeHeight: 0, _width: 0, UseCors: true }); }; export const scrollToAnnotation = (annotationId: string) => { @@ -71,7 +146,7 @@ export namespace Hypothesis { detail: annotationId, bubbles: true })); - }, 200); + }, 250); document.addEventListener('scrollSuccess', onSuccess); // listen for success message from client setTimeout(() => !success && clearTimeout(interval), 10000); // give up if no success after 10s diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 954b8b998..5c34233b4 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -104,47 +104,7 @@ export class MainView extends React.Component { } }); }); - document.addEventListener("linkAnnotationToDash", async (e: any) => { // listen for event from Hypothes.is plugin to link an annotation to Dash - const annotationId = e.detail.id; - const annotationUri = e.detail.uri; - const sourceDoc = await Hypothesis.getSourceWebDoc(annotationUri); - console.log("sourceDoc: ", sourceDoc.title); - - if (!DocumentLinksButton.StartLink) { // starts link only if there are none already started (else, a listener in DocumentLinksButton will handle link completion) - runInAction(() => { - DocumentLinksButton.AnnotationId = annotationId; - DocumentLinksButton.AnnotationUri = annotationUri; - DocumentLinksButton.StartLink = sourceDoc; - }); - } else { // if a link's already started in Dash, send event to DocumentLinksButton tofinish the link to the annotation - document.dispatchEvent(new CustomEvent<{ id: string, uri: string, sourceDoc: Doc }>("completeLinkToAnnotation", { - detail: { - id: annotationId, - uri: annotationUri, - sourceDoc: sourceDoc - }, - bubbles: true - })); - } - }); - - // reaction(() => SelectionManager.SelectedDocuments(), selected => { - // console.log("selection changed"); - // const selectedWebDocs = selected.map(docView => docView.props.Document).filter(doc => doc.type === DocumentType.WEB); - // const urls = selectedWebDocs.map(doc => Cast(doc.data, WebField)?.url.href).filter(url => url !== undefined); - // console.log("urls", urls); - - // const frame = document.getElementById('hyp_sidebar') as HTMLIFrameElement; - // console.log("contentwindow?", frame.contentDocument); - // if (frame.contentWindow) { - // frame.contentWindow.postMessage("hello sidebar", window.origin); - // } - - // document.dispatchEvent(new CustomEvent('showAnnotations', { - // detail: urls, - // bubbles: true - // })); - // }); + document.addEventListener("linkAnnotationToDash", Hypothesis.linkListener); } componentWillUnMount() { @@ -152,6 +112,7 @@ export class MainView extends React.Component { window.removeEventListener("pointerdown", this.globalPointerDown); window.removeEventListener("pointerup", this.globalPointerUp); window.removeEventListener("paste", KeyManager.Instance.paste as any); + document.removeEventListener("linkAnnotationToDash", Hypothesis.linkListener); } constructor(props: Readonly<{}>) { diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 764758eee..c3d81bda4 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -74,7 +74,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque if (e.key === "?") { ContextMenu.Instance.setDefaultItem("?", (str: string) => { const textDoc = Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { - _width: 200, x, y, _nativeHeight: 962, _nativeWidth: 800, isAnnotating: false, + _width: 200, x, y, _nativeHeight: 962, _nativeWidth: 850, isAnnotating: false, title: "bing", UseCors: true }); this.props.addDocTab(textDoc, "onRight"); diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index 8503bcbeb..40a16961a 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -11,7 +11,7 @@ import { ContextMenu } from '../ContextMenu'; import './LinkMenuItem.scss'; import React = require("react"); import { DocumentManager } from '../../util/DocumentManager'; -import { setupMoveUpEvents, emptyFunction, Utils } from '../../../Utils'; +import { setupMoveUpEvents, emptyFunction, Utils, simulateMouseClick } from '../../../Utils'; import { DocumentView } from '../nodes/DocumentView'; import { DocumentLinksButton } from '../nodes/DocumentLinksButton'; import { LinkDocPreview } from '../nodes/LinkDocPreview'; @@ -20,6 +20,7 @@ import { Id } from '../../../fields/FieldSymbols'; import { Tooltip } from '@material-ui/core'; import { DocumentType } from '../../documents/DocumentTypes'; import { undoBatch } from '../../util/UndoManager'; +import { WebField } from '../../../fields/URLField'; library.add(faEye, faEdit, faTimes, faArrowRight, faChevronDown, faChevronUp, faPencilAlt, faEyeSlash); @@ -156,7 +157,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> { const linkDoc = this.props.linkDoc; if (linkDoc.followLinkLocation === "openExternal" && this.props.destinationDoc.type === DocumentType.WEB) { - window.open(Hypothesis.makeAnnotationUrl(StrCast(linkDoc.annotationId), StrCast(linkDoc.annotationUri)), '_blank'); + window.open(`${StrCast(linkDoc.annotationUri)}#annotations:${StrCast(linkDoc.annotationId)}`, '_blank'); return; } @@ -244,7 +245,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> { <FontAwesomeIcon className="destination-icon" icon={destinationIcon} size="sm" /></div> <p className="linkMenu-destination-title" onPointerDown={this.followDefault}> - {this.props.linkDoc.linksToAnnotation ? "Annotation in" : ""} {title} + {this.props.linkDoc.linksToAnnotation && Cast(this.props.destinationDoc.data, WebField)?.url.href === this.props.linkDoc.annotationUri ? "Annotation in" : ""} {title} </p> </div> {this.props.linkDoc.description !== "" ? <p className="linkMenu-description"> diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index be7c3e135..3736cd3b2 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -40,35 +40,6 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp @observable public static AnnotationId: string | undefined; @observable public static AnnotationUri: string | undefined; - componentDidMount() { - document.addEventListener("completeLinkToAnnotation", this.onLinkFromAnnotation); - } - - componentWillUnmount() { - document.removeEventListener("completeLinkToAnnotation", this.onLinkFromAnnotation); - } - - @action - onLinkFromAnnotation = async (e: any) => { - const annotationId: string = e.detail.id; - const annotationUri: string = e.detail.uri; - const sourceDoc: Doc = e.detail.sourceDoc; - - // DocumentLinksButton.StartLink && this.props.View.props.Document.type === DocumentType.WEB && console.log( - // sourceDoc.title, - // this.props.View.props.Document.title, - // sourceDoc.data, - // this.props.View.props.Document.data, - // Doc.AreProtosEqual(sourceDoc, this.props.View.props.Document)); - - if (Doc.AreProtosEqual(sourceDoc, this.props.View.props.Document) && sourceDoc !== DocumentLinksButton.StartLink) { - DocumentLinksButton.AnnotationId = annotationId; - DocumentLinksButton.AnnotationUri = annotationUri; - this.finishLinkClick(500, 100); - console.log("completed link from annotation"); - } - } - @action @undoBatch onLinkButtonMoved = (e: PointerEvent) => { if (this.props.InMenu && this.props.StartLink) { @@ -181,15 +152,12 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp setTimeout(action(() => DocumentLinksButton.StartLink!._link = this.props.View._link = undefined), 0); LinkManager.currentLink = linkDoc; - // if the link is to a Hypothes.is annotation - if (DocumentLinksButton.AnnotationId && DocumentLinksButton.AnnotationUri) { - // figure out whether the StartLink doc or the current doc is the one to be linked to the annotation (the one NOT containing the annotation) - const toBeLinked: Doc = DocumentLinksButton.AnnotationUri === Cast(DocumentLinksButton.StartLink.data, WebField)?.url.href ? - this.props.View.props.Document : DocumentLinksButton.StartLink; + 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(toBeLinked.title), Utils.prepend("/doc/" + toBeLinked[Id]), DocumentLinksButton.AnnotationId); // update and link placeholder annotation + Hypothesis.makeLink(StrCast(targetDoc.title), Utils.prepend("/doc/" + targetDoc[Id]), DocumentLinksButton.AnnotationId); // edit annotation to add a Dash hyperlink to the linked doc } runInAction(() => { |