From 165c79d82f31b2bca46ad107751213337c062fc3 Mon Sep 17 00:00:00 2001
From: Fedor Katurov <gotham48@gmail.com>
Date: Mon, 17 Oct 2022 12:29:04 +0600
Subject: [PATCH] added lab loader

---
 src/containers/lab/LabGrid/index.tsx    | 45 ++----------------
 src/containers/lab/LabLoading/index.tsx | 36 +++++++++++++++
 src/hooks/lab/useGetLabNodes.ts         | 61 +++++++++++++++----------
 src/layouts/LabLayout/index.tsx         |  7 ++-
 4 files changed, 83 insertions(+), 66 deletions(-)
 create mode 100644 src/containers/lab/LabLoading/index.tsx

diff --git a/src/containers/lab/LabGrid/index.tsx b/src/containers/lab/LabGrid/index.tsx
index 98b9e173..a8b9c1a3 100644
--- a/src/containers/lab/LabGrid/index.tsx
+++ b/src/containers/lab/LabGrid/index.tsx
@@ -1,52 +1,17 @@
-import React, { FC } from 'react';
+import { FC, memo } from 'react';
 
 import { Columns } from '~/components/containers/Columns';
 import { InfiniteScroll } from '~/components/containers/InfiniteScroll';
 import { LabNoResults } from '~/components/lab/LabNoResults';
 import { LabNode } from '~/components/lab/LabNode';
-import { EMPTY_NODE, NODE_TYPES } from '~/constants/node';
 import { useLabContext } from '~/utils/context/LabContextProvider';
-import { values } from '~/utils/ramda';
 
 import styles from './styles.module.scss';
 
 interface IProps {}
 
-const breakpointCols = {
-  default: 2,
-  1280: 1,
-};
-
-const getRandomNodeType = () =>
-  values(NODE_TYPES)[Math.floor(Math.random() * values(NODE_TYPES).length)];
-
-const LoadingNode = () => (
-  <LabNode
-    node={{ ...EMPTY_NODE, type: getRandomNodeType() }}
-    isLoading
-    lastSeen=""
-    commentCount={0}
-  />
-);
-
-const LabGrid: FC<IProps> = () => {
-  const { isLoading, nodes, hasMore, loadMore, search, setSearch } = useLabContext();
-
-  if (isLoading) {
-    return (
-      <Columns>
-        <LoadingNode />
-        <LoadingNode />
-        <LoadingNode />
-        <LoadingNode />
-        <LoadingNode />
-        <LoadingNode />
-        <LoadingNode />
-        <LoadingNode />
-        <LoadingNode />
-      </Columns>
-    );
-  }
+const LabGrid: FC<IProps> = memo(() => {
+  const { nodes, hasMore, loadMore, search, setSearch } = useLabContext();
 
   if (search && !nodes.length) {
     return <LabNoResults resetSearch={() => setSearch('')} />;
@@ -56,7 +21,7 @@ const LabGrid: FC<IProps> = () => {
     <InfiniteScroll hasMore={hasMore} loadMore={loadMore}>
       <div className={styles.wrap}>
         <Columns>
-          {nodes.map(node => (
+          {nodes.map((node) => (
             <LabNode
               node={node.node}
               key={node.node.id}
@@ -68,6 +33,6 @@ const LabGrid: FC<IProps> = () => {
       </div>
     </InfiniteScroll>
   );
-};
+});
 
 export { LabGrid };
diff --git a/src/containers/lab/LabLoading/index.tsx b/src/containers/lab/LabLoading/index.tsx
new file mode 100644
index 00000000..2e0b74bd
--- /dev/null
+++ b/src/containers/lab/LabLoading/index.tsx
@@ -0,0 +1,36 @@
+import React, { FC, memo } from 'react';
+
+import { Columns } from '~/components/containers/Columns';
+import { LabNode } from '~/components/lab/LabNode';
+import { EMPTY_NODE, NODE_TYPES } from '~/constants/node';
+import { values } from '~/utils/ramda';
+
+interface LabLoadingProps {}
+
+const getRandomNodeType = () =>
+  values(NODE_TYPES)[Math.floor(Math.random() * values(NODE_TYPES).length)];
+
+const LoadingNode = memo(() => (
+  <LabNode
+    node={{ ...EMPTY_NODE, type: getRandomNodeType() }}
+    isLoading
+    lastSeen=""
+    commentCount={0}
+  />
+));
+
+const LabLoading: FC<LabLoadingProps> = memo(() => (
+  <Columns>
+    <LoadingNode />
+    <LoadingNode />
+    <LoadingNode />
+    <LoadingNode />
+    <LoadingNode />
+    <LoadingNode />
+    <LoadingNode />
+    <LoadingNode />
+    <LoadingNode />
+  </Columns>
+));
+
+export { LabLoading };
diff --git a/src/hooks/lab/useGetLabNodes.ts b/src/hooks/lab/useGetLabNodes.ts
index 06dea212..144b274c 100644
--- a/src/hooks/lab/useGetLabNodes.ts
+++ b/src/hooks/lab/useGetLabNodes.ts
@@ -10,24 +10,25 @@ import { INode } from '~/types';
 import { GetLabNodesRequest, ILabNode, LabNodesSort } from '~/types/lab';
 import { flatten, uniqBy } from '~/utils/ramda';
 
-const getKey: (isUser: boolean, sort?: LabNodesSort, search?: string) => SWRInfiniteKeyLoader = (
-  isUser,
-  sort,
-  search
-) => (index, prev: ILabNode[]) => {
-  if (!isUser) return null;
-  if (index > 0 && (!prev?.length || prev.length < 20)) return null;
+const getKey: (
+  isUser: boolean,
+  sort?: LabNodesSort,
+  search?: string,
+) => SWRInfiniteKeyLoader =
+  (isUser, sort, search) => (index, prev: ILabNode[]) => {
+    if (!isUser) return null;
+    if (index > 0 && (!prev?.length || prev.length < 20)) return null;
 
-  const props: GetLabNodesRequest = {
-    limit: 20,
-    offset: index * 20,
-    sort: sort || LabNodesSort.New,
-    search: search || '',
+    const props: GetLabNodesRequest = {
+      limit: 20,
+      offset: index * 20,
+      sort: sort || LabNodesSort.New,
+      search: search || '',
+    };
+
+    return JSON.stringify(props);
   };
 
-  return JSON.stringify(props);
-};
-
 const parseKey = (key: string): GetLabNodesRequest => {
   try {
     return JSON.parse(key);
@@ -49,12 +50,15 @@ export const useGetLabNodes = (sort?: LabNodesSort, search?: string) => {
     },
     {
       fallbackData: [labStore.nodes],
-      onSuccess: data => labStore.setNodes(flatten(data)),
+      onSuccess: (data) => labStore.setNodes(flatten(data)),
       dedupingInterval: 300,
-    }
+    },
   );
 
-  const nodes = useMemo(() => uniqBy(n => n.node.id, flatten(data || [])), [data]);
+  const nodes = useMemo(
+    () => uniqBy((n) => n.node.id, flatten(data || [])),
+    [data],
+  );
   const hasMore = (data?.[size - 1]?.length || 0) >= 1;
   const loadMore = useCallback(() => setSize(size + 1), [setSize, size]);
 
@@ -63,20 +67,29 @@ export const useGetLabNodes = (sort?: LabNodesSort, search?: string) => {
     async (node: ILabNode) => {
       await mutate([[node], ...(data || [])]);
     },
-    [data, mutate]
+    [data, mutate],
   );
 
   /** updates node on cache */
   const updateNode = useCallback(
     async (nodeId: number, node: Partial<INode>) => {
       await mutate(
-        data?.map(page =>
-          page.map(it => (it.node.id === nodeId ? { ...it, node: { ...it.node, node } } : it))
-        )
+        data?.map((page) =>
+          page.map((it) =>
+            it.node.id === nodeId ? { ...it, node: { ...it.node, node } } : it,
+          ),
+        ),
       );
     },
-    [data, mutate]
+    [data, mutate],
   );
 
-  return { nodes, isLoading: !data && isValidating, hasMore, loadMore, unshift, updateNode };
+  return {
+    nodes,
+    isLoading: !!data?.length && isValidating,
+    hasMore,
+    loadMore,
+    unshift,
+    updateNode,
+  };
 };
diff --git a/src/layouts/LabLayout/index.tsx b/src/layouts/LabLayout/index.tsx
index b5c6fb23..02a42b1d 100644
--- a/src/layouts/LabLayout/index.tsx
+++ b/src/layouts/LabLayout/index.tsx
@@ -1,9 +1,10 @@
-import React, { FC } from 'react';
+import React, { FC, useMemo } from 'react';
 
 import { Group } from '~/components/containers/Group';
 import { Sticky } from '~/components/containers/Sticky';
 import { LabHead } from '~/components/lab/LabHead';
 import { LabGrid } from '~/containers/lab/LabGrid';
+import { LabLoading } from '~/containers/lab/LabLoading';
 import { LabStats } from '~/containers/lab/LabStats';
 import { Container } from '~/containers/main/Container';
 import { SidebarRouter } from '~/containers/main/SidebarRouter';
@@ -13,6 +14,8 @@ import styles from './styles.module.scss';
 
 interface IProps {}
 
+const loader = <LabLoading />;
+
 const LabLayout: FC<IProps> = () => {
   const { isLoading } = useLabContext();
 
@@ -25,7 +28,7 @@ const LabLayout: FC<IProps> = () => {
               <LabHead isLoading={isLoading} />
             </div>
 
-            <LabGrid />
+            {isLoading ? loader : <LabGrid />}
           </Group>
 
           <div className={styles.panel}>