From 81022123a1601c249423d71750b2138b3c5c43b3 Mon Sep 17 00:00:00 2001 From: Johnny Date: Tue, 27 Jan 2026 23:37:32 +0800 Subject: [PATCH] chore: simplify page loading logic --- web/src/components/Skeleton.tsx | 56 +++++++++---------- web/src/components/Spinner.tsx | 19 ------- .../components/UserMemoMap/UserMemoMap.tsx | 9 +-- web/src/layouts/RootLayout.tsx | 13 +---- web/src/main.tsx | 7 +-- web/src/pages/AuthCallback.tsx | 9 +-- web/src/router/MemoDetailRedirect.tsx | 8 --- web/src/router/index.tsx | 48 +++++----------- 8 files changed, 47 insertions(+), 122 deletions(-) delete mode 100644 web/src/components/Spinner.tsx delete mode 100644 web/src/router/MemoDetailRedirect.tsx diff --git a/web/src/components/Skeleton.tsx b/web/src/components/Skeleton.tsx index 94514405..7b87140b 100644 --- a/web/src/components/Skeleton.tsx +++ b/web/src/components/Skeleton.tsx @@ -1,55 +1,49 @@ import { cn } from "@/lib/utils"; -interface Props { +interface SkeletonProps { showCreator?: boolean; count?: number; } -// Memo card skeleton component for list loading states -const MemoCardSkeleton = ({ showCreator = false, index = 0 }: { showCreator?: boolean; index?: number }) => ( -
- {/* Header section */} -
-
+const skeletonBase = "bg-muted/70 rounded animate-pulse"; + +const MemoCardSkeleton = ({ showCreator, index }: { showCreator?: boolean; index: number }) => ( +
+
+
{showCreator ? ( -
-
-
-
-
+
+
+
+
+
) : ( -
+
)}
- {/* Action buttons skeleton */} -
-
-
+
+
+
- - {/* Content section */} -
-
-
-
- {index % 2 === 0 &&
} -
+
+
+
+ {index % 2 === 0 &&
}
); /** - * Skeleton loading state for memo lists. - * Use this for initial memo list loading and pagination. - * For generic page/route loading, use Spinner instead. + * Memo list loading skeleton - shows card structure while loading. + * Only use for memo lists in PagedMemoList component. */ -const Skeleton = ({ showCreator = false, count = 4 }: Props) => ( +const Skeleton = ({ showCreator = false, count = 4 }: SkeletonProps) => (
- {Array.from({ length: count }).map((_, index) => ( - + {Array.from({ length: count }, (_, i) => ( + ))}
); diff --git a/web/src/components/Spinner.tsx b/web/src/components/Spinner.tsx deleted file mode 100644 index 39a58a7e..00000000 --- a/web/src/components/Spinner.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { LoaderIcon } from "lucide-react"; -import { cn } from "@/lib/utils"; - -interface Props { - className?: string; - size?: "sm" | "md" | "lg"; -} - -const Spinner = ({ className, size = "md" }: Props) => { - const sizeClasses = { - sm: "w-4 h-4", - md: "w-6 h-6", - lg: "w-8 h-8", - }; - - return ; -}; - -export default Spinner; diff --git a/web/src/components/UserMemoMap/UserMemoMap.tsx b/web/src/components/UserMemoMap/UserMemoMap.tsx index 05f3a4d4..a7ddc99f 100644 --- a/web/src/components/UserMemoMap/UserMemoMap.tsx +++ b/web/src/components/UserMemoMap/UserMemoMap.tsx @@ -8,7 +8,6 @@ import { MapContainer, Marker, Popup, useMap } from "react-leaflet"; import MarkerClusterGroup from "react-leaflet-cluster"; import { Link } from "react-router-dom"; import { defaultMarkerIcon, ThemedTileLayer } from "@/components/map/map-utils"; -import Spinner from "@/components/Spinner"; import { useInfiniteMemos } from "@/hooks/useMemoQueries"; import { cn } from "@/lib/utils"; import { State } from "@/types/proto/api/v1/common_pb"; @@ -64,13 +63,7 @@ const UserMemoMap = ({ creator, className }: Props) => { const memosWithLocation = useMemo(() => data?.pages.flatMap((page) => page.memos).filter((memo) => memo.location) || [], [data]); - if (isLoading) { - return ( -
- -
- ); - } + if (isLoading) return null; const defaultCenter = { lat: 48.8566, lng: 2.3522 }; diff --git a/web/src/layouts/RootLayout.tsx b/web/src/layouts/RootLayout.tsx index 7a296ff3..0f05a4c5 100644 --- a/web/src/layouts/RootLayout.tsx +++ b/web/src/layouts/RootLayout.tsx @@ -1,8 +1,7 @@ -import { Suspense, useEffect, useMemo } from "react"; +import { useEffect, useMemo } from "react"; import { Outlet, useLocation, useSearchParams } from "react-router-dom"; import usePrevious from "react-use/lib/usePrevious"; import Navigation from "@/components/Navigation"; -import Spinner from "@/components/Spinner"; import { useInstance } from "@/contexts/InstanceContext"; import { useMemoFilterContext } from "@/contexts/MemoFilterContext"; import useCurrentUser from "@/hooks/useCurrentUser"; @@ -47,15 +46,7 @@ const RootLayout = () => {
)}
- - -
- } - > - - +
); diff --git a/web/src/main.tsx b/web/src/main.tsx index 3b3f525d..b3599aa4 100644 --- a/web/src/main.tsx +++ b/web/src/main.tsx @@ -8,7 +8,6 @@ import { RouterProvider } from "react-router-dom"; import "./i18n"; import "./index.css"; import { ErrorBoundary } from "@/components/ErrorBoundary"; -import Spinner from "@/components/Spinner"; import { AuthProvider, useAuth } from "@/contexts/AuthContext"; import { InstanceProvider, useInstance } from "@/contexts/InstanceContext"; import { ViewProvider } from "@/contexts/ViewContext"; @@ -41,11 +40,7 @@ function AppInitializer({ children }: { children: React.ReactNode }) { }, [initAuth, initInstance]); if (!authInitialized || !instanceInitialized) { - return ( -
- -
- ); + return null; } return <>{children}; diff --git a/web/src/pages/AuthCallback.tsx b/web/src/pages/AuthCallback.tsx index e7ebf2d6..f48e4f63 100644 --- a/web/src/pages/AuthCallback.tsx +++ b/web/src/pages/AuthCallback.tsx @@ -2,7 +2,6 @@ import { timestampDate } from "@bufbuild/protobuf/wkt"; import { useEffect, useState } from "react"; import { useSearchParams } from "react-router-dom"; import { setAccessToken } from "@/auth-state"; -import Spinner from "@/components/Spinner"; import { authServiceClient } from "@/connect"; import { useAuth } from "@/contexts/AuthContext"; import { absolutifyLink } from "@/helpers/utils"; @@ -110,13 +109,11 @@ const AuthCallback = () => { })(); }, [searchParams, navigateTo]); + if (state.loading) return null; + return (
- {state.loading ? ( - - ) : ( -
{state.errorMessage}
- )} +
{state.errorMessage}
); }; diff --git a/web/src/router/MemoDetailRedirect.tsx b/web/src/router/MemoDetailRedirect.tsx deleted file mode 100644 index 2b89907c..00000000 --- a/web/src/router/MemoDetailRedirect.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import { Navigate, useParams } from "react-router-dom"; - -const MemoDetailRedirect = () => { - const { uid } = useParams(); - return ; -}; - -export default MemoDetailRedirect; diff --git a/web/src/router/index.tsx b/web/src/router/index.tsx index fc4fdd26..99f3a26f 100644 --- a/web/src/router/index.tsx +++ b/web/src/router/index.tsx @@ -1,8 +1,6 @@ -import type { ComponentType } from "react"; -import { lazy, Suspense } from "react"; +import { lazy } from "react"; import { createBrowserRouter } from "react-router-dom"; import App from "@/App"; -import Spinner from "@/components/Spinner"; import MainLayout from "@/layouts/MainLayout"; import RootLayout from "@/layouts/RootLayout"; import Home from "@/pages/Home"; @@ -20,7 +18,6 @@ const Setting = lazy(() => import("@/pages/Setting")); const SignIn = lazy(() => import("@/pages/SignIn")); const SignUp = lazy(() => import("@/pages/SignUp")); const UserProfile = lazy(() => import("@/pages/UserProfile")); -const MemoDetailRedirect = lazy(() => import("./MemoDetailRedirect")); import { ROUTES } from "./routes"; @@ -28,19 +25,6 @@ import { ROUTES } from "./routes"; export const Routes = ROUTES; export { ROUTES }; -// Helper component to reduce Suspense boilerplate for lazy routes -const LazyRoute = ({ component: Component }: { component: ComponentType }) => ( - - -
- } - > - - -); - const router = createBrowserRouter([ { path: "/", @@ -49,10 +33,10 @@ const router = createBrowserRouter([ { path: Routes.AUTH, children: [ - { path: "", element: }, - { path: "admin", element: }, - { path: "signup", element: }, - { path: "callback", element: }, + { path: "", element: }, + { path: "admin", element: }, + { path: "signup", element: }, + { path: "callback", element: }, ], }, { @@ -63,20 +47,18 @@ const router = createBrowserRouter([ element: , children: [ { path: "", element: }, - { path: Routes.EXPLORE, element: }, - { path: Routes.ARCHIVED, element: }, - { path: "u/:username", element: }, + { path: Routes.EXPLORE, element: }, + { path: Routes.ARCHIVED, element: }, + { path: "u/:username", element: }, ], }, - { path: Routes.ATTACHMENTS, element: }, - { path: Routes.INBOX, element: }, - { path: Routes.SETTING, element: }, - { path: "memos/:uid", element: }, - // Redirect old path to new path - { path: "m/:uid", element: }, - { path: "403", element: }, - { path: "404", element: }, - { path: "*", element: }, + { path: Routes.ATTACHMENTS, element: }, + { path: Routes.INBOX, element: }, + { path: Routes.SETTING, element: }, + { path: "memos/:uid", element: }, + { path: "403", element: }, + { path: "404", element: }, + { path: "*", element: }, ], }, ],