feat: add skeleton loader

This commit is contained in:
Henry
2025-10-29 21:24:55 +00:00
parent 8a6003169e
commit 580e002900

View File

@@ -18,6 +18,7 @@ interface OSSFriendsResponse {
export default function OSSFriendsView() { export default function OSSFriendsView() {
const [ossFriends, setOSSFriends] = useState<OSSFriend[]>([]); const [ossFriends, setOSSFriends] = useState<OSSFriend[]>([]);
const [isLoading, setIsLoading] = useState<boolean>(true);
const fetchOSSFriends = async (): Promise<OSSFriend[]> => { const fetchOSSFriends = async (): Promise<OSSFriend[]> => {
try { try {
const response = await fetch("/api/oss-friends"); const response = await fetch("/api/oss-friends");
@@ -36,8 +37,12 @@ export default function OSSFriendsView() {
useEffect(() => { useEffect(() => {
async function fetchData() { async function fetchData() {
const data = await fetchOSSFriends(); try {
setOSSFriends(data); const data = await fetchOSSFriends();
setOSSFriends(data);
} finally {
setIsLoading(false);
}
} }
void fetchData(); void fetchData();
}, []); }, []);
@@ -59,38 +64,54 @@ export default function OSSFriendsView() {
</div> </div>
<div className="mx-auto w-full max-w-7xl px-4 pb-24"> <div className="mx-auto w-full max-w-7xl px-4 pb-24">
<div className="grid grid-cols-1 gap-4 md:grid-cols-3"> <div className="grid grid-cols-1 gap-4 md:grid-cols-3">
{ossFriends.map((friend, idx) => { {isLoading &&
return ( Array.from({ length: 12 }).map((_, idx) => (
<div <div
key={`oss-friend-${idx}`} key={`oss-friend-skeleton-${idx}`}
className="group relative rounded-2xl border border-light-200 bg-light-50 p-6 transition-all duration-200 hover:shadow-sm dark:border-dark-200 dark:bg-dark-50" className="group relative h-[200px] rounded-2xl border border-light-200 bg-light-50 p-6 dark:border-dark-200 dark:bg-dark-50"
> >
<div className="mb-4 flex flex-col gap-2"> <div className="mb-4 flex flex-col gap-3">
<Link <div className="h-5 w-2/3 animate-pulse rounded-sm bg-light-200 dark:bg-dark-200" />
href={friend.href} <div className="h-3 w-full animate-pulse rounded-sm bg-light-200 dark:bg-dark-200" />
target="_blank" <div className="h-3 w-5/6 animate-pulse rounded-sm bg-light-200 dark:bg-dark-200" />
rel="noopener noreferrer"
>
<h3 className="text-lg font-semibold text-light-1000 dark:text-dark-1000">
{friend.name}
</h3>
</Link>
<p className="text-sm leading-relaxed text-light-900 dark:text-dark-900">
{friend.description}
</p>
</div> </div>
<Button <div className="h-8 w-28 animate-pulse rounded-md bg-light-200 dark:bg-dark-200" />
href={friend.href}
variant="secondary"
rel="noopener noreferrer"
size="sm"
>
{t`Learn more`}
</Button>
</div> </div>
); ))}
})}
{!isLoading &&
ossFriends.map((friend, idx) => {
return (
<div
key={`oss-friend-${idx}`}
className="group relative rounded-2xl border border-light-200 bg-light-50 p-6 transition-all duration-200 hover:shadow-sm dark:border-dark-200 dark:bg-dark-50"
>
<div className="mb-4 flex flex-col gap-2">
<Link
href={friend.href}
target="_blank"
rel="noopener noreferrer"
>
<h3 className="text-lg font-semibold text-light-1000 dark:text-dark-1000">
{friend.name}
</h3>
</Link>
<p className="text-sm leading-relaxed text-light-900 dark:text-dark-900">
{friend.description}
</p>
</div>
<Button
href={friend.href}
variant="secondary"
rel="noopener noreferrer"
size="sm"
>
{t`Learn more`}
</Button>
</div>
);
})}
</div> </div>
</div> </div>
</div> </div>