From d818ef151ca65008e5c6bb5e92b709decb3026d8 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 14 Apr 2025 18:35:49 -0400
Subject: fixed how templates are expanded to avoid template sub-component
 conflicts by changing how field keys are named.   fixed various Cast
 functions to be more typesafe by including undefined as part of return type. 
 overhaul of Doc.MakeClone, MakeCopy, FindRefernces - makeClone is no longer
 async.  fixed inlined docs in text docs.

---
 .../views/nodes/formattedText/FormattedTextBox.tsx | 55 ++++++++++++----------
 1 file changed, 31 insertions(+), 24 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 cfb6dfa12..dc23a695d 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -214,9 +214,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
     }
 
     getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
-        const rootDoc = Doc.isTemplateDoc(this._props.docViewPath().lastElement()?.Document) ? this.Document : this.rootDoc;
-        if (!pinProps && this.EditorView?.state.selection.empty) return rootDoc;
-        const anchor = Docs.Create.ConfigDocument({ title: StrCast(rootDoc.title), annotationOn: rootDoc });
+        if (!pinProps && this.EditorView?.state.selection.empty) return this.rootDoc;
+        const anchor = Docs.Create.ConfigDocument({ title: StrCast(this.rootDoc?.title), annotationOn: this.rootDoc });
         this.addDocument(anchor);
         this._finishingLink = true;
         this.makeLinkAnchor(anchor, OpenWhere.addRight, undefined, 'Anchored Selection', false, addAsAnnotation);
@@ -245,7 +244,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                     anchor.followLinkAudio = true;
                     let stopFunc: () => void = emptyFunction;
                     target.$mediaState = mediaState.Recording;
-                    DictationManager.recordAudioAnnotation(target, Doc.LayoutFieldKey(target), stop => { stopFunc = stop }); // prettier-ignore
+                    DictationManager.recordAudioAnnotation(target, Doc.LayoutDataKey(target), stop => { stopFunc = stop }); // prettier-ignore
 
                     const reactionDisposer = reaction(
                         () => target.mediaState,
@@ -427,8 +426,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         const oldAutoLinks = Doc.Links(this.Document).filter(
             link => 
                 ((!Doc.isTemplateForField(this.Document) &&
-                    (!Doc.isTemplateForField(DocCast(link.link_anchor_1)) || !Doc.AreProtosEqual(DocCast(link.link_anchor_1), this.Document)) &&
-                    (!Doc.isTemplateForField(DocCast(link.link_anchor_2)) || !Doc.AreProtosEqual(DocCast(link.link_anchor_2), this.Document))) ||
+                    ((DocCast(link.link_anchor_1) && !Doc.isTemplateForField(DocCast(link.link_anchor_1)!)) || !Doc.AreProtosEqual(DocCast(link.link_anchor_1), this.Document)) &&
+                    ((DocCast(link.link_anchor_2) && !Doc.isTemplateForField(DocCast(link.link_anchor_2)!)) || !Doc.AreProtosEqual(DocCast(link.link_anchor_2), this.Document))) ||
                  (Doc.isTemplateForField(this.Document) && (link.link_anchor_1 === this.Document || link.link_anchor_2 === this.Document))) &&
                 link.link_relationship === LinkManager.AutoKeywords
         ); // prettier-ignore
@@ -450,18 +449,18 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
     updateTitle = () => {
         const title = StrCast(this.dataDoc.title, Cast(this.dataDoc.title, RichTextField, null)?.Text);
         if (
-            !this._props.dontRegisterView && // (this.Document.isTemplateForField === "text" || !this.Document.isTemplateForField) && // only update the title if the data document's data field is changing
+            !this._props.dontRegisterView && // only update the title if the data document's data field is changing
             title.startsWith('-') &&
             this.EditorView &&
             !this.dataDoc.title_custom &&
-            (Doc.LayoutFieldKey(this.Document) === this.fieldKey || this.fieldKey === 'text')
+            (Doc.LayoutDataKey(this.Document) === this.fieldKey || this.fieldKey === 'text')
         ) {
             let node = this.EditorView.state.doc;
             while (node.firstChild && node.firstChild.type.name !== 'text') node = node.firstChild;
             const str = node.textContent;
             const prefix = '-';
 
-            const cfield = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc.title));
+            const cfield = ComputedField.DisableCompute(() => FieldValue(this.dataDoc.title));
             if (!(cfield instanceof ComputedField)) {
                 this.dataDoc.title = (prefix + str.substring(0, Math.min(40, str.length)) + (str.length > 40 ? '...' : '')).trim();
             }
@@ -588,7 +587,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         }
         const dragData = de.complete.docDragData;
         if (dragData) {
-            const dataDoc = Doc.IsDelegateField(DocCast(this.layoutDoc.proto), this.fieldKey) ? DocCast(this.layoutDoc.proto) : this.dataDoc;
+            const layoutProto = DocCast(this.layoutDoc.proto);
+            const dataDoc = layoutProto && Doc.IsDelegateField(layoutProto, this.fieldKey) ? layoutProto : this.dataDoc;
             const effectiveAcl = GetEffectiveAcl(dataDoc);
             const draggedDoc = dragData.droppedDocuments.lastElement();
             let added: Opt<boolean>;
@@ -596,7 +596,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
             if ([AclEdit, AclAdmin, AclSelfEdit].includes(effectiveAcl) && !dragData.draggedDocuments.includes(this.Document)) {
                 // replace text contents when dragging with Alt
                 if (de.altKey) {
-                    const fieldKey = Doc.LayoutFieldKey(draggedDoc);
+                    const fieldKey = Doc.LayoutDataKey(draggedDoc);
                     if (draggedDoc[fieldKey] instanceof RichTextField && !Doc.AreProtosEqual(draggedDoc, this.Document)) {
                         Doc.GetProto(this.dataDoc)[this.fieldKey] = Field.Copy(draggedDoc[fieldKey]);
                     }
@@ -1003,16 +1003,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                 const link = CreateLinkToActiveAudio(textanchorFunc, false).lastElement();
                 if (link) {
                     link.$isDictation = true;
-                    const audioanchor = Cast(link.link_anchor_2, Doc, null);
-                    const textanchor = Cast(link.link_anchor_1, Doc, null);
+                    const audioanchor = DocCast(link.link_anchor_2);
+                    const textanchor = DocCast(link.link_anchor_1);
                     if (audioanchor) {
                         audioanchor.backgroundColor = 'tan';
                         const audiotag = this.EditorView.state.schema.nodes.audiotag.create({
                             timeCode: NumCast(audioanchor._timecodeToShow),
                             audioId: audioanchor[Id],
-                            textId: textanchor[Id],
+                            textId: textanchor?.[Id] ?? '',
                         });
-                        textanchor.$title = 'dictation:' + audiotag.attrs.timeCode;
+                        textanchor && (textanchor.$title = 'dictation:' + audiotag.attrs.timeCode);
                         const tr = this.EditorView.state.tr.insert(this.EditorView.state.doc.content.size, audiotag);
                         const tr2 = tr.setSelection(TextSelection.create(tr.doc, tr.doc.content.size));
                         this.EditorView.dispatch(tr.setSelection(TextSelection.create(tr2.doc, tr2.doc.content.size)));
@@ -1147,11 +1147,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
 
     // if the scroll height has changed and we're in layout_autoHeight mode, then we need to update the textHeight component of the doc.
     // Since we also monitor all component height changes, this will update the document's height.
-    resetNativeHeight = (scrollHeight: number) => {
-        const nh = this.layoutDoc.isTemplateForField ? 0 : NumCast(this.layoutDoc._nativeHeight);
+    resetNativeHeight = action((scrollHeight: number) => {
         this.layoutDoc['_' + this.fieldKey + '_height'] = scrollHeight;
-        if (nh) this.layoutDoc._nativeHeight = scrollHeight;
-    };
+        if (!this.layoutDoc.isTemplateForField) this.layoutDoc._nativeHeight = scrollHeight;
+    });
 
     addPlugin = (plugin: Plugin) => {
         const editorView = this.EditorView;
@@ -1216,8 +1215,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                 const dataData = this.dataDoc[this.fieldKey];
                 const layoutData = Doc.AreProtosEqual(this.layoutDoc, this.dataDoc) ? undefined : this.layoutDoc[this.fieldKey];
                 const dataTime = dataData ? (DateCast(this.dataDoc[this.fieldKey + '_modificationDate'])?.date.getTime() ?? 0) : 0;
-                const layoutTime = layoutData && this.dataDoc[this.fieldKey + '_autoUpdate'] ? (DateCast(DocCast(this.layoutDoc)[this.fieldKey + '_modificationDate'])?.date.getTime() ?? 0) : 0;
-                const protoTime = protoData && this.dataDoc[this.fieldKey + '_autoUpdate'] ? (DateCast(DocCast(this.dataDoc.proto)[this.fieldKey + '_modificationDate'])?.date.getTime() ?? 0) : 0;
+                const layoutTime = layoutData && this.dataDoc[this.fieldKey + '_autoUpdate'] ? (DateCast(DocCast(this.layoutDoc)?.[this.fieldKey + '_modificationDate'])?.date.getTime() ?? 0) : 0;
+                const protoTime = protoData && this.dataDoc[this.fieldKey + '_autoUpdate'] ? (DateCast(DocCast(this.dataDoc.proto)?.[this.fieldKey + '_modificationDate'])?.date.getTime() ?? 0) : 0;
                 const recentData = dataTime >= layoutTime ? (protoTime >= dataTime ? protoData : dataData) : layoutTime >= protoTime ? layoutData : protoData;
                 const whichData = recentData ?? (this.layoutDoc.isTemplateDoc ? layoutData : protoData) ?? protoData;
                 return !whichData ? undefined : { data: RTFCast(whichData), str: Field.toString(DocCast(whichData) ?? StrCast(whichData)) };
@@ -1313,7 +1312,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
             // } catch (err) {
             //     console.log('Drop failed', err);
             // }
-            this.addDocument?.(DocCast(this.Document.image));
+            DocCast(this.Document.image) && this.addDocument?.(DocCast(this.Document.image)!);
         }
 
         //if (this.Document.image) this.addDocument?.(DocCast(this.Document.image));
@@ -1426,8 +1425,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         const rtfField = Cast((!curText && this.layoutDoc[this.fieldKey]) || this.dataDoc[fieldKey], RichTextField);
         if (this.ProseRef) {
             this.EditorView?.destroy();
+            const edState = () => {
+                try {
+                    return rtfField?.Data ? EditorState.fromJSON(config, JSON.parse(rtfField.Data)) : EditorState.create(config);
+                } catch {
+                    return EditorState.create(config);
+                }
+            };
             this._editorView = new EditorView(this.ProseRef, {
-                state: rtfField?.Data ? EditorState.fromJSON(config, JSON.parse(rtfField.Data)) : EditorState.create(config),
+                state: edState(),
                 handleScrollToSelection: this.scrollToSelection,
                 dispatchTransaction: this.dispatchTransaction,
                 nodeViews: FormattedTextBox._nodeViews(this),
@@ -1439,7 +1445,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
             (this.EditorView as unknown as { scrollToSelection: unknown }).scrollToSelection = this.scrollToSelection;
             const { state } = this._editorView;
             if (!rtfField) {
-                const dataDoc = Doc.IsDelegateField(DocCast(this.layoutDoc.proto), this.fieldKey) ? DocCast(this.layoutDoc.proto) : this.dataDoc;
+                const layoutProto = DocCast(this.layoutDoc.proto);
+                const dataDoc = layoutProto && Doc.IsDelegateField(layoutProto, this.fieldKey) ? layoutProto : this.dataDoc;
                 const startupText = Field.toString(dataDoc[fieldKey] as FieldType);
                 const textAlign = StrCast(this.dataDoc[this.fieldKey + '_align'], StrCast(Doc.UserDoc().textAlign)) || 'left';
                 if (textAlign !== 'left') {
-- 
cgit v1.2.3-70-g09d2

