From 1b34f85f0e3188861c6804666f02b4495cab033c Mon Sep 17 00:00:00 2001
From: Armand Philippot <git@armandphilippot.com>
Date: Mon, 14 Feb 2022 15:48:34 +0100
Subject: chore: improve comment form user experience

---
 src/components/CommentForm/CommentForm.tsx | 150 +++++++++++++++++++----------
 1 file changed, 98 insertions(+), 52 deletions(-)

(limited to 'src/components/CommentForm/CommentForm.tsx')

diff --git a/src/components/CommentForm/CommentForm.tsx b/src/components/CommentForm/CommentForm.tsx
index 0ea3276..762bb75 100644
--- a/src/components/CommentForm/CommentForm.tsx
+++ b/src/components/CommentForm/CommentForm.tsx
@@ -1,68 +1,107 @@
 import { ButtonSubmit } from '@components/Buttons';
 import { Form, FormItem, Input, TextArea } from '@components/Form';
 import Notice from '@components/Notice/Notice';
+import Spinner from '@components/Spinner/Spinner';
 import { createComment } from '@services/graphql/mutations';
+import { NoticeType } from '@ts/types/app';
 import { ForwardedRef, forwardRef, useState } from 'react';
 import { useIntl } from 'react-intl';
 import styles from './CommentForm.module.scss';
 
 const CommentForm = (
-  {
-    articleId,
-    parentId = 0,
-    isReply = false,
-  }: {
-    articleId: number;
-    parentId?: number;
-    isReply?: boolean;
-  },
+  { articleId, parentId = 0 }: { articleId: number; parentId?: number },
   ref: ForwardedRef<HTMLInputElement>
 ) => {
   const intl = useIntl();
-  const [name, setName] = useState('');
-  const [email, setEmail] = useState('');
-  const [website, setWebsite] = useState('');
-  const [message, setMessage] = useState('');
-  const [isSuccess, setIsSuccess] = useState(false);
-  const [isApproved, setIsApproved] = useState(false);
+  const [name, setName] = useState<string>('');
+  const [email, setEmail] = useState<string>('');
+  const [website, setWebsite] = useState<string>('');
+  const [comment, setComment] = useState<string>('');
+  const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
+  const [notice, setNotice] = useState<string>();
+  const [noticeType, setNoticeType] = useState<NoticeType>('success');
 
   const resetForm = () => {
     setName('');
     setEmail('');
     setWebsite('');
-    setMessage('');
+    setComment('');
+    setIsSubmitting(false);
   };
 
-  const submitHandler = async (e: SubmitEvent) => {
-    e.preventDefault();
+  const isEmptyString = (value: string): boolean => value.trim() === '';
+  const areRequiredFieldsSet = (): boolean =>
+    !isEmptyString(name) && !isEmptyString(email) && !isEmptyString(comment);
 
-    if (name && email && message && articleId) {
-      const data = {
-        author: name,
-        authorEmail: email,
-        authorUrl: website,
-        content: message,
-        parent: parentId,
-        commentOn: articleId,
-        mutationId: 'createComment',
-      };
-      const createdComment = await createComment(data);
+  const sendComment = async () => {
+    const data = {
+      author: name,
+      authorEmail: email,
+      authorUrl: website,
+      content: comment,
+      parent: parentId,
+      commentOn: articleId,
+      mutationId: 'createComment',
+    };
 
-      if (createdComment.success) setIsSuccess(true);
-      if (isSuccess) {
-        resetForm();
-        if (createdComment.comment?.approved) setIsApproved(true);
+    const createdComment = await createComment(data);
 
-        setTimeout(() => {
-          setIsSuccess(false);
-          setIsApproved(false);
-        }, 8000);
+    if (createdComment.success) {
+      resetForm();
+      setNoticeType('success');
+      if (createdComment.comment?.approved) {
+        setNotice(
+          intl.formatMessage({
+            defaultMessage: 'Thanks for your comment!',
+            description: 'CommentForm: success notice',
+          })
+        );
+      } else {
+        setNotice(
+          intl.formatMessage({
+            defaultMessage:
+              'Thanks for your comment! It is now awaiting moderation.',
+            description: 'CommentForm: success notice but awaiting moderation',
+          })
+        );
       }
+
+      setTimeout(() => {
+        setNotice(undefined);
+      }, 10000);
+    } else {
+      setNoticeType('error');
+      setNotice(
+        intl.formatMessage({
+          defaultMessage:
+            'An unexpected error happened. Comment cannot be submitted.',
+          description: 'CommentForm: error notice',
+        })
+      );
+    }
+  };
+
+  const submitHandler = async (e: SubmitEvent) => {
+    e.preventDefault();
+    setNotice(undefined);
+    setIsSubmitting(true);
+
+    if (areRequiredFieldsSet()) {
+      sendComment();
     } else {
-      setIsSuccess(false);
+      setIsSubmitting(false);
+      setNoticeType('warning');
+      setNotice(
+        intl.formatMessage({
+          defaultMessage:
+            'Some required fields are empty. Comment cannot be submitted.',
+          description: 'CommentForm: missing required fields',
+        })
+      );
     }
   };
 
+  const isReply = parentId !== 0;
   const wrapperClasses = `${styles.wrapper} ${
     isReply ? styles['wrapper--reply'] : ''
   }`;
@@ -72,7 +111,7 @@ const CommentForm = (
       <h2 className={styles.title}>
         {intl.formatMessage({
           defaultMessage: 'Leave a comment',
-          description: 'CommentForm: form title',
+          description: 'CommentForm: Form title',
         })}
       </h2>
       <Form
@@ -97,6 +136,7 @@ const CommentForm = (
           <Input
             id="commenter-email"
             name="commenter-email"
+            type="email"
             label={intl.formatMessage({
               defaultMessage: 'Email',
               description: 'CommentForm: Email field label',
@@ -120,18 +160,24 @@ const CommentForm = (
         </FormItem>
         <FormItem>
           <TextArea
-            id="commenter-message"
-            name="commenter-message"
+            id="commenter-comment"
+            name="commenter-comment"
             label={intl.formatMessage({
               defaultMessage: 'Comment',
               description: 'CommentForm: Comment field label',
             })}
-            value={message}
-            setValue={setMessage}
+            value={comment}
+            setValue={setComment}
             required={true}
           />
         </FormItem>
         <FormItem>
+          <noscript>
+            {intl.formatMessage({
+              defaultMessage: 'Javascript is required to post a comment.',
+              description: 'CommentForm: noscript tag',
+            })}
+          </noscript>
           <ButtonSubmit>
             {intl.formatMessage({
               defaultMessage: 'Send',
@@ -139,16 +185,16 @@ const CommentForm = (
             })}
           </ButtonSubmit>
         </FormItem>
-        {isSuccess && !isApproved && (
-          <Notice type="success">
-            {intl.formatMessage({
-              defaultMessage:
-                'Thanks for your comment! It is now awaiting moderation.',
-              description: 'CommentForm: Comment sent success message',
-            })}
-          </Notice>
-        )}
       </Form>
+      {isSubmitting && (
+        <Spinner
+          message={intl.formatMessage({
+            defaultMessage: 'Submitting...',
+            description: 'CommentForm: submitting message',
+          })}
+        />
+      )}
+      {notice && <Notice type={noticeType}>{notice}</Notice>}
     </div>
   );
 };
-- 
cgit v1.2.3

