diff --git a/package-lock.json b/package-lock.json index 401f4de..fa00e9b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -817,7 +817,6 @@ }, "node_modules/@clack/prompts/node_modules/is-unicode-supported": { "version": "1.3.0", - "extraneous": true, "inBundle": true, "license": "MIT", "engines": { diff --git a/src/feat/Projects/components/ProjectDetails.tsx b/src/feat/Projects/components/ProjectDetails.tsx index 43403b4..651cd1b 100644 --- a/src/feat/Projects/components/ProjectDetails.tsx +++ b/src/feat/Projects/components/ProjectDetails.tsx @@ -5,9 +5,12 @@ import { type TransitionTo, } from '@react-spring/web'; import { useQuery } from 'convex/react'; +import { useCallback, useState } from 'react'; +import useMeasure from 'react-use/lib/useMeasure'; import { Flex, type FlexProps } from 'styled-system/jsx'; import { api } from '~/convex/api'; import { Image } from '~/ui/Image'; +import { Loader } from '~/ui/Loader'; import { Markdown } from '~/ui/Markdown'; import { MediaEmbed } from '~/ui/MediaEmbed'; import type { Directions } from '../hooks/useNextPrev'; @@ -61,16 +64,33 @@ const ScrollContainer = (props: FlexProps) => { ); }; -const CoverImage = (props: { projectId: ProjectId }) => { - const { projectId } = props; +const CoverImage = (props: { + hasSeenImages: Set; + projectId: ProjectId; +}) => { + const { projectId, hasSeenImages } = props; + const [containerRef, containerRect] = useMeasure(); + const [isLoading, setLoading] = useState(true); const coverImage = useQuery(api.projects.loadProjectCoverImage, { projectId, }); + const handleLoad = useCallback(() => { + hasSeenImages.add(projectId); + setLoading(false); + }, [hasSeenImages, projectId]); if (!coverImage) { return null; } + const { aspectRatio } = coverImage; + const calculatedHeight = containerRect.width + ? Math.min( + Math.round(containerRect.width * aspectRatio), + containerRect.height, + ) + : 0; + return ( { overflow="hidden" position="relative" py={[8, 0]} + ref={containerRef} width="100%" _selection={{ bg: 'transparent' }} > {coverImage.alt} handleLoad()} options={{ width: 1280, quality: 75, fit: 'cover' }} src={coverImage.publicUrl} - useAnimation={false} + style={{ minHeight: `${calculatedHeight}px` }} + useAnimation={!hasSeenImages.has(projectId)} width="100%" - flexShrink={0} _selection={{ bg: 'transparent' }} /> + {isLoading && ( + + )} ); }; @@ -160,6 +193,7 @@ export const ProjectDetails = (props: { projectId: ProjectId; }) => { const { direction, projectId } = props; + const [hasSeenImages] = useState>(() => new Set()); const transitions = useTransition(projectId, { from: fromTransition(direction), enter: { @@ -188,7 +222,7 @@ export const ProjectDetails = (props: { }} > - +