From d34d212ea550a3c1ca16747fadeb9e69c936cb5d Mon Sep 17 00:00:00 2001
From: Sophie Zhang <sophie_zhang@brown.edu>
Date: Wed, 1 Mar 2023 23:49:11 -0500
Subject: feat: simple typing animation

---
 .../views/nodes/formattedText/FormattedTextBox.tsx | 97 +++++++---------------
 1 file changed, 31 insertions(+), 66 deletions(-)

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

diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 35c845deb..65b96c281 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -65,20 +65,12 @@ import applyDevTools = require('prosemirror-dev-tools');
 import React = require('react');
 import { Configuration, OpenAIApi } from 'openai';
 import { gptAPICall, GPTCallType } from '../../../apis/gpt/Summarization';
-import ReactLoading from 'react-loading';
-import Typist from 'react-typist';
 const translateGoogleApi = require('translate-google-api');
 
 export const GoogleRef = 'googleDocId';
 
 type PullHandler = (exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>, dataDoc: Doc) => void;
 
-enum GPTStatus {
-    LOADING,
-    TYPING,
-    NONE,
-}
-
 @observer
 export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     public static LayoutString(fieldStr: string) {
@@ -181,19 +173,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         };
     }
 
-    // State for GPT Typing animation
-    @observable
-    private gptStatus: GPTStatus = GPTStatus.NONE;
-    @observable
-    private gptPrompt: string = '';
+    // State for GPT
     @observable
     private gptRes: string = '';
 
-    @action
-    private setGPTStatus = (status: GPTStatus) => {
-        this.gptStatus = status;
-    };
-
     public static PasteOnLoad: ClipboardEvent | undefined;
     public static SelectOnLoad = '';
     public static DontSelectInitialText = false; // whether initial text should be selected or not
@@ -877,31 +860,29 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         });
     };
 
+    animateRes = (resIndex: number) => {
+        if (resIndex < this.gptRes.length) {
+            this.dataDoc.text = (this.dataDoc.text as RichTextField)?.Text + this.gptRes[resIndex];
+            setTimeout(() => {
+                this.animateRes(resIndex + 1);
+            }, 20);
+        }
+    };
+
     askGPT = action(async () => {
         try {
-            this.gptPrompt = (this.dataDoc.text as RichTextField)?.Text;
-            this.setGPTStatus(GPTStatus.LOADING);
             let res = await gptAPICall((this.dataDoc.text as RichTextField)?.Text, GPTCallType.COMPLETION);
             // let res = await this.mockGPT();
             if (res) {
                 this.gptRes = res;
-                this.setGPTStatus(GPTStatus.TYPING);
-            } else {
-                this.setGPTStatus(GPTStatus.NONE);
+                this.animateRes(0);
             }
         } catch (err) {
             console.log(err);
             this.dataDoc.text = (this.dataDoc.text as RichTextField)?.Text + 'Something went wrong';
-            this.setGPTStatus(GPTStatus.NONE);
         }
     });
 
-    setGPTText = action(() => {
-        this.dataDoc.text = (this.dataDoc.text as RichTextField)?.Text + this.gptRes;
-        this.gptRes = '';
-        this.setGPTStatus(GPTStatus.NONE);
-    });
-
     generateImage = async () => {
         console.log('Generate image from text: ', (this.dataDoc.text as RichTextField)?.Text);
         try {
@@ -1997,45 +1978,29 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                     onPointerUp={this.onPointerUp}
                     onPointerDown={this.onPointerDown}
                     onDoubleClick={this.onDoubleClick}>
-                    {this.gptStatus === GPTStatus.NONE || this.gptStatus === GPTStatus.LOADING ? (
+                    <div
+                        className={`formattedTextBox-outer${selected ? '-selected' : ''}`}
+                        ref={this._scrollRef}
+                        style={{
+                            width: this.props.dontSelectOnLoad ? '100%' : `calc(100% - ${this.sidebarWidthPercent})`,
+                            pointerEvents: !active && !SnappingManager.GetIsDragging() ? 'none' : undefined,
+                            overflow: this.layoutDoc._singleLine ? 'hidden' : this.layoutDoc._autoHeight ? 'visible' : undefined,
+                        }}
+                        onScroll={this.onScroll}
+                        onDrop={this.ondrop}>
                         <div
-                            className={`formattedTextBox-outer${selected ? '-selected' : ''}`}
-                            ref={this._scrollRef}
+                            className={minimal ? 'formattedTextBox-minimal' : `formattedTextBox-inner${rounded}${selPaddingClass}`}
+                            ref={this.createDropTarget}
                             style={{
-                                width: this.props.dontSelectOnLoad ? '100%' : `calc(100% - ${this.sidebarWidthPercent})`,
-                                pointerEvents: !active && !SnappingManager.GetIsDragging() ? 'none' : undefined,
-                                overflow: this.layoutDoc._singleLine ? 'hidden' : this.layoutDoc._autoHeight ? 'visible' : undefined,
+                                padding: StrCast(this.layoutDoc._textBoxPadding),
+                                paddingLeft: StrCast(this.layoutDoc._textBoxPaddingX, `${paddingX - selPad}px`),
+                                paddingRight: StrCast(this.layoutDoc._textBoxPaddingX, `${paddingX - selPad}px`),
+                                paddingTop: StrCast(this.layoutDoc._textBoxPaddingY, `${paddingY - selPad}px`),
+                                paddingBottom: StrCast(this.layoutDoc._textBoxPaddingY, `${paddingY - selPad}px`),
+                                pointerEvents: !active && !SnappingManager.GetIsDragging() ? (this.layoutDoc.isLinkButton ? 'none' : undefined) : undefined,
                             }}
-                            onScroll={this.onScroll}
-                            onDrop={this.ondrop}>
-                            <div
-                                className={minimal ? 'formattedTextBox-minimal' : `formattedTextBox-inner${rounded}${selPaddingClass}`}
-                                ref={this.createDropTarget}
-                                style={{
-                                    padding: StrCast(this.layoutDoc._textBoxPadding),
-                                    paddingLeft: StrCast(this.layoutDoc._textBoxPaddingX, `${paddingX - selPad}px`),
-                                    paddingRight: StrCast(this.layoutDoc._textBoxPaddingX, `${paddingX - selPad}px`),
-                                    paddingTop: StrCast(this.layoutDoc._textBoxPaddingY, `${paddingY - selPad}px`),
-                                    paddingBottom: StrCast(this.layoutDoc._textBoxPaddingY, `${paddingY - selPad}px`),
-                                    pointerEvents: !active && !SnappingManager.GetIsDragging() ? (this.layoutDoc.isLinkButton ? 'none' : undefined) : undefined,
-                                }}
-                            />
-                        </div>
-                    ) : (
-                        <div className="gpt-typing-wrapper">
-                            <div>{this.gptPrompt}</div>
-                            <br />
-                            <Typist
-                                key={this.gptRes}
-                                avgTypingDelay={15}
-                                cursor={{ hideWhenDone: true }}
-                                onTypingDone={() => {
-                                    this.setGPTText();
-                                }}>
-                                <div>{this.gptRes}</div>
-                            </Typist>
-                        </div>
-                    )}
+                        />
+                    </div>
                     {this.noSidebar || this.props.dontSelectOnLoad || !this.SidebarShown || this.sidebarWidthPercent === '0%' ? null : this.sidebarCollection}
                     {this.noSidebar || this.Document._noSidebar || this.props.dontSelectOnLoad || this.Document._singleLine ? null : this.sidebarHandle}
                     {this.audioHandle}
-- 
cgit v1.2.3-70-g09d2

