From 422c32346747933cd212670f606f10fa9c901bcc Mon Sep 17 00:00:00 2001 From: javayhu Date: Wed, 27 Aug 2025 00:45:09 +0800 Subject: [PATCH] chore: replace Image component with BlogImage for improved loading handling in BlogCard --- src/components/blog/blog-card.tsx | 56 ++++++++++++------------------ src/components/blog/blog-image.tsx | 40 +++++++++++++++++++++ src/lib/constants.ts | 6 ---- 3 files changed, 63 insertions(+), 39 deletions(-) create mode 100644 src/components/blog/blog-image.tsx diff --git a/src/components/blog/blog-card.tsx b/src/components/blog/blog-card.tsx index e77587d..7d587c9 100644 --- a/src/components/blog/blog-card.tsx +++ b/src/components/blog/blog-card.tsx @@ -1,9 +1,9 @@ import { Skeleton } from '@/components/ui/skeleton'; import { LocaleLink } from '@/i18n/navigation'; -import { PLACEHOLDER_IMAGE } from '@/lib/constants'; import { formatDate } from '@/lib/formatter'; import { type BlogType, authorSource, categorySource } from '@/lib/source'; import Image from 'next/image'; +import BlogImage from './blog-image'; interface BlogCardProps { locale: string; @@ -23,34 +23,29 @@ export default function BlogCard({ locale, post }: BlogCardProps) {
{/* Image container - fixed aspect ratio */}
- {image && ( -
- {title +
+ - {blogCategories && blogCategories.length > 0 && ( -
-
- {blogCategories.map((category, index) => ( - - {category?.data.name} - - ))} -
+ {/* categories */} + {blogCategories && blogCategories.length > 0 && ( +
+
+ {blogCategories.map((category, index) => ( + + {category?.data.name} + + ))}
- )} -
- )} +
+ )} +
{/* Post info container */} @@ -99,12 +94,7 @@ export function BlogCardSkeleton() { return (
- Loading placeholder +
diff --git a/src/components/blog/blog-image.tsx b/src/components/blog/blog-image.tsx new file mode 100644 index 0000000..49c3213 --- /dev/null +++ b/src/components/blog/blog-image.tsx @@ -0,0 +1,40 @@ +'use client'; + +import { Skeleton } from '@/components/ui/skeleton'; +import Image from 'next/image'; +import { useState } from 'react'; + +interface BlogImageProps { + src: string; + alt: string; + title?: string; +} + +export default function BlogImage({ src, alt, title }: BlogImageProps) { + const [imageLoading, setImageLoading] = useState(true); + + const handleImageLoad = () => { + setImageLoading(false); + }; + + return ( +
+ {/* loading skeleton */} + {imageLoading && ( + + )} + + {/* actual image */} + {alt} +
+ ); +} diff --git a/src/lib/constants.ts b/src/lib/constants.ts index 856a240..b6abc30 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -2,9 +2,3 @@ * in next 30 days for credits expiration */ export const CREDITS_EXPIRATION_DAYS = 30; - -/** - * placeholder image for blog post card - */ -export const PLACEHOLDER_IMAGE = - 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAoJJREFUWEfFl4lu4zAMRO3cx/9/au6reMaOdkxTTl0grQFCRoqaT+SQotq2bV9N8rRt28xms87m83l553eZ/9vr9Wpkz+ezkT0ej+6dv1X81AFw7M4FBACPVn2c1Z3zLgDeJwHgeLFYdAARYioAEAKJEG2WAjl3gCwNYymQQ9b7/V4spmIAwO6Wy2VnAMikBWlDURBELf8CuN1uHQSrPwMAHK5WqwFELQ01AIXdAa7XawfAb3p6AOwK5+v1ugAoEq4FRSFLgavfQ49jAGQpAE5wjgGCeRrGdBArwHOPcwFcLpcGU1X0IsBuN5tNgYhaiFFwHTiAwq8I+O5xfj6fOz38K+X/fYAdb7fbAgFAjIJ6Aav3AYlQ6nfnDoDz0+lUxNiLALvf7XaDNGQ6GANQBKR85V27B4D3QQRw7hGIYlQKWGM79hSweyCUe1blXhEAogfABwHAXAcqSYkxCtHLUK3XBajSc4Dj8dilAeiSAgD2+30BAEKV4GKcAuDqB4TdYwBgPQByCgApUBoE4EJUGvxUjF3Q69/zLw3g/HA45ABKgdIQu+JPIyDnisCfAxAFNFM0EFNQ64gfS0EUoQP8ighrZSjn3oziZEQpauyKbfjbZchHUL/3AS/Dd30gAkxuRACgfO+EWQW8qwI1o+wseNuKcQiESjALvwNoMI0TcRzD4lFcPYwIM+JTF5x6HOs8yI7jeB5oKhpMRFH9UwaSCDB2Jmg4rc6E2TT0biIaG0rQhNqyhpHBcayTTSXH6vcDL7/sdqRK8LkwTsU499E8vRcAojHcZ4AxABdilgrp4lsXk8oVqgwh7+6H3phqd8J0Kk4vbx/+sZqCD/vNLya/5dT9fAH8g1WdNGgwbQAAAABJRU5ErkJggg==';