From 85c24ec4dcc39551e8c7de83d3482ec15472c030 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 31 Mar 2022 12:01:37 -0400
Subject: added autoLinks to formattedTextBoxes.  Set auto link target by
 titling documents with a prefix '@'.

---
 .../views/nodes/formattedText/FormattedTextBox.tsx | 48 ++++++++++++++--------
 1 file changed, 32 insertions(+), 16 deletions(-)

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

diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index c12c4acf0..ab2d84893 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -63,6 +63,7 @@ import React = require("react");
 import { SidebarAnnos } from '../../SidebarAnnos';
 import { Colors } from '../../global/globalEnums';
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
+import { LinkManager } from '../../../util/LinkManager';
 const translateGoogleApi = require("translate-google-api");
 
 export interface FormattedTextBoxProps {
@@ -346,7 +347,23 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         }
     }
 
+    autoLink = () => {
+        if (this._editorView) {
+            const newAutoLinks = new Set<Doc>();
+            const oldAutoLinks = DocListCast(this.props.Document.links).filter(link => link.linkRelationship = "automatic");
+            const f = this._editorView.state.selection.from;
+            const t = this._editorView.state.selection.to;
+            var tr = this._editorView.state.tr as any;
+            tr = tr.removeMark(0, tr.doc.content.size, this._editorView!.state.schema.marks.autoLinkAnchor);
+            DocListCast(Doc.UserDoc().myPublishedDocs).forEach(term => tr = this.hyperlinkTerm(tr, term, newAutoLinks));
+            tr = tr.setSelection(new TextSelection(tr.doc.resolve(f), tr.doc.resolve(t)));
+            this._editorView?.dispatch(tr);
+            oldAutoLinks.filter(oldLink => !newAutoLinks.has(oldLink)).forEach(LinkManager.Instance.deleteLink);
+        }
+    }
+
     updateTitle = () => {
+        const oldAutoLinks = DocListCast(this.props.Document.links).filter(link => link.linkRelationship = "automatic");
         if (!this.props.dontRegisterView &&  // (this.props.Document.isTemplateForField === "text" || !this.props.Document.isTemplateForField) && // only update the title if the data document's data field is changing
             StrCast(this.dataDoc.title).startsWith("-") && this._editorView && !this.dataDoc["title-custom"] &&
             (Doc.LayoutFieldKey(this.rootDoc) === this.fieldKey || this.fieldKey === "text")) {
@@ -357,26 +374,24 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         }
     }
 
-    // needs a better API for taking in a set of words with target documents instead of just one target
-    hyperlinkTerms = (terms: string[], target: Doc) => {
-        if (this._editorView && (this._editorView as any).docView && terms.some(t => t)) {
-            const res1 = terms.filter(t => t).map(term => this.findInNode(this._editorView!, this._editorView!.state.doc, term));
-            let tr = this._editorView.state.tr;
-            const flattened1: TextSelection[] = [];
-            res1.map(r => r.map(h => flattened1.push(h)));
+    // creates links between terms in a document and documents which have a matching Id
+    hyperlinkTerm = (tr: any, target: Doc, newAutoLinks: Set<Doc>) => {
+        if (this._editorView && (this._editorView as any).docView) {
+            const flattened1 = this.findInNode(this._editorView, this._editorView.state.doc, StrCast(target.title).replace(/^@/, ""));
+            var alink: Doc | undefined;
             flattened1.forEach((flat, i) => {
-                const flattened: TextSelection[] = [];
-                const res = terms.filter(t => t).map(term => this.findInNode(this._editorView!, this._editorView!.state.doc, term));
-                res.map(r => r.map(h => flattened.push(h)));
+                const flattened = this.findInNode(this._editorView!, this._editorView!.state.doc, StrCast(target.title).replace(/^@/, ""));
                 this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex;
-                const anchor = Docs.Create.TextanchorDocument();
-                const alink = DocUtils.MakeLink({ doc: anchor }, { doc: target }, "automatic")!;
-                const allAnchors = [{ href: Doc.localServerPath(anchor), title: "a link", anchorId: anchor[Id] }];
-                const link = this._editorView!.state.schema.marks.linkAnchor.create({ allAnchors, title: "auto link", location });
+                alink = alink ?? (DocListCast(this.Document.links).find(link =>
+                    Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), this.rootDoc) &&
+                    Doc.AreProtosEqual(Cast(link.anchor2, Doc, null), target)) || DocUtils.MakeLink({ doc: this.props.Document }, { doc: target }, "automatic")!);
+                newAutoLinks.add(alink);
+                const allAnchors = [{ href: Doc.localServerPath(this.props.Document), title: "a link", anchorId: this.props.Document[Id] }];
+                const link = this._editorView!.state.schema.marks.autoLinkAnchor.create({ allAnchors, linkDoc: alink[Id], title: "auto link", location });
                 tr = tr.addMark(flattened[i].from, flattened[i].to, link);
             });
-            this._editorView.dispatch(tr);
         }
+        return tr;
     }
     highlightSearchTerms = (terms: string[], backward: boolean) => {
         if (this._editorView && (this._editorView as any).docView && terms.some(t => t)) {
@@ -1256,7 +1271,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             !this.props.isSelected(true) && editor.dispatch(editor.state.tr.setSelection(new TextSelection(editor.state.doc.resolve(pcords?.pos || 0))));
             let target = (e.target as any).parentElement; // hrefs are stored on the database of the <a> node that wraps the hyerlink <span>
             while (target && !target.dataset?.targethrefs) target = target.parentElement;
-            FormattedTextBoxComment.update(this, editor, undefined, target?.dataset?.targethrefs);
+            FormattedTextBoxComment.update(this, editor, undefined, target?.dataset?.targethrefs, target?.dataset.linkdoc);
         }
         (e.nativeEvent as any).formattedHandled = true;
 
@@ -1399,6 +1414,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
 
     @action
     onBlur = (e: any) => {
+        this.autoLink();
         FormattedTextBox.Focused === this && (FormattedTextBox.Focused = undefined);
         if (RichTextMenu.Instance?.view === this._editorView && !this.props.isSelected(true)) {
             RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined);
-- 
cgit v1.2.3-70-g09d2

