From 1763ff090ea734ca275c3c28edc6c303c935b801 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 25 Jan 2023 12:59:50 -0500
Subject: added a linkFollow o[ption to choose between opening target, or
 highest level collection containing target. fixed adding marker annotations
 to pdf/web/etc.  fixed following link to wikipedia pages to not create a new
 document each time.  made searchBox's search function static so that it can
 be called programmatically. Fixed LinkDocPreview to not flicker when doing a
 nopreview link follow.  changed PlayTrail to restore state of all freeform
 collections containing source anchor.

---
 src/client/views/nodes/LinkDocPreview.tsx | 72 ++++++++++++++++++-------------
 1 file changed, 43 insertions(+), 29 deletions(-)

(limited to 'src/client/views/nodes/LinkDocPreview.tsx')

diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 7eb42994b..1eab06381 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -4,7 +4,7 @@ import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import wiki from 'wikijs';
 import { Doc, DocCastAsync, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
-import { Cast, NumCast, StrCast } from '../../../fields/Types';
+import { Cast, DocCast, NumCast, PromiseValue, StrCast } from '../../../fields/Types';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnNone, setupMoveUpEvents } from '../../../Utils';
 import { DocServer } from '../../DocServer';
 import { Docs, DocUtils } from '../../documents/Documents';
@@ -17,6 +17,8 @@ import { Transform } from '../../util/Transform';
 import { DocumentView, DocumentViewSharedProps, OpenWhere } from './DocumentView';
 import './LinkDocPreview.scss';
 import React = require('react');
+import { SearchUtil } from '../../util/SearchUtil';
+import { SearchBox } from '../search/SearchBox';
 
 interface LinkDocPreviewProps {
     linkDoc?: Doc;
@@ -36,6 +38,8 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
         LinkDocPreview.LinkInfo !== info && (LinkDocPreview.LinkInfo = info);
     }
 
+    static _instance: Opt<LinkDocPreview>;
+
     _infoRef = React.createRef<HTMLDivElement>();
     _linkDocRef = React.createRef<HTMLDivElement>();
     @observable public static LinkInfo: Opt<LinkDocPreviewProps>;
@@ -46,6 +50,11 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
     @observable _toolTipText = '';
     @observable _hrefInd = 0;
 
+    constructor(props: any) {
+        super(props);
+        LinkDocPreview._instance = this;
+    }
+
     @action init() {
         var linkTarget = this.props.linkDoc;
         this._linkSrc = this.props.linkSrc;
@@ -62,6 +71,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
             this._markerTargetDoc = this._targetDoc = linkTarget;
         }
         this._toolTipText = '';
+        this.updateHref();
     }
     componentDidUpdate(props: any) {
         if (props.linkSrc !== this.props.linkSrc || props.linkDoc !== this.props.linkDoc || props.hrefs !== this.props.hrefs) this.init();
@@ -71,6 +81,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
         document.addEventListener('pointerdown', this.onPointerDown, true);
     }
 
+    @action
     componentWillUnmount() {
         LinkDocPreview.SetLinkInfo(undefined);
         document.removeEventListener('pointerdown', this.onPointerDown, true);
@@ -80,7 +91,8 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
         !this._linkDocRef.current?.contains(e.target as any) && LinkDocPreview.Clear(); // close preview when not clicking anywhere other than the info bar of the preview
     };
 
-    @computed get href() {
+    @action
+    updateHref() {
         if (this.props.hrefs?.length) {
             const href = this.props.hrefs[this._hrefInd];
             if (href.indexOf(Doc.localServerPath()) !== 0) {
@@ -90,33 +102,33 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                         .page(href.replace('https://en.wikipedia.org/wiki/', ''))
                         .then(page => page.summary().then(action(summary => (this._toolTipText = summary.substring(0, 500)))));
                 } else {
-                    setTimeout(action(() => (this._toolTipText = 'url => ' + href)));
+                    this._toolTipText = 'url => ' + href;
                 }
             } else {
                 // hyperlink to a document .. decode doc id and retrieve from the server. this will trigger vals() being invalidated
-                const anchorDoc = href.replace(Doc.localServerPath(), '').split('?')[0];
-                anchorDoc &&
-                    DocServer.GetRefField(anchorDoc).then(
-                        action(anchor => {
-                            if (anchor instanceof Doc && DocListCast(anchor.links).length) {
-                                this._linkDoc = this._linkDoc ?? DocListCast(anchor.links)[0];
-                                const automaticLink = this._linkDoc.linkRelationship === LinkManager.AutoKeywords;
-                                if (automaticLink) {
-                                    // automatic links specify the target in the link info, not the source
-                                    const linkTarget = anchor;
-                                    this._linkSrc = LinkManager.getOppositeAnchor(this._linkDoc, linkTarget);
-                                    this._markerTargetDoc = this._targetDoc = linkTarget;
-                                } else {
-                                    this._linkSrc = anchor;
-                                    const linkTarget = LinkManager.getOppositeAnchor(this._linkDoc, this._linkSrc);
-                                    this._markerTargetDoc = linkTarget;
-                                    this._targetDoc = /*linkTarget?.type === DocumentType.MARKER &&*/ linkTarget?.annotationOn ? Cast(linkTarget.annotationOn, Doc, null) ?? linkTarget : linkTarget;
-                                }
-                                this._toolTipText = '';
-                                if (LinkDocPreview.LinkInfo?.noPreview || this._linkSrc?.followLinkToggle || this._markerTargetDoc?.type === DocumentType.PRES) this.followLink();
+                const anchorDocId = href.replace(Doc.localServerPath(), '').split('?')[0];
+                const anchorDoc = anchorDocId ? PromiseValue(DocCast(DocServer.GetCachedRefField(anchorDocId) ?? DocServer.GetRefField(anchorDocId))) : undefined;
+                anchorDoc?.then?.(
+                    action(anchor => {
+                        if (anchor instanceof Doc && DocListCast(anchor.links).length) {
+                            this._linkDoc = this._linkDoc ?? DocListCast(anchor.links)[0];
+                            const automaticLink = this._linkDoc.linkRelationship === LinkManager.AutoKeywords;
+                            if (automaticLink) {
+                                // automatic links specify the target in the link info, not the source
+                                const linkTarget = anchor;
+                                this._linkSrc = LinkManager.getOppositeAnchor(this._linkDoc, linkTarget);
+                                this._markerTargetDoc = this._targetDoc = linkTarget;
+                            } else {
+                                this._linkSrc = anchor;
+                                const linkTarget = LinkManager.getOppositeAnchor(this._linkDoc, this._linkSrc);
+                                this._markerTargetDoc = linkTarget;
+                                this._targetDoc = /*linkTarget?.type === DocumentType.MARKER &&*/ linkTarget?.annotationOn ? Cast(linkTarget.annotationOn, Doc, null) ?? linkTarget : linkTarget;
                             }
-                        })
-                    );
+                            this._toolTipText = 'link to ' + this._targetDoc?.title;
+                            if (LinkDocPreview.LinkInfo?.noPreview || this._linkSrc?.followLinkToggle || this._markerTargetDoc?.type === DocumentType.PRES) this.followLink();
+                        }
+                    })
+                );
             }
             return href;
         }
@@ -158,11 +170,14 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
     };
 
     followLink = () => {
+        LinkDocPreview.Clear();
         if (this._linkDoc && this._linkSrc) {
-            LinkDocPreview.Clear();
             LinkFollower.FollowLink(this._linkDoc, this._linkSrc, this.props.docProps, false);
         } else if (this.props.hrefs?.length) {
-            this.props.docProps?.addDocTab(Docs.Create.WebDocument(this.props.hrefs[0], { title: this.props.hrefs[0], _nativeWidth: 850, _width: 200, _height: 400, useCors: true }), OpenWhere.addRight);
+            const webDoc =
+                Array.from(SearchBox.staticSearchCollection(Doc.MyFilesystem, this.props.hrefs[0]).keys()).lastElement() ??
+                Docs.Create.WebDocument(this.props.hrefs[0], { title: this.props.hrefs[0], _nativeWidth: 850, _width: 200, _height: 400, useCors: true });
+            this.props.docProps?.addDocTab(webDoc, OpenWhere.lightbox);
         }
     };
 
@@ -208,7 +223,6 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
     }
 
     @computed get docPreview() {
-        const href = this.href; // needs to be here to trigger lookup of web pages and docs on server
         return (!this._linkDoc || !this._targetDoc || !this._linkSrc) && !this._toolTipText ? null : (
             <div className="linkDocPreview-inner">
                 {!this.props.showHeader ? null : this.previewHeader}
@@ -285,7 +299,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                 className="linkDocPreview"
                 ref={this._linkDocRef}
                 onPointerDown={this.followLinkPointerDown}
-                style={{ left: this.props.location[0], top: this.props.location[1], width: this.width() + borders, height: this.height() + borders + (this.props.showHeader ? 37 : 0) }}>
+                style={{ display: !this._toolTipText ? 'none' : undefined, left: this.props.location[0], top: this.props.location[1], width: this.width() + borders, height: this.height() + borders + (this.props.showHeader ? 37 : 0) }}>
                 {this.docPreview}
             </div>
         );
-- 
cgit v1.2.3-70-g09d2

