From d489ed1071b989ffee1b476d15d36fb2dfb5797a Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 18 Oct 2020 21:41:31 -0400 Subject: resets scrollTop when navigating to a new webpage in WebBox. set url on proto's data field when creating a webPage, not delegate. --- src/client/views/collections/CollectionSubView.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/client/views/collections/CollectionSubView.tsx') diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index f3e563422..f0cf54db4 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -406,7 +406,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: this.addDocument(alias); } else { console.log("Adding ..."); - const newDoc = Docs.Create.WebDocument(uriList, { + const newDoc = Docs.Create.WebDocument(uriList.split("#annotations:")[0], {// clean hypothes.is URLs that reference a specific annotation (eg. https://en.wikipedia.org/wiki/Cartoon#annotations:t7qAeNbCEeqfG5972KR2Ig) ...options, _fitWidth: true, title: uriList.split("#annotations:")[0], @@ -416,7 +416,6 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: useCors: true }); console.log(" ... " + newDoc.title); - 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) console.log(" ... " + this.addDocument(newDoc) + " " + newDoc.title); } return; -- cgit v1.2.3-70-g09d2 From b75fc9edafb72a6cf5b1e15a3d2b5300c053f92f Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 22 Oct 2020 01:38:03 -0400 Subject: fixed webBoxes to switch URLS when hyperlink is clicked (and page is not cross-origin). fixed some undo navigation web stuff. fixed drag/drop to create anchor that bounds the original selection. fixed html clippings to not have a nativewith/height so that they reflow --- src/client/documents/Documents.ts | 2 +- src/client/views/collections/CollectionSubView.tsx | 14 +++++++++----- src/client/views/nodes/WebBox.tsx | 20 +++++++++++++------- 3 files changed, 23 insertions(+), 13 deletions(-) (limited to 'src/client/views/collections/CollectionSubView.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index d7c9af1a3..d7af88b72 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1101,7 +1101,7 @@ export namespace DocUtils { }); } ctor = Docs.Create.WebDocument; - options = { ...options, _fitWidth: true, _nativeWidth: 850, _width: 400, _height: 512, title: path, }; + options = { ...options, _fitWidth: true, _width: 400, _height: 512, title: path, }; } return ctor ? ctor(path, options) : undefined; } diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index f0cf54db4..5c3c70be2 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -344,12 +344,16 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: Doc.GetProto(htmlDoc)["data-text"] = Doc.GetProto(htmlDoc).text = text; this.props.addDocument(htmlDoc); if (srcWeb) { - const focusNode = (SelectionManager.SelectedDocuments()[0].ContentDiv?.getElementsByTagName("iframe")?.[0]?.contentDocument?.getSelection()?.focusNode as any); + const iframe = SelectionManager.SelectedDocuments()[0].ContentDiv?.getElementsByTagName("iframe")?.[0]; + const focusNode = (iframe?.contentDocument?.getSelection()?.focusNode as any); if (focusNode) { - const rect = "getBoundingClientRect" in focusNode ? focusNode.getBoundingClientRect() : focusNode?.parentElement.getBoundingClientRect(); - const x = (rect?.x || 0); - const y = NumCast(srcWeb._scrollTop) + (rect?.y || 0); - const anchor = Docs.Create.FreeformDocument([], { _backgroundColor: "transparent", _width: 75, _height: 40, x, y, annotationOn: srcWeb }); + const rects = iframe?.contentWindow?.getSelection()?.getRangeAt(0).getClientRects(); + "getBoundingClientRect" in focusNode ? focusNode.getBoundingClientRect() : focusNode?.parentElement.getBoundingClientRect(); + const x = (rects && Array.from(rects).reduce((x: any, r: DOMRect) => x === undefined || r.x < x ? r.x : x, undefined as any)) || 0; + const y = NumCast(srcWeb._scrollTop) + ((rects && Array.from(rects).reduce((y: any, r: DOMRect) => y === undefined || r.y < y ? r.y : y, undefined as any)) || 0); + const r = (rects && Array.from(rects).reduce((x: any, r: DOMRect) => x === undefined || r.x + r.width > x ? r.x + r.width : x, undefined as any)) || 0; + const b = NumCast(srcWeb._scrollTop) + ((rects && Array.from(rects).reduce((y: any, r: DOMRect) => y === undefined || r.y + r.height > y ? r.y + r.height : y, undefined as any)) || 0); + const anchor = Docs.Create.FreeformDocument([], { _backgroundColor: "transparent", _width: r - x, _height: b - y, x, y, annotationOn: srcWeb }); anchor.context = srcWeb; const key = Doc.LayoutFieldKey(srcWeb); Doc.AddDocToList(srcWeb, key + "-annotations", anchor); diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index eb9e08e20..168fee807 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -72,14 +72,23 @@ export class WebBox extends ViewBoxAnnotatableComponent { const iframe = this._iframeRef.current; if (iframe && iframe.contentDocument) { iframe.setAttribute("enable-annotation", "true"); + iframe.contentDocument.addEventListener("click", undoBatch(action(e => { + const href = (e.target as any)?.href; + if (href) { + this._url = href.replace(Utils.prepend(""), Cast(this.dataDoc[this.fieldKey], WebField, null)?.url.origin); + this.submitURL(); + } + }))); iframe.contentDocument.addEventListener('pointerdown', this.iframedown, false); iframe.contentDocument.addEventListener('scroll', this.iframeScrolled, false); this.layoutDoc.scrollHeight = iframe.contentDocument.children?.[0].scrollHeight || 1000; @@ -178,14 +187,11 @@ export class WebBox extends ViewBoxAnnotatableComponent) => { - this._url = e.target.value; - } - onUrlDragover = (e: React.DragEvent) => { e.preventDefault(); } + + @undoBatch @action onUrlDrop = (e: React.DragEvent) => { const { dataTransfer } = e; -- cgit v1.2.3-70-g09d2 From 040d65234f52dc3337483acdb2c1c6d0cc8fefa2 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 22 Oct 2020 13:30:01 -0400 Subject: added alert for uknown filetype uploads --- src/client/util/CurrentUserUtils.ts | 3 +++ src/client/views/collections/CollectionSubView.tsx | 2 ++ 2 files changed, 5 insertions(+) (limited to 'src/client/views/collections/CollectionSubView.tsx') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 38bfe3fb6..07447b93c 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1124,6 +1124,9 @@ export class CurrentUserUtils { const disposer = OverlayView.ShowSpinner(); DocListCastAsync(importDocs.data).then(async list => { const results = await DocUtils.uploadFilesToDocs(Array.from(input.files || []), {}); + if (results.length !== input.files?.length) { + alert("Error uploading files - possibly due to unsupported file types") + } list?.splice(0, 0, ...results); disposer(); }); diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 5c3c70be2..0dd033e47 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -482,6 +482,8 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: } else { if (text && !text.includes("https://")) { UndoManager.RunInBatch(() => this.addDocument(Docs.Create.TextDocument(text, { ...options, title: text.substring(0, 20), _width: 400, _height: 315 })), "drop"); + } else { + alert("Document updloaded failed - possibly an unsupported file type.") } } disposer(); -- cgit v1.2.3-70-g09d2 From 749873972fba8ed8d5b34b73b43947d6356b882e Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 22 Oct 2020 22:23:39 -0400 Subject: added more caching for linkManager queries. --- src/client/util/CurrentUserUtils.ts | 4 +- src/client/util/LinkManager.ts | 106 ++++++--------------- src/client/views/collections/CollectionMenu.tsx | 2 +- src/client/views/collections/CollectionSubView.tsx | 2 +- src/client/views/collections/TabDocView.tsx | 2 +- 5 files changed, 33 insertions(+), 83 deletions(-) (limited to 'src/client/views/collections/CollectionSubView.tsx') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 07447b93c..5886aa13f 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1125,7 +1125,7 @@ export class CurrentUserUtils { DocListCastAsync(importDocs.data).then(async list => { const results = await DocUtils.uploadFilesToDocs(Array.from(input.files || []), {}); if (results.length !== input.files?.length) { - alert("Error uploading files - possibly due to unsupported file types") + alert("Error uploading files - possibly due to unsupported file types"); } list?.splice(0, 0, ...results); disposer(); @@ -1212,7 +1212,5 @@ Scripting.addGlobal(function createNewPresentation() { return MainView.Instance. "creates a new presentation when called"); Scripting.addGlobal(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); }, "returns all the links to the document or its annotations", "(doc: any)"); -Scripting.addGlobal(function directLinks(doc: any) { return new List(LinkManager.Instance.getAllDirectLinks(doc)); }, - "returns all the links directly to the document", "(doc: any)"); Scripting.addGlobal(function importDocument() { return CurrentUserUtils.importDocument(); }, "imports files from device directly into the import sidebar"); diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 1ba6cff6d..c3ab7c6e7 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -25,94 +25,52 @@ export class LinkManager { public static currentLink: Opt; - public static get Instance(): LinkManager { - return this._instance || (this._instance = new this()); - } - - private constructor() { - } - + public static get Instance(): LinkManager { return this._instance || (this._instance = new this()); } - public getAllLinks(): Doc[] { - const lset = new Set(DocListCast(Doc.LinkDBDoc().data)); - SharingManager.Instance.users.forEach(user => { - DocListCast(user.linkDatabase?.data).map(doc => { - lset.add(doc); - }); - }); - return Array.from(lset); - } + public addLink(linkDoc: Doc) { return Doc.AddDocToList(Doc.LinkDBDoc(), "data", linkDoc); } - public addLink(linkDoc: Doc): boolean { - return Doc.AddDocToList(Doc.LinkDBDoc(), "data", linkDoc); - } + public deleteLink(linkDoc: Doc) { return Doc.RemoveDocFromList(Doc.LinkDBDoc(), "data", linkDoc); } - public deleteLink(linkDoc: Doc): boolean { - return Doc.RemoveDocFromList(Doc.LinkDBDoc(), "data", linkDoc); - } + public deleteAllLinksOnAnchor(anchor: Doc) { LinkManager.Instance.getAllRelatedLinks(anchor).forEach(linkDoc => LinkManager.Instance.deleteLink(linkDoc)); } - // finds all links that contain the given anchor - public getAllDirectLinks(anchor: Doc): Doc[] { - const related = LinkManager.Instance.getAllLinks().filter(link => link).filter(link => { - const a1 = Cast(link.anchor1, Doc, null); - const a2 = Cast(link.anchor2, Doc, null); - const protomatch1 = Doc.AreProtosEqual(anchor, a1); - const protomatch2 = Doc.AreProtosEqual(anchor, a2); - return ((a1?.author !== undefined && a2?.author !== undefined) || link.author === Doc.CurrentUserEmail) && (protomatch1 || protomatch2 || Doc.AreProtosEqual(link, anchor)); - }); - return related; - } + public getAllRelatedLinks(anchor: Doc) { return this.relatedLinker(anchor); } // finds all links that contain the given anchor - relatedLinker = computedFn(function realtedLinker(this: any, anchor: Doc) { - const related = LinkManager.Instance.getAllDirectLinks(anchor); - DocListCast(anchor[Doc.LayoutFieldKey(anchor) + "-annotations"]).map(anno => { - related.push(...LinkManager.Instance.getAllRelatedLinks(anno)); - }); - return related; - }.bind(this), true); + public getAllDirectLinks(anchor: Doc): Doc[] { return this.directLinker(anchor); } // finds all links that contain the given anchor - // finds all links that contain the given anchor - public getAllRelatedLinks(anchor: Doc): Doc[] { - return this.relatedLinker(anchor); - } + public getAllLinks(): Doc[] { return this.allLinks(); } - public deleteAllLinksOnAnchor(anchor: Doc) { - const related = LinkManager.Instance.getAllRelatedLinks(anchor); - related.forEach(linkDoc => LinkManager.Instance.deleteLink(linkDoc)); - } + allLinks = computedFn(function allLinks(this: any) { + const lset = new Set(DocListCast(Doc.LinkDBDoc().data)); + SharingManager.Instance.users.forEach(user => DocListCast(user.linkDatabase?.data).map(doc => lset.add(doc))); + return Array.from(lset); + }, true); - // gets the groups associates with an anchor in a link - public getAnchorGroups(linkDoc: Doc, anchor?: Doc): Array { - if (Doc.AreProtosEqual(anchor, Cast(linkDoc.anchor1, Doc, null))) { - return DocListCast(linkDoc.anchor1Groups); - } else { - return DocListCast(linkDoc.anchor2Groups); - } - } - public addGroupToAnchor(linkDoc: Doc, anchor: Doc, groupDoc: Doc, replace: boolean = false) { - Doc.GetProto(linkDoc).linkRelationship = groupDoc.linkRelationship; - } + directLinker = computedFn(function directLinker(this: any, anchor: Doc) { + return LinkManager.Instance.getAllLinks().filter(link => { + const a1 = Cast(link?.anchor1, Doc, null); + const a2 = Cast(link?.anchor2, Doc, null); + return link && ((a1?.author !== undefined && a2?.author !== undefined) || link.author === Doc.CurrentUserEmail) && (Doc.AreProtosEqual(anchor, a1) || Doc.AreProtosEqual(anchor, a2) || Doc.AreProtosEqual(link, anchor)); + }); + }, true); - // removes group doc of given group type only from given anchor on given link - public removeGroupFromAnchor(linkDoc: Doc, anchor: Doc, groupType: string) { - Doc.GetProto(linkDoc).linkRelationship = "-ungrouped-"; - } + relatedLinker = computedFn(function relatedLinker(this: any, anchor: Doc) { + const related = LinkManager.Instance.directLinker(anchor); + DocListCast(anchor[Doc.LayoutFieldKey(anchor) + "-annotations"]).map(anno => related.push(...LinkManager.Instance.getAllRelatedLinks(anno))); + return related; + }, true); // returns map of group type to anchor's links in that group type public getRelatedGroupedLinks(anchor: Doc): Map> { - const related = this.getAllRelatedLinks(anchor); const anchorGroups = new Map>(); - related.forEach(link => { + this.getAllRelatedLinks(anchor).forEach(link => { if (!link.linkRelationship || link?.linkRelationship !== "-ungrouped-") { const group = anchorGroups.get(StrCast(link.linkRelationship)); anchorGroups.set(StrCast(link.linkRelationship), group ? [...group, link] : [link]); - } else { // if link is in no groups then put it in default group const group = anchorGroups.get("*"); anchorGroups.set("*", group ? [...group, link] : [link]); } - }); return anchorGroups; } @@ -120,21 +78,15 @@ export class LinkManager { // returns a list of all metadata docs associated with the given group type public getAllMetadataDocsInGroup(groupType: string): Array { const md: Doc[] = []; - const allLinks = LinkManager.Instance.getAllLinks(); - allLinks.forEach(linkDoc => { - if (StrCast(linkDoc.linkRelationship).toUpperCase() === groupType.toUpperCase()) { md.push(linkDoc); } - }); + LinkManager.Instance.getAllLinks().forEach(linkDoc => StrCast(linkDoc.linkRelationship).toUpperCase() === groupType.toUpperCase() && md.push(linkDoc)); return md; } // checks if a link with the given anchors exists public doesLinkExist(anchor1: Doc, anchor2: Doc): boolean { - const allLinks = LinkManager.Instance.getAllLinks(); - const index = allLinks.findIndex(linkDoc => { - return (Doc.AreProtosEqual(Cast(linkDoc.anchor1, Doc, null), anchor1) && Doc.AreProtosEqual(Cast(linkDoc.anchor2, Doc, null), anchor2)) || - (Doc.AreProtosEqual(Cast(linkDoc.anchor1, Doc, null), anchor2) && Doc.AreProtosEqual(Cast(linkDoc.anchor2, Doc, null), anchor1)); - }); - return index !== -1; + return -1 !== LinkManager.Instance.getAllLinks().findIndex(linkDoc => + (Doc.AreProtosEqual(Cast(linkDoc.anchor1, Doc, null), anchor1) && Doc.AreProtosEqual(Cast(linkDoc.anchor2, Doc, null), anchor2)) || + (Doc.AreProtosEqual(Cast(linkDoc.anchor1, Doc, null), anchor2) && Doc.AreProtosEqual(Cast(linkDoc.anchor2, Doc, null), anchor1))); } // finds the opposite anchor of a given anchor in a link diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index fed9be062..abad23e0d 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -429,7 +429,7 @@ export class CollectionViewBaseChrome extends React.Component; const targetDoc = this.selectedDoc; {/* return (!targetDoc || (targetDoc._viewType !== CollectionViewType.Freeform && targetDoc.type !== DocumentType.IMG)) ? (null) :
{"Pin to presentation trail with current view"}
} placement="top"> */ } - return (targetDoc && targetDoc.type !== DocumentType.PRES && (targetDoc._viewType === CollectionViewType.Freeform || targetDoc._viewType === CollectionViewType.Stacking || targetDoc.type === DocumentType.VID || targetDoc.type === DocumentType.IMG || targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.VIDEO || targetDoc.type === DocumentType.RTF || targetDoc.type === DocumentType.COMPARISON)) ?
{"Pin to presentation trail with current view"}
} placement="top"> + return (targetDoc && targetDoc.type !== DocumentType.PRES && (targetDoc._viewType === CollectionViewType.Freeform || targetDoc._viewType === CollectionViewType.Stacking || targetDoc.type === DocumentType.VID || targetDoc.type === DocumentType.IMG || targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.VID || targetDoc.type === DocumentType.RTF || targetDoc.type === DocumentType.COMPARISON)) ?
{"Pin to presentation trail with current view"}
} placement="top">