Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UXIT-1415 - Rework Image Logic #648

Closed
wants to merge 17 commits into from
Closed

UXIT-1415 - Rework Image Logic #648

wants to merge 17 commits into from

Conversation

barbaraperic
Copy link
Collaborator

@barbaraperic barbaraperic commented Sep 11, 2024

📝 Description

This PR refactors the code to use the native Next image directly, replacing the StaticImage and DynamicImage components.

  • Type: Refactor

🛠️ Key Changes

  • Deleted StaticImage and DynamicImage component and replaced with Next's Image

📌 To-Do Before Merging

  • Handle fallback cases

🧪 How to Test

  • Setup: [Setup details]
  • Steps to Test:
    1. Check images on each page
    2. Test fallback cases

@barbaraperic barbaraperic self-assigned this Sep 11, 2024
Copy link

vercel bot commented Sep 11, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
filecoin-foundation-site ❌ Failed (Inspect) Oct 1, 2024 8:24am

Copy link

[FF] Rework Image Logic


const { src: fallbackSrc, alt: fallbackAlt } = graphicsData.imageFallback

async function getImageSrc(src: string | StaticImport) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
async function getImageSrc(src: string | StaticImport) {
async function getImageSrc(src: StaticImport["src"]) {

const { src: fallbackSrc, alt: fallbackAlt } = graphicsData.imageFallback

async function getImageSrc(src: string | StaticImport) {
const isStaticImport = typeof src === 'object'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can rename isStaticImport to isLocalImage to be consistent with the types defined in imageTypes.ts?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would probably rename everything to use static or dynamic since those terms come straight from Next.js docs. Is there an example where this wouldn't work?

Comment on lines 21 to 31
switch (true) {
case isStaticImport:
return src
case noImage:
return fallbackSrc
case isRemoteImage:
const remoteImageExists = await checkRemoteImageExists(src)
return remoteImageExists ? src : fallbackSrc
default:
return src
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It took me a little while to understand the switch (true) statement, I had never seen this pattern before. And probably because I'm just back from holidays 😅

Just curious, is there a benefit to this over if statements? Or is it a personal preference?

async function getImageSrc(src: string | StaticImport) {
const isStaticImport = typeof src === 'object'
const isRemoteImage =
typeof src === 'string' && (src.startsWith('http') || src.startsWith('//'))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about doing typeof src === 'string' && src !== "" instead?

Comment on lines 15 to 41
async function getImageSrc(src: string | StaticImport) {
const isStaticImport = typeof src === 'object'
const isRemoteImage =
typeof src === 'string' && (src.startsWith('http') || src.startsWith('//'))
const noImage = !src

switch (true) {
case isStaticImport:
return src
case noImage:
return fallbackSrc
case isRemoteImage:
const remoteImageExists = await checkRemoteImageExists(src)
return remoteImageExists ? src : fallbackSrc
default:
return src
}
}

async function checkRemoteImageExists(url: string) {
try {
const response = await fetch(url, { method: 'HEAD' })
return response.ok
} catch (error) {
return false
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move getImageSrc and checkRemoteImageExists below ImageWithFallback?

Comment on lines 33 to 41
<ImageWithFallback
fill
alt={alt}
src={src}
blurDataURL={blurDataURL}
style={{ objectFit: 'cover' }}
className="h-full w-full rounded-lg"
sizes={buildImageSizeProp({
startSize: '100vw',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as before - the prop definition suggests a local image, but the implementation suggests a remote image. Maybe we can use Next's Image component here?

Comment on lines 21 to 25
<ImageWithFallback
src={src}
height={height}
width={width}
blurDataURL={blurDataURL}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above.

Comment on lines 23 to 28
<ImageWithFallback
src={src}
alt={alt}
height={height}
width={width}
blurDataURL={blurDataURL}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above.

@@ -36,6 +36,8 @@ export function FeaturedBlogPosts() {
category,
})

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The import of graphicsData can be deleted at the top of the file.

/>
))}
{ecosystemProjects.map(({ slug, title, description, image }) => {
const { src } = image || {}
Copy link
Collaborator

@CharlyMartin CharlyMartin Sep 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The import of graphicsData can be deleted at the top of the file.

@CharlyMartin
Copy link
Collaborator

CharlyMartin commented Sep 30, 2024

First off, this implementation is a readability improvement over the initial one, so good one! 🙂

I’ve left some comments, but my main question is about ImageWithFallback: Should this component handle static imports?

// src/app/_components/ImageWithFallback.tsx

const isStaticImport = typeof src === 'object'

From what I understand, local images “never” fail to load because they’re imported at build time. If there’s an issue with the import, the build itself would fail.

Because of this, I’m tempted to suggest that ImageWithFallback should only handle cases where an image might fail to load - when it comes from a remote URL. This would let us rely on the native Image from next/image wherever we import local images and ImageWithFallback with remote ones. And it would simplify the implementation of ImageWithFallback.

This split also aligns with the (subtle) differences in handling local vs. remote images in Next.js. Typically, local images don’t need a wrapper container since they come with predefined width and height, computed by Next. Remote images, on the other hand, often need a wrapper as they are usually set to fill the parent.

I understand that this approach means they are two image components, which might not be what we want. I don't see it as a problem, but maybe it will be confusing in the long run, Not sure, let me know what you think :)

<div className="relative aspect-video">
<DynamicImage
<ImageWithFallback
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to name it explicitly WithFallback? I kinda expect a fallback out of the box?


type ArticleHeaderProps = {
image?: ImageProps
image?: RemoteImageData | LocalImageData
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reason that this is both types?

Comment on lines 54 to +55
alt: '',
...(image || {
...graphicsData.imageFallback,
}),
fallback: graphicsData.imageFallback,
src: src || '',
Copy link
Collaborator

@mirhamasala mirhamasala Oct 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was hoping we wouldn't have to use generic fallbacks to '' anymore...

/>
))}
{ecosystemProjects.map(({ slug, title, description, image }) => {
const { src } = image || {}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment >>

/>
))}
{grantGraduates.map(({ title, description, slug, image }) => {
const { src } = image || {}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment >>

@mirhamasala
Copy link
Collaborator

@barbaraperic @CharlyMartin I’m still feeling a bit unclear about this implementation. It’s hard to distinguish how images are being used, specifically what parts are static and what parts are dynamic. My hope was that we could first deconstruct everything, use Next.js’ Image component consistently throughout, and then, if necessary, start refactoring from there (as outlined in the spec).

Copy link

[FF] Rework Image Logic

@mirhamasala
Copy link
Collaborator

Closing in favor of #697 and #700.

@mirhamasala mirhamasala deleted the bp/rework-images branch October 29, 2024 16:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants