Files
hope-events/frontend/src/components/shared/ApiImage.tsx
T
joshuaandClaude Opus 5.5 958a5a5950 Fix frontend lint: links, unused code, quotes, img elements
- Internal links use next/link instead of <a> (no full page reload).
- Remove dead code: RegisterForm's unreachable Yoco checkout branch, the
  unused AttendeeForms on the registration success page, unused Geist
  fonts, and assorted unused variables and catch bindings.
- Escape quotes in JSX text; replace @ts-ignore in apiFetch with a cast.
- Keep the intentional raw <img> elements (data URLs, runtime API host)
  with a documented lint exception; the event editor uses ApiImage.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 11:10:00 +02:00

42 lines
1.8 KiB
TypeScript

"use client";
import React from "react";
import { resolveToApiOrigin } from "@/lib/api";
type Props = {
src?: string | null;
alt?: string;
className?: string;
style?: React.CSSProperties;
};
export function ApiImage({ src, alt = "", className, style }: Props) {
const hasSrc = !!(src || "").trim();
const [resolved, setResolved] = React.useState<string | null>(null);
React.useEffect(() => {
const raw = (src || "").trim();
if (!raw) { setResolved(null); return; }
// Compute in the browser so that localhost origins are swapped to the actual device hostname.
// This has to stay in an effect (not computed during render) because the server-rendered pass
// has no `window`, so resolving synchronously would make the client's first render disagree
// with the server-rendered HTML (a hydration mismatch) — the round trip here is a browser-vs-
// server naming difference, not a network fetch.
const url = resolveToApiOrigin(raw);
setResolved(url);
}, [src]);
// No image was ever provided for this item — render nothing, same as before.
if (!hasSrc) return null;
// A src was provided but the browser-resolved URL isn't ready yet — reserve the same box the
// real <img> will occupy so the surrounding layout (e.g. a card) doesn't collapse to zero height
// and then jump once resolved — same class of flash as an unauthenticated nav link showing too early.
if (!resolved) return <div className={`${className || ""} bg-gray-100 animate-pulse`} style={style} aria-hidden="true" />;
// A plain <img>: the API host is only known at runtime and the images have no fixed size,
// so next/image's optimizer (which needs both up front) doesn't fit here.
// eslint-disable-next-line @next/next/no-img-element
return <img src={resolved} alt={alt} className={className} style={style} />;
}