diff --git a/web/src/components/PagedMemoList/PagedMemoList.tsx b/web/src/components/PagedMemoList/PagedMemoList.tsx index ccd69d23..58a9e72a 100644 --- a/web/src/components/PagedMemoList/PagedMemoList.tsx +++ b/web/src/components/PagedMemoList/PagedMemoList.tsx @@ -8,7 +8,7 @@ import { userServiceClient } from "@/connect"; import { useMemoFilterContext } from "@/contexts/MemoFilterContext"; import { useNewMemo } from "@/contexts/NewMemoContext"; import { useView } from "@/contexts/ViewContext"; -import { DEFAULT_LIST_MEMOS_PAGE_SIZE, SKELETON_LOADING_DELAY_MS } from "@/helpers/consts"; +import { DEFAULT_LIST_MEMOS_PAGE_SIZE, LOADING_INDICATOR_DELAY_MS } from "@/helpers/consts"; import { useDelayedFlag } from "@/hooks/useDelayedFlag"; import { useInfiniteMemos } from "@/hooks/useMemoQueries"; import { hoistMemoToFront } from "@/hooks/useMemoSorting"; @@ -21,7 +21,6 @@ import ColumnGrid, { columnCountForWidth, GRID_GAP } from "../ColumnGrid"; import MemoEditor from "../MemoEditor"; import MemoFilters from "../MemoFilters"; import Placeholder from "../Placeholder"; -import Skeleton from "../Skeleton"; // Memo identity for React keys and the grid's sticky column assignments. The pages use it // for their renderer keys too, so flow-list and grid identity can never drift apart. @@ -31,8 +30,7 @@ export const getMemoKey = (memo: Memo) => `${memo.name}-${memo.updateTime}`; // grid centers in the leftover space instead of filling it. const MAX_COLUMN_WIDTH = 420; -// The grid packs cards into columns, so a card-shaped skeleton doesn't fit; use a spinner. -const GridLoader = () => ( +const Loader = () => (
@@ -146,8 +144,8 @@ const PagedMemoList = (props: Props) => { { enabled: props.enabled ?? true }, ); - // Only show the skeleton once loading exceeds the delay, so fast loads don't flash it. - const showSkeleton = useDelayedFlag(isLoading, SKELETON_LOADING_DELAY_MS); + // Only show the spinner once loading exceeds the delay, so fast loads don't flash it. + const showLoader = useDelayedFlag(isLoading, LOADING_INDICATOR_DELAY_MS); // Flatten pages into a single array of memos const memos = useMemo(() => data?.pages.flatMap((page) => page.memos) || [], [data]); @@ -234,10 +232,10 @@ const PagedMemoList = (props: Props) => { ) : undefined; - // Pagination skeleton, empty state, and back-to-top are identical across both layouts. + // Pagination spinner, empty state, and back-to-top are identical across both layouts. const footer = ( <> - {isFetchingNextPage && (useGrid ? : )} + {isFetchingNextPage && } {!isFetchingNextPage && !hasNextPage && sortedMemoList.length === 0 && !memoEditor && ( )} @@ -253,14 +251,10 @@ const PagedMemoList = (props: Props) => {
- {/* During initial load, show the skeleton only after the delay; render nothing before then to avoid a flash. */} + {/* During initial load, show the spinner only after the delay; render nothing before then to avoid a flash. */} {isLoading ? ( - showSkeleton ? ( - useGrid ? ( - - ) : ( - - ) + showLoader ? ( + ) : null ) : useGrid ? ( <> diff --git a/web/src/components/Skeleton.tsx b/web/src/components/Skeleton.tsx deleted file mode 100644 index 7b87140b..00000000 --- a/web/src/components/Skeleton.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { cn } from "@/lib/utils"; - -interface SkeletonProps { - showCreator?: boolean; - count?: number; -} - -const skeletonBase = "bg-muted/70 rounded animate-pulse"; - -const MemoCardSkeleton = ({ showCreator, index }: { showCreator?: boolean; index: number }) => ( -
-
-
- {showCreator ? ( -
-
-
-
-
-
-
- ) : ( -
- )} -
-
-
-
-
-
-
-
-
- {index % 2 === 0 &&
} -
-
-); - -/** - * Memo list loading skeleton - shows card structure while loading. - * Only use for memo lists in PagedMemoList component. - */ -const Skeleton = ({ showCreator = false, count = 4 }: SkeletonProps) => ( -
- {Array.from({ length: count }, (_, i) => ( - - ))} -
-); - -export default Skeleton; diff --git a/web/src/helpers/consts.ts b/web/src/helpers/consts.ts index 66ac0a90..7daa0b0b 100644 --- a/web/src/helpers/consts.ts +++ b/web/src/helpers/consts.ts @@ -4,6 +4,6 @@ export const TAB_SPACE_WIDTH = 2; // DEFAULT_LIST_MEMOS_PAGE_SIZE is the default page size for list memos request. export const DEFAULT_LIST_MEMOS_PAGE_SIZE = 16; -// SKELETON_LOADING_DELAY_MS is how long a load must take before the loading skeleton appears. -// Loads that finish faster than this never render the skeleton, avoiding a flash on fast/self-hosted networks. -export const SKELETON_LOADING_DELAY_MS = 250; +// LOADING_INDICATOR_DELAY_MS is how long a load must take before the loading spinner appears. +// Loads that finish faster than this never render the spinner, avoiding a flash on fast/self-hosted networks. +export const LOADING_INDICATOR_DELAY_MS = 250; diff --git a/web/src/hooks/useDelayedFlag.ts b/web/src/hooks/useDelayedFlag.ts index aa2823a9..a888b812 100644 --- a/web/src/hooks/useDelayedFlag.ts +++ b/web/src/hooks/useDelayedFlag.ts @@ -4,7 +4,7 @@ import { useEffect, useState } from "react"; * Returns a flag that turns true only after `active` has stayed true for `delay` ms, * and turns false immediately once `active` becomes false. * - * Useful for delaying loading indicators (e.g. skeletons) so they don't flash on fast operations. + * Useful for delaying loading indicators (e.g. spinners) so they don't flash on fast operations. */ export const useDelayedFlag = (active: boolean, delay: number): boolean => { const [delayed, setDelayed] = useState(false);