From 647b66c965f5896d784de0f321d31cc712937e1c Mon Sep 17 00:00:00 2001
From: eleanor-park <eleanor_park@brown.edu>
Date: Fri, 26 Jul 2024 01:23:06 -0400
Subject: Revert "Merge branch 'master' into eleanor-gptdraw"

This reverts commit decbefe23a1da35c838222bafe8a2c029c6ea794, reversing
changes made to 8ca26551622d36b7856f5c1865498fa9e5d888b5.
---
 src/client/views/nodes/WebBox.tsx | 1 -
 1 file changed, 1 deletion(-)

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

diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index da947face..8835ea5e7 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -45,7 +45,6 @@ import { LinkInfo } from './LinkDocPreview';
 import { OpenWhere } from './OpenWhere';
 import './WebBox.scss';
 
-// eslint-disable-next-line @typescript-eslint/no-var-requires
 const { CreateImage } = require('./WebBoxRenderer');
 
 @observer
-- 
cgit v1.2.3-70-g09d2


From b453414dc2bb11d56c5dcb2a73d389eefabff7d5 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 2 Sep 2024 12:16:43 -0400
Subject: cleaned up updateIcon to use promises to avoid a bunch of setTimeouts

---
 src/client/util/DropConverter.ts                   |  3 +-
 src/client/views/InkTranscription.tsx              | 14 ++------
 src/client/views/ViewBoxInterface.ts               |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 src/client/views/nodes/DocumentView.tsx            | 13 ++++++-
 src/client/views/nodes/PDFBox.tsx                  | 42 +++++++++++-----------
 src/client/views/nodes/VideoBox.tsx                | 15 ++++----
 src/client/views/nodes/WebBox.tsx                  | 29 ++++++---------
 src/client/views/smartdraw/AnnotationPalette.tsx   | 40 +++++++--------------
 src/client/views/smartdraw/SmartDrawHandler.tsx    | 14 +-------
 10 files changed, 71 insertions(+), 103 deletions(-)

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

diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
index 57c79cff3..0ede44298 100644
--- a/src/client/util/DropConverter.ts
+++ b/src/client/util/DropConverter.ts
@@ -92,7 +92,8 @@ export function makeUserTemplateButton(doc: Doc) {
  * Similar to makeUserTemplateButton, but rather than creating a draggable button for the template, it takes in
  * an ImageField that will display.
  */
-export function makeUserTemplateImage(doc: Doc, image: ImageField) {
+export function makeUserTemplateImage(doc: Doc, imageHref: string | undefined) {
+    const image = imageHref ?? 'http://www.cs.brown.edu/~bcz/noImage.png';
     const layoutDoc = doc; // doc.layout instanceof Doc && doc.layout.isTemplateForField ? doc.layout : doc;
     if (layoutDoc.type !== DocumentType.FONTICON) {
         !layoutDoc.isTemplateDoc && makeTemplate(layoutDoc);
diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx
index 485a0e7c4..26e0aa372 100644
--- a/src/client/views/InkTranscription.tsx
+++ b/src/client/views/InkTranscription.tsx
@@ -3,7 +3,7 @@ import { action, observable } from 'mobx';
 import * as React from 'react';
 import { Doc, DocListCast } from '../../fields/Doc';
 import { InkData, InkField, InkTool } from '../../fields/InkField';
-import { Cast, DateCast, ImageCast, NumCast, StrCast } from '../../fields/Types';
+import { Cast, DateCast, DocCast, ImageCast, NumCast, StrCast } from '../../fields/Types';
 import { aggregateBounds } from '../../Utils';
 import { DocumentType } from '../documents/DocumentTypes';
 import { CollectionFreeFormView, MarqueeView } from './collections/collectionFreeForm';
@@ -273,7 +273,7 @@ export class InkTranscription extends React.Component {
                     DocumentView.getDocumentView(this.currGroup)?.ComponentView?.updateIcon?.();
                     this.currGroup.transcription = text;
                     this.currGroup.title = text;
-                    let image = await this.getIcon();
+                    let image = await DocumentView.GetDocImage(this.currGroup);
                     const pathname = image?.url.href as string;
                     console.log(image?.url);
                     console.log(image);
@@ -300,16 +300,6 @@ export class InkTranscription extends React.Component {
             }
         }
     };
-    async getIcon() {
-        const docView = DocumentView.getDocumentView(this.currGroup);
-        console.log(this.currGroup);
-        if (docView) {
-            console.log(docView);
-            docView.ComponentView?.updateIcon?.();
-            return new Promise<ImageField | undefined>(res => setTimeout(() => res(ImageCast(docView.Document.icon)), 1000));
-        }
-        return undefined;
-    }
     imageUrlToBase64 = async (imageUrl: string): Promise<string> => {
         try {
             const response = await fetch(imageUrl);
diff --git a/src/client/views/ViewBoxInterface.ts b/src/client/views/ViewBoxInterface.ts
index 111771ba0..f66f6062e 100644
--- a/src/client/views/ViewBoxInterface.ts
+++ b/src/client/views/ViewBoxInterface.ts
@@ -22,7 +22,7 @@ export abstract class ViewBoxInterface<P> extends ObservableReactComponent<React
         return ''; //
     }
     promoteCollection?: () => void; // moves contents of collection to parent
-    updateIcon?: (usePanelDimensions?: boolean) => void; // updates the icon representation of the document
+    updateIcon?: (usePanelDimensions?: boolean) => Promise<void>; // updates the icon representation of the document
     getAnchor?: (addAsAnnotation: boolean, pinData?: PinProps) => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box)
     restoreView?: (viewSpec: Doc) => boolean;
     scrollPreview?: (docView: DocumentView, doc: Doc, focusSpeed: number, options: FocusViewOptions) => Opt<number>; // returns the duration of the focus
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 880d1cd74..4b4a07757 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1878,7 +1878,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     updateIcon = (usePanelDimensions?: boolean) =>
         UpdateIcon(
-            this.layoutDoc[Id] + '-icon' + new Date().getTime(),
+            this.layoutDoc[Id] + '_icon_' + new Date().getTime(),
             this.DocumentView?.().ContentDiv!,
             usePanelDimensions ? this._props.PanelWidth() : NumCast(this.layoutDoc._width),
             usePanelDimensions ? this._props.PanelHeight() : NumCast(this.layoutDoc._height),
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 4cc7eb0ff..c279badf4 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -16,7 +16,7 @@ import { List } from '../../../fields/List';
 import { PrefetchProxy } from '../../../fields/Proxy';
 import { listSpec } from '../../../fields/Schema';
 import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, Cast, DocCast, NumCast, RTFCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { BoolCast, Cast, DocCast, ImageCast, NumCast, RTFCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { AudioField } from '../../../fields/URLField';
 import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
 import { AudioAnnoState } from '../../../server/SharedMediaTypes';
@@ -1087,6 +1087,17 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
      * Pins a Doc to the current presentation trail. (see TabDocView for implementation)
      */
     public static PinDoc: (docIn: Doc | Doc[], pinProps: PinProps) => void;
+
+    /**
+     * Renders an image of a Doc into the Doc's icon field, then returns a promise for the image value
+     * @param doc Doc to snapshot
+     * @returns promise of icon ImageField
+     */
+    public static GetDocImage(doc: Doc) {
+        return DocumentView.getDocumentView(doc)
+            ?.ComponentView?.updateIcon?.()
+            .then(() => ImageCast(DocCast(doc).icon));
+    }
     /**
      * The DocumentView below the cursor at the start of a gesture (that receives the pointerDown event).  Used by GestureOverlay to determine the doc a gesture should apply to.
      */
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index b17275a1e..209c5abbc 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -179,28 +179,26 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     updateIcon = () => {
         // currently we render pdf icons as text labels
         const docViewContent = this.DocumentView?.().ContentDiv;
-        const filename = this.layoutDoc[Id] + '-icon' + new Date().getTime();
-        this._pdfViewer?._mainCont.current &&
-            docViewContent &&
-            UpdateIcon(
-                filename,
-                docViewContent,
-                NumCast(this.layoutDoc._width),
-                NumCast(this.layoutDoc._height),
-                this._props.PanelWidth(),
-                this._props.PanelHeight(),
-                NumCast(this.layoutDoc._layout_scrollTop),
-                NumCast(this.dataDoc[this.fieldKey + '_nativeHeight'], 1),
-                true,
-                this.layoutDoc[Id] + '-icon',
-                (iconFile: string, nativeWidth: number, nativeHeight: number) => {
-                    setTimeout(() => {
-                        this.dataDoc.icon = new ImageField(iconFile);
-                        this.dataDoc.icon_nativeWidth = nativeWidth;
-                        this.dataDoc.icon_nativeHeight = nativeHeight;
-                    }, 500);
-                }
-            );
+        const filename = this.layoutDoc[Id] + '_icon_' + new Date().getTime();
+        return !(this._pdfViewer?._mainCont.current && docViewContent)
+            ? new Promise<void>(res => res())
+            : UpdateIcon(
+                  filename,
+                  docViewContent,
+                  NumCast(this.layoutDoc._width),
+                  NumCast(this.layoutDoc._height),
+                  this._props.PanelWidth(),
+                  this._props.PanelHeight(),
+                  NumCast(this.layoutDoc._layout_scrollTop),
+                  NumCast(this.dataDoc[this.fieldKey + '_nativeHeight'], 1),
+                  true,
+                  this.layoutDoc[Id] + '_icon_' + new Date().getTime(),
+                  (iconFile: string, nativeWidth: number, nativeHeight: number) => {
+                      this.dataDoc.icon = new ImageField(iconFile);
+                      this.dataDoc.icon_nativeWidth = nativeWidth;
+                      this.dataDoc.icon_nativeHeight = nativeHeight;
+                  }
+              );
     };
 
     componentWillUnmount() {
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 4933869a7..d653b27d7 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -298,18 +298,19 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
             const retitled = StrCast(this.Document.title).replace(/[ -.:]/g, '');
             const encodedFilename = encodeURIComponent(('snapshot' + retitled + '_' + (this.layoutDoc._layout_currentTimecode || 0).toString()).replace(/[./?=]/g, '_'));
             const filename = basename(encodedFilename);
-            ClientUtils.convertDataUri(dataUrl, filename).then((returnedFilename: string) => returnedFilename && (cb ?? this.createSnapshotLink)(returnedFilename, downX, downY));
+            return ClientUtils.convertDataUri(dataUrl, filename).then((returnedFilename: string) => {
+                if (returnedFilename) (cb ?? this.createSnapshotLink)(returnedFilename, downX, downY);
+            });
         }
+        return new Promise<void>(res => res());
     };
 
-    updateIcon = () => {
-        const makeIcon = (returnedfilename: string) => {
+    updateIcon = () =>
+        this.Snapshot(undefined, undefined, (returnedfilename: string) => {
             this.dataDoc.icon = new ImageField(returnedfilename);
             this.dataDoc.icon_nativeWidth = NumCast(this.layoutDoc._width);
             this.dataDoc.icon_nativeHeight = NumCast(this.layoutDoc._height);
-        };
-        this.Snapshot(undefined, undefined, makeIcon);
-    };
+        });
 
     // creates link for snapshot
     createSnapshotLink = (imagePath: string, downX?: number, downY?: number) => {
@@ -459,7 +460,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
             const url = field.url.href;
             const subitems: ContextMenuProps[] = [];
             subitems.push({ description: 'Full Screen', event: this.FullScreen, icon: 'expand' });
-            subitems.push({ description: 'Take Snapshot', event: this.Snapshot, icon: 'expand-arrows-alt' });
+            subitems.push({ description: 'Take Snapshot', event: () => this.Snapshot(), icon: 'expand-arrows-alt' });
             this.Document.type === DocumentType.SCREENSHOT &&
                 subitems.push({
                     description: 'Screen Capture',
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 8088bddd4..a5788d02a 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -144,38 +144,31 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     };
 
     updateIcon = async () => {
-        if (!this._iframe) return;
+        if (!this._iframe) return new Promise<void>(res => res());
         const scrollTop = NumCast(this.layoutDoc._layout_scrollTop);
         const nativeWidth = NumCast(this.layoutDoc.nativeWidth);
         const nativeHeight = (nativeWidth * this._props.PanelHeight()) / this._props.PanelWidth();
         let htmlString = this._iframe.contentDocument && new XMLSerializer().serializeToString(this._iframe.contentDocument);
         if (!htmlString) {
-            htmlString = await (await fetch(ClientUtils.CorsProxy(this.webField!.href))).text();
+            htmlString = await fetch(ClientUtils.CorsProxy(this.webField!.href)).then(response => response.text());
         }
         this.layoutDoc.thumb = undefined;
         this.Document.thumbLockout = true; // lock to prevent multiple thumb updates.
-        CreateImage(this._webUrl.endsWith('/') ? this._webUrl.substring(0, this._webUrl.length - 1) : this._webUrl, this._iframe.contentDocument?.styleSheets ?? [], htmlString, nativeWidth, nativeHeight, scrollTop)
+        return (CreateImage(this._webUrl.endsWith('/') ? this._webUrl.substring(0, this._webUrl.length - 1) : this._webUrl, this._iframe.contentDocument?.styleSheets ?? [], htmlString, nativeWidth, nativeHeight, scrollTop) as Promise<string>)
             .then((dataUrl: string) => {
                 if (dataUrl.includes('<!DOCTYPE')) {
                     console.log('BAD DATA IN THUMB CREATION');
                     return;
                 }
-                ClientUtils.convertDataUri(dataUrl, this.layoutDoc[Id] + '-icon' + new Date().getTime(), true, this.layoutDoc[Id] + '-icon').then(returnedfilename =>
-                    setTimeout(
-                        action(() => {
-                            this.Document.thumbLockout = false;
-                            this.layoutDoc.thumb = new ImageField(returnedfilename);
-                            this.layoutDoc.thumbScrollTop = scrollTop;
-                            this.layoutDoc.thumbNativeWidth = nativeWidth;
-                            this.layoutDoc.thumbNativeHeight = nativeHeight;
-                        }),
-                        500
-                    )
-                );
+                return ClientUtils.convertDataUri(dataUrl, this.layoutDoc[Id] + '_icon_' + new Date().getTime(), true, this.layoutDoc[Id] + '_icon_').then(returnedfilename => {
+                    this.Document.thumbLockout = false;
+                    this.layoutDoc.thumb = new ImageField(returnedfilename);
+                    this.layoutDoc.thumbScrollTop = scrollTop;
+                    this.layoutDoc.thumbNativeWidth = nativeWidth;
+                    this.layoutDoc.thumbNativeHeight = nativeHeight;
+                });
             })
-            .catch((error: object) => {
-                console.error('oops, something went wrong!', error);
-            });
+            .catch((error: object) => console.error('oops, something went wrong!', error));
     };
 
     componentDidMount() {
diff --git a/src/client/views/smartdraw/AnnotationPalette.tsx b/src/client/views/smartdraw/AnnotationPalette.tsx
index 72364be09..2ebe5bca5 100644
--- a/src/client/views/smartdraw/AnnotationPalette.tsx
+++ b/src/client/views/smartdraw/AnnotationPalette.tsx
@@ -6,7 +6,7 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import { AiOutlineSend } from 'react-icons/ai';
 import ReactLoading from 'react-loading';
-import { returnEmptyFilter, returnFalse, returnTrue } from '../../../ClientUtils';
+import { ClientUtils, returnEmptyFilter, returnFalse, returnTrue } from '../../../ClientUtils';
 import { emptyFunction } from '../../../Utils';
 import { Doc, DocListCast, returnEmptyDoclist } from '../../../fields/Doc';
 import { DocData } from '../../../fields/DocSymbols';
@@ -25,6 +25,7 @@ import { DocumentView, DocumentViewInternal } from '../nodes/DocumentView';
 import { FieldView } from '../nodes/FieldView';
 import './AnnotationPalette.scss';
 import { DrawingOptions, SmartDrawHandler } from './SmartDrawHandler';
+import { setTime } from 'react-datepicker/dist/date_utils';
 
 interface AnnotationPaletteProps {
     Document: Doc;
@@ -109,26 +110,20 @@ export class AnnotationPalette extends ObservableReactComponent<AnnotationPalett
      */
     public static addToPalette = async (doc: Doc) => {
         if (!doc.savedAsAnno) {
-            const clone = await Doc.MakeClone(doc);
-            clone.clone.title = doc.title;
-            const image = ((await AnnotationPalette.getIcon(doc)) ?? ImageCast(doc[Doc.LayoutFieldKey(doc)]))?.[Copy]();
-            if (image) {
-                const imageTemplate = makeUserTemplateImage(clone.clone, image);
-                Doc.AddDocToList(Doc.MyAnnos, 'data', imageTemplate);
-                doc.savedAsAnno = true;
-            }
+            Doc.MakeClone(doc).then(cloneMap =>
+                DocumentView.getDocumentView(doc)
+                    ?.ComponentView?.updateIcon?.(true)
+                    .then(() => {
+                        const { clone } = cloneMap;
+                        clone.title = doc.title;
+                        const image = ImageCast(doc.icon, ImageCast(clone[Doc.LayoutFieldKey(clone)]))?.url?.href;
+                        Doc.AddDocToList(Doc.MyAnnos, 'data', makeUserTemplateImage(clone, image));
+                        doc.savedAsAnno = true;
+                    })
+            );
         }
     };
 
-    public static getIcon(group: Doc) {
-        const docView = DocumentView.getDocumentView(group);
-        if (docView) {
-            docView.ComponentView?.updateIcon?.(true);
-            return new Promise<ImageField | undefined>(res => setTimeout(() => res(ImageCast(docView.Document.icon)), 1000));
-        }
-        return undefined;
-    }
-
     /**
      * Calls the draw with GPT functions in SmartDrawHandler to allow users to generate drawings straight from
      * the annotation palette.
@@ -184,15 +179,6 @@ export class AnnotationPalette extends ObservableReactComponent<AnnotationPalett
         this.resetPalette(true);
     };
 
-    async getIcon(group: Doc) {
-        const docView = DocumentView.getDocumentView(group);
-        if (docView) {
-            docView.ComponentView?.updateIcon?.(true);
-            return new Promise<ImageField | undefined>(res => setTimeout(() => res(ImageCast(docView.Document.icon)), 1000));
-        }
-        return undefined;
-    }
-
     render() {
         return (
             <div className="annotation-palette" style={{ zIndex: 1000 }} onClick={e => e.stopPropagation()}>
diff --git a/src/client/views/smartdraw/SmartDrawHandler.tsx b/src/client/views/smartdraw/SmartDrawHandler.tsx
index 879358742..8271c0959 100644
--- a/src/client/views/smartdraw/SmartDrawHandler.tsx
+++ b/src/client/views/smartdraw/SmartDrawHandler.tsx
@@ -249,7 +249,7 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
      * Sends request to GPT API to recolor a selected ink document or group of ink documents.
      */
     colorWithGPT = async (drawing: Doc) => {
-        const img = await this.getIcon(drawing);
+        const img = await DocumentView.GetDocImage(drawing);
         const { href } = ImageCast(img).url;
         const hrefParts = href.split('.');
         const hrefComplete = `${hrefParts[0]}_o.${hrefParts[1]}`;
@@ -286,18 +286,6 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
         });
     }, 'color strokes');
 
-    /**
-     * Gets an image snapshot of a doc. In this class, it's used to snapshot a selected ink stroke/group to use for GPT color.
-     */
-    async getIcon(doc: Doc) {
-        const docView = DocumentView.getDocumentView(doc);
-        if (docView) {
-            docView.ComponentView?.updateIcon?.();
-            return new Promise<ImageField | undefined>(res => setTimeout(() => res(ImageCast(docView.Document.icon)), 1000));
-        }
-        return undefined;
-    }
-
     renderDisplay() {
         return (
             <div
-- 
cgit v1.2.3-70-g09d2

