108 lines
2.4 KiB
TypeScript
108 lines
2.4 KiB
TypeScript
"use client";
|
|
/* eslint-disable @next/next/no-img-element */
|
|
|
|
import Image from "next/image";
|
|
import type { MouseEventHandler, ReactNode } from "react";
|
|
import { useState } from "react";
|
|
|
|
import {
|
|
isBrowserLocalChatImageSource,
|
|
normalizeChatImageSource,
|
|
} from "@/lib/chat/chat_media_url";
|
|
import { useCachedChatMediaUrl } from "@/lib/chat/use_cached_chat_media_url";
|
|
|
|
export interface ChatMediaImageProps {
|
|
characterId: string;
|
|
messageId?: string;
|
|
remoteUrl: string;
|
|
alt?: string;
|
|
className?: string;
|
|
nativeClassName?: string;
|
|
errorClassName?: string;
|
|
width?: number;
|
|
height?: number;
|
|
fill?: boolean;
|
|
sizes?: string;
|
|
showErrorFallback?: boolean;
|
|
onClick?: MouseEventHandler<HTMLImageElement>;
|
|
children?: ReactNode;
|
|
}
|
|
|
|
export function ChatMediaImage({
|
|
characterId,
|
|
messageId,
|
|
remoteUrl,
|
|
alt = "",
|
|
className,
|
|
nativeClassName,
|
|
errorClassName,
|
|
width = 240,
|
|
height = 240,
|
|
fill = false,
|
|
sizes,
|
|
showErrorFallback = true,
|
|
onClick,
|
|
children,
|
|
}: ChatMediaImageProps) {
|
|
const [errorSrc, setErrorSrc] = useState<string | null>(null);
|
|
const { mediaUrl, isUsingCachedMedia, reportMediaError } =
|
|
useCachedChatMediaUrl({
|
|
characterId,
|
|
messageId,
|
|
remoteUrl,
|
|
kind: "image",
|
|
});
|
|
|
|
const src = normalizeChatImageSource(mediaUrl || remoteUrl);
|
|
const shouldUseNativeImage = isBrowserLocalChatImageSource(src);
|
|
const hasError = errorSrc === src;
|
|
|
|
const handleImageError = () => {
|
|
if (isUsingCachedMedia) {
|
|
reportMediaError();
|
|
return;
|
|
}
|
|
setErrorSrc(src);
|
|
};
|
|
|
|
if (hasError && showErrorFallback) {
|
|
return <div className={errorClassName}>🖼</div>;
|
|
}
|
|
|
|
return (
|
|
<>
|
|
{shouldUseNativeImage ? (
|
|
<img
|
|
className={nativeClassName ?? className}
|
|
src={src}
|
|
alt={alt}
|
|
onError={handleImageError}
|
|
onClick={onClick}
|
|
/>
|
|
) : fill ? (
|
|
<Image
|
|
className={className}
|
|
src={src}
|
|
alt={alt}
|
|
fill
|
|
sizes={sizes}
|
|
onError={handleImageError}
|
|
onClick={onClick}
|
|
/>
|
|
) : (
|
|
<Image
|
|
className={className}
|
|
src={src}
|
|
alt={alt}
|
|
width={width}
|
|
height={height}
|
|
sizes={sizes}
|
|
onError={handleImageError}
|
|
onClick={onClick}
|
|
/>
|
|
)}
|
|
{hasError ? null : children}
|
|
</>
|
|
);
|
|
}
|