Files
Nitro-V3/src/common/layout/LayoutPetImageView.tsx
T
simoleo89 f57266af03 Update 3 IGetImageListener.imageReady call sites to v8 single-arg signature
IGetImageListener.imageReady(result: IImageResult) takes a single
IImageResult object (with .id, .data, .image), but three call sites in
the client still used the old 3-arg destructure '(id, texture, image)
=> ...'. The renderer's RoomEngine.ts already passes
'new ImageResult(...)' to the listener, so the runtime payload matches
the new contract; the old call-site shape just type-errored.

Migrated:
- LayoutPetImageView (pet thumbnail loader)
- LayoutRoomObjectImageView (furniture thumbnail loader)
- useFurniturePresentWidget (gift box image generator)

Also tightened imageFailed handlers from 'imageFailed: null' to a
proper no-op arrow — the interface requires a callback.
2026-05-11 21:34:08 +02:00

126 lines
3.7 KiB
TypeScript

import { GetRoomEngine, IPetCustomPart, PetFigureData, TextureUtils, Vector3d } from '@nitrots/nitro-renderer';
import { CSSProperties, FC, useEffect, useMemo, useRef, useState } from 'react';
import { Base, BaseProps } from '../Base';
interface LayoutPetImageViewProps extends BaseProps<HTMLDivElement>
{
figure?: string;
typeId?: number;
paletteId?: number;
petColor?: number;
customParts?: IPetCustomPart[];
posture?: string;
headOnly?: boolean;
direction?: number;
scale?: number;
}
export const LayoutPetImageView: FC<LayoutPetImageViewProps> = props =>
{
const { figure = '', typeId = -1, paletteId = -1, petColor = 0xFFFFFF, customParts = [], posture = 'std', headOnly = false, direction = 0, scale = 1, style = {}, ...rest } = props;
const [ petUrl, setPetUrl ] = useState<string>(null);
const [ width, setWidth ] = useState(0);
const [ height, setHeight ] = useState(0);
const isDisposed = useRef(false);
const getStyle = useMemo(() =>
{
let newStyle: CSSProperties = {};
if(petUrl && petUrl.length) newStyle.backgroundImage = `url(${ petUrl })`;
if(scale !== 1)
{
newStyle.transform = `scale(${ scale })`;
if(!(scale % 1)) newStyle.imageRendering = 'pixelated';
}
newStyle.width = width;
newStyle.height = height;
if(Object.keys(style).length) newStyle = { ...newStyle, ...style };
return newStyle;
}, [ petUrl, scale, style, width, height ]);
useEffect(() =>
{
let url = null;
let petTypeId = typeId;
let petPaletteId = paletteId;
let petColor1 = petColor;
let petCustomParts: IPetCustomPart[] = customParts;
let petHeadOnly = headOnly;
if(figure && figure.length)
{
const petFigureData = new PetFigureData(figure);
petTypeId = petFigureData.typeId;
petPaletteId = petFigureData.paletteId;
petColor1 = petFigureData.color;
petCustomParts = petFigureData.customParts;
}
if(petTypeId === 16) petHeadOnly = false;
const imageResult = GetRoomEngine().getRoomObjectPetImage(petTypeId, petPaletteId, petColor1, new Vector3d((direction * 45)), 64, {
imageReady: async (result) =>
{
if(isDisposed.current) return;
const { image, data: texture } = result;
if(image)
{
setPetUrl(image.src);
setWidth(image.width);
setHeight(image.height);
}
else if(texture)
{
setPetUrl(await TextureUtils.generateImageUrl(texture));
setWidth(texture.width);
setHeight(texture.height);
}
},
imageFailed: () =>
{
// no-op
}
}, petHeadOnly, 0, petCustomParts, posture);
if(imageResult)
{
(async () =>
{
const image = await imageResult.getImage();
if(image)
{
setPetUrl(image.src);
setWidth(image.width);
setHeight(image.height);
}
})();
}
}, [ figure, typeId, paletteId, petColor, customParts, posture, headOnly, direction ]);
useEffect(() =>
{
isDisposed.current = false;
return () =>
{
isDisposed.current = true;
};
}, []);
const url = `url('${ petUrl }')`;
return <Base classNames={ [ 'pet-image' ] } style={ getStyle } { ...rest } />;
};