From 41d46b1a13bd3a3a00f703437a026d6516d88a23 Mon Sep 17 00:00:00 2001 From: codytseng Date: Fri, 14 Feb 2025 12:17:01 +0800 Subject: [PATCH] feat: optimize display effect when image loading fails --- src/components/Image/index.tsx | 59 ++++++++++++++----- src/components/ImageCarousel/index.tsx | 5 +- src/components/ImageGallery/index.tsx | 15 +++-- src/components/NoteCard/GroupMetadataCard.tsx | 6 +- src/components/NoteCard/LiveEventCard.tsx | 3 +- .../NoteCard/LongFormArticleCard.tsx | 3 +- src/components/NoteList/index.tsx | 26 +------- src/components/PictureNoteCard/index.tsx | 2 +- src/components/ProfileBanner/index.tsx | 3 +- src/components/WebPreview/index.tsx | 5 +- .../secondary/ProfileEditorPage/index.tsx | 2 +- src/pages/secondary/ProfilePage/index.tsx | 2 +- 12 files changed, 76 insertions(+), 55 deletions(-) diff --git a/src/components/Image/index.tsx b/src/components/Image/index.tsx index 8d1b38d..003de77 100644 --- a/src/components/Image/index.tsx +++ b/src/components/Image/index.tsx @@ -2,20 +2,29 @@ import { Skeleton } from '@/components/ui/skeleton' import { cn } from '@/lib/utils' import { TImageInfo } from '@/types' import { decode } from 'blurhash' +import { ImageOff } from 'lucide-react' import { HTMLAttributes, useEffect, useState } from 'react' export default function Image({ image: { url, blurHash }, alt, className = '', + classNames = {}, + hideIfError = false, ...props }: HTMLAttributes & { + classNames?: { + wrapper?: string + errorPlaceholder?: string + } image: TImageInfo alt?: string + hideIfError?: boolean }) { const [isLoading, setIsLoading] = useState(true) const [displayBlurHash, setDisplayBlurHash] = useState(true) const [blurDataUrl, setBlurDataUrl] = useState(null) + const [hasError, setHasError] = useState(false) useEffect(() => { if (blurHash) { @@ -36,23 +45,41 @@ export default function Image({ } }, [blurHash]) + if (hideIfError && hasError) return null + return ( -
- {isLoading && } - {alt} { - setIsLoading(false) - setTimeout(() => setDisplayBlurHash(false), 500) - }} - /> - {displayBlurHash && blurDataUrl && ( +
+ {isLoading && } + {!hasError ? ( + {alt} { + setIsLoading(false) + setTimeout(() => setDisplayBlurHash(false), 500) + }} + onError={() => { + setIsLoading(false) + setHasError(true) + }} + /> + ) : ( +
+ +
+ )} + {displayBlurHash && blurDataUrl && !hasError && ( ( handlePhotoClick(e, index)} /> diff --git a/src/components/ImageGallery/index.tsx b/src/components/ImageGallery/index.tsx index 7d66174..97f4d85 100644 --- a/src/components/ImageGallery/index.tsx +++ b/src/components/ImageGallery/index.tsx @@ -38,8 +38,11 @@ export default function ImageGallery({ className={cn( 'rounded-lg', !disableLightbox ? 'cursor-auto' : '', - size === 'small' ? 'h-[15vh]' : 'h-[30vh]' + size === 'small' ? 'max-h-[15vh]' : 'max-h-[30vh]' )} + classNames={{ + errorPlaceholder: cn('aspect-square', size === 'small' ? 'h-[15vh]' : 'h-[30vh]') + }} image={images[0]} onClick={(e) => handlePhotoClick(e, 0)} /> @@ -50,7 +53,7 @@ export default function ImageGallery({ {images.map((image, i) => ( handlePhotoClick(e, i)} /> @@ -63,7 +66,7 @@ export default function ImageGallery({ {images.map((image, i) => ( handlePhotoClick(e, i)} /> @@ -72,11 +75,11 @@ export default function ImageGallery({ ) } else { imageContent = ( -
+
{images.map((image, i) => ( handlePhotoClick(e, i)} /> @@ -86,7 +89,7 @@ export default function ImageGallery({ } return ( -
+
{imageContent} {index >= 0 && !disableLightbox && diff --git a/src/components/NoteCard/GroupMetadataCard.tsx b/src/components/NoteCard/GroupMetadataCard.tsx index 1d9eea0..baaafb0 100644 --- a/src/components/NoteCard/GroupMetadataCard.tsx +++ b/src/components/NoteCard/GroupMetadataCard.tsx @@ -93,7 +93,11 @@ export default function GroupMetadataCard({
{metadata.picture && ( - + )}
{metadata.name}
diff --git a/src/components/NoteCard/LiveEventCard.tsx b/src/components/NoteCard/LiveEventCard.tsx index db6e482..91e2cf5 100644 --- a/src/components/NoteCard/LiveEventCard.tsx +++ b/src/components/NoteCard/LiveEventCard.tsx @@ -120,6 +120,7 @@ export default function LiveEventCard({ )}
@@ -148,7 +149,7 @@ export default function LiveEventCard({
{metadata.image && ( - + )}
{!embedded && } diff --git a/src/components/NoteCard/LongFormArticleCard.tsx b/src/components/NoteCard/LongFormArticleCard.tsx index 15ce274..33fb403 100644 --- a/src/components/NoteCard/LongFormArticleCard.tsx +++ b/src/components/NoteCard/LongFormArticleCard.tsx @@ -113,6 +113,7 @@ export default function LongFormArticleCard({ )}
@@ -141,7 +142,7 @@ export default function LongFormArticleCard({
{metadata.image && ( - + )}
{!embedded && } diff --git a/src/components/NoteList/index.tsx b/src/components/NoteList/index.tsx index bd42d3b..9354dd4 100644 --- a/src/components/NoteList/index.tsx +++ b/src/components/NoteList/index.tsx @@ -9,8 +9,8 @@ import { useMuteList } from '@/providers/MuteListProvider' import { useNostr } from '@/providers/NostrProvider' import { useScreenSize } from '@/providers/ScreenSizeProvider' import client from '@/services/client.service' -import relayInfoService from '@/services/relay-info.service' import storage from '@/services/local-storage.service' +import relayInfoService from '@/services/relay-info.service' import { TNoteListMode } from '@/types' import dayjs from 'dayjs' import { Event, Filter, kinds } from 'nostr-tools' @@ -326,30 +326,10 @@ function PictureNoteCardMasonry({ } function LoadingSkeleton({ isPictures }: { isPictures: boolean }) { - const { isLargeScreen } = useScreenSize() + const { t } = useTranslation() if (isPictures) { - return ( -
- {[...Array(isLargeScreen ? 3 : 2)].map((_, i) => ( -
- -
- -
- - -
-
-
- ))} -
- ) + return
{t('loading...')}
} return ( diff --git a/src/components/PictureNoteCard/index.tsx b/src/components/PictureNoteCard/index.tsx index 5b6724b..24214fe 100644 --- a/src/components/PictureNoteCard/index.tsx +++ b/src/components/PictureNoteCard/index.tsx @@ -38,7 +38,7 @@ export default function PictureNoteCard({ return (
push(toNote(event))}> - + {images.length > 1 && (
diff --git a/src/components/ProfileBanner/index.tsx b/src/components/ProfileBanner/index.tsx index 3335237..604f437 100644 --- a/src/components/ProfileBanner/index.tsx +++ b/src/components/ProfileBanner/index.tsx @@ -1,4 +1,5 @@ import { generateImageByPubkey } from '@/lib/pubkey' +import { cn } from '@/lib/utils' import { useEffect, useMemo, useState } from 'react' import Image from '../Image' @@ -26,7 +27,7 @@ export default function ProfileBanner({ {`${pubkey} setBannerUrl(defaultBanner)} /> ) diff --git a/src/components/WebPreview/index.tsx b/src/components/WebPreview/index.tsx index 4884ee2..2fa036f 100644 --- a/src/components/WebPreview/index.tsx +++ b/src/components/WebPreview/index.tsx @@ -30,7 +30,7 @@ export default function WebPreview({ if (isSmallScreen && image) { return (
- +
{hostname}
{title}
@@ -50,7 +50,8 @@ export default function WebPreview({ {image && ( )}
diff --git a/src/pages/secondary/ProfileEditorPage/index.tsx b/src/pages/secondary/ProfileEditorPage/index.tsx index 012ce61..abb91e8 100644 --- a/src/pages/secondary/ProfileEditorPage/index.tsx +++ b/src/pages/secondary/ProfileEditorPage/index.tsx @@ -109,7 +109,7 @@ const ProfileEditorPage = forwardRef(({ index }: { index?: number }, ref) => {
{uploadingBanner ? ( diff --git a/src/pages/secondary/ProfilePage/index.tsx b/src/pages/secondary/ProfilePage/index.tsx index 8ad9eac..c25660f 100644 --- a/src/pages/secondary/ProfilePage/index.tsx +++ b/src/pages/secondary/ProfilePage/index.tsx @@ -82,7 +82,7 @@ const ProfilePage = forwardRef(({ id, index }: { id?: string; index?: number },