Skip to content

Commit 436f6b7

Browse files
committed
Fix MaskedAvatar and PublisherAvatar sizing issues, improved loading & fallback states (#5238)
## Problem solved Short description of the bug fixed or feature added <!-- start pr-codex --> --- ## PR-Codex overview This PR primarily focuses on updating the `PublisherAvatar` component usage across several files, replacing the `boxSize` prop with a `className` prop for styling. Additionally, it enhances the `Img` component with improved loading behavior. ### Detailed summary - Replaced `boxSize` with `className` in `PublisherAvatar` components in: - `apps/dashboard/src/components/explore/publisher/index.tsx` - `apps/dashboard/src/pages/profile/[profileAddress].tsx` - `apps/dashboard/src/components/contract-components/publisher/publisher-header.tsx` - `apps/dashboard/src/pages/community/ambassadors.tsx` - Updated `MaskedAvatar` component to use `className` instead of `boxSize`. - Modified `MaskedAvatar` component's rendering logic to use a new `Img` component. - Enhanced `Img` component with a loading state and error handling using a `ref`. > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` <!-- end pr-codex -->
1 parent df8b2f6 commit 436f6b7

File tree

8 files changed

+40
-26
lines changed

8 files changed

+40
-26
lines changed

apps/dashboard/src/@/components/blocks/Img.tsx

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/* eslint-disable @next/next/no-img-element */
22
"use client";
3-
import { useState } from "react";
3+
import { useRef, useState } from "react";
4+
import { useIsomorphicLayoutEffect } from "../../lib/useIsomorphicLayoutEffect";
45
import { cn } from "../../lib/utils";
56

67
type imgElementProps = React.DetailedHTMLProps<
@@ -25,19 +26,39 @@ export function Img(props: imgElementProps) {
2526
const { className, fallback, skeleton, ...restProps } = props;
2627
const defaultSkeleton = <div className="animate-pulse bg-accent" />;
2728
const defaultFallback = <div className="bg-muted" />;
29+
const imgRef = useRef<HTMLImageElement>(null);
30+
31+
useIsomorphicLayoutEffect(() => {
32+
const imgEl = imgRef.current;
33+
if (!imgEl) {
34+
return;
35+
}
36+
if (imgEl.complete) {
37+
setStatus("loaded");
38+
} else {
39+
function handleLoad() {
40+
setStatus("loaded");
41+
}
42+
imgEl.addEventListener("load", handleLoad);
43+
return () => {
44+
imgEl.removeEventListener("load", handleLoad);
45+
};
46+
}
47+
}, []);
2848

2949
return (
3050
<div className="relative">
3151
<img
3252
{...restProps}
33-
onLoad={() => {
34-
setStatus("loaded");
35-
}}
53+
// avoid setting empty src string to prevent request to the entire page
54+
src={restProps.src || undefined}
55+
ref={imgRef}
3656
onError={() => {
3757
setStatus("fallback");
3858
}}
3959
style={{
4060
opacity: status === "loaded" ? 1 : 0,
61+
...restProps.style,
4162
}}
4263
alt={restProps.alt || ""}
4364
className={cn(
@@ -48,6 +69,7 @@ export function Img(props: imgElementProps) {
4869

4970
{status !== "loaded" && (
5071
<div
72+
style={restProps.style}
5173
className={cn(
5274
"fade-in-0 absolute inset-0 overflow-hidden transition-opacity duration-300 [&>*]:h-full [&>*]:w-full",
5375
className,

apps/dashboard/src/components/contract-components/publisher/publisher-header.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export const PublisherHeader: React.FC<PublisherHeaderProps> = ({
5454
>
5555
<PublisherAvatar
5656
alt="Publisher avatar"
57-
boxSize={14}
57+
className="size-14"
5858
address={ensQuery.data?.ensName || wallet}
5959
/>
6060
</Link>

apps/dashboard/src/components/explore/publisher/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export const ContractPublisher: React.FC<ContractPublisherProps> = async ({
3030
>
3131
<PublisherAvatar
3232
isPending={false}
33-
boxSize={5}
33+
className="size-5"
3434
address={addressOrEns || ""}
3535
/>
3636

apps/dashboard/src/components/hackathon/Judges.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export const Judges = ({ TRACKING_CATEGORY }: JudgesProps) => {
3535
];
3636

3737
return (
38-
<div className="relative flex flex-col gap-8">
38+
<div className="container relative flex max-w-[1200px] flex-col gap-8">
3939
<Heading size="title.2xl">Judges</Heading>
4040
<SimpleGrid
4141
columns={{ base: 1, lg: 4 }}
@@ -46,10 +46,9 @@ export const Judges = ({ TRACKING_CATEGORY }: JudgesProps) => {
4646
{judges.map((judge) => (
4747
<Flex key={judge.name} flexDir="column" gap={2} alignItems="center">
4848
<MaskedAvatar
49-
boxSize={40}
5049
src={judge.image}
5150
alt={judge.name}
52-
className="rounded-full"
51+
className="size-52"
5352
/>
5453
<Heading size="title.sm" mt={4} textAlign="center">
5554
{judge.name}

apps/dashboard/src/components/hackathon/JudgesEarn.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export const JudgesEarn = () => {
2727
];
2828

2929
return (
30-
<div className="relative flex flex-col gap-8">
30+
<div className="container relative flex max-w-[1200px] flex-col gap-8">
3131
<Heading size="title.2xl">Judges</Heading>
3232
<SimpleGrid
3333
columns={{ base: 1, lg: 4 }}
@@ -38,10 +38,9 @@ export const JudgesEarn = () => {
3838
{judges.map((judge) => (
3939
<Flex key={judge.name} flexDir="column" gap={2} alignItems="center">
4040
<MaskedAvatar
41-
boxSize={40}
4241
src={judge.image}
4342
alt={judge.name}
44-
className="rounded-full"
43+
className="size-52"
4544
/>
4645
<Heading size="title.sm" mt={4} textAlign="center">
4746
{judge.name}

apps/dashboard/src/pages/community/ambassadors.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -599,7 +599,7 @@ const Ambassadors: ThirdwebNextPage = () => {
599599
<MaskedAvatar
600600
src={ambassador.profileImage}
601601
alt={ambassador.name}
602-
boxSize={20}
602+
className="size-20"
603603
/>
604604
<Heading size="title.sm">{ambassador.name}</Heading>
605605
</Flex>
@@ -619,7 +619,7 @@ const Ambassadors: ThirdwebNextPage = () => {
619619
>
620620
<Flex flexDir="column" gap={0}>
621621
<Heading as="h2" size="display.md" mb={4} textAlign="center">
622-
Ready to
622+
Ready to{" "}
623623
<Heading
624624
size="display.md"
625625
as="span"

apps/dashboard/src/pages/profile/[profileAddress].tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ const UserPage: ThirdwebNextPage = (props: UserPageProps) => {
116116
<Flex gap={{ base: 4, md: 8 }} align="center" w="full">
117117
<PublisherAvatar
118118
address={ens.data?.ensName || props.profileAddress}
119-
boxSize={30}
119+
className="size-30"
120120
/>
121121
<Flex direction="column" gap={0}>
122122
<Heading
Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,30 @@
1-
import { SkeletonContainer } from "@/components/ui/skeleton";
1+
import { Img } from "@/components/blocks/Img";
22
import { cn } from "@/lib/utils";
33
import hexagon from "./hexagon.png";
44

55
export interface MaskedAvatarProps {
66
src: string;
77
isPending?: boolean;
88
alt?: string;
9-
boxSize?: number;
109
className?: string;
1110
}
1211

1312
export const MaskedAvatar: React.FC<MaskedAvatarProps> = ({
1413
src,
1514
alt,
16-
boxSize = 12,
1715
className,
1816
}) => {
1917
return (
20-
<SkeletonContainer
21-
className={cn(className, `size-${boxSize}`)}
18+
<Img
19+
className={cn("size-12 object-cover", className)}
20+
src={src}
21+
alt={alt}
2222
style={{
2323
WebkitMaskImage: `url("${hexagon.src}")`,
2424
WebkitMaskSize: "cover",
2525
mask: `url("${hexagon.src}")`,
2626
maskSize: "cover",
2727
}}
28-
skeletonData={undefined}
29-
loadedData={src}
30-
render={(v) => (
31-
// eslint-disable-next-line @next/next/no-img-element
32-
<img className="object-cover" src={v} alt={alt || ""} />
33-
)}
3428
/>
3529
);
3630
};

0 commit comments

Comments
 (0)