From 7b588f2cae664437f7ab549e542d552547d40b35 Mon Sep 17 00:00:00 2001
From: AudreyKj <38159391+AudreyKj@users.noreply.github.com>
Date: Thu, 21 Oct 2021 15:07:19 +0200
Subject: [PATCH] [#2514] fixed ui bugs

---
 .../Inbox/MessageInput/InputSelector.tsx      | 41 +++++++++++-----
 .../ui/src/pages/Inbox/MessageInput/index.tsx | 48 ++++++++++++++-----
 .../Messenger/MessengerContainer/index.tsx    | 10 ++--
 lib/typescript/render/attachments.ts          |  1 +
 .../render/components/Image/index.module.scss |  1 +
 .../components/ImageWithFallback/index.tsx    | 30 ++++++++----
 lib/typescript/render/index.ts                |  1 +
 .../components/QuickReplies/index.tsx         |  9 +++-
 .../components/RichCard/Media/index.tsx       |  2 +-
 .../components/GenericTemplate/index.tsx      |  4 +-
 .../components/QuickReplies/index.tsx         |  4 +-
 .../components/RichCard/Media/index.tsx       |  2 +-
 .../google/components/Suggestions/index.tsx   |  8 ++--
 13 files changed, 113 insertions(+), 48 deletions(-)

diff --git a/frontend/ui/src/pages/Inbox/MessageInput/InputSelector.tsx b/frontend/ui/src/pages/Inbox/MessageInput/InputSelector.tsx
index 006c1b5a68..c5b769d6e5 100644
--- a/frontend/ui/src/pages/Inbox/MessageInput/InputSelector.tsx
+++ b/frontend/ui/src/pages/Inbox/MessageInput/InputSelector.tsx
@@ -3,22 +3,24 @@ import styles from './InputSelector.module.scss';
 import {ReactComponent as Close} from 'assets/images/icons/close.svg';
 import {SourceMessage} from 'render';
 import {Source, Message} from 'model';
+import {FileInfo} from './index';
 
 type InputSelectorProps = {
   messageType: 'template' | 'suggestedReplies' | 'message';
   message: Message;
   source: Source;
   contentResizedHeight: number;
+  fileInfo: FileInfo | null;
   removeElementFromInput: () => void;
 };
 
 export const InputSelector = (props: InputSelectorProps) => {
-  const {source, message, messageType, removeElementFromInput, contentResizedHeight} = props;
+  const {source, message, messageType, removeElementFromInput, contentResizedHeight, fileInfo} = props;
   const [closeIconWidth, setCloseIconWidth] = useState('');
   const [closeIconHeight, setCloseIconHeight] = useState('');
+  const [closeButtonSelector, setCloseButtonSelector] = useState(false);
 
-  const removeSelectedButton = useRef(null);
-  const fileSelectorDiv = useRef<HTMLDivElement>(null);
+  const fileSelectorDiv = useRef(null);
   const removeFileButton = useRef(null);
 
   const scaleInputSelector = () => {
@@ -32,15 +34,30 @@ export const InputSelector = (props: InputSelectorProps) => {
 
         setCloseIconHeight(iconSize);
         setCloseIconWidth(iconSize);
+        setCloseButtonSelector(true);
 
         if (removeFileButton && removeFileButton.current) {
           removeFileButton.current.style.width = buttonSize;
           removeFileButton.current.style.height = buttonSize;
         }
+      } else {
+        setCloseButtonSelector(true);
       }
 
       fileSelectorDiv.current.style.transform = `scale(${scaleRatio})`;
       fileSelectorDiv.current.style.transformOrigin = 'left';
+    } else {
+      if (fileInfo && fileInfo?.size >= 1 && fileInfo?.type !== 'audio' && fileInfo?.type !== 'file') {
+        setTimeout(() => {
+          setCloseButtonSelector(true);
+        }, 1000);
+      } else if (fileInfo && fileInfo?.size < 1 && fileInfo?.type !== 'audio' && fileInfo?.type !== 'file') {
+        setTimeout(() => {
+          setCloseButtonSelector(true);
+        }, 500);
+      } else {
+        setCloseButtonSelector(true);
+      }
     }
   };
 
@@ -50,14 +67,16 @@ export const InputSelector = (props: InputSelectorProps) => {
 
   return (
     <div className={styles.container} ref={fileSelectorDiv}>
-      <button className={styles.removeButton} onClick={removeElementFromInput} ref={removeSelectedButton}>
-        <Close
-          style={{
-            width: closeIconWidth ?? '',
-            height: closeIconHeight ?? '',
-          }}
-        />
-      </button>
+      {closeButtonSelector && (
+        <button className={styles.removeButton} onClick={removeElementFromInput} ref={removeFileButton}>
+          <Close
+            style={{
+              width: closeIconWidth ?? '',
+              height: closeIconHeight ?? '',
+            }}
+          />
+        </button>
+      )}
       <SourceMessage message={message} source={source} contentType={messageType} />
     </div>
   );
diff --git a/frontend/ui/src/pages/Inbox/MessageInput/index.tsx b/frontend/ui/src/pages/Inbox/MessageInput/index.tsx
index 4344864047..6f7b5bd899 100644
--- a/frontend/ui/src/pages/Inbox/MessageInput/index.tsx
+++ b/frontend/ui/src/pages/Inbox/MessageInput/index.tsx
@@ -4,7 +4,16 @@ import {sendMessages} from '../../../actions/messages';
 import {withRouter} from 'react-router-dom';
 import {Button, SimpleLoader} from 'components';
 import {cyMessageSendButton, cyMessageTextArea, cySuggestionsButton} from 'handles';
-import {getOutboundMapper} from 'render';
+import {
+  getOutboundMapper,
+  getAttachmentType,
+  isSupportedByInstagramMessenger,
+  imageExtensions,
+  fileExtensions,
+  videoExtensions,
+  audioExtensions,
+  instagramImageExtensions,
+} from 'render';
 import {Message, SuggestedReply, Suggestions, Template, Source} from 'model';
 import {isEmpty} from 'lodash-es';
 
@@ -24,15 +33,6 @@ import {InputOptions} from './InputOptions';
 import styles from './index.module.scss';
 import {HttpClientInstance} from '../../../httpClient';
 import {FacebookMapper} from 'render/outbound/facebook';
-import {
-  getAttachmentType,
-  isSupportedByInstagramMessenger,
-  imageExtensions,
-  fileExtensions,
-  videoExtensions,
-  audioExtensions,
-  instagramImageExtensions,
-} from 'render/attachments';
 import {InputSelector} from './InputSelector';
 import {usePrevious} from '../../../services/hooks/usePrevious';
 
@@ -53,6 +53,7 @@ type Props = {
   hideSuggestedReplies: () => void;
   draggedAndDroppedFile: File;
   setDraggedAndDroppedFile: React.Dispatch<React.SetStateAction<File | null>>;
+  setDragAndDropDisabled: React.Dispatch<React.SetStateAction<boolean>>;
 } & ConnectedProps<typeof connector>;
 
 interface SelectedTemplate {
@@ -60,7 +61,12 @@ interface SelectedTemplate {
   source: Source;
 }
 
-interface SelectedSuggestedReply {
+export interface FileInfo {
+  size: number;
+  type: string;
+}
+
+export interface SelectedSuggestedReply {
   message: SuggestedReply;
 }
 
@@ -74,6 +80,7 @@ const MessageInput = (props: Props) => {
     sendMessages,
     draggedAndDroppedFile,
     setDraggedAndDroppedFile,
+    setDragAndDropDisabled,
     config,
   } = props;
 
@@ -90,6 +97,7 @@ const MessageInput = (props: Props) => {
   const [uploadedFileUrl, setUploadedFileUrl] = useState<string | null>(null);
   const [fileUploadErrorPopUp, setFileUploadErrorPopUp] = useState<string>('');
   const [loadingSelector, setLoadingSelector] = useState(false);
+  const [fileInfo, setFileInfo] = useState<null | {size: number; type: string}>(null);
   const prevConversationId = usePrevious(conversation.id);
 
   const textAreaRef = useRef(null);
@@ -147,9 +155,18 @@ const MessageInput = (props: Props) => {
   useEffect(() => {
     if (fileToUpload) {
       setLoadingSelector(true);
+      setDragAndDropDisabled(true);
     }
   }, [fileToUpload]);
 
+  useEffect(() => {
+    if (isElementSelected()) {
+      setDragAndDropDisabled(true);
+    } else if (config.components['media-resolver'].enabled && (source === 'facebook' || source === 'instagram')) {
+      setDragAndDropDisabled(false);
+    }
+  }, [selectedTemplate, selectedSuggestedReply, uploadedFileUrl]);
+
   useEffect(() => {
     if (textAreaRef && textAreaRef.current) {
       textAreaRef.current.style.height = 'inherit';
@@ -170,6 +187,7 @@ const MessageInput = (props: Props) => {
 
   const uploadFile = (file: File) => {
     const fileSizeInMB = file.size / Math.pow(1024, 2);
+    const maxFileSizeAllowed = 15;
 
     //instagram upload errors
     if (source === 'instagram') {
@@ -186,8 +204,10 @@ const MessageInput = (props: Props) => {
     }
 
     //facebook upload errors
-    if (fileSizeInMB >= 25) {
-      return setFileUploadErrorPopUp('Failed to upload the file. The maximum file size allowed is 25MB.');
+    if (fileSizeInMB >= maxFileSizeAllowed) {
+      return setFileUploadErrorPopUp(
+        `Failed to upload the file. The maximum file size allowed is ${maxFileSizeAllowed}MB.`
+      );
     }
 
     if (!getAttachmentType(file.name)) {
@@ -198,6 +218,7 @@ const MessageInput = (props: Props) => {
       return setFileUploadErrorPopUp(message);
     }
 
+    setFileInfo({size: fileSizeInMB, type: getAttachmentType(file.name)});
     setFileToUpload(file);
   };
 
@@ -419,6 +440,7 @@ const MessageInput = (props: Props) => {
                   messageType={selectedTemplate ? 'template' : selectedSuggestedReply ? 'suggestedReplies' : 'message'}
                   removeElementFromInput={removeElementFromInput}
                   contentResizedHeight={contentResizedHeight}
+                  fileInfo={fileInfo}
                 />
               </>
             )}
diff --git a/frontend/ui/src/pages/Inbox/Messenger/MessengerContainer/index.tsx b/frontend/ui/src/pages/Inbox/Messenger/MessengerContainer/index.tsx
index 674d854078..90a8a6b68a 100644
--- a/frontend/ui/src/pages/Inbox/Messenger/MessengerContainer/index.tsx
+++ b/frontend/ui/src/pages/Inbox/Messenger/MessengerContainer/index.tsx
@@ -97,21 +97,22 @@ const MessengerContainer = ({
   };
 
   const handleDragEnter = (event: React.DragEvent<HTMLDivElement>) => {
+    if (dragAndDropDisabled) return;
+
     event.preventDefault();
     event.stopPropagation();
 
-    if (dragAndDropDisabled) return;
-
     dragCounter++;
 
     setIsFileDragged(true);
   };
 
   const handleFileDrop = (event: React.DragEvent<HTMLDivElement>) => {
+    if (dragAndDropDisabled) return;
+
     event.preventDefault();
     event.stopPropagation();
 
-    if (dragAndDropDisabled) return;
     dragCounter++;
     const file = event.dataTransfer.files[0];
     setDraggedAndDroppedFile(file);
@@ -119,10 +120,10 @@ const MessengerContainer = ({
   };
 
   const handleDragLeave = (event: React.DragEvent<HTMLDivElement>) => {
+    if (dragAndDropDisabled) return;
     event.preventDefault();
     event.stopPropagation();
 
-    if (dragAndDropDisabled) return;
     dragCounter--;
     if (dragCounter === 0) {
       setIsFileDragged(false);
@@ -164,6 +165,7 @@ const MessengerContainer = ({
                   source={currentConversation.channel.source as Source}
                   draggedAndDroppedFile={draggedAndDroppedFile}
                   setDraggedAndDroppedFile={setDraggedAndDroppedFile}
+                  setDragAndDropDisabled={setDragAndDropDisabled}
                 />
               </>
             )}
diff --git a/lib/typescript/render/attachments.ts b/lib/typescript/render/attachments.ts
index f20cb864ec..bd69144b2c 100644
--- a/lib/typescript/render/attachments.ts
+++ b/lib/typescript/render/attachments.ts
@@ -9,6 +9,7 @@ export const fileExtensions = [
   'docx',
   'rtf',
   'tex',
+  'txt',
   'wpd',
   'psd',
   'svg',
diff --git a/lib/typescript/render/components/Image/index.module.scss b/lib/typescript/render/components/Image/index.module.scss
index 80c801aa23..eacbe7dd55 100644
--- a/lib/typescript/render/components/Image/index.module.scss
+++ b/lib/typescript/render/components/Image/index.module.scss
@@ -15,5 +15,6 @@
 
 .messageListItemImageBlock {
   max-height: 200px;
+  max-width: 300px;
   border-radius: 8px;
 }
diff --git a/lib/typescript/render/components/ImageWithFallback/index.tsx b/lib/typescript/render/components/ImageWithFallback/index.tsx
index a77207bd7a..8f9a1179c2 100644
--- a/lib/typescript/render/components/ImageWithFallback/index.tsx
+++ b/lib/typescript/render/components/ImageWithFallback/index.tsx
@@ -4,6 +4,7 @@ type ImageRenderProps = {
   src: string;
   alt?: string;
   className?: string;
+  isTemplate?: boolean;
 };
 
 /**
@@ -14,7 +15,7 @@ type ImageRenderProps = {
  */
 const failedUrls = [];
 
-export const ImageWithFallback = ({src, alt, className}: ImageRenderProps) => {
+export const ImageWithFallback = ({src, alt, className, isTemplate}: ImageRenderProps) => {
   const [imageFailed, setImageFailed] = useState(failedUrls.includes(src));
 
   useEffect(() => {
@@ -27,13 +28,24 @@ export const ImageWithFallback = ({src, alt, className}: ImageRenderProps) => {
   };
 
   return (
-    <a href={src} target="_blank" rel="noopener noreferrer">
-      <img
-        className={className}
-        src={imageFailed ? 'https://s3.amazonaws.com/assets.airy.co/fallbackMediaImage.svg' : src}
-        alt={imageFailed ? 'The image failed to load' : alt}
-        onError={() => loadingFailed()}
-      />
-    </a>
+    <>
+      {isTemplate ? (
+        <img
+          className={className}
+          src={imageFailed ? 'https://s3.amazonaws.com/assets.airy.co/fallbackMediaImage.svg' : src}
+          alt={imageFailed ? 'The image failed to load' : alt}
+          onError={() => loadingFailed()}
+        />
+      ) : (
+        <a href={src} target="_blank" rel="noopener noreferrer">
+          <img
+            className={className}
+            src={imageFailed ? 'https://s3.amazonaws.com/assets.airy.co/fallbackMediaImage.svg' : src}
+            alt={imageFailed ? 'The image failed to load' : alt}
+            onError={() => loadingFailed()}
+          />
+        </a>
+      )}
+    </>
   );
 };
diff --git a/lib/typescript/render/index.ts b/lib/typescript/render/index.ts
index 6f789fa4ef..8b99a1ccec 100644
--- a/lib/typescript/render/index.ts
+++ b/lib/typescript/render/index.ts
@@ -2,3 +2,4 @@ export * from './props';
 export * from './SourceMessage';
 export * from './SourceMessagePreview';
 export * from './outbound';
+export * from './attachments';
diff --git a/lib/typescript/render/providers/chatplugin/components/QuickReplies/index.tsx b/lib/typescript/render/providers/chatplugin/components/QuickReplies/index.tsx
index 9ea497b396..e9b6919888 100644
--- a/lib/typescript/render/providers/chatplugin/components/QuickReplies/index.tsx
+++ b/lib/typescript/render/providers/chatplugin/components/QuickReplies/index.tsx
@@ -44,9 +44,14 @@ export const QuickReplies = ({
 
       <div className={styles.container}>
         {quickReplies.map((reply: QuickReply) => (
-          <button key={reply.title} className={styles.replyButton} onClick={() => clickPostback(reply)}>
+          <button type="button" key={reply.title} className={styles.replyButton} onClick={() => clickPostback(reply)}>
             {reply.image_url && (
-              <ImageWithFallback className={styles.quickReplyImage} alt={reply.title} src={reply.image_url} />
+              <ImageWithFallback
+                className={styles.quickReplyImage}
+                alt={reply.title}
+                src={reply.image_url}
+                isTemplate
+              />
             )}
             <h1 key={reply.title} className={styles.title}>
               {reply.title}
diff --git a/lib/typescript/render/providers/chatplugin/components/RichCard/Media/index.tsx b/lib/typescript/render/providers/chatplugin/components/RichCard/Media/index.tsx
index 2e1c798a54..604c5fcdf5 100644
--- a/lib/typescript/render/providers/chatplugin/components/RichCard/Media/index.tsx
+++ b/lib/typescript/render/providers/chatplugin/components/RichCard/Media/index.tsx
@@ -28,5 +28,5 @@ const getHeight = (height: MediaHeight): string => {
 };
 
 export const Media = ({height, contentInfo: {altText, fileUrl}}: MediaRenderProps) => (
-  <ImageWithFallback src={fileUrl} alt={altText} className={`${styles.mediaImage} ${getHeight(height)}`} />
+  <ImageWithFallback src={fileUrl} alt={altText} className={`${styles.mediaImage} ${getHeight(height)}`} isTemplate />
 );
diff --git a/lib/typescript/render/providers/facebook/components/GenericTemplate/index.tsx b/lib/typescript/render/providers/facebook/components/GenericTemplate/index.tsx
index 7332fd595d..ea450ab60d 100644
--- a/lib/typescript/render/providers/facebook/components/GenericTemplate/index.tsx
+++ b/lib/typescript/render/providers/facebook/components/GenericTemplate/index.tsx
@@ -16,7 +16,9 @@ export const GenericTemplate = ({template}: GenericTemplateRendererProps) => {
     <Carousel>
       {template.elements.map((element, idx) => (
         <div key={`template-${idx}`} className={styles.template}>
-          {element.image_url?.length && <ImageWithFallback className={styles.templateImage} src={element.image_url} />}
+          {element.image_url?.length && (
+            <ImageWithFallback className={styles.templateImage} src={element.image_url} isTemplate />
+          )}
           <div className={styles.innerTemplate}>
             <div className={styles.templateTitle}>{element.title}</div>
             <div className={styles.templateSubtitle}>{element.subtitle}</div>
diff --git a/lib/typescript/render/providers/facebook/components/QuickReplies/index.tsx b/lib/typescript/render/providers/facebook/components/QuickReplies/index.tsx
index b524ea01d9..c9fb56d246 100644
--- a/lib/typescript/render/providers/facebook/components/QuickReplies/index.tsx
+++ b/lib/typescript/render/providers/facebook/components/QuickReplies/index.tsx
@@ -27,8 +27,8 @@ export const QuickReplies = ({quickReplies, fromContact, text, attachment}: Quic
 
     <div className={styles.container}>
       {quickReplies.map(({title, image_url: imageUrl}) => (
-        <button key={title} className={styles.replyButton}>
-          {imageUrl && <ImageWithFallback className={styles.quickReplyImage} alt={title} src={imageUrl} />}
+        <button type="button" key={title} className={styles.replyButton}>
+          {imageUrl && <ImageWithFallback className={styles.quickReplyImage} alt={title} src={imageUrl} isTemplate />}
           <h1 key={title} className={styles.title}>
             {title}
           </h1>
diff --git a/lib/typescript/render/providers/google/components/RichCard/Media/index.tsx b/lib/typescript/render/providers/google/components/RichCard/Media/index.tsx
index d076856061..52767f1b64 100644
--- a/lib/typescript/render/providers/google/components/RichCard/Media/index.tsx
+++ b/lib/typescript/render/providers/google/components/RichCard/Media/index.tsx
@@ -28,5 +28,5 @@ const getHeight = (height: MediaHeight): string => {
 };
 
 export const Media = ({height, contentInfo: {altText, fileUrl}}: MediaRenderProps) => (
-  <ImageWithFallback src={fileUrl} alt={altText} className={`${styles.mediaImage} ${getHeight(height)}`} />
+  <ImageWithFallback src={fileUrl} alt={altText} className={`${styles.mediaImage} ${getHeight(height)}`} isTemplate />
 );
diff --git a/lib/typescript/render/providers/google/components/Suggestions/index.tsx b/lib/typescript/render/providers/google/components/Suggestions/index.tsx
index 99aa32400e..e89cda5b16 100644
--- a/lib/typescript/render/providers/google/components/Suggestions/index.tsx
+++ b/lib/typescript/render/providers/google/components/Suggestions/index.tsx
@@ -30,7 +30,7 @@ export const Suggestions = ({text, fallback, image, suggestions, fromContact}: S
       {(suggestions as SuggestionsUnion[]).map(elem => {
         if ('reply' in elem) {
           return (
-            <button key={elem.reply.text} className={styles.replyButton}>
+            <button type="button" key={elem.reply.text} className={styles.replyButton}>
               <h1 key={elem.reply.text} className={styles.title}>
                 {elem.reply.text}
               </h1>
@@ -40,7 +40,7 @@ export const Suggestions = ({text, fallback, image, suggestions, fromContact}: S
 
         if ('action' in elem) {
           return (
-            <button key={elem.action.text} className={styles.replyButton}>
+            <button type="button" key={elem.action.text} className={styles.replyButton}>
               <img
                 className={styles.actionImage}
                 alt={elem.action.openUrlAction ? 'link icon' : 'phone icon'}
@@ -66,7 +66,7 @@ export const Suggestions = ({text, fallback, image, suggestions, fromContact}: S
 
         if ('authenticationRequest' in elem) {
           return (
-            <button key={elem.authenticationRequest.oauth.clientId} className={styles.replyButton}>
+            <button type="button" key={elem.authenticationRequest.oauth.clientId} className={styles.replyButton}>
               <h1 key={elem.authenticationRequest.oauth.clientId} className={styles.title}>
                 Authenticate with Google
               </h1>
@@ -76,7 +76,7 @@ export const Suggestions = ({text, fallback, image, suggestions, fromContact}: S
 
         if ('liveAgentRequest' in elem) {
           return (
-            <button key={Math.floor(Math.random() * 50)} className={styles.replyButton}>
+            <button type="button" key={Math.floor(Math.random() * 50)} className={styles.replyButton}>
               <h1 key={Math.floor(Math.random() * 50)} className={styles.title}>
                 Message a live agent on Google&apos;s Business Messages
               </h1>