diff options
-rw-r--r-- | src/client/util/DropConverter.ts | 3 | ||||
-rw-r--r-- | src/client/views/InkTranscription.tsx | 14 | ||||
-rw-r--r-- | src/client/views/ViewBoxInterface.ts | 2 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 2 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 13 | ||||
-rw-r--r-- | src/client/views/nodes/PDFBox.tsx | 42 | ||||
-rw-r--r-- | src/client/views/nodes/VideoBox.tsx | 15 | ||||
-rw-r--r-- | src/client/views/nodes/WebBox.tsx | 29 | ||||
-rw-r--r-- | src/client/views/smartdraw/AnnotationPalette.tsx | 40 | ||||
-rw-r--r-- | src/client/views/smartdraw/SmartDrawHandler.tsx | 14 |
10 files changed, 71 insertions, 103 deletions
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 |