From 39e801f6f3f2593c04ace0ef6469ef7f4c74eaa8 Mon Sep 17 00:00:00 2001
From: Fedor Katurov <gotham48@gmail.com>
Date: Tue, 4 Jan 2022 21:10:05 +0700
Subject: [PATCH] added toasts

---
 package.json                               |  1 +
 src/constants/dom/index.ts                 |  1 +
 src/containers/App.tsx                     |  3 ++
 src/hooks/comments/useCommentFormFormik.ts |  1 -
 src/hooks/node/useNodeFormFormik.ts        |  3 +-
 src/utils/errors/showToast.ts              | 33 +++++++++++++++++++---
 src/utils/providers/ToastProvider.tsx      | 11 ++++++++
 src/utils/toast/index.tsx                  | 20 +++++++++++++
 src/utils/toast/styles.module.scss         | 14 +++++++++
 yarn.lock                                  | 12 ++++++++
 10 files changed, 92 insertions(+), 7 deletions(-)
 create mode 100644 src/constants/dom/index.ts
 create mode 100644 src/utils/providers/ToastProvider.tsx
 create mode 100644 src/utils/toast/index.tsx
 create mode 100644 src/utils/toast/styles.module.scss

diff --git a/package.json b/package.json
index 1a7a2f22..e4c3e625 100644
--- a/package.json
+++ b/package.json
@@ -29,6 +29,7 @@
     "react": "^17.0.1",
     "react-dom": "^17.0.1",
     "react-dropzone": "^11.4.2",
+    "react-hot-toast": "^2.1.1",
     "react-lazyload": "^3.2.0",
     "react-masonry-css": "^1.0.16",
     "react-popper": "^2.2.3",
diff --git a/src/constants/dom/index.ts b/src/constants/dom/index.ts
new file mode 100644
index 00000000..7e58eff9
--- /dev/null
+++ b/src/constants/dom/index.ts
@@ -0,0 +1 @@
+export const isTablet = () => window.innerWidth < 599;
diff --git a/src/containers/App.tsx b/src/containers/App.tsx
index ea207916..388670c2 100644
--- a/src/containers/App.tsx
+++ b/src/containers/App.tsx
@@ -13,6 +13,8 @@ import { SWRConfigProvider } from '~/utils/providers/SWRConfigProvider';
 import { observer } from 'mobx-react';
 import { useGlobalLoader } from '~/hooks/dom/useGlobalLoader';
 import { SearchProvider } from '~/utils/providers/SearchProvider';
+import { Toaster } from 'react-hot-toast';
+import { ToastProvider } from '~/utils/providers/ToastProvider';
 
 const App: VFC = observer(() => {
   useGlobalLoader();
@@ -25,6 +27,7 @@ const App: VFC = observer(() => {
             <PageCoverProvider>
               <SearchProvider>
                 <MainLayout>
+                  <ToastProvider />
                   <Modal />
                   <Sprites />
 
diff --git a/src/hooks/comments/useCommentFormFormik.ts b/src/hooks/comments/useCommentFormFormik.ts
index 84e792f4..3bb869a5 100644
--- a/src/hooks/comments/useCommentFormFormik.ts
+++ b/src/hooks/comments/useCommentFormFormik.ts
@@ -44,7 +44,6 @@ export const useCommentFormFormik = (
         await sendData(values);
         onSuccess(helpers)();
       } catch (error) {
-        console.log('error', error);
         onSuccess(helpers)(error);
       }
     },
diff --git a/src/hooks/node/useNodeFormFormik.ts b/src/hooks/node/useNodeFormFormik.ts
index 00b43af4..0312f67a 100644
--- a/src/hooks/node/useNodeFormFormik.ts
+++ b/src/hooks/node/useNodeFormFormik.ts
@@ -8,14 +8,13 @@ import { showErrorToast } from '~/utils/errors/showToast';
 
 const validationSchema = object().shape({});
 
-const afterSubmit = ({ resetForm, setStatus, setSubmitting, setErrors }: FormikHelpers<INode>) => (
+const afterSubmit = ({ resetForm, setSubmitting, setErrors }: FormikHelpers<INode>) => (
   e?: string,
   errors?: Record<string, string>
 ) => {
   setSubmitting(false);
 
   if (e) {
-    setStatus(e);
     showErrorToast(e);
     return;
   }
diff --git a/src/utils/errors/showToast.ts b/src/utils/errors/showToast.ts
index 2b2cd335..fa0f6992 100644
--- a/src/utils/errors/showToast.ts
+++ b/src/utils/errors/showToast.ts
@@ -1,8 +1,21 @@
-const handle = (message: string) => console.warn(message);
+import { hideToast, showToastError } from '~/utils/toast';
+import { has, path } from 'ramda';
+import { ERROR_LITERAL, ERRORS } from '~/constants/errors';
+
+let toastId = '';
+
+const handleUnknown = (message: string) => console.warn(message);
+const handleTranslated = (message: string) => {
+  if (toastId) {
+    hideToast(toastId);
+  }
+
+  toastId = showToastError(ERROR_LITERAL[message]);
+};
 
 export const showErrorToast = (error: unknown) => {
-  if (typeof error === 'string') {
-    handle(error);
+  if (typeof error === 'string' && has(error, ERROR_LITERAL)) {
+    handleTranslated(error);
     return;
   }
 
@@ -11,5 +24,17 @@ export const showErrorToast = (error: unknown) => {
     return;
   }
 
-  handle(error.message);
+  // TODO: Network error
+  if (error.message === 'Network Error') {
+    handleTranslated(ERRORS.NETWORK_ERROR);
+    return;
+  }
+
+  const messageFromBackend = String(path(['response', 'data', 'error'], error));
+  if (messageFromBackend && has(messageFromBackend, ERROR_LITERAL)) {
+    handleTranslated(messageFromBackend);
+    return;
+  }
+
+  handleUnknown(error.message);
 };
diff --git a/src/utils/providers/ToastProvider.tsx b/src/utils/providers/ToastProvider.tsx
new file mode 100644
index 00000000..bc8d46ee
--- /dev/null
+++ b/src/utils/providers/ToastProvider.tsx
@@ -0,0 +1,11 @@
+import { Toaster } from 'react-hot-toast';
+import React from 'react';
+
+const containerStyle = {
+  top: 10,
+  left: 10,
+  bottom: 10,
+  right: 10,
+};
+
+export const ToastProvider = () => <Toaster containerStyle={containerStyle} />;
diff --git a/src/utils/toast/index.tsx b/src/utils/toast/index.tsx
new file mode 100644
index 00000000..a53812de
--- /dev/null
+++ b/src/utils/toast/index.tsx
@@ -0,0 +1,20 @@
+import React from 'react';
+import toast from 'react-hot-toast';
+import styles from './styles.module.scss';
+import { ToastOptions } from 'react-hot-toast/dist/core/types';
+import classNames from 'classnames';
+import { isTablet } from '~/constants/dom';
+
+const defaultOptions: ToastOptions = {
+  icon: null,
+  duration: 3000,
+  position: isTablet() ? 'top-center' : 'bottom-center',
+};
+
+export const showToastError = (message: string) =>
+  toast.error(t => <span onClick={() => toast.dismiss(t.id)}>{message}</span>, {
+    ...defaultOptions,
+    className: classNames(styles.toast, styles.error),
+  });
+
+export const hideToast = (id: string) => toast.dismiss(id);
diff --git a/src/utils/toast/styles.module.scss b/src/utils/toast/styles.module.scss
new file mode 100644
index 00000000..71bfde4e
--- /dev/null
+++ b/src/utils/toast/styles.module.scss
@@ -0,0 +1,14 @@
+@import "src/styles/variables";
+
+.toast {
+  @include outer_shadow;
+  cursor: pointer;
+}
+
+.error {
+  font: $font_14_semibold;
+  background: $red_gradient_alt;
+  color: white;
+  user-select: none;
+  text-transform: uppercase;
+}
diff --git a/yarn.lock b/yarn.lock
index af27d920..7b30944d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5390,6 +5390,11 @@ globule@^1.0.0:
     lodash "~4.17.10"
     minimatch "~3.0.2"
 
+goober@^2.0.35:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/goober/-/goober-2.1.1.tgz#2328a6dae015c3cd30fc55a70090037a244ad2f6"
+  integrity sha512-TkGCqHxE4g5DtdpwxFCi53bXRtvw0BoSgCihVSIOioe9kfkqin5wXG8BQKykN0tjzmxZJ81qU2KWinZf5qKVlw==
+
 graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2:
   version "4.2.4"
   resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
@@ -9429,6 +9434,13 @@ react-fast-compare@^3.0.1:
   resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb"
   integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==
 
+react-hot-toast@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/react-hot-toast/-/react-hot-toast-2.1.1.tgz#56409ab406b534e9e58274cf98d80355ba0fdda0"
+  integrity sha512-Odrp4wue0fHh0pOfZt5H+9nWCMtqs3wdlFSzZPp7qsxfzmbE26QmGWIh6hG43CukiPeOjA8WQhBJU8JwtWvWbQ==
+  dependencies:
+    goober "^2.0.35"
+
 react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4:
   version "16.13.1"
   resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"