fix(banners): show website logo as placeholder banner image when no platform-provided imgSrc

- On /banners, if a VerifiedProfile has empty imgSrc (no banner from user/platform), render the site logo (/icon-512.png) as a branded visual placeholder inside linked or static frame.
- Introduced LogoPreview, LogoPlaceholder, LogoImg styled components for contained square logo with padding/bg.
- Removed redundant text-only ProfileLink (the logo image now provides the visual + click target when href present).
- Updated comments and logic in BannerItem.
- Always provides an image slot now for uniform card layout (real banner or site logo).
- Typecheck clean; e2e smoke test for /banners passes.
- Fallback only affects UI rendering (data can still omit imgSrc); matches request for transquinnftw.com/banners.
- Uses existing static icon from manifest/public for the 'logo of the website'.
This commit is contained in:
Natalie 2026-06-23 06:53:41 -04:00
parent 98daf8def0
commit 30d9a082ae

View file

@ -107,22 +107,40 @@ const ImageFrame = styled.div`
}
`;
// Text link shown when a profile URL exists but no badge image has been provided.
const ProfileLink = styled.a`
display: inline-flex;
align-items: center;
gap: 0.4rem;
// Placeholder logo shown on /banners when a verified platform entry has no banner/badge image provided
// (by the platform or in the data). Uses the site's own icon as a branded fallback visual.
const LogoPlaceholder = styled.div`
align-self: flex-start;
font-size: 0.85rem;
font-weight: ${p => p.theme.typography.fontWeight.medium};
color: ${p => p.theme.colors.primary};
border-radius: ${p => p.theme.borderRadius.sm};
overflow: hidden;
background: ${p => p.theme.colors.background.primary};
padding: 1rem;
line-height: 0;
`;
const LogoPreview = styled.a`
display: block;
align-self: flex-start;
border-radius: ${p => p.theme.borderRadius.sm};
overflow: hidden;
background: ${p => p.theme.colors.background.primary};
padding: 1rem;
line-height: 0;
text-decoration: none;
transition: opacity 150ms;
&:hover {
text-decoration: underline;
opacity: 0.9;
}
`;
const LogoImg = styled.img`
width: 120px;
height: 120px;
object-fit: contain;
display: block;
`;
function BannerItem({ profile }: { profile: VerifiedProfile }): ReactNode {
const trackOutlink = useOutlinkTracker();
@ -134,6 +152,10 @@ function BannerItem({ profile }: { profile: VerifiedProfile }): ReactNode {
const hasLink = profile.href.trim().length > 0;
const hasDescription = profile.description.trim().length > 0;
// Fallback to the website's own logo (icon-512.png) when the platform/data provides no banner image.
const bannerSrc = hasImage ? profile.imgSrc : '/icon-512.png';
const bannerAlt = hasImage ? profile.imgAlt : 'Quinn logo';
return (
<MagicCard as={BannerCard}>
<PlatformRow>
@ -144,34 +166,39 @@ function BannerItem({ profile }: { profile: VerifiedProfile }): ReactNode {
</VerifiedBadge>
</PlatformRow>
{hasImage && (hasLink ? (
<BannerPreview
{hasImage ? (
hasLink ? (
<BannerPreview
href={profile.href}
rel="noopener"
target="_blank"
title={bannerAlt}
onClick={handleBannerClick}
>
<img src={bannerSrc} alt={bannerAlt} />
</BannerPreview>
) : (
<ImageFrame>
<img src={bannerSrc} alt={bannerAlt} />
</ImageFrame>
)
) : hasLink ? (
<LogoPreview
href={profile.href}
rel="noopener"
target="_blank"
title={profile.imgAlt}
title={bannerAlt}
onClick={handleBannerClick}
>
<img src={profile.imgSrc} alt={profile.imgAlt} />
</BannerPreview>
<LogoImg src={bannerSrc} alt={bannerAlt} />
</LogoPreview>
) : (
<ImageFrame>
<img src={profile.imgSrc} alt={profile.imgAlt} />
</ImageFrame>
))}
<LogoPlaceholder>
<LogoImg src={bannerSrc} alt={bannerAlt} />
</LogoPlaceholder>
)}
{hasDescription && <Description>{profile.description}</Description>}
{!hasImage && hasLink && (
<ProfileLink
href={profile.href}
rel="noopener"
target="_blank"
onClick={handleBannerClick}
>
View verified profile
</ProfileLink>
)}
</MagicCard>
);
}