From a5bf418707321711559782f0348063681c553cbb Mon Sep 17 00:00:00 2001
From: Naafiyan Ahmed <naafiyan@gmail.com>
Date: Wed, 10 Aug 2022 16:11:23 -0400
Subject: added placeholder text box for files being uploaded

---
 src/client/views/collections/CollectionSubView.tsx | 43 +++++++++++++++++++++-
 1 file changed, 42 insertions(+), 1 deletion(-)

(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 5479929bd..35a14704d 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -448,6 +448,46 @@ export function CollectionSubView<X>(moreProps?: X) {
             }
             this.slowLoadDocuments(files, options, generatedDocuments, text, completed, e.clientX, e.clientY, addDocument).then(batch.end);
         }
+
+        /**
+         * Creates a placeholder doc view for files being uploaded and removes placeholder docs once files are uplodaded.
+         *
+         * @param files the files to upload that we want to create placeholders for
+         * @param options the document options (primarily the x and y coordinates to put doc)
+         * @param text in the case of youtube the text is the url to the video
+         * @returns a disposer action that removes the placeholders created after files get uploaded
+         */
+        placeHolderDisposer = (files: File[] | string, options: DocumentOptions, text: string) => {
+            // TODO: nda - create a specialized view for placeholder upload with a spinner and ability to retry upload
+            let placeholders: Doc[] = [];
+            // handle yt case
+            if (typeof files === 'string') {
+                placeholders.push(Docs.Create.TextDocument('Loading: ' + text, { ...options, title: text, _width: 500, _height: 200 }));
+            } else {
+                // every other doc type is an array of File
+
+                // Get the file names as a text
+                let textStr = '';
+                files.forEach(file => {
+                    textStr += file.name + '\n';
+                });
+                placeholders.push(Docs.Create.TextDocument('Loading: \n' + textStr, { ...options, title: files.length + ' files', _width: 500, _height: files.length * 40 }));
+            }
+            // disposer action to remove placeholders once files are uploaded
+            const remove = action(() => {
+                if (!this.props.DataDoc) {
+                    return;
+                }
+                for (let i = 0; i < placeholders.length; i++) {
+                    Doc.RemoveDocFromList(this.props.DataDoc, 'data', placeholders[i]);
+                }
+            });
+            placeholders.forEach(pl => {
+                this.addDocument(pl);
+            });
+            return remove;
+        };
+
         slowLoadDocuments = async (
             files: File[] | string,
             options: DocumentOptions,
@@ -458,7 +498,8 @@ export function CollectionSubView<X>(moreProps?: X) {
             clientY: number,
             addDocument: (doc: Doc | Doc[]) => boolean
         ) => {
-            const disposer = OverlayView.Instance.addElement(<ReactLoading type={'spinningBubbles'} color={'green'} height={250} width={250} />, { x: clientX - 125, y: clientY - 125 });
+            const disposer = this.placeHolderDisposer(files, options, text);
+            // const disposer = OverlayView.Instance.addElement(<ReactLoading type={'spinningBubbles'} color={'green'} height={250} width={250} />, { x: clientX - 125, y: clientY - 125 });
             if (typeof files === 'string') {
                 generatedDocuments.push(...(await DocUtils.uploadYoutubeVideo(files, options)));
             } else {
-- 
cgit v1.2.3-70-g09d2


From 6c349731fb84f1c44993d2bc55410da19db29a07 Mon Sep 17 00:00:00 2001
From: Naafiyan Ahmed <naafiyan@gmail.com>
Date: Wed, 10 Aug 2022 16:16:00 -0400
Subject: added placeholder text box for files being uploaded

---
 src/client/views/collections/CollectionSubView.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(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 35a14704d..2f3f57bac 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -462,7 +462,7 @@ export function CollectionSubView<X>(moreProps?: X) {
             let placeholders: Doc[] = [];
             // handle yt case
             if (typeof files === 'string') {
-                placeholders.push(Docs.Create.TextDocument('Loading: ' + text, { ...options, title: text, _width: 500, _height: 200 }));
+                placeholders.push(Docs.Create.TextDocument('Loading: ' + text, { ...options, title: text, _width: 400, _height: 30 }));
             } else {
                 // every other doc type is an array of File
 
-- 
cgit v1.2.3-70-g09d2


From 820d40eea4eeb5977889e0ef6c35f9092df44b4b Mon Sep 17 00:00:00 2001
From: Naafiyan Ahmed <naafiyan@gmail.com>
Date: Wed, 10 Aug 2022 17:08:47 -0400
Subject: created placeholder loading box but have to get location of final doc
 to update with loading box

---
 src/client/documents/DocumentTypes.ts              |  3 ++-
 src/client/documents/Documents.ts                  | 18 +++++++++++++
 src/client/views/collections/CollectionSubView.tsx |  8 ++++--
 src/client/views/nodes/DocumentContentsView.tsx    |  2 ++
 src/client/views/nodes/LoadingBox.scss             |  7 +++++
 src/client/views/nodes/LoadingBox.tsx              | 31 ++++++++++++++++++++++
 6 files changed, 66 insertions(+), 3 deletions(-)
 create mode 100644 src/client/views/nodes/LoadingBox.scss
 create mode 100644 src/client/views/nodes/LoadingBox.tsx

(limited to 'src/client/views/collections/CollectionSubView.tsx')

diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts
index 9dfadf778..d99cd2dac 100644
--- a/src/client/documents/DocumentTypes.ts
+++ b/src/client/documents/DocumentTypes.ts
@@ -26,6 +26,7 @@ export enum DocumentType {
     FUNCPLOT = 'funcplot', // function plotter
     MAP = 'map',
     DATAVIZ = 'dataviz',
+    LOADING = 'loading',
 
     // special purpose wrappers that either take no data or are compositions of lower level types
     LINK = 'link',
@@ -63,5 +64,5 @@ export enum CollectionViewType {
     Grid = 'grid',
     Pile = 'pileup',
     StackedTimeline = 'stacked timeline',
-    NoteTaking = "notetaking"
+    NoteTaking = 'notetaking',
 }
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index c7ea04839..6ccb4358a 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -48,6 +48,7 @@ import { KeyValueBox } from '../views/nodes/KeyValueBox';
 import { LabelBox } from '../views/nodes/LabelBox';
 import { LinkBox } from '../views/nodes/LinkBox';
 import { LinkDescriptionPopup } from '../views/nodes/LinkDescriptionPopup';
+import { LoadingBox } from '../views/nodes/LoadingBox';
 import { MapBox } from '../views/nodes/MapBox/MapBox';
 import { PDFBox } from '../views/nodes/PDFBox';
 import { RecordingBox } from '../views/nodes/RecordingBox/RecordingBox';
@@ -648,6 +649,13 @@ export namespace Docs {
                     options: { _fitWidth: true, nativeDimModifiable: true, links: '@links(self)' },
                 },
             ],
+            [
+                DocumentType.LOADING,
+                {
+                    layout: { view: LoadingBox, dataField: defaultDataKey },
+                    options: { _fitWidth: true, _fitHeight: true, nativeDimModifiable: true, links: '@links(self)' },
+                },
+            ],
         ]);
 
         const suffix = 'Proto';
@@ -875,6 +883,16 @@ export namespace Docs {
             return InstanceFromProto(Prototypes.get(DocumentType.COLOR), '', options);
         }
 
+        export function LoadingDocument(title: string, text: string, width?: number, height?: number, options: DocumentOptions = {}) {
+            let myWidth = 300;
+            let myHeight = 300;
+            if (height && width) {
+                myWidth = width;
+                myHeight = height;
+            }
+            return InstanceFromProto(Prototypes.get(DocumentType.LOADING), '', { ...options, title, text, _width: myWidth, _height: myHeight });
+        }
+
         export function RTFDocument(field: RichTextField, options: DocumentOptions = {}, fieldKey: string = 'text') {
             return InstanceFromProto(Prototypes.get(DocumentType.RTF), field, options, undefined, fieldKey);
         }
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 2f3f57bac..79f629072 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -462,7 +462,8 @@ export function CollectionSubView<X>(moreProps?: X) {
             let placeholders: Doc[] = [];
             // handle yt case
             if (typeof files === 'string') {
-                placeholders.push(Docs.Create.TextDocument('Loading: ' + text, { ...options, title: text, _width: 400, _height: 30 }));
+                placeholders.push(Docs.Create.LoadingDocument('Loading...', text, 500, 500, { ...options }));
+                // placeholders.push(Docs.Create.TextDocument('Loading: ' + text, { ...options, title: text, _width: 400, _height: 30 }));
             } else {
                 // every other doc type is an array of File
 
@@ -471,7 +472,9 @@ export function CollectionSubView<X>(moreProps?: X) {
                 files.forEach(file => {
                     textStr += file.name + '\n';
                 });
-                placeholders.push(Docs.Create.TextDocument('Loading: \n' + textStr, { ...options, title: files.length + ' files', _width: 500, _height: files.length * 40 }));
+                placeholders.push(Docs.Create.LoadingDocument('Loading...', textStr, 500, 500, { ...options }));
+
+                // placeholders.push(Docs.Create.TextDocument('Loading: \n' + textStr, { ...options, title: files.length + ' files', _width: 500, _height: files.length * 40 }));
             }
             // disposer action to remove placeholders once files are uploaded
             const remove = action(() => {
@@ -498,6 +501,7 @@ export function CollectionSubView<X>(moreProps?: X) {
             clientY: number,
             addDocument: (doc: Doc | Doc[]) => boolean
         ) => {
+            // TODO: once loading thing is moved it should update the x and y of the file it is placeholder for
             const disposer = this.placeHolderDisposer(files, options, text);
             // const disposer = OverlayView.Instance.addElement(<ReactLoading type={'spinningBubbles'} color={'green'} height={250} width={250} />, { x: clientX - 125, y: clientY - 125 });
             if (typeof files === 'string') {
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 381436a56..4d4985f2a 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -43,6 +43,7 @@ import { VideoBox } from './VideoBox';
 import { WebBox } from './WebBox';
 import React = require('react');
 import XRegExp = require('xregexp');
+import { LoadingBox } from './LoadingBox';
 
 const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
 
@@ -267,6 +268,7 @@ export class DocumentContentsView extends React.Component<
                     DataVizBox,
                     HTMLtag,
                     ComparisonBox,
+                    LoadingBox,
                 }}
                 bindings={bindings}
                 jsx={layoutFrame}
diff --git a/src/client/views/nodes/LoadingBox.scss b/src/client/views/nodes/LoadingBox.scss
new file mode 100644
index 000000000..239faa78e
--- /dev/null
+++ b/src/client/views/nodes/LoadingBox.scss
@@ -0,0 +1,7 @@
+.loadingBoxContainer {
+    display: flex;
+    flex-direction: column;
+    align-content: center;
+    justify-content: center;
+    background-color: #fdfdfd;
+}
diff --git a/src/client/views/nodes/LoadingBox.tsx b/src/client/views/nodes/LoadingBox.tsx
new file mode 100644
index 000000000..0e0619241
--- /dev/null
+++ b/src/client/views/nodes/LoadingBox.tsx
@@ -0,0 +1,31 @@
+import { observer } from 'mobx-react';
+import { ViewBoxAnnotatableComponent } from '../DocComponent';
+import { FieldView, FieldViewProps } from './FieldView';
+import * as React from 'react';
+import './LoadingBox.scss';
+import ReactLoading from 'react-loading';
+
+export interface LoadingBoxProps {
+    title: string;
+    text: string;
+}
+
+@observer
+export class LoadingBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(LoadingBox, fieldKey);
+    }
+
+    constructor(props: any) {
+        super(props);
+    }
+
+    render() {
+        return (
+            <div className="loadingBoxContainer">
+                <span>Loading: {this.dataDoc.text}</span>
+                <ReactLoading type={'spinningBubbles'} color={'blue'} height={100} width={100} />
+            </div>
+        );
+    }
+}
-- 
cgit v1.2.3-70-g09d2


From bb2b38b0e47eaf8e64554d101b605bf35a178239 Mon Sep 17 00:00:00 2001
From: Naafiyan Ahmed <naafiyan@gmail.com>
Date: Tue, 16 Aug 2022 16:43:01 -0400
Subject: updated placeholder

---
 src/.DS_Store                                      | Bin 10244 -> 10244 bytes
 src/client/documents/Documents.ts                  |  63 +++++++++++++------
 src/client/util/CurrentUserUtils.ts                |   1 +
 src/client/views/collections/CollectionSubView.tsx |  70 +++++++--------------
 src/client/views/nodes/LoadingBox.tsx              |  25 ++++++++
 5 files changed, 94 insertions(+), 65 deletions(-)

(limited to 'src/client/views/collections/CollectionSubView.tsx')

diff --git a/src/.DS_Store b/src/.DS_Store
index 4751acf44..4ed785983 100644
Binary files a/src/.DS_Store and b/src/.DS_Store differ
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 6ccb4358a..d8497e3af 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1,4 +1,5 @@
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
+import { files } from 'jszip';
 import { action, runInAction } from 'mobx';
 import { basename } from 'path';
 import { DateField } from '../../fields/DateField';
@@ -794,7 +795,7 @@ export namespace Docs {
          * only when creating a DockDocument from the current user's already existing
          * main document.
          */
-        function InstanceFromProto(proto: Doc, data: Field | undefined, options: DocumentOptions, delegId?: string, fieldKey: string = 'data', protoId?: string) {
+        function InstanceFromProto(proto: Doc, data: Field | undefined, options: DocumentOptions, delegId?: string, fieldKey: string = 'data', protoId?: string, placeholderDoc?: Doc) {
             const viewKeys = ['x', 'y', 'system']; // keys that should be addded to the view document even though they don't begin with an "_"
             const { omit: dataProps, extract: viewProps } = OmitKeys(options, viewKeys, '^_');
 
@@ -813,13 +814,22 @@ export namespace Docs {
             // without this, if a doc has no annotations but the user has AddOnly privileges, they won't be able to add an annotation because they would have needed to create the field's list which they don't have permissions to do.
             dataProps[fieldKey + '-annotations'] = new List<Doc>();
             dataProps[fieldKey + '-sidebar'] = new List<Doc>();
-            const dataDoc = Doc.assign(Doc.MakeDelegate(proto, protoId), dataProps, undefined, true);
+
+            const dataDoc = Doc.assign(placeholderDoc ? Doc.GetProto(placeholderDoc) : Doc.MakeDelegate(proto, protoId), dataProps, undefined, true);
+
+            if (placeholderDoc) {
+                dataDoc.proto = proto;
+            }
 
             const viewFirstProps: { [id: string]: any } = {};
             viewFirstProps['acl-Public'] = options['_acl-Public'] ? options['_acl-Public'] : Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
             viewFirstProps['acl-Override'] = 'None';
             viewFirstProps.author = Doc.CurrentUserEmail;
-            const viewDoc = Doc.assign(Doc.MakeDelegate(dataDoc, delegId), viewFirstProps, true, true);
+            let viewDoc: Doc;
+            if (placeholderDoc) {
+                viewDoc = Doc.assign(placeholderDoc, viewFirstProps, true, true);
+            }
+            viewDoc = Doc.assign(Doc.MakeDelegate(dataDoc, delegId), viewFirstProps, true, true);
             Doc.assign(viewDoc, viewProps, true, true);
             ![DocumentType.LINK, DocumentType.MARKER, DocumentType.LABEL].includes(viewDoc.type as any) && DocUtils.MakeLinkToActiveAudio(() => viewDoc);
 
@@ -883,14 +893,12 @@ export namespace Docs {
             return InstanceFromProto(Prototypes.get(DocumentType.COLOR), '', options);
         }
 
-        export function LoadingDocument(title: string, text: string, width?: number, height?: number, options: DocumentOptions = {}) {
-            let myWidth = 300;
-            let myHeight = 300;
-            if (height && width) {
-                myWidth = width;
-                myHeight = height;
-            }
-            return InstanceFromProto(Prototypes.get(DocumentType.LOADING), '', { ...options, title, text, _width: myWidth, _height: myHeight });
+        export const filesToDocs = new Map<Doc, File>();
+
+        export function LoadingDocument(file: File, options: DocumentOptions, ytString?: string) {
+            const loading = InstanceFromProto(Prototypes.get(DocumentType.LOADING), file.name, { _height: 300, _width: 300, ...options });
+            // filesToDocs.set(loading, file);
+            return loading;
         }
 
         export function RTFDocument(field: RichTextField, options: DocumentOptions = {}, fieldKey: string = 'text') {
@@ -969,13 +977,13 @@ export namespace Docs {
             return I;
         }
 
-        export function PdfDocument(url: string, options: DocumentOptions = {}) {
+        export function PdfDocument(url: string, options: DocumentOptions = {}, overwriteDoc?: Doc) {
             const width = options._width || undefined;
             const height = options._height || undefined;
             const nwid = options._nativeWidth || undefined;
             const nhght = options._nativeHeight || undefined;
             if (!nhght && width && height && nwid) options._nativeHeight = (Number(nwid) * Number(height)) / Number(width);
-            return InstanceFromProto(Prototypes.get(DocumentType.PDF), new PdfField(url), options);
+            return InstanceFromProto(Prototypes.get(DocumentType.PDF), new PdfField(url), options, undefined, undefined, undefined, overwriteDoc);
         }
 
         export function WebDocument(url: string, options: DocumentOptions = {}) {
@@ -1462,8 +1470,8 @@ export namespace DocUtils {
         return created;
     }
 
-    export async function DocumentFromType(type: string, path: string, options: DocumentOptions): Promise<Opt<Doc>> {
-        let ctor: ((path: string, options: DocumentOptions) => Doc | Promise<Doc | undefined>) | undefined = undefined;
+    export async function DocumentFromType(type: string, path: string, options: DocumentOptions, rootDoc?: Doc): Promise<Opt<Doc>> {
+        let ctor: ((path: string, options: DocumentOptions, overwriteDoc?: Doc) => Doc | Promise<Doc | undefined>) | undefined = undefined;
         if (type.indexOf('image') !== -1) {
             ctor = Docs.Create.ImageDocument;
             if (!options._width) options._width = 300;
@@ -1512,7 +1520,7 @@ export namespace DocUtils {
             options = { ...options, _width: 400, _height: 512, title: path };
         }
 
-        return ctor ? ctor(path, options) : undefined;
+        return ctor ? ctor(path, options, rootDoc) : undefined;
     }
 
     export function addDocumentCreatorMenuItems(docTextAdder: (d: Doc) => void, docAdder: (d: Doc) => void, x: number, y: number, simpleMenu: boolean = false, pivotField?: string, pivotValue?: string): void {
@@ -1728,14 +1736,14 @@ export namespace DocUtils {
         return dd;
     }
 
-    async function processFileupload(generatedDocuments: Doc[], name: string, type: string, result: Error | Upload.FileInformation, options: DocumentOptions) {
+    async function processFileupload(generatedDocuments: Doc[], name: string, type: string, result: Error | Upload.FileInformation, options: DocumentOptions, rootDoc?: Doc) {
         if (result instanceof Error) {
             alert(`Upload failed: ${result.message}`);
             return;
         }
         const full = { ...options, _width: 400, title: name };
         const pathname = Utils.prepend(result.accessPaths.agnostic.client);
-        const doc = await DocUtils.DocumentFromType(type, pathname, full);
+        const doc = await DocUtils.DocumentFromType(type, pathname, full, rootDoc);
         if (doc) {
             const proto = Doc.GetProto(doc);
             proto.text = result.rawText;
@@ -1813,6 +1821,25 @@ export namespace DocUtils {
         return generatedDocuments;
     }
 
+    export function uploadFileToDoc(file: File, options: DocumentOptions, overwriteDoc: Doc) {
+        const generatedDocuments: Doc[] = [];
+        Networking.UploadFilesToServer([file]).then(upfiles => {
+            const {
+                source: { name, type },
+                result,
+            } = upfiles.lastElement();
+            console.log(name, type);
+            name && type && processFileupload(generatedDocuments, name, type, result, options, overwriteDoc);
+        });
+    }
+
+    export function generatePlaceHolder(file: File, options: DocumentOptions) {
+        return Docs.Create.LoadingDocument(file, options);
+        // placeholder.file = file
+        // TODO: nda - modify loading doc so it only takes in options
+        // Docs.Create.LoadingDocument(options, )
+    }
+
     // copies the specified drag factory document
     export function copyDragFactory(dragFactory: Doc) {
         if (!dragFactory) return undefined;
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index d19874720..492513a61 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -196,6 +196,7 @@ export class CurrentUserUtils {
             makeIconTemplate(DocumentType.RTF,   "text",  { iconTemplate:DocumentType.LABEL, _showTitle: "creationDate"}),
             makeIconTemplate(DocumentType.IMG,   "data",  { iconTemplate:DocumentType.IMG,   _height: undefined}),
             makeIconTemplate(DocumentType.COL,   "icon",  { iconTemplate:DocumentType.IMG}),
+            makeIconTemplate(DocumentType.COL,   "icon",  { iconTemplate:DocumentType.IMG}),
             makeIconTemplate(DocumentType.VID,   "icon",  { iconTemplate:DocumentType.IMG}),
             makeIconTemplate(DocumentType.BUTTON,"data",  { iconTemplate:DocumentType.FONTICON}),
             //nasty hack .. templates are looked up exclusively by type -- but we want a template for a document with a certain field (transcription) .. so this hack and the companion hack in createCustomView does this for now
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 79f629072..1ff4f5ab8 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -449,48 +449,6 @@ export function CollectionSubView<X>(moreProps?: X) {
             this.slowLoadDocuments(files, options, generatedDocuments, text, completed, e.clientX, e.clientY, addDocument).then(batch.end);
         }
 
-        /**
-         * Creates a placeholder doc view for files being uploaded and removes placeholder docs once files are uplodaded.
-         *
-         * @param files the files to upload that we want to create placeholders for
-         * @param options the document options (primarily the x and y coordinates to put doc)
-         * @param text in the case of youtube the text is the url to the video
-         * @returns a disposer action that removes the placeholders created after files get uploaded
-         */
-        placeHolderDisposer = (files: File[] | string, options: DocumentOptions, text: string) => {
-            // TODO: nda - create a specialized view for placeholder upload with a spinner and ability to retry upload
-            let placeholders: Doc[] = [];
-            // handle yt case
-            if (typeof files === 'string') {
-                placeholders.push(Docs.Create.LoadingDocument('Loading...', text, 500, 500, { ...options }));
-                // placeholders.push(Docs.Create.TextDocument('Loading: ' + text, { ...options, title: text, _width: 400, _height: 30 }));
-            } else {
-                // every other doc type is an array of File
-
-                // Get the file names as a text
-                let textStr = '';
-                files.forEach(file => {
-                    textStr += file.name + '\n';
-                });
-                placeholders.push(Docs.Create.LoadingDocument('Loading...', textStr, 500, 500, { ...options }));
-
-                // placeholders.push(Docs.Create.TextDocument('Loading: \n' + textStr, { ...options, title: files.length + ' files', _width: 500, _height: files.length * 40 }));
-            }
-            // disposer action to remove placeholders once files are uploaded
-            const remove = action(() => {
-                if (!this.props.DataDoc) {
-                    return;
-                }
-                for (let i = 0; i < placeholders.length; i++) {
-                    Doc.RemoveDocFromList(this.props.DataDoc, 'data', placeholders[i]);
-                }
-            });
-            placeholders.forEach(pl => {
-                this.addDocument(pl);
-            });
-            return remove;
-        };
-
         slowLoadDocuments = async (
             files: File[] | string,
             options: DocumentOptions,
@@ -501,13 +459,31 @@ export function CollectionSubView<X>(moreProps?: X) {
             clientY: number,
             addDocument: (doc: Doc | Doc[]) => boolean
         ) => {
+            // create placeholder docs
+            // inside placeholder docs have some func that
+
             // TODO: once loading thing is moved it should update the x and y of the file it is placeholder for
-            const disposer = this.placeHolderDisposer(files, options, text);
+            let pileUpDoc = undefined;
             // const disposer = OverlayView.Instance.addElement(<ReactLoading type={'spinningBubbles'} color={'green'} height={250} width={250} />, { x: clientX - 125, y: clientY - 125 });
             if (typeof files === 'string') {
-                generatedDocuments.push(...(await DocUtils.uploadYoutubeVideo(files, options)));
+                // uploadYoutubeVideo and similar should return a placeholder, one for each placeholder
+                // generatedDocuments.push(Docs.Create.LoadingDocument(files, options));
             } else {
-                generatedDocuments.push(...(await DocUtils.uploadFilesToDocs(files, options)));
+                // uploadFilesToDocs and similar should return a placeholder, one for each placeholder
+                generatedDocuments.push(
+                    ...files.map(file => {
+                        const loading = Docs.Create.LoadingDocument(file, options);
+                        // now that there is a doc do whatever slowload was going to do with that file
+                        if (typeof file === 'string') {
+                            // uploadYoutubeVideo and similar should return a placeholder, one for each placeholder
+                            // (await DocUtils.uploadYoutubeVideo(files, options)));
+                        } else {
+                            // uploadFilesToDocs and similar should return a placeholder, one for each placeholder
+                            DocUtils.uploadFileToDoc(file, {}, loading);
+                        }
+                        return loading;
+                    })
+                );
             }
             if (generatedDocuments.length) {
                 // Creating a dash document
@@ -523,7 +499,8 @@ export function CollectionSubView<X>(moreProps?: X) {
                 if (completed) completed(set);
                 else {
                     if (isFreeformView && generatedDocuments.length > 1) {
-                        addDocument(DocUtils.pileup(generatedDocuments, options.x as number, options.y as number)!);
+                        pileUpDoc = DocUtils.pileup(generatedDocuments, options.x as number, options.y as number)!;
+                        addDocument(pileUpDoc);
                     } else {
                         generatedDocuments.forEach(addDocument);
                     }
@@ -535,7 +512,6 @@ export function CollectionSubView<X>(moreProps?: X) {
                     alert('Document upload failed - possibly an unsupported file type.');
                 }
             }
-            disposer();
         };
     }
 
diff --git a/src/client/views/nodes/LoadingBox.tsx b/src/client/views/nodes/LoadingBox.tsx
index 0e0619241..9d4668dde 100644
--- a/src/client/views/nodes/LoadingBox.tsx
+++ b/src/client/views/nodes/LoadingBox.tsx
@@ -4,6 +4,7 @@ import { FieldView, FieldViewProps } from './FieldView';
 import * as React from 'react';
 import './LoadingBox.scss';
 import ReactLoading from 'react-loading';
+import { Docs, DocUtils } from '../../documents/Documents';
 
 export interface LoadingBoxProps {
     title: string;
@@ -16,6 +17,30 @@ export class LoadingBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
         return FieldView.LayoutString(LoadingBox, fieldKey);
     }
 
+    componentDidMount() {
+        // const file = Docs.Create.filesToDocs.get(this.rootDoc);
+        // if (file) {
+        //     console.log('Got to file');
+        //     Docs.Create.filesToDocs.delete(this.rootDoc);
+        //     // now that there is a doc do whatever slowload was going to do with that file
+        //     if (typeof file === 'string') {
+        //         // uploadYoutubeVideo and similar should return a placeholder, one for each placeholder
+        //         // (await DocUtils.uploadYoutubeVideo(files, options)));
+        //     } else {
+        //         // uploadFilesToDocs and similar should return a placeholder, one for each placeholder
+        //         DocUtils.uploadFileToDoc(file, {}, this.rootDoc);
+        //     }
+        // } else {
+        //     // check if file now exists on server or not
+        //     // if it does we need to retreieve it and create the appropriate doc (rest of what uploadFileToDoc was doing minus uploading)
+        //     // if it doesn't display an error message "upload failed"
+        // }
+        // query endpoints to:
+        // check if file now exists on server or not
+        // if it does we need to retreieve it and create the appropriate doc (rest of what uploadFileToDoc was doing minus uploading)
+        // if it doesn't display an error message "upload failed"
+    }
+
     constructor(props: any) {
         super(props);
     }
-- 
cgit v1.2.3-70-g09d2


From 27945b9a931fa9504404174dd08964556dc3aea2 Mon Sep 17 00:00:00 2001
From: Naafiyan Ahmed <naafiyan@gmail.com>
Date: Tue, 16 Aug 2022 21:16:03 -0400
Subject: added loading for diff doc types

---
 src/client/documents/Documents.ts                  | 47 ++++++++++++++++------
 src/client/views/collections/CollectionSubView.tsx | 11 ++---
 src/client/views/nodes/LoadingBox.scss             |  5 +++
 src/client/views/nodes/LoadingBox.tsx              | 12 +++---
 4 files changed, 48 insertions(+), 27 deletions(-)

(limited to 'src/client/views/collections/CollectionSubView.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index d8497e3af..e54fe16de 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -844,9 +844,9 @@ export namespace Docs {
             return viewDoc;
         }
 
-        export function ImageDocument(url: string | ImageField, options: DocumentOptions = {}) {
+        export function ImageDocument(url: string | ImageField, options: DocumentOptions = {}, overwriteDoc?: Doc) {
             const imgField = url instanceof ImageField ? url : new ImageField(url);
-            return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: basename(imgField.url.href), ...options });
+            return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: basename(imgField.url.href), ...options }, undefined, undefined, undefined, overwriteDoc);
         }
 
         export function PresDocument(options: DocumentOptions = {}) {
@@ -857,12 +857,12 @@ export namespace Docs {
             return InstanceFromProto(Prototypes.get(DocumentType.SCRIPTING), script ? script : undefined, { ...options, layout: fieldKey ? ScriptingBox.LayoutString(fieldKey) : undefined });
         }
 
-        export function VideoDocument(url: string, options: DocumentOptions = {}) {
-            return InstanceFromProto(Prototypes.get(DocumentType.VID), new VideoField(url), options);
+        export function VideoDocument(url: string, options: DocumentOptions = {}, overwriteDoc?: Doc) {
+            return InstanceFromProto(Prototypes.get(DocumentType.VID), new VideoField(url), options, undefined, undefined, undefined, overwriteDoc);
         }
 
-        export function YoutubeDocument(url: string, options: DocumentOptions = {}) {
-            return InstanceFromProto(Prototypes.get(DocumentType.YOUTUBE), new YoutubeField(url), options);
+        export function YoutubeDocument(url: string, options: DocumentOptions = {}, overwriteDoc?: Doc) {
+            return InstanceFromProto(Prototypes.get(DocumentType.YOUTUBE), new YoutubeField(url), options, undefined, undefined, undefined, overwriteDoc);
         }
 
         export function WebCamDocument(url: string, options: DocumentOptions = {}) {
@@ -877,8 +877,16 @@ export namespace Docs {
             return InstanceFromProto(Prototypes.get(DocumentType.COMPARISON), '', options);
         }
 
-        export function AudioDocument(url: string, options: DocumentOptions = {}) {
-            return InstanceFromProto(Prototypes.get(DocumentType.AUDIO), new AudioField(url), { ...options, backgroundColor: ComputedField.MakeFunction("this._mediaState === 'playing' ? 'green':'gray'") as any });
+        export function AudioDocument(url: string, options: DocumentOptions = {}, overwriteDoc?: Doc) {
+            return InstanceFromProto(
+                Prototypes.get(DocumentType.AUDIO),
+                new AudioField(url),
+                { ...options, backgroundColor: ComputedField.MakeFunction("this._mediaState === 'playing' ? 'green':'gray'") as any },
+                undefined,
+                undefined,
+                undefined,
+                overwriteDoc
+            );
         }
 
         export function RecordingDocument(url: string, options: DocumentOptions = {}) {
@@ -895,8 +903,10 @@ export namespace Docs {
 
         export const filesToDocs = new Map<Doc, File>();
 
-        export function LoadingDocument(file: File, options: DocumentOptions, ytString?: string) {
-            const loading = InstanceFromProto(Prototypes.get(DocumentType.LOADING), file.name, { _height: 300, _width: 300, ...options });
+        export function LoadingDocument(file: File | string, options: DocumentOptions, ytString?: string) {
+            const fileName = typeof file == 'string' ? file : file.name;
+            options.title = fileName;
+            const loading = InstanceFromProto(Prototypes.get(DocumentType.LOADING), fileName, { _height: 150, _width: 150, ...options });
             // filesToDocs.set(loading, file);
             return loading;
         }
@@ -1114,8 +1124,8 @@ export namespace Docs {
             return InstanceFromProto(Prototypes.get(DocumentType.PRESELEMENT), undefined, { ...(options || {}) });
         }
 
-        export function DataVizDocument(url: string, options?: DocumentOptions) {
-            return InstanceFromProto(Prototypes.get(DocumentType.DATAVIZ), new CsvField(url), { title: 'Data Viz', ...options });
+        export function DataVizDocument(url: string, options?: DocumentOptions, overwriteDoc?: Doc) {
+            return InstanceFromProto(Prototypes.get(DocumentType.DATAVIZ), new CsvField(url), { title: 'Data Viz', ...options }, undefined, undefined, undefined, overwriteDoc);
         }
 
         export function DockDocument(documents: Array<Doc>, config: string, options: DocumentOptions, id?: string) {
@@ -1809,6 +1819,18 @@ export namespace DocUtils {
         }
         return generatedDocuments;
     }
+
+    export function uploadYoutubeVideoLoading(videoId: string, options: DocumentOptions, overwriteDoc?: Doc) {
+        const generatedDocuments: Doc[] = [];
+        Networking.UploadYoutubeToServer(videoId).then(upfiles => {
+            const {
+                source: { name, type },
+                result,
+            } = upfiles.lastElement();
+            name && type && processFileupload(generatedDocuments, name, type, result, options, overwriteDoc);
+        });
+    }
+
     export async function uploadFilesToDocs(files: File[], options: DocumentOptions) {
         const generatedDocuments: Doc[] = [];
         const upfiles = await Networking.UploadFilesToServer(files);
@@ -1828,7 +1850,6 @@ export namespace DocUtils {
                 source: { name, type },
                 result,
             } = upfiles.lastElement();
-            console.log(name, type);
             name && type && processFileupload(generatedDocuments, name, type, result, options, overwriteDoc);
         });
     }
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 1ff4f5ab8..30467efa0 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -468,19 +468,16 @@ export function CollectionSubView<X>(moreProps?: X) {
             if (typeof files === 'string') {
                 // uploadYoutubeVideo and similar should return a placeholder, one for each placeholder
                 // generatedDocuments.push(Docs.Create.LoadingDocument(files, options));
+                const loading = Docs.Create.LoadingDocument(files, options);
+                generatedDocuments.push(loading);
+                DocUtils.uploadYoutubeVideoLoading(files, {}, loading);
             } else {
                 // uploadFilesToDocs and similar should return a placeholder, one for each placeholder
                 generatedDocuments.push(
                     ...files.map(file => {
                         const loading = Docs.Create.LoadingDocument(file, options);
                         // now that there is a doc do whatever slowload was going to do with that file
-                        if (typeof file === 'string') {
-                            // uploadYoutubeVideo and similar should return a placeholder, one for each placeholder
-                            // (await DocUtils.uploadYoutubeVideo(files, options)));
-                        } else {
-                            // uploadFilesToDocs and similar should return a placeholder, one for each placeholder
-                            DocUtils.uploadFileToDoc(file, {}, loading);
-                        }
+                        DocUtils.uploadFileToDoc(file, {}, loading);
                         return loading;
                     })
                 );
diff --git a/src/client/views/nodes/LoadingBox.scss b/src/client/views/nodes/LoadingBox.scss
index 239faa78e..e8890cd82 100644
--- a/src/client/views/nodes/LoadingBox.scss
+++ b/src/client/views/nodes/LoadingBox.scss
@@ -5,3 +5,8 @@
     justify-content: center;
     background-color: #fdfdfd;
 }
+
+.text {
+    text-overflow: ellipsis;
+    text-align: center;
+}
diff --git a/src/client/views/nodes/LoadingBox.tsx b/src/client/views/nodes/LoadingBox.tsx
index 9d4668dde..96620aff9 100644
--- a/src/client/views/nodes/LoadingBox.tsx
+++ b/src/client/views/nodes/LoadingBox.tsx
@@ -4,12 +4,7 @@ import { FieldView, FieldViewProps } from './FieldView';
 import * as React from 'react';
 import './LoadingBox.scss';
 import ReactLoading from 'react-loading';
-import { Docs, DocUtils } from '../../documents/Documents';
-
-export interface LoadingBoxProps {
-    title: string;
-    text: string;
-}
+import { StrCast } from '../../../fields/Types';
 
 @observer
 export class LoadingBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
@@ -18,6 +13,7 @@ export class LoadingBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     }
 
     componentDidMount() {
+        console.log(this.rootDoc);
         // const file = Docs.Create.filesToDocs.get(this.rootDoc);
         // if (file) {
         //     console.log('Got to file');
@@ -48,7 +44,9 @@ export class LoadingBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     render() {
         return (
             <div className="loadingBoxContainer">
-                <span>Loading: {this.dataDoc.text}</span>
+                <p className="text">Loading:</p>
+                <br></br>
+                <p className="text">{StrCast(this.rootDoc.title)}</p>
                 <ReactLoading type={'spinningBubbles'} color={'blue'} height={100} width={100} />
             </div>
         );
-- 
cgit v1.2.3-70-g09d2


From 0178de4ab9ffd11630b700f9c02468b74beabd14 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 17 Aug 2022 12:31:26 -0400
Subject: fixed dragging docs on web and pdf to work better by temporarily
 adding transparent docs to the opaque layer so they can get drop events.  
 cleaned eraser and pen interaction code and made erasing strokes work faster
 and avoid hanging by not intersecting strokes that are already partially
 deleted.

---
 src/Utils.ts                                       |   3 +-
 src/client/documents/Documents.ts                  |  26 +---
 src/client/views/collections/CollectionSubView.tsx |  12 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 140 ++++++---------------
 .../views/nodes/CollectionFreeFormDocumentView.tsx |   5 +-
 src/client/views/nodes/DocumentView.tsx            |   6 +-
 src/client/views/nodes/VideoBox.tsx                |  11 +-
 src/client/views/nodes/WebBox.scss                 |  17 +--
 src/client/views/nodes/WebBox.tsx                  | 110 ++++++++--------
 src/client/views/pdf/PDFViewer.tsx                 |  75 +++++------
 10 files changed, 153 insertions(+), 252 deletions(-)

(limited to 'src/client/views/collections/CollectionSubView.tsx')

diff --git a/src/Utils.ts b/src/Utils.ts
index 528a429d0..9e002ebd4 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -111,6 +111,7 @@ export namespace Utils {
 
     const isTransparentFunctionHack = 'isTransparent(__value__)';
     export const noRecursionHack = '__noRecursion';
+    export const noDragsDocFilter = 'noDragDocs:any:check';
     export function IsRecursiveFilter(val: string) {
         return !val.includes(noRecursionHack);
     }
@@ -125,7 +126,7 @@ export namespace Utils {
         // bcz: isTransparent(__value__) is a hack.  it would be nice to have acual functions be parsed, but now Doc.matchFieldValue is hardwired to recognize just this one
         return `backgroundColor:${isTransparentFunctionHack},${noRecursionHack}:x`; // bcz: hack.  noRecursion should probably be either another ':' delimited field, or it should be a modifier to the comparision (eg., check, x, etc) field
     }
-    export function PropUnsetFilter(prop: string) {
+    export function IsPropUnsetFilter(prop: string) {
         return `${prop}:any,${noRecursionHack}:unset`;
     }
 
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index cf9ed43e1..e579bfd8a 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1143,30 +1143,6 @@ export namespace Docs {
 }
 
 export namespace DocUtils {
-    export function Excluded(d: Doc, docFilters: string[]) {
-        const filterFacets: { [key: string]: { [value: string]: string } } = {}; // maps each filter key to an object with value=>modifier fields
-        docFilters.forEach(filter => {
-            const fields = filter.split(':');
-            const key = fields[0];
-            const value = fields[1];
-            const modifiers = fields[2];
-            if (!filterFacets[key]) {
-                filterFacets[key] = {};
-            }
-            filterFacets[key][value] = modifiers;
-        });
-
-        if (d.z) return false;
-        for (const facetKey of Object.keys(filterFacets)) {
-            const facet = filterFacets[facetKey];
-            const xs = Object.keys(facet).filter(value => facet[value] === 'x');
-            const failsNotEqualFacets = xs?.some(value => Doc.matchFieldValue(d, facetKey, value));
-            if (failsNotEqualFacets) {
-                return true;
-            }
-        }
-        return false;
-    }
     /**
      * @param docs
      * @param docFilters
@@ -1200,7 +1176,7 @@ export namespace DocUtils {
                       return false;
                   }
 
-                  for (const facetKey of Object.keys(filterFacets).filter(fkey => fkey !== 'cookies')) {
+                  for (const facetKey of Object.keys(filterFacets).filter(fkey => fkey !== 'cookies' && fkey !== Utils.noDragsDocFilter.split(':')[0])) {
                       const facet = filterFacets[facetKey];
 
                       // facets that match some value in the field of the document (e.g. some text field)
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 5479929bd..e33bb77de 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -88,11 +88,11 @@ export function CollectionSubView<X>(moreProps?: X) {
         }
         collectionFilters = () => this._focusFilters ?? StrListCast(this.props.Document._docFilters);
         collectionRangeDocFilters = () => this._focusRangeFilters ?? Cast(this.props.Document._docRangeFilters, listSpec('string'), []);
+        // child filters apply to the descendants of the documents in this collection
         childDocFilters = () => [...(this.props.docFilters?.().filter(f => Utils.IsRecursiveFilter(f)) || []), ...this.collectionFilters()];
+        // unrecursive filters apply to the documents in the collection, but no their children.  See Utils.noRecursionHack
         unrecursiveDocFilters = () => [...(this.props.docFilters?.().filter(f => !Utils.IsRecursiveFilter(f)) || [])];
         childDocRangeFilters = () => [...(this.props.docRangeFilters?.() || []), ...this.collectionRangeDocFilters()];
-        IsFiltered = () =>
-            this.collectionFilters().length || this.collectionRangeDocFilters().length ? 'hasFilter' : this.props.docFilters?.().filter(f => Utils.IsRecursiveFilter(f)).length || this.props.docRangeFilters().length ? 'inheritsFilter' : undefined;
         searchFilterDocs = () => this.props.searchFilterDocs?.() ?? DocListCast(this.props.Document._searchFilterDocs);
         @computed.struct get childDocs() {
             TraceMobx();
@@ -122,13 +122,11 @@ export function CollectionSubView<X>(moreProps?: X) {
                 return childDocs.filter(cd => !cd.cookies); // remove any documents that require a cookie if there are no filters to provide one
             }
 
-            // console.log(Doc.ActiveDashboard._docFilters);
-            // if (!this.props.Document._docFilters && this.props.Document.currentFilter) {
-            //     (this.props.Document.currentFilter as Doc).filterBoolean = (this.props.ContainingCollectionDoc?.currentFilter as Doc)?.filterBoolean;
-            // }
             const docsforFilter: Doc[] = [];
             childDocs.forEach(d => {
-                // if (DocUtils.Excluded(d, docFilters)) return;
+                // dragging facets
+                const dragged = this.props.docFilters?.().some(f => f.includes(Utils.noDragsDocFilter));
+                if (dragged && DragManager.docsBeingDragged.includes(d)) return false;
                 let notFiltered = d.z || Doc.IsSystem(d) || DocUtils.FilterDocs([d], this.unrecursiveDocFilters(), docRangeFilters, viewSpecScript, this.props.Document).length > 0;
                 if (notFiltered) {
                     notFiltered = (!searchDocs.length || searchDocs.includes(d)) && DocUtils.FilterDocs([d], childDocFilters, docRangeFilters, viewSpecScript, this.props.Document).length > 0;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index f3074543b..45a5e30ff 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -109,8 +109,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     @observable _hLines: number[] | undefined;
     @observable _vLines: number[] | undefined;
     @observable _firstRender = true; // this turns off rendering of the collection's content so that there's instant feedback when a tab is switched of what content will be shown.
-    @observable _pullCoords: number[] = [0, 0];
-    @observable _pullDirection: string = '';
     @observable _showAnimTimeline = false;
     @observable _clusterSets: Doc[][] = [];
     @observable _deleteList: DocumentView[] = [];
@@ -465,20 +463,23 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     getClusterColor = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => {
         let styleProp = this.props.styleProvider?.(doc, props, property); // bcz: check 'props'  used to be renderDepth + 1
-        if (property !== StyleProp.BackgroundColor) return styleProp;
-        const cluster = NumCast(doc?.cluster);
-        if (this.Document._useClusters) {
-            if (this._clusterSets.length <= cluster) {
-                setTimeout(() => doc && this.updateCluster(doc));
-            } else {
-                // choose a cluster color from a palette
-                const colors = ['#da42429e', '#31ea318c', 'rgba(197, 87, 20, 0.55)', '#4a7ae2c4', 'rgba(216, 9, 255, 0.5)', '#ff7601', '#1dffff', 'yellow', 'rgba(27, 130, 49, 0.55)', 'rgba(0, 0, 0, 0.268)'];
-                styleProp = colors[cluster % colors.length];
-                const set = this._clusterSets[cluster]?.filter(s => s.backgroundColor);
-                // override the cluster color with an explicitly set color on a non-background document.  then override that with an explicitly set color on a background document
-                set?.map(s => (styleProp = StrCast(s.backgroundColor)));
-            }
-        } //else if (doc && NumCast(doc.group, -1) !== -1) styleProp = "gray";
+        switch (property) {
+            case StyleProp.BackgroundColor:
+                const cluster = NumCast(doc?.cluster);
+                if (this.Document._useClusters) {
+                    if (this._clusterSets.length <= cluster) {
+                        setTimeout(() => doc && this.updateCluster(doc));
+                    } else {
+                        // choose a cluster color from a palette
+                        const colors = ['#da42429e', '#31ea318c', 'rgba(197, 87, 20, 0.55)', '#4a7ae2c4', 'rgba(216, 9, 255, 0.5)', '#ff7601', '#1dffff', 'yellow', 'rgba(27, 130, 49, 0.55)', 'rgba(0, 0, 0, 0.268)'];
+                        styleProp = colors[cluster % colors.length];
+                        const set = this._clusterSets[cluster]?.filter(s => s.backgroundColor);
+                        // override the cluster color with an explicitly set color on a non-background document.  then override that with an explicitly set color on a background document
+                        set?.map(s => (styleProp = StrCast(s.backgroundColor)));
+                    }
+                }
+                break;
+        }
         return styleProp;
     };
 
@@ -523,17 +524,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                     case InkTool.Pen:
                         break; // the GestureOverlay handles ink stroke input -- either as gestures, or drying as ink strokes that are added to document views
                     case InkTool.Eraser:
-                        document.addEventListener('pointermove', this.onEraserMove);
-                        document.addEventListener('pointerup', this.onEraserUp);
                         this._batch = UndoManager.StartBatch('collectionErase');
-                        e.stopPropagation();
-                        e.preventDefault();
+                        setupMoveUpEvents(this, e, this.onEraserMove, this.onEraserUp, emptyFunction);
                         break;
                     case InkTool.None:
                         if (!(this.props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine))) {
                             this._hitCluster = this.pickCluster(this.getTransform().transformPoint(e.clientX, e.clientY));
-                            document.addEventListener('pointermove', this.onPointerMove);
-                            document.addEventListener('pointerup', this.onPointerUp);
+                            setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, emptyFunction, false);
                         }
                         break;
                 }
@@ -698,23 +695,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     };
     @action
     onEraserUp = (e: PointerEvent): void => {
-        if (!InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
-            document.removeEventListener('pointermove', this.onEraserMove);
-            document.removeEventListener('pointerup', this.onEraserUp);
-            this._deleteList.forEach(ink => ink.props.removeDocument?.(ink.rootDoc));
-            this._deleteList = [];
-            this._batch?.end();
-        }
-    };
-
-    @action
-    onPointerUp = (e: PointerEvent): void => {
-        if (!InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
-            document.removeEventListener('pointermove', this.onPointerMove);
-            document.removeEventListener('pointerup', this.onPointerUp);
-            this.removeMoveListeners();
-            this.removeEndListeners();
-        }
+        this._deleteList.forEach(ink => ink.props.removeDocument?.(ink.rootDoc));
+        this._deleteList = [];
+        this._batch?.end();
     };
 
     onClick = (e: React.MouseEvent) => {
@@ -752,46 +735,42 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
      *  However, if Shift is held, then no segmentation is done -- instead any intersected stroke is deleted in its entirety.
      */
     @action
-    onEraserMove = (e: PointerEvent) => {
+    onEraserMove = (e: PointerEvent, down: number[], delta: number[]) => {
         const currPoint = { X: e.clientX, Y: e.clientY };
-        this.getEraserIntersections({ X: this._lastX, Y: this._lastY }, currPoint).forEach(intersect => {
+        this.getEraserIntersections({ X: currPoint.X - delta[0], Y: currPoint.Y - delta[1] }, currPoint).forEach(intersect => {
             if (!this._deleteList.includes(intersect.inkView)) {
                 this._deleteList.push(intersect.inkView);
                 SetActiveInkWidth(StrCast(intersect.inkView.rootDoc.strokeWidth?.toString()) || '1');
                 SetActiveInkColor(StrCast(intersect.inkView.rootDoc.color?.toString()) || 'black');
                 // create a new curve by appending all curves of the current segment together in order to render a single new stroke.
-                !e.shiftKey &&
+                if (!e.shiftKey) {
                     this.segmentInkStroke(intersect.inkView, intersect.t).forEach(segment =>
                         GestureOverlay.Instance.dispatchGesture(
                             GestureUtils.Gestures.Stroke,
                             segment.reduce((data, curve) => [...data, ...curve.points.map(p => intersect.inkView.ComponentView?.ptToScreen?.({ X: p.x, Y: p.y }) ?? { X: 0, Y: 0 })], [] as PointData[])
                         )
                     );
+                }
                 // Lower ink opacity to give the user a visual indicator of deletion.
                 intersect.inkView.layoutDoc.opacity = 0.5;
+                intersect.inkView.layoutDoc.dontIntersect = true;
             }
         });
-        this._lastX = currPoint.X;
-        this._lastY = currPoint.Y;
-
-        e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document'  however it does mark the event as cancelBubble=true which we test for in the move event handlers
-        e.preventDefault();
+        return false;
     };
 
     @action
-    onPointerMove = (e: PointerEvent): void => {
-        if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) return;
+    onPointerMove = (e: PointerEvent): boolean => {
+        if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) return false;
         if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
             Doc.ActiveTool = InkTool.None;
             if (this.props.isContentActive(true)) e.stopPropagation();
         } else if (!e.cancelBubble) {
             if (this.tryDragCluster(e, this._hitCluster)) {
-                document.removeEventListener('pointermove', this.onPointerMove);
-                document.removeEventListener('pointerup', this.onPointerUp);
+                return true;
             } else this.pan(e);
-            e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document'  however it does mark the event as cancelBubble=true which we test for in the move event handlers
-            e.preventDefault();
         }
+        return false;
     };
 
     /**
@@ -801,6 +780,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     getEraserIntersections = (lastPoint: { X: number; Y: number }, currPoint: { X: number; Y: number }) => {
         const eraserMin = { X: Math.min(lastPoint.X, currPoint.X), Y: Math.min(lastPoint.Y, currPoint.Y) };
         const eraserMax = { X: Math.max(lastPoint.X, currPoint.X), Y: Math.max(lastPoint.Y, currPoint.Y) };
+
         return this.childDocs
             .map(doc => DocumentManager.Instance.getDocumentView(doc, this.props.CollectionView))
             .filter(inkView => inkView?.ComponentView instanceof InkingStroke)
@@ -888,7 +868,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         const tVals: number[] = [];
         // Iterating through all ink strokes in the current freeform collection.
         this.childDocs
-            .filter(doc => doc.type === DocumentType.INK)
+            .filter(doc => doc.type === DocumentType.INK && !doc.dontIntersect)
             .forEach(doc => {
                 const otherInk = DocumentManager.Instance.getDocumentView(doc, this.props.CollectionView)?.ComponentView as InkingStroke;
                 const { inkData: otherInkData } = otherInk?.inkScaledData() ?? { inkData: [] };
@@ -919,7 +899,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                         e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document'  however it does mark the event as cancelBubble=true which we test for in the move event handlers
                         e.preventDefault();
                         document.removeEventListener('pointermove', this.onPointerMove);
-                        document.removeEventListener('pointerup', this.onPointerUp);
                         return;
                     }
                     // TODO: nda - this allows us to pan collections with finger -> only want to do this when collection is selected'
@@ -967,13 +946,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                         const centerY = Math.min(pt1.clientY, pt2.clientY) + Math.abs(pt2.clientY - pt1.clientY) / 2;
                         // const transformed = this.getTransform().inverse().transformPoint(centerX, centerY);
 
-                        if (!this._pullDirection) {
-                            // if we are not bezel movement
-                            this.pan({ clientX: centerX, clientY: centerY });
-                        } else {
-                            this._pullCoords = [centerX, centerY];
-                        }
-
                         this._lastX = centerX;
                         this._lastY = centerY;
                     }
@@ -998,24 +970,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 const centerY = Math.min(pt1.clientY, pt2.clientY) + Math.abs(pt2.clientY - pt1.clientY) / 2;
                 this._lastX = centerX;
                 this._lastY = centerY;
-                const screenBox = this._mainCont?.getBoundingClientRect();
-
-                // determine if we are using a bezel movement
-                if (screenBox) {
-                    if (screenBox.right - centerX < 100) {
-                        this._pullCoords = [centerX, centerY];
-                        this._pullDirection = 'right';
-                    } else if (centerX - screenBox.left < 100) {
-                        this._pullCoords = [centerX, centerY];
-                        this._pullDirection = 'left';
-                    } else if (screenBox.bottom - centerY < 100) {
-                        this._pullCoords = [centerX, centerY];
-                        this._pullDirection = 'bottom';
-                    } else if (centerY - screenBox.top < 100) {
-                        this._pullCoords = [centerX, centerY];
-                        this._pullDirection = 'top';
-                    }
-                }
 
                 this.removeMoveListeners();
                 this.addMoveListeners();
@@ -1027,19 +981,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     };
 
     cleanUpInteractions = () => {
-        switch (this._pullDirection) {
-            case 'left':
-            case 'right':
-            case 'top':
-            case 'bottom':
-                CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([], { title: 'New Collection' }), this._pullDirection);
-        }
-
-        this._pullDirection = '';
-        this._pullCoords = [0, 0];
-
-        document.removeEventListener('pointermove', this.onPointerMove);
-        document.removeEventListener('pointerup', this.onPointerUp);
         this.removeMoveListeners();
         this.removeEndListeners();
     };
@@ -1380,7 +1321,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         const { z, color, zIndex } = params.pair.layout;
         const { x, y, opacity } =
             this.Document._currentFrame === undefined
-                ? { x: params.pair.layout.x, y: params.pair.layout.y, opacity: this.props.styleProvider?.(params.pair.layout, this.props, StyleProp.Opacity) }
+                ? { x: params.pair.layout.x, y: params.pair.layout.y, opacity: this.props.childOpacity ? this.props.childOpacity() : this.props.styleProvider?.(params.pair.layout, this.props, StyleProp.Opacity) }
                 : CollectionFreeFormDocumentView.getValues(params.pair.layout, NumCast(this.Document._currentFrame));
         return {
             x: NumCast(x),
@@ -1915,7 +1856,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 getContainerTransform={this.getContainerTransform}
                 getTransform={this.getTransform}
                 isAnnotationOverlay={this.isAnnotationOverlay}>
-                <div className="marqueeView-div" ref={this._marqueeRef} style={{ opacity: this.props.dontRenderDocuments ? 0 : undefined }}>
+                <div className="marqueeView-div" ref={this._marqueeRef} style={{ opacity: this.props.dontRenderDocuments ? 0.7 : undefined }}>
                     {this.layoutDoc._backgroundGridShow ? (
                         <div>
                             <CollectionFreeFormBackgroundGrid // bcz : UGHH  don't know why, but if we don't wrap in a div, then PDF's don't render whenn taking snapshot of a dashboard and the background grid is on!!?
@@ -1996,15 +1937,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 {this._firstRender ? this.placeholder : this.marqueeView}
                 {this.props.noOverlay ? null : <CollectionFreeFormOverlayView elements={this.elementFunc} />}
 
-                <div
-                    className={'pullpane-indicator'}
-                    style={{
-                        display: this._pullDirection ? 'block' : 'none',
-                        top: clientRect ? (this._pullDirection === 'bottom' ? this._pullCoords[1] - clientRect.y : 0) : 'auto',
-                        left: clientRect ? (this._pullDirection === 'right' ? this._pullCoords[0] - clientRect.x : 0) : 'auto',
-                        width: clientRect ? (this._pullDirection === 'left' ? this._pullCoords[0] - clientRect.left : this._pullDirection === 'right' ? clientRect.right - this._pullCoords[0] : clientRect.width) : 0,
-                        height: clientRect ? (this._pullDirection === 'top' ? this._pullCoords[1] - clientRect.top : this._pullDirection === 'bottom' ? clientRect.bottom - this._pullCoords[1] : clientRect.height) : 0,
-                    }}></div>
                 {
                     // uncomment to show snap lines
                     <div className="snapLines" style={{ position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', pointerEvents: 'none' }}>
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 78f351f4f..e19e2d525 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,4 +1,4 @@
-import { action, computed, observable, trace } from 'mobx';
+import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc, Opt } from '../../../fields/Doc';
 import { List } from '../../../fields/List';
@@ -6,13 +6,12 @@ import { listSpec } from '../../../fields/Schema';
 import { ComputedField } from '../../../fields/ScriptField';
 import { Cast, NumCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
-import { DashColor, numberRange, OmitKeys } from '../../../Utils';
+import { numberRange } from '../../../Utils';
 import { DocumentManager } from '../../util/DocumentManager';
 import { SelectionManager } from '../../util/SelectionManager';
 import { Transform } from '../../util/Transform';
 import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
 import { DocComponent } from '../DocComponent';
-import { InkingStroke } from '../InkingStroke';
 import { StyleProp } from '../StyleProvider';
 import './CollectionFreeFormDocumentView.scss';
 import { DocumentView, DocumentViewProps } from './DocumentView';
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index bcc55eab4..172adcafe 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -970,7 +970,11 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     collectionFilters = () => StrListCast(this.props.Document._docFilters);
     collectionRangeDocFilters = () => StrListCast(this.props.Document._docRangeFilters);
     @computed get showFilterIcon() {
-        return this.collectionFilters().length || this.collectionRangeDocFilters().length ? 'hasFilter' : this.props.docFilters?.().filter(f => Utils.IsRecursiveFilter(f)).length || this.props.docRangeFilters().length ? 'inheritsFilter' : undefined;
+        return this.collectionFilters().length || this.collectionRangeDocFilters().length
+            ? 'hasFilter'
+            : this.props.docFilters?.().filter(f => Utils.IsRecursiveFilter(f) && f !== Utils.noDragsDocFilter).length || this.props.docRangeFilters().length
+            ? 'inheritsFilter'
+            : undefined;
     }
     rootSelected = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || (this.props.Document.rootDocument && this.props.rootSelected?.(outsideReaction)) || false;
     panelHeight = () => this.props.PanelHeight() - this.headerMargin;
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index a3d501153..0ff15f93b 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -831,16 +831,15 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     // stretches vertically or horizontally depending on video orientation so video fits full screen
     fullScreenSize() {
         if (this._videoRef && this._videoRef.videoHeight / this._videoRef.videoWidth > 1) {
-            return { height: '100%' };
-        } else {
-            return { width: '100%' };
+            //prettier-ignore
+            return ({ height: '100%' });
         }
+        //prettier-ignore
+        return ({ width: '100%' });
     }
 
     // for zoom slider, sets timeline waveform zoom
-    zoom = (zoom: number) => {
-        this.timeline?.setZoom(zoom);
-    };
+    zoom = (zoom: number) => this.timeline?.setZoom(zoom);
 
     // plays link
     playLink = (doc: Doc) => {
diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss
index d8dd074a5..85986ff27 100644
--- a/src/client/views/nodes/WebBox.scss
+++ b/src/client/views/nodes/WebBox.scss
@@ -1,9 +1,11 @@
-@import "../global/globalCssVariables.scss";
-
+@import '../global/globalCssVariables.scss';
 
 .webBox {
     height: 100%;
-    position: relative;
+    width: 100%;
+    top: 0;
+    left: 0;
+    position: absolute;
     display: flex;
 
     .webBox-sideResizer {
@@ -84,7 +86,6 @@
             background: none;
         }
 
-
         .webBox-overlayCont {
             position: absolute;
             width: calc(100% - 40px);
@@ -95,7 +96,7 @@
             justify-content: center;
             align-items: center;
             overflow: hidden;
-            transition: left .5s;
+            transition: left 0.5s;
             pointer-events: all;
 
             .webBox-searchBar {
@@ -158,7 +159,7 @@
             left: 0;
             cursor: text;
             padding: 15px;
-            height: 100%
+            height: 100%;
         }
 
         .webBox-cont {
@@ -235,7 +236,7 @@
         height: 25px;
         align-items: center;
 
-        >svg {
+        > svg {
             margin: auto;
         }
     }
@@ -257,4 +258,4 @@
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index ca9f363c1..6c2e42f86 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -1,5 +1,5 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, trace } from 'mobx';
 import { observer } from 'mobx-react';
 import * as WebRequest from 'web-request';
 import { Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
@@ -11,7 +11,7 @@ import { listSpec } from '../../../fields/Schema';
 import { Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types';
 import { ImageField, WebField } from '../../../fields/URLField';
 import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, getWordAtPoint, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils';
+import { emptyFunction, getWordAtPoint, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, smoothScroll, StopEvent, Utils } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
 import { ScriptingGlobals } from '../../util/ScriptingGlobals';
 import { SnappingManager } from '../../util/SnappingManager';
@@ -35,6 +35,7 @@ import { LinkDocPreview } from './LinkDocPreview';
 import { VideoBox } from './VideoBox';
 import './WebBox.scss';
 import React = require('react');
+import { DragManager } from '../../util/DragManager';
 const { CreateImage } = require('./WebBoxRenderer');
 const _global = (window /* browser */ || global) /* node */ as any;
 const htmlToText = require('html-to-text');
@@ -818,6 +819,56 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return this._showSidebar || this.layoutDoc._showSidebar ? true : false;
     }
 
+    @computed get webpage() {
+        const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
+        const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this.props.pointerEvents?.() as any);
+        const scale = previewScale * (this.props.NativeDimScaling?.() || 1);
+        const renderAnnotations = (docFilters?: () => string[]) => (
+            <CollectionFreeFormView
+                {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
+                renderDepth={this.props.renderDepth + 1}
+                isAnnotationOverlay={true}
+                fieldKey={this.annotationKey}
+                CollectionView={undefined}
+                setPreviewCursor={this.setPreviewCursor}
+                PanelWidth={this.panelWidth}
+                PanelHeight={this.panelHeight}
+                ScreenToLocalTransform={this.scrollXf}
+                NativeDimScaling={returnOne}
+                dropAction={'alias'}
+                docFilters={docFilters}
+                select={emptyFunction}
+                bringToFront={emptyFunction}
+                styleProvider={this.childStyleProvider}
+                whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+                removeDocument={this.removeDocument}
+                moveDocument={this.moveDocument}
+                addDocument={this.addDocument}
+                childPointerEvents={this.props.isContentActive() ? 'all' : undefined}
+                pointerEvents={this.annotationPointerEvents}
+            />
+        );
+        return (
+            <div
+                className={'webBox-outerContent'}
+                ref={this._outerRef}
+                style={{
+                    height: `${100 / scale}%`,
+                    pointerEvents,
+                }}
+                onWheel={StopEvent} // block wheel events from propagating since they're handled by the iframe
+                onScroll={e => this.setDashScrollTop(this._outerRef.current?.scrollTop || 0)}
+                onPointerDown={this.onMarqueeDown}>
+                <div className={'webBox-innerContent'} style={{ height: this._webPageHasBeenRendered ? NumCast(this.scrollHeight, 50) : '100%', pointerEvents }}>
+                    {this.content}
+                    {<div style={{ display: DragManager.docsBeingDragged.length ? 'none' : undefined, mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div>}
+                    {renderAnnotations(this.opaqueFilter)}
+                    {this.annotationLayer}
+                </div>
+            </div>
+        );
+    }
+
     @computed get searchUI() {
         return (
             <div className="webBox-ui" onPointerDown={e => e.stopPropagation()} style={{ display: this.props.isContentActive() ? 'flex' : 'none' }}>
@@ -859,9 +910,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
     scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop));
     anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
-    basicFilter = () => [...this.props.docFilters(), Utils.PropUnsetFilter('textInlineAnnotations')];
     transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()];
-    opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()];
+    opaqueFilter = () => [...this.props.docFilters(), Utils.noDragsDocFilter, ...(DragManager.docsBeingDragged.length ? [] : [Utils.IsOpaqueFilter()])];
     childStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string): any => {
         if (doc instanceof Doc && property === StyleProp.PointerEvents) {
             if (doc.textInlineAnnotations) return 'none';
@@ -871,37 +921,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     pointerEvents = () => (!this._draggingSidebar && this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none');
     annotationPointerEvents = () => (this._isAnnotating || SnappingManager.GetIsDragging() ? 'all' : 'none');
     render() {
-        const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this.props.pointerEvents?.() as any);
         const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
+        const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this.props.pointerEvents?.() as any);
         const scale = previewScale * (this.props.NativeDimScaling?.() || 1);
-        const renderAnnotations = (docFilters?: () => string[]) => (
-            <CollectionFreeFormView
-                {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
-                renderDepth={this.props.renderDepth + 1}
-                isAnnotationOverlay={true}
-                fieldKey={this.annotationKey}
-                CollectionView={undefined}
-                setPreviewCursor={this.setPreviewCursor}
-                PanelWidth={this.panelWidth}
-                PanelHeight={this.panelHeight}
-                ScreenToLocalTransform={this.scrollXf}
-                NativeDimScaling={returnOne}
-                dropAction={'alias'}
-                docFilters={docFilters || this.basicFilter}
-                dontRenderDocuments={docFilters ? false : true}
-                select={emptyFunction}
-                bringToFront={emptyFunction}
-                whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-                removeDocument={this.removeDocument}
-                moveDocument={this.moveDocument}
-                addDocument={this.addDocument}
-                styleProvider={this.childStyleProvider}
-                childPointerEvents={this.props.isContentActive() ? 'all' : undefined}
-                pointerEvents={this.annotationPointerEvents}
-            />
-        );
         return (
-            <div className="webBox" ref={this._mainCont} style={{ pointerEvents: this.pointerEvents(), display: this.props.thumbShown?.() ? 'none' : undefined }}>
+            <div className="webBox" ref={this._mainCont} style={{ pointerEvents: this.pointerEvents(), display: !SnappingManager.GetIsDragging() && this.props.thumbShown?.() ? 'none' : undefined }}>
                 <div className="webBox-background" style={{ backgroundColor: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor) }} />
                 <div
                     className="webBox-container"
@@ -911,27 +935,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                         pointerEvents,
                     }}
                     onContextMenu={this.specificContextMenu}>
-                    <div
-                        className={'webBox-outerContent'}
-                        ref={this._outerRef}
-                        style={{
-                            height: `${100 / scale}%`,
-                            pointerEvents,
-                        }}
-                        onWheel={e => {
-                            e.stopPropagation();
-                            e.preventDefault();
-                        }} // block wheel events from propagating since they're handled by the iframe
-                        onScroll={e => this.setDashScrollTop(this._outerRef.current?.scrollTop || 0)}
-                        onPointerDown={this.onMarqueeDown}>
-                        <div className={'webBox-innerContent'} style={{ height: this._webPageHasBeenRendered ? NumCast(this.scrollHeight, 50) : '100%', pointerEvents }}>
-                            {this.content}
-                            <div style={{ mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div>
-                            {renderAnnotations(this.opaqueFilter)}
-                            {SnappingManager.GetIsDragging() ? null : renderAnnotations()}
-                            {this.annotationLayer}
-                        </div>
-                    </div>
+                    {this.webpage}
                     {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (
                         <div style={{ transformOrigin: 'top left', transform: `scale(${1 / scale})` }}>
                             <MarqueeAnnotator
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 2c83082b7..a45edfbca 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -9,6 +9,7 @@ import { Cast, NumCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
 import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, OmitKeys, smoothScroll, Utils } from '../../../Utils';
 import { DocUtils } from '../../documents/Documents';
+import { DragManager } from '../../util/DragManager';
 import { SelectionManager } from '../../util/SelectionManager';
 import { SharingManager } from '../../util/SharingManager';
 import { SnappingManager } from '../../util/SnappingManager';
@@ -487,9 +488,8 @@ export class PDFViewer extends React.Component<IViewerProps> {
     overlayTransform = () => this.scrollXf().scale(1 / NumCast(this.props.layoutDoc._viewScale, 1));
     panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
     panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
-    basicFilter = () => [...this.props.docFilters(), Utils.PropUnsetFilter('textInlineAnnotations')];
     transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()];
-    opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()];
+    opaqueFilter = () => [...this.props.docFilters(), Utils.noDragsDocFilter, ...(DragManager.docsBeingDragged.length ? [] : [Utils.IsOpaqueFilter()])];
     childStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string): any => {
         if (doc instanceof Doc && property === StyleProp.PointerEvents) {
             if (doc.textInlineAnnotations) return 'none';
@@ -498,56 +498,43 @@ export class PDFViewer extends React.Component<IViewerProps> {
         return this.props.styleProvider?.(doc, props, property);
     };
 
-    renderAnnotations = (docFilters?: () => string[], dontRender?: boolean) => (
-        <CollectionFreeFormView
-            {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
-            isAnnotationOverlay={true}
-            fieldKey={this.props.fieldKey + '-annotations'}
-            setPreviewCursor={this.setPreviewCursor}
-            PanelHeight={this.panelHeight}
-            PanelWidth={this.panelWidth}
-            dropAction={'alias'}
-            select={emptyFunction}
-            bringToFront={emptyFunction}
-            docFilters={docFilters || this.basicFilter}
-            styleProvider={this.childStyleProvider}
-            dontRenderDocuments={dontRender}
-            CollectionView={undefined}
-            ScreenToLocalTransform={this.overlayTransform}
-            renderDepth={this.props.renderDepth + 1}
-        />
+    renderAnnotations = (docFilters?: () => string[], mixBlendMode?: any, display?: string) => (
+        <div
+            className="pdfViewerDash-overlay"
+            style={{
+                mixBlendMode: mixBlendMode,
+                display: display,
+                transform: `scale(${NumCast(this.props.layoutDoc._viewScale, 1)})`,
+            }}>
+            <CollectionFreeFormView
+                {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
+                renderDepth={this.props.renderDepth + 1}
+                isAnnotationOverlay={true}
+                fieldKey={this.props.fieldKey + '-annotations'}
+                CollectionView={undefined}
+                setPreviewCursor={this.setPreviewCursor}
+                PanelHeight={this.panelHeight}
+                PanelWidth={this.panelWidth}
+                ScreenToLocalTransform={this.overlayTransform}
+                dropAction={'alias'}
+                docFilters={docFilters}
+                select={emptyFunction}
+                bringToFront={emptyFunction}
+                styleProvider={this.childStyleProvider}
+            />
+        </div>
     );
     @computed get overlayTransparentAnnotations() {
-        return this.renderAnnotations(this.transparentFilter, false);
+        return this.renderAnnotations(this.transparentFilter, 'multiply', DragManager.docsBeingDragged.length ? 'none' : undefined);
     }
     @computed get overlayOpaqueAnnotations() {
-        return this.renderAnnotations(this.opaqueFilter, false);
-    }
-    @computed get overlayClickableAnnotations() {
-        return <div style={{ height: NumCast(this.props.rootDoc.scrollHeight) }}>{this.renderAnnotations(undefined, true)}</div>;
+        return this.renderAnnotations(this.opaqueFilter, this.allAnnotations.some(anno => anno.mixBlendMode) ? 'hard-light' : undefined);
     }
     @computed get overlayLayer() {
         return (
             <div style={{ pointerEvents: SnappingManager.GetIsDragging() ? 'all' : 'none' }}>
-                <div
-                    className="pdfViewerDash-overlay"
-                    style={{
-                        pointerEvents: SnappingManager.GetIsDragging() ? 'all' : 'none',
-                        mixBlendMode: 'multiply',
-                        transform: `scale(${NumCast(this.props.layoutDoc._viewScale, 1)})`,
-                    }}>
-                    {this.overlayTransparentAnnotations}
-                </div>
-                <div
-                    className="pdfViewerDash-overlay"
-                    style={{
-                        pointerEvents: SnappingManager.GetIsDragging() ? 'all' : 'none',
-                        mixBlendMode: this.allAnnotations.some(anno => anno.mixBlendMode) ? 'hard-light' : undefined,
-                        transform: `scale(${NumCast(this.props.layoutDoc._viewScale, 1)})`,
-                    }}>
-                    {this.overlayOpaqueAnnotations}
-                    {this.overlayClickableAnnotations}
-                </div>
+                {this.overlayTransparentAnnotations}
+                {this.overlayOpaqueAnnotations}
             </div>
         );
     }
-- 
cgit v1.2.3-70-g09d2


From 94c38310c6b54d93e907007f20ba032d12697ca0 Mon Sep 17 00:00:00 2001
From: Naafiyan Ahmed <naafiyan@gmail.com>
Date: Thu, 18 Aug 2022 12:33:34 -0400
Subject: fixed sizing bug

---
 src/client/documents/Documents.ts                  | 14 +++++--------
 src/client/views/collections/CollectionSubView.tsx |  2 ++
 src/client/views/nodes/LoadingBox.tsx              | 24 ++++++++++++++++++----
 3 files changed, 27 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 e54fe16de..f3ab7c788 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -827,9 +827,12 @@ export namespace Docs {
             viewFirstProps.author = Doc.CurrentUserEmail;
             let viewDoc: Doc;
             if (placeholderDoc) {
+                placeholderDoc._height = Number(options._height);
+                placeholderDoc._width = Number(options._width);
                 viewDoc = Doc.assign(placeholderDoc, viewFirstProps, true, true);
+            } else {
+                viewDoc = Doc.assign(Doc.MakeDelegate(dataDoc, delegId), viewFirstProps, true, true);
             }
-            viewDoc = Doc.assign(Doc.MakeDelegate(dataDoc, delegId), viewFirstProps, true, true);
             Doc.assign(viewDoc, viewProps, true, true);
             ![DocumentType.LINK, DocumentType.MARKER, DocumentType.LABEL].includes(viewDoc.type as any) && DocUtils.MakeLinkToActiveAudio(() => viewDoc);
 
@@ -901,7 +904,7 @@ export namespace Docs {
             return InstanceFromProto(Prototypes.get(DocumentType.COLOR), '', options);
         }
 
-        export const filesToDocs = new Map<Doc, File>();
+        export const filesToDocs = new Map<Doc, File | string>();
 
         export function LoadingDocument(file: File | string, options: DocumentOptions, ytString?: string) {
             const fileName = typeof file == 'string' ? file : file.name;
@@ -1854,13 +1857,6 @@ export namespace DocUtils {
         });
     }
 
-    export function generatePlaceHolder(file: File, options: DocumentOptions) {
-        return Docs.Create.LoadingDocument(file, options);
-        // placeholder.file = file
-        // TODO: nda - modify loading doc so it only takes in options
-        // Docs.Create.LoadingDocument(options, )
-    }
-
     // copies the specified drag factory document
     export function copyDragFactory(dragFactory: Doc) {
         if (!dragFactory) return undefined;
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 30467efa0..fd2c722d2 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -470,6 +470,7 @@ export function CollectionSubView<X>(moreProps?: X) {
                 // generatedDocuments.push(Docs.Create.LoadingDocument(files, options));
                 const loading = Docs.Create.LoadingDocument(files, options);
                 generatedDocuments.push(loading);
+                Docs.Create.filesToDocs.set(loading, files);
                 DocUtils.uploadYoutubeVideoLoading(files, {}, loading);
             } else {
                 // uploadFilesToDocs and similar should return a placeholder, one for each placeholder
@@ -477,6 +478,7 @@ export function CollectionSubView<X>(moreProps?: X) {
                     ...files.map(file => {
                         const loading = Docs.Create.LoadingDocument(file, options);
                         // now that there is a doc do whatever slowload was going to do with that file
+                        Docs.Create.filesToDocs.set(loading, file);
                         DocUtils.uploadFileToDoc(file, {}, loading);
                         return loading;
                     })
diff --git a/src/client/views/nodes/LoadingBox.tsx b/src/client/views/nodes/LoadingBox.tsx
index 96620aff9..249461b67 100644
--- a/src/client/views/nodes/LoadingBox.tsx
+++ b/src/client/views/nodes/LoadingBox.tsx
@@ -5,6 +5,8 @@ import * as React from 'react';
 import './LoadingBox.scss';
 import ReactLoading from 'react-loading';
 import { StrCast } from '../../../fields/Types';
+import { computed, observable } from 'mobx';
+import { Docs } from '../../documents/Documents';
 
 @observer
 export class LoadingBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
@@ -12,6 +14,12 @@ export class LoadingBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
         return FieldView.LayoutString(LoadingBox, fieldKey);
     }
 
+    @computed
+    private get isLoading() {
+        const file = Docs.Create.filesToDocs.get(this.rootDoc);
+        return file ? true : false;
+    }
+
     componentDidMount() {
         console.log(this.rootDoc);
         // const file = Docs.Create.filesToDocs.get(this.rootDoc);
@@ -44,10 +52,18 @@ export class LoadingBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     render() {
         return (
             <div className="loadingBoxContainer">
-                <p className="text">Loading:</p>
-                <br></br>
-                <p className="text">{StrCast(this.rootDoc.title)}</p>
-                <ReactLoading type={'spinningBubbles'} color={'blue'} height={100} width={100} />
+                {this.isLoading ? (
+                    <div>
+                        <p className="text">Loading:</p>
+                        <br></br>
+                        <span className="text">{StrCast(this.rootDoc.title)}</span>
+                        <ReactLoading type={'spinningBubbles'} color={'blue'} height={100} width={100} />
+                    </div>
+                ) : (
+                    <div>
+                        <span>Error Loading File: {StrCast(this.rootDoc.title)}</span>
+                    </div>
+                )}
             </div>
         );
     }
-- 
cgit v1.2.3-70-g09d2


From b7c4d65a3bf04ff7d2c10d93be282a1b0a4650b3 Mon Sep 17 00:00:00 2001
From: Naafiyan Ahmed <naafiyan@gmail.com>
Date: Fri, 19 Aug 2022 12:32:23 -0400
Subject: added comments, cleaned up code, could not get spinner to center

---
 src/client/documents/Documents.ts                  |  8 ++-
 src/client/views/collections/CollectionSubView.tsx |  4 +-
 src/client/views/nodes/LoadingBox.scss             | 21 +++++++
 src/client/views/nodes/LoadingBox.tsx              | 64 ++++++++++------------
 4 files changed, 58 insertions(+), 39 deletions(-)

(limited to 'src/client/views/collections/CollectionSubView.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index f3ab7c788..9e140ccd1 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -815,6 +815,7 @@ export namespace Docs {
             dataProps[fieldKey + '-annotations'] = new List<Doc>();
             dataProps[fieldKey + '-sidebar'] = new List<Doc>();
 
+            // users placeholderDoc as proto if it exists
             const dataDoc = Doc.assign(placeholderDoc ? Doc.GetProto(placeholderDoc) : Doc.MakeDelegate(proto, protoId), dataProps, undefined, true);
 
             if (placeholderDoc) {
@@ -826,6 +827,7 @@ export namespace Docs {
             viewFirstProps['acl-Override'] = 'None';
             viewFirstProps.author = Doc.CurrentUserEmail;
             let viewDoc: Doc;
+            // determines whether viewDoc should be created using placeholder Doc or deafult
             if (placeholderDoc) {
                 placeholderDoc._height = Number(options._height);
                 placeholderDoc._width = Number(options._width);
@@ -904,13 +906,13 @@ export namespace Docs {
             return InstanceFromProto(Prototypes.get(DocumentType.COLOR), '', options);
         }
 
-        export const filesToDocs = new Map<Doc, File | string>();
+        // Mapping of all loading docs to files, i.e. keeps track of files being uploaded in current session
+        export const docsToFiles = new Map<Doc, File | string>();
 
         export function LoadingDocument(file: File | string, options: DocumentOptions, ytString?: string) {
             const fileName = typeof file == 'string' ? file : file.name;
             options.title = fileName;
-            const loading = InstanceFromProto(Prototypes.get(DocumentType.LOADING), fileName, { _height: 150, _width: 150, ...options });
-            // filesToDocs.set(loading, file);
+            const loading = InstanceFromProto(Prototypes.get(DocumentType.LOADING), fileName, { _height: 150, _width: 200, ...options });
             return loading;
         }
 
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index fd2c722d2..eef485b95 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -470,7 +470,7 @@ export function CollectionSubView<X>(moreProps?: X) {
                 // generatedDocuments.push(Docs.Create.LoadingDocument(files, options));
                 const loading = Docs.Create.LoadingDocument(files, options);
                 generatedDocuments.push(loading);
-                Docs.Create.filesToDocs.set(loading, files);
+                Docs.Create.docsToFiles.set(loading, files);
                 DocUtils.uploadYoutubeVideoLoading(files, {}, loading);
             } else {
                 // uploadFilesToDocs and similar should return a placeholder, one for each placeholder
@@ -478,7 +478,7 @@ export function CollectionSubView<X>(moreProps?: X) {
                     ...files.map(file => {
                         const loading = Docs.Create.LoadingDocument(file, options);
                         // now that there is a doc do whatever slowload was going to do with that file
-                        Docs.Create.filesToDocs.set(loading, file);
+                        Docs.Create.docsToFiles.set(loading, file);
                         DocUtils.uploadFileToDoc(file, {}, loading);
                         return loading;
                     })
diff --git a/src/client/views/nodes/LoadingBox.scss b/src/client/views/nodes/LoadingBox.scss
index e8890cd82..f6912f547 100644
--- a/src/client/views/nodes/LoadingBox.scss
+++ b/src/client/views/nodes/LoadingBox.scss
@@ -6,7 +6,28 @@
     background-color: #fdfdfd;
 }
 
+.textContainer {
+    margin: 5px;
+}
+
+.textContainer {
+    justify-content: center;
+    align-content: center;
+}
+
+.textContainer,
 .text {
+    overflow: hidden;
     text-overflow: ellipsis;
+    max-width: 80%;
+    text-align: center;
+}
+
+.headerText {
+    text-align: center;
+    font-weight: bold;
+}
+
+.spinner {
     text-align: center;
 }
diff --git a/src/client/views/nodes/LoadingBox.tsx b/src/client/views/nodes/LoadingBox.tsx
index 249461b67..ab8878fc1 100644
--- a/src/client/views/nodes/LoadingBox.tsx
+++ b/src/client/views/nodes/LoadingBox.tsx
@@ -5,46 +5,42 @@ import * as React from 'react';
 import './LoadingBox.scss';
 import ReactLoading from 'react-loading';
 import { StrCast } from '../../../fields/Types';
-import { computed, observable } from 'mobx';
+import { computed } from 'mobx';
 import { Docs } from '../../documents/Documents';
 
+/**
+ * LoadingBox Class represents a placeholder doc for documents that are currently
+ * being uploaded to the server and being fetched by the client. The LoadingBox doc is then used to
+ * generate the actual type of doc that is required once the document has been successfully uploaded.
+ *
+ * Design considerations:
+ * We are using the docToFiles map in Documents to keep track of all files being uploaded in one session of the client.
+ * If the file is not found we assume an error has occurred with the file upload, e.g. it has been interrupted by a client refresh
+ * or network issues. The docToFiles essentially gets reset everytime the page is refreshed.
+ *
+ * TODOs:
+ *  1) ability to query server to retrieve files that already exist if users upload duplicate files.
+ *  2) ability to restart upload if there is an error
+ *  3) detect network error and notify the user
+ *  4 )if file upload gets interrupted, it still gets uploaded to the server if there are no network interruptions which leads to unused space. this could be
+ *      handled with (1)
+ *
+ * @author naafiyan
+ */
 @observer
 export class LoadingBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     public static LayoutString(fieldKey: string) {
         return FieldView.LayoutString(LoadingBox, fieldKey);
     }
 
-    @computed
-    private get isLoading() {
-        const file = Docs.Create.filesToDocs.get(this.rootDoc);
+    /**
+     * Returns true if file is uploading, false otherwise
+     */
+    @computed private get isLoading(): boolean {
+        const file = Docs.Create.docsToFiles.get(this.rootDoc);
         return file ? true : false;
     }
 
-    componentDidMount() {
-        console.log(this.rootDoc);
-        // const file = Docs.Create.filesToDocs.get(this.rootDoc);
-        // if (file) {
-        //     console.log('Got to file');
-        //     Docs.Create.filesToDocs.delete(this.rootDoc);
-        //     // now that there is a doc do whatever slowload was going to do with that file
-        //     if (typeof file === 'string') {
-        //         // uploadYoutubeVideo and similar should return a placeholder, one for each placeholder
-        //         // (await DocUtils.uploadYoutubeVideo(files, options)));
-        //     } else {
-        //         // uploadFilesToDocs and similar should return a placeholder, one for each placeholder
-        //         DocUtils.uploadFileToDoc(file, {}, this.rootDoc);
-        //     }
-        // } else {
-        //     // check if file now exists on server or not
-        //     // if it does we need to retreieve it and create the appropriate doc (rest of what uploadFileToDoc was doing minus uploading)
-        //     // if it doesn't display an error message "upload failed"
-        // }
-        // query endpoints to:
-        // check if file now exists on server or not
-        // if it does we need to retreieve it and create the appropriate doc (rest of what uploadFileToDoc was doing minus uploading)
-        // if it doesn't display an error message "upload failed"
-    }
-
     constructor(props: any) {
         super(props);
     }
@@ -53,15 +49,15 @@ export class LoadingBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
         return (
             <div className="loadingBoxContainer">
                 {this.isLoading ? (
-                    <div>
-                        <p className="text">Loading:</p>
-                        <br></br>
+                    <div className="textContainer">
+                        <p className="headerText">Loading:</p>
                         <span className="text">{StrCast(this.rootDoc.title)}</span>
                         <ReactLoading type={'spinningBubbles'} color={'blue'} height={100} width={100} />
                     </div>
                 ) : (
-                    <div>
-                        <span>Error Loading File: {StrCast(this.rootDoc.title)}</span>
+                    <div className="textContainer">
+                        <p className="headerText">Error Loading File: </p>
+                        <span className="text">{StrCast(this.rootDoc.title)}</span>
                     </div>
                 )}
             </div>
-- 
cgit v1.2.3-70-g09d2


From 5b20224b59f4a6ebc02fbd0a68441bb241c68d63 Mon Sep 17 00:00:00 2001
From: Naafiyan Ahmed <naafiyan@gmail.com>
Date: Fri, 19 Aug 2022 14:37:32 -0400
Subject: cleaned up comments

---
 src/client/views/collections/CollectionSubView.tsx | 5 -----
 1 file changed, 5 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 d7d988c19..e70cfb13c 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -460,18 +460,13 @@ export function CollectionSubView<X>(moreProps?: X) {
             // create placeholder docs
             // inside placeholder docs have some func that
 
-            // TODO: once loading thing is moved it should update the x and y of the file it is placeholder for
             let pileUpDoc = undefined;
-            // const disposer = OverlayView.Instance.addElement(<ReactLoading type={'spinningBubbles'} color={'green'} height={250} width={250} />, { x: clientX - 125, y: clientY - 125 });
             if (typeof files === 'string') {
-                // uploadYoutubeVideo and similar should return a placeholder, one for each placeholder
-                // generatedDocuments.push(Docs.Create.LoadingDocument(files, options));
                 const loading = Docs.Create.LoadingDocument(files, options);
                 generatedDocuments.push(loading);
                 Docs.Create.docsToFiles.set(loading, files);
                 DocUtils.uploadYoutubeVideoLoading(files, {}, loading);
             } else {
-                // uploadFilesToDocs and similar should return a placeholder, one for each placeholder
                 generatedDocuments.push(
                     ...files.map(file => {
                         const loading = Docs.Create.LoadingDocument(file, options);
-- 
cgit v1.2.3-70-g09d2


From 7f2556568ab635274c483d102fa4555d12e14835 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 30 Aug 2022 13:55:41 -0400
Subject: fixed loadingDocument issues with overwriting height with NaN and
 setting 'data' field unnecessarily and at the expense of being able to set it
 for replacement docs when using animation frames

---
 src/client/documents/Documents.ts                  | 34 ++++++++++------------
 src/client/views/GlobalKeyHandler.ts               |  9 +++---
 src/client/views/collections/CollectionSubView.tsx |  6 ++--
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  6 ++--
 src/client/views/nodes/LoadingBox.tsx              | 29 ++++--------------
 5 files changed, 30 insertions(+), 54 deletions(-)

(limited to 'src/client/views/collections/CollectionSubView.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 9446eb70c..b22e16633 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -653,7 +653,7 @@ export namespace Docs {
             [
                 DocumentType.LOADING,
                 {
-                    layout: { view: LoadingBox, dataField: defaultDataKey },
+                    layout: { view: LoadingBox, dataField: '' },
                     options: { _fitWidth: true, _fitHeight: true, nativeDimModifiable: true, links: '@links(self)' },
                 },
             ],
@@ -806,14 +806,15 @@ export namespace Docs {
             dataProps.isPrototype = true;
             dataProps.author = Doc.CurrentUserEmail;
             dataProps.creationDate = new DateField();
-            dataProps[`${fieldKey}-lastModified`] = new DateField();
-
-            dataProps[fieldKey] = data;
-
-            // so that the list of annotations is already initialised, prevents issues in addonly.
-            // without this, if a doc has no annotations but the user has AddOnly privileges, they won't be able to add an annotation because they would have needed to create the field's list which they don't have permissions to do.
-            dataProps[fieldKey + '-annotations'] = new List<Doc>();
-            dataProps[fieldKey + '-sidebar'] = new List<Doc>();
+            if (fieldKey) {
+                dataProps[`${fieldKey}-lastModified`] = new DateField();
+                dataProps[fieldKey] = data;
+
+                // so that the list of annotations is already initialised, prevents issues in addonly.
+                // without this, if a doc has no annotations but the user has AddOnly privileges, they won't be able to add an annotation because they would have needed to create the field's list which they don't have permissions to do.
+                dataProps[fieldKey + '-annotations'] = new List<Doc>();
+                dataProps[fieldKey + '-sidebar'] = new List<Doc>();
+            }
 
             // users placeholderDoc as proto if it exists
             const dataDoc = Doc.assign(placeholderDoc ? Doc.GetProto(placeholderDoc) : Doc.MakeDelegate(proto, protoId), dataProps, undefined, true);
@@ -829,8 +830,8 @@ export namespace Docs {
             let viewDoc: Doc;
             // determines whether viewDoc should be created using placeholder Doc or default
             if (placeholderDoc) {
-                placeholderDoc._height = Number(options._height);
-                placeholderDoc._width = Number(options._width);
+                placeholderDoc._height = options._height !== undefined ? Number(options._height) : undefined;
+                placeholderDoc._width = options._width !== undefined ? Number(options._width) : undefined;
                 viewDoc = Doc.assign(placeholderDoc, viewFirstProps, true, true);
             } else {
                 viewDoc = Doc.assign(Doc.MakeDelegate(dataDoc, delegId), viewFirstProps, true, true);
@@ -846,6 +847,7 @@ export namespace Docs {
 
             updateCachedAcls(dataDoc);
             updateCachedAcls(viewDoc);
+
             return viewDoc;
         }
 
@@ -905,15 +907,8 @@ export namespace Docs {
         export function ColorDocument(options: DocumentOptions = {}) {
             return InstanceFromProto(Prototypes.get(DocumentType.COLOR), '', options);
         }
-
-        // Mapping of all loading docs to files, i.e. keeps track of files being uploaded in current session
-        export const docsToFiles = new Map<Doc, File | string>();
-
         export function LoadingDocument(file: File | string, options: DocumentOptions, ytString?: string) {
-            const fileName = typeof file == 'string' ? file : file.name;
-            options.title = fileName;
-            const loading = InstanceFromProto(Prototypes.get(DocumentType.LOADING), fileName, { _height: 150, _width: 200, ...options });
-            return loading;
+            return InstanceFromProto(Prototypes.get(DocumentType.LOADING), undefined, { _height: 150, _width: 200, title: typeof file == 'string' ? file : file.name, ...options }, undefined, '');
         }
 
         export function RTFDocument(field: RichTextField, options: DocumentOptions = {}, fieldKey: string = 'text') {
@@ -1756,6 +1751,7 @@ export namespace DocUtils {
         const full = { ...options, _width: 400, title: name };
         const pathname = Utils.prepend(result.accessPaths.agnostic.client);
         const doc = await DocUtils.DocumentFromType(type, pathname, full, rootDoc);
+        rootDoc && (rootDoc.isLoading = undefined);
         if (doc) {
             const proto = Doc.GetProto(doc);
             proto.text = result.rawText;
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 88ce457c6..50518eec1 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -84,6 +84,7 @@ export class KeyManager {
     });
 
     private unmodified = action((keyname: string, e: KeyboardEvent) => {
+        const hasFffView = SelectionManager.Views().some(dv => dv.props.CollectionFreeFormDocumentView?.());
         switch (keyname) {
             case 'u':
                 if (document.activeElement?.tagName === 'INPUT' || document.activeElement?.tagName === 'TEXTAREA') {
@@ -160,16 +161,16 @@ export class KeyManager {
                 break;
             case 'arrowleft':
                 UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge(-1, 0)), 'nudge left');
-                return { stopPropagation: true, preventDefault: true };
+                return { stopPropagation: hasFffView, preventDefault: hasFffView };
             case 'arrowright':
                 UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge?.(1, 0)), 'nudge right');
-                return { stopPropagation: true, preventDefault: true };
+                return { stopPropagation: hasFffView, preventDefault: hasFffView };
             case 'arrowup':
                 UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge?.(0, -1)), 'nudge up');
-                return { stopPropagation: true, preventDefault: true };
+                return { stopPropagation: hasFffView, preventDefault: hasFffView };
             case 'arrowdown':
                 UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge?.(0, 1)), 'nudge down');
-                return { stopPropagation: true, preventDefault: true };
+                return { stopPropagation: hasFffView, preventDefault: hasFffView };
         }
 
         return {
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index e70cfb13c..4227955ef 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -464,14 +464,13 @@ export function CollectionSubView<X>(moreProps?: X) {
             if (typeof files === 'string') {
                 const loading = Docs.Create.LoadingDocument(files, options);
                 generatedDocuments.push(loading);
-                Docs.Create.docsToFiles.set(loading, files);
+                loading.isLoading = true;
                 DocUtils.uploadYoutubeVideoLoading(files, {}, loading);
             } else {
                 generatedDocuments.push(
                     ...files.map(file => {
                         const loading = Docs.Create.LoadingDocument(file, options);
-                        // now that there is a doc do whatever slowload was going to do with that file
-                        Docs.Create.docsToFiles.set(loading, file);
+                        loading.isLoading = true;
                         DocUtils.uploadFileToDoc(file, {}, loading);
                         return loading;
                     })
@@ -516,5 +515,4 @@ import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'
 import { DragManager, dropActionType } from '../../util/DragManager';
 import { SelectionManager } from '../../util/SelectionManager';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
-import { OverlayView } from '../OverlayView';
 import { CollectionView, CollectionViewProps } from './CollectionView';
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index cfa4bb160..7907470dc 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -35,7 +35,7 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
 export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps>() {
     public static animFields: { key: string; val?: number }[] = [{ key: '_height' }, { key: '_width' }, { key: 'x' }, { key: 'y' }, { key: '_scrollTop' }, { key: 'opacity', val: 1 }, { key: 'viewScale', val: 1 }, { key: 'panX' }, { key: 'panY' }]; // fields that are configured to be animatable using animation frames
     public static animStringFields = ['backgroundColor', 'color']; // fields that are configured to be animatable using animation frames
-    public static animDataFields = ['data', 'text']; // fields that are configured to be animatable using animation frames
+    public static animDataFields = (doc: Doc) => (Doc.LayoutFieldKey(doc) ? [Doc.LayoutFieldKey(doc)] : []); // fields that are configured to be animatable using animation frames
     @observable _animPos: number[] | undefined = undefined;
     @observable _contentView: DocumentView | undefined | null;
     get displayName() {
@@ -121,7 +121,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                     const findexed = Cast(doc[`${val}-indexed`], listSpec('string'), null);
                     findexed?.length <= timecode + 1 && findexed.push(undefined as any as string);
                 });
-                CollectionFreeFormDocumentView.animDataFields.forEach(val => {
+                CollectionFreeFormDocumentView.animDataFields(doc).forEach(val => {
                     const findexed = Cast(doc[`${val}-indexed`], listSpec(InkField), null);
                     findexed?.length <= timecode + 1 && findexed.push(undefined as any);
                 });
@@ -173,7 +173,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
             }
             CollectionFreeFormDocumentView.animFields.forEach(val => (doc[val.key] = ComputedField.MakeInterpolatedNumber(val.key, 'activeFrame', doc, currTimecode, val.val)));
             CollectionFreeFormDocumentView.animStringFields.forEach(val => (doc[val] = ComputedField.MakeInterpolatedString(val, 'activeFrame', doc, currTimecode)));
-            CollectionFreeFormDocumentView.animDataFields.forEach(val => (Doc.GetProto(doc)[val] = ComputedField.MakeInterpolatedDataField(val, 'activeFrame', Doc.GetProto(doc), currTimecode)));
+            CollectionFreeFormDocumentView.animDataFields(doc).forEach(val => (Doc.GetProto(doc)[val] = ComputedField.MakeInterpolatedDataField(val, 'activeFrame', Doc.GetProto(doc), currTimecode)));
             const targetDoc = doc.type === DocumentType.RTF ? Doc.GetProto(doc) : doc; // data fields, like rtf 'text' exist on the data doc, so
             doc !== targetDoc && (targetDoc.context = doc.context); // the computed fields don't see the layout doc -- need to copy the context to the data doc (HACK!!!) and set the activeFrame on the data doc (HACK!!!)
             targetDoc.activeFrame = ComputedField.MakeFunction('self.context?._currentFrame||0');
diff --git a/src/client/views/nodes/LoadingBox.tsx b/src/client/views/nodes/LoadingBox.tsx
index 90bf90095..f3243f6cd 100644
--- a/src/client/views/nodes/LoadingBox.tsx
+++ b/src/client/views/nodes/LoadingBox.tsx
@@ -35,33 +35,14 @@ export class LoadingBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
         return FieldView.LayoutString(LoadingBox, fieldKey);
     }
 
-    /**
-     * Returns true if file is uploading, false otherwise
-     */
-    @computed private get isLoading(): boolean {
-        const file = Docs.Create.docsToFiles.get(this.rootDoc);
-        return file ? true : false;
-    }
-
-    constructor(props: any) {
-        super(props);
-    }
-
     render() {
         return (
             <div className="loadingBoxContainer">
-                {this.isLoading ? (
-                    <div className="textContainer">
-                        <p className="headerText">Loading:</p>
-                        <span className="text">{StrCast(this.rootDoc.title)}</span>
-                        <ReactLoading type={'spinningBubbles'} color={'blue'} height={100} width={100} />
-                    </div>
-                ) : (
-                    <div className="textContainer">
-                        <p className="headerText">Error Loading File: </p>
-                        <span className="text">{StrCast(this.rootDoc.title)}</span>
-                    </div>
-                )}
+                <div className="textContainer">
+                    <p className="headerText">{this.rootDoc.isLoading ? 'Loading:' : 'Error Loading File:'}</p>
+                    <span className="text">{StrCast(this.rootDoc.title)}</span>
+                    {!this.rootDoc.isLoading ? null : <ReactLoading type={'spinningBubbles'} color={'blue'} height={100} width={100} />}
+                </div>
             </div>
         );
     }
-- 
cgit v1.2.3-70-g09d2

