refactor(client): optimize suspense components
Signed-off-by: Noa Virellia <noa@requiem.garden>
This commit is contained in:
@@ -7,7 +7,6 @@ import * as React from 'react';
|
|||||||
import NixOSLogo from '@/assets/nixos.svg?react';
|
import NixOSLogo from '@/assets/nixos.svg?react';
|
||||||
import { NavMain } from '@/components/nav-main';
|
import { NavMain } from '@/components/nav-main';
|
||||||
import { NavSecondary } from '@/components/nav-secondary';
|
import { NavSecondary } from '@/components/nav-secondary';
|
||||||
import { NavUser } from '@/components/nav-user';
|
|
||||||
import {
|
import {
|
||||||
Sidebar,
|
Sidebar,
|
||||||
SidebarContent,
|
SidebarContent,
|
||||||
@@ -17,6 +16,7 @@ import {
|
|||||||
SidebarMenuButton,
|
SidebarMenuButton,
|
||||||
SidebarMenuItem,
|
SidebarMenuItem,
|
||||||
} from '@/components/ui/sidebar';
|
} from '@/components/ui/sidebar';
|
||||||
|
import { NavUser } from './nav-user';
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
user: {
|
user: {
|
||||||
@@ -48,7 +48,7 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
|||||||
<SidebarMenuItem>
|
<SidebarMenuItem>
|
||||||
<SidebarMenuButton
|
<SidebarMenuButton
|
||||||
asChild
|
asChild
|
||||||
className="data-[slot=sidebar-menu-button]:!p-1.5"
|
className="data-[slot=sidebar-menu-button]:p-1.5!"
|
||||||
>
|
>
|
||||||
<a href="#">
|
<a href="#">
|
||||||
<NixOSLogo />
|
<NixOSLogo />
|
||||||
|
|||||||
20
client/src/components/hoc/with-fallback.tsx
Normal file
20
client/src/components/hoc/with-fallback.tsx
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import type { ReactNode } from 'react';
|
||||||
|
import React, { Suspense } from 'react';
|
||||||
|
|
||||||
|
export function withFallback<P extends object>(
|
||||||
|
Component: React.ComponentType<P>,
|
||||||
|
fallback: ReactNode,
|
||||||
|
) {
|
||||||
|
const Wrapped: React.FC<P> = (props) => {
|
||||||
|
return (
|
||||||
|
<Suspense fallback={fallback}>
|
||||||
|
<Component {...props} />
|
||||||
|
</Suspense>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Wrapped.displayName = `withFallback(${Component.displayName! || Component.name || 'Component'
|
||||||
|
})`;
|
||||||
|
|
||||||
|
return Wrapped;
|
||||||
|
}
|
||||||
@@ -24,8 +24,10 @@ import {
|
|||||||
} from '@/components/ui/sidebar';
|
} from '@/components/ui/sidebar';
|
||||||
import { useUserInfo } from '@/hooks/data/useUserInfo';
|
import { useUserInfo } from '@/hooks/data/useUserInfo';
|
||||||
import { useLogout } from '@/hooks/useLogout';
|
import { useLogout } from '@/hooks/useLogout';
|
||||||
|
import { withFallback } from './hoc/with-fallback';
|
||||||
|
import { Skeleton } from './ui/skeleton';
|
||||||
|
|
||||||
export function NavUser() {
|
function NavUser_() {
|
||||||
const { isMobile } = useSidebar();
|
const { isMobile } = useSidebar();
|
||||||
const { data: user } = useUserInfo();
|
const { data: user } = useUserInfo();
|
||||||
const { logout } = useLogout();
|
const { logout } = useLogout();
|
||||||
@@ -83,3 +85,20 @@ export function NavUser() {
|
|||||||
</SidebarMenu>
|
</SidebarMenu>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function NavUserSkeleton() {
|
||||||
|
return (
|
||||||
|
<SidebarMenuButton
|
||||||
|
size="lg"
|
||||||
|
>
|
||||||
|
<Skeleton className="h-8 w-8 rounded-lg" />
|
||||||
|
<div className="flex flex-col flex-1 gap-1">
|
||||||
|
<Skeleton className="h-3 w-16" />
|
||||||
|
<Skeleton className="h-3 w-24" />
|
||||||
|
</div>
|
||||||
|
<IconDotsVertical className="ml-auto size-4" />
|
||||||
|
</SidebarMenuButton>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const NavUser = withFallback(NavUser_, <NavUserSkeleton />);
|
||||||
|
|||||||
9
client/src/components/workbenchCards/card-skeleton.tsx
Normal file
9
client/src/components/workbenchCards/card-skeleton.tsx
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { Skeleton } from '../ui/skeleton';
|
||||||
|
|
||||||
|
export function CardSkeleton() {
|
||||||
|
return (
|
||||||
|
<Skeleton
|
||||||
|
className="gap-6 rounded-xl py-6 h-full"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -2,11 +2,12 @@ import { Badge } from '@/components/ui/badge';
|
|||||||
import { Card, CardAction, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardAction, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
import { useUserInfo } from '@/hooks/data/useUserInfo';
|
import { useUserInfo } from '@/hooks/data/useUserInfo';
|
||||||
import { QrDialog } from '../checkin/qr-dialog';
|
import { QrDialog } from '../checkin/qr-dialog';
|
||||||
|
import { withFallback } from '../hoc/with-fallback';
|
||||||
|
import { CardSkeleton } from './card-skeleton';
|
||||||
|
|
||||||
export function CheckinCard() {
|
function CheckinCard_() {
|
||||||
const { data } = useUserInfo();
|
const { data } = useUserInfo();
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
<Card className="@container/card">
|
<Card className="@container/card">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardDescription>签到状态</CardDescription>
|
<CardDescription>签到状态</CardDescription>
|
||||||
@@ -25,6 +26,7 @@ export function CheckinCard() {
|
|||||||
</QrDialog>
|
</QrDialog>
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
</Card>
|
</Card>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const CheckinCard = withFallback(CheckinCard_, <CardSkeleton />);
|
||||||
|
|||||||
@@ -4,7 +4,24 @@ import { ThemeProvider } from '@/components/theme-provider';
|
|||||||
import { Toaster } from '@/components/ui/sonner';
|
import { Toaster } from '@/components/ui/sonner';
|
||||||
import '@/index.css';
|
import '@/index.css';
|
||||||
|
|
||||||
const queryClient = new QueryClient();
|
const queryClient = new QueryClient({
|
||||||
|
defaultOptions: {
|
||||||
|
queries: {
|
||||||
|
retry: (failureCount, error: any) => {
|
||||||
|
// eslint-disable-next-line ts/no-unsafe-assignment
|
||||||
|
const status
|
||||||
|
// eslint-disable-next-line ts/no-unsafe-member-access
|
||||||
|
= error?.response?.status ?? error?.status;
|
||||||
|
|
||||||
|
if (status >= 400 && status < 500) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return failureCount < 3;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
function RootLayout() {
|
function RootLayout() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -13,18 +13,16 @@ export const Route = createFileRoute('/_sidebarLayout/')({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
function SectionCards() {
|
|
||||||
return (
|
|
||||||
<div className="*:data-[slot=card]:from-primary/5 *:data-[slot=card]:to-card dark:*:data-[slot=card]:bg-card grid grid-cols-1 gap-4 px-4 *:data-[slot=card]:bg-gradient-to-t *:data-[slot=card]:shadow-xs lg:px-6 @xl/main:grid-cols-2 @5xl/main:grid-cols-4">
|
|
||||||
<CheckinCard />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function Index() {
|
function Index() {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-4 py-4 md:gap-6 md:py-6">
|
<div className="flex flex-col gap-4 py-4 md:gap-6 md:py-6">
|
||||||
<SectionCards />
|
{/* Section Cards */}
|
||||||
|
<div className="*:data-[slot=card]:from-primary/5 *:data-[slot=card]:to-card dark:*:data-[slot=card]:bg-card
|
||||||
|
grid grid-cols-1 gap-4 px-4 auto-rows-[minmax(175px,auto)] items-stretch
|
||||||
|
*:data-[slot=card]:bg-linear-to-t *:data-[slot=card]:shadow-xs lg:px-6 @xl/main:grid-cols-2 @5xl/main:grid-cols-4"
|
||||||
|
>
|
||||||
|
<CheckinCard />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user