From fec79d2b5b8feb361e489c9ee41ee720507d0806 Mon Sep 17 00:00:00 2001
From: Sophie Zhang <sophie_zhang@brown.edu>
Date: Thu, 27 Jul 2023 13:23:42 -0400
Subject: changes

---
 src/client/views/pdf/GPTPopup/GPTPopup.tsx | 129 +++++++++++++++++++++--------
 1 file changed, 96 insertions(+), 33 deletions(-)

(limited to 'src/client/views/pdf/GPTPopup/GPTPopup.tsx')

diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index 9f28cb5d1..aeee90d16 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -1,4 +1,5 @@
 import React = require('react');
+import './GPTPopup.scss';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, observable } from 'mobx';
 import { observer } from 'mobx-react';
@@ -6,13 +7,14 @@ import ReactLoading from 'react-loading';
 import Typist from 'react-typist';
 import { Doc } from '../../../../fields/Doc';
 import { DocUtils, Docs } from '../../../documents/Documents';
-import './GPTPopup.scss';
-import { Button, IconButton } from 'browndash-components';
-import { StrCast } from '../../../../fields/Types';
+import { Button, IconButton, Type } from 'browndash-components';
+import { NumCast, StrCast } from '../../../../fields/Types';
+import { CgClose } from 'react-icons/cg';
 
 export enum GPTPopupMode {
     SUMMARY,
     EDIT,
+    IMAGE,
 }
 
 interface GPTPopupProps {}
@@ -39,6 +41,14 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
     public setText = (text: string) => {
         this.text = text;
     };
+
+    @observable
+    public imageUrls: string[] = [];
+    @action
+    public setImgUrls = (imgs: string[]) => {
+        this.imageUrls = imgs;
+    };
+
     @observable
     public mode: GPTPopupMode = GPTPopupMode.SUMMARY;
     @action
@@ -58,6 +68,8 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
     public setDone = (done: boolean) => {
         this.done = done;
     };
+
+    // change what can be a ref into a ref
     @observable
     private sidebarId: string = '';
     @action
@@ -65,19 +77,30 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
         this.sidebarId = id;
     };
 
-    // reference
-    // if (focusNode) {
-    //     const anchor = srcWeb?.ComponentView?.getAnchor?.(true);
-    //     anchor && DocUtils.MakeLink(htmlDoc, anchor, {});
-    // }
+    // pdfs and webpages
+    @observable
+    private targetAnchor: Doc | undefined;
+    @action
+    public setTargetAnchor = (anchor: Doc) => {
+        this.targetAnchor = anchor;
+    };
+
     @observable
-    private pdfAnchor: Doc | undefined;
+    private imgTargetDoc: Doc | undefined;
     @action
-    public setPdfAnchor = (anchor: Doc) => {
-        this.pdfAnchor = anchor;
+    public setImgTargetDoc = (anchor: Doc) => {
+        this.imgTargetDoc = anchor;
+    };
+
+    @observable
+    private textAnchor: Doc | undefined;
+    @action
+    public setTextAnchor = (anchor: Doc) => {
+        this.textAnchor = anchor;
     };
 
     public addDoc: (doc: Doc | Doc[], sidebarKey?: string | undefined) => boolean = () => false;
+    public addToCollection: ((doc: Doc | Doc[], annotationKey?: string | undefined) => boolean) | undefined;
 
     /**
      * Transfers the summarization text to a sidebar annotation text document.
@@ -90,13 +113,42 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
             _layout_autoHeight: true,
         });
         this.addDoc(newDoc, this.sidebarId);
-        if (this.pdfAnchor) {
-            DocUtils.MakeLink(newDoc, this.pdfAnchor, {
+        if (this.targetAnchor) {
+            DocUtils.MakeLink(newDoc, this.targetAnchor, {
                 link_relationship: 'GPT Summary',
             });
         }
     };
 
+    /**
+     * Transfers the image urls to actual image docs
+     */
+    private transferToImage = (source: string) => {
+        console.log('Text Anchor', this.textAnchor);
+        console.log('Whole doc anchor', this.imgTargetDoc);
+        const textAnchor = this.textAnchor ?? this.imgTargetDoc;
+        if (!textAnchor) return;
+        const newDoc = Docs.Create.ImageDocument(source, {
+            x: NumCast(textAnchor.x) + NumCast(textAnchor._width) + 10,
+            y: NumCast(textAnchor.y),
+            _height: 200,
+            _width: 200,
+            data_nativeWidth: 1024,
+            data_nativeHeight: 1024,
+        });
+        if (Doc.IsInMyOverlay(textAnchor)) {
+            newDoc.overlayX = textAnchor.x;
+            newDoc.overlayY = NumCast(textAnchor.y) + NumCast(textAnchor._height);
+            Doc.AddToMyOverlay(newDoc);
+        } else {
+            this.addToCollection?.(newDoc);
+        }
+        // Create link between prompt and image
+        DocUtils.MakeLink(textAnchor, newDoc, { link_relationship: 'Image Prompt' });
+    };
+
+    private getPreviewUrl = (source: string) => source.split('.').join('_m.');
+
     constructor(props: GPTPopupProps) {
         super(props);
         GPTPopup.Instance = this;
@@ -108,6 +160,26 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
         }
     };
 
+    imageBox = () => {
+        return (
+            <div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
+                {this.heading('GENERATED IMAGE')}
+                <div className="image-content-wrapper">
+                    {this.imageUrls.map(rawSrc => (
+                        <div className="img-wrapper">
+                            <div className="img-container">
+                                <img key={rawSrc} src={this.getPreviewUrl(rawSrc)} width={150} height={150} alt="dalle generation" />
+                            </div>
+                            <div className="btn-container">
+                                <Button text="Save Image" onClick={() => this.transferToImage(rawSrc)} color={StrCast(Doc.UserDoc().userColor)} type={Type.TERT} />
+                            </div>
+                        </div>
+                    ))}
+                </div>
+            </div>
+        );
+    };
+
     summaryBox = () => (
         <>
             <div>
@@ -135,30 +207,21 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
                 <div className="btns-wrapper">
                     {this.done ? (
                         <>
-                            {/* <button className="icon-btn" onClick={this.callSummaryApi}>
-                                <FontAwesomeIcon icon="redo-alt" size="lg" />
-                            </button>
-                            <button
-                                className="text-btn"
-                                onClick={e => {
-                                    this.transferToText();
-                                }}>
-                                Transfer to Text
-                            </button> */}
-                            <IconButton onClick={this.callSummaryApi} icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} color={StrCast(Doc.UserDoc().userVariantColor)} />
-                            <Button text="Transfer To Text" onClick={this.transferToText} color={StrCast(Doc.UserDoc().userVariantColor)} />
+                            <IconButton tooltip="Generate Again" onClick={this.callSummaryApi} icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} color={StrCast(Doc.UserDoc().userVariantColor)} />
+                            <Button tooltip="Transfer to text" text="Transfer To Text" onClick={this.transferToText} color={StrCast(Doc.UserDoc().userVariantColor)} type={Type.TERT} />
                         </>
                     ) : (
                         <div className="summarizing">
                             <span>Summarizing</span>
                             <ReactLoading type="bubbles" color="#bcbcbc" width={20} height={20} />
-                            <button
-                                className="btn-secondary"
-                                onClick={e => {
+                            <Button
+                                text="Stop Animation"
+                                onClick={() => {
                                     this.setDone(true);
-                                }}>
-                                Stop Animation
-                            </button>
+                                }}
+                                color={StrCast(Doc.UserDoc().userVariantColor)}
+                                type={Type.TERT}
+                            />
                         </div>
                     )}
                 </div>
@@ -216,14 +279,14 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
     heading = (headingText: string) => (
         <div className="summary-heading">
             <label className="summary-text">{headingText}</label>
-            {this.loading && <ReactLoading type="spin" color="#bcbcbc" width={14} height={14} />}
+            {this.loading ? <ReactLoading type="spin" color="#bcbcbc" width={14} height={14} /> : <IconButton color={StrCast(Doc.UserDoc().userColor)} tooltip="close" icon={<CgClose size="16px" />} onClick={() => this.setVisible(false)} />}
         </div>
     );
 
     render() {
         return (
             <div className="summary-box" style={{ display: this.visible ? 'flex' : 'none' }}>
-                {this.mode === GPTPopupMode.SUMMARY ? this.summaryBox() : this.editBox()}
+                {this.mode === GPTPopupMode.SUMMARY ? this.summaryBox() : this.mode === GPTPopupMode.IMAGE ? this.imageBox() : this.editBox()}
             </div>
         );
     }
-- 
cgit v1.2.3-70-g09d2

