瀏覽代碼

[feat] Refinements to UX for fetching data from Notion

Sergio Mattei 2 年之前
父節點
當前提交
9ac7df12d6
共有 5 個檔案被更改,包括 69 行新增28 行删除
  1. 5
    6
      src/components/ListComponent.css
  2. 39
    11
      src/components/ListComponent.tsx
  3. 12
    0
      src/components/SkeletonText.tsx
  4. 5
    11
      src/pages/LawListPage.tsx
  5. 8
    0
      src/types.ts

+ 5
- 6
src/components/ListComponent.css 查看文件

@@ -1,14 +1,13 @@
1
-
2 1
 .list {
3
-    border-radius: 20px; 
4
-    padding: 0 0 0 0;
5
-    
2
+  border-radius: 20px;
3
+  padding: 0 0 0 0;
6 4
 }
7 5
 
8 6
 .ListItemText {
9
-    font-family:'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
7
+  font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
8
+  width: 100%;
10 9
 }
11 10
 
12 11
 ion-list-header {
13
-    /* color: "primary"; */
12
+  /* color: "primary"; */
14 13
 }

+ 39
- 11
src/components/ListComponent.tsx 查看文件

@@ -1,9 +1,34 @@
1 1
 import { IonContent, IonItem, IonLabel, IonListHeader } from "@ionic/react";
2 2
 import "../components/ListComponent.css";
3
+import SkeletonText from "./SkeletonText";
3 4
 
4
-const ListComponent: React.FC<{ title?: string; laws: string[] }> = (props) => {
5
+/**
6
+ * This component creates a shimmering "skeleton" version of the list component.
7
+ * It shows only when content isn't available yet (loading).
8
+ */
9
+function ListComponentSkeleton({ items = 5 }: { items?: number }) {
10
+  return (
11
+    <>
12
+      {Array(items)
13
+        .fill(0)
14
+        .map((_, i) => (
15
+          <IonItem key={i}>
16
+            <SkeletonText />
17
+          </IonItem>
18
+        ))}
19
+    </>
20
+  );
21
+}
22
+
23
+const ListComponent: React.FC<{
24
+  title?: string;
25
+  laws: string[];
26
+  loading?: boolean;
27
+}> = (props) => {
5 28
   const title = props.title;
6 29
   const laws = props.laws;
30
+  const loading = props.loading ?? false;
31
+
7 32
   return (
8 33
     <IonContent>
9 34
       {title ? (
@@ -12,16 +37,19 @@ const ListComponent: React.FC<{ title?: string; laws: string[] }> = (props) => {
12 37
         </IonListHeader>
13 38
       ) : null}
14 39
 
15
-      {laws.map((lawName: string) => {
16
-        return (
17
-          <IonItem
18
-            key={lawName}
19
-            routerLink={`/article/${encodeURIComponent(lawName)}`}
20
-          >
21
-            <p className="ListItemText">{lawName}</p>
22
-          </IonItem>
23
-        );
24
-      })}
40
+      {loading && <ListComponentSkeleton />}
41
+
42
+      {!loading &&
43
+        laws.map((lawName: string) => {
44
+          return (
45
+            <IonItem
46
+              key={lawName}
47
+              routerLink={`/article/${encodeURIComponent(lawName)}`}
48
+            >
49
+              <p className="ListItemText">{lawName}</p>
50
+            </IonItem>
51
+          );
52
+        })}
25 53
     </IonContent>
26 54
   );
27 55
 };

+ 12
- 0
src/components/SkeletonText.tsx 查看文件

@@ -0,0 +1,12 @@
1
+import { IonSkeletonText } from "@ionic/react";
2
+import React from "react";
3
+
4
+function skeletonRange() {
5
+  return Math.floor(Math.random() * (75 - 25 + 1)) + 25;
6
+}
7
+
8
+export default function SkeletonText() {
9
+  return (
10
+    <IonSkeletonText animated={true} style={{ width: `${skeletonRange()}%` }} />
11
+  );
12
+}

+ 5
- 11
src/pages/LawListPage.tsx 查看文件

@@ -8,15 +8,10 @@ import {
8 8
   IonToolbar,
9 9
 } from "@ionic/react";
10 10
 import { alertCircleOutline } from "ionicons/icons";
11
-import { useEffect } from "react";
12 11
 import useSWR from "swr";
13 12
 import ListComponent from "../components/ListComponent";
14 13
 import { fetcher } from "../lib/api";
15
-
16
-interface Topic {
17
-  blockId: string;
18
-  name: string;
19
-}
14
+import { Topic } from "../types";
20 15
 
21 16
 const LawListPage: React.FC = () => {
22 17
   const { data, error } = useSWR<Topic[]>(
@@ -38,12 +33,11 @@ const LawListPage: React.FC = () => {
38 33
             <IonTitle size="large">Leyes</IonTitle>
39 34
           </IonToolbar>
40 35
         </IonHeader>
41
-        {data && <ListComponent laws={data.map((item) => item.name)} />}
42
-        <IonLoading
43
-          isOpen={isLoading}
44
-          duration={isLoading ? 50 : undefined}
45
-          message="Loading..."
36
+        <ListComponent
37
+          loading={isLoading}
38
+          laws={data ? data.map((item) => item.name) : []}
46 39
         />
40
+
47 41
         <IonToast
48 42
           icon={alertCircleOutline}
49 43
           color="danger"

+ 8
- 0
src/types.ts 查看文件

@@ -0,0 +1,8 @@
1
+export interface Topic {
2
+  blockId: string;
3
+  name: string;
4
+}
5
+
6
+export interface Error {
7
+  status: string;
8
+}