2025-12-22 12:19:00 +00:00
|
|
|
import { Stage, Layer, Image, Rect } from "react-konva";
|
|
|
|
|
import type { SightingType } from "../../../../utils/types";
|
|
|
|
|
import { useCreateVideoSnapshot } from "../../hooks/useCreateVideoSnapshot";
|
|
|
|
|
import { useEffect, useState } from "react";
|
2025-12-22 16:10:34 +00:00
|
|
|
import { useCameraSettingsContext } from "../../../../app/context/CameraSettingsContext";
|
2025-12-22 12:19:00 +00:00
|
|
|
|
|
|
|
|
type VideoFeedProps = {
|
|
|
|
|
mostRecentSighting: SightingType;
|
|
|
|
|
isLoading: boolean;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const VideoFeed = ({ mostRecentSighting, isLoading }: VideoFeedProps) => {
|
2025-12-22 16:10:34 +00:00
|
|
|
const { state: cameraSettings, dispatch } = useCameraSettingsContext();
|
|
|
|
|
|
|
|
|
|
const mode = cameraSettings.mode;
|
2025-12-22 12:19:00 +00:00
|
|
|
const [size, setSize] = useState<{ width: number; height: number }>({ width: 1280, height: 960 });
|
2025-12-22 16:10:34 +00:00
|
|
|
|
2025-12-22 12:19:00 +00:00
|
|
|
const { image, plateRect, plateTrack } = useCreateVideoSnapshot(mostRecentSighting);
|
|
|
|
|
|
|
|
|
|
const handleModeChange = (newMode: number) => {
|
2025-12-22 16:10:34 +00:00
|
|
|
if (newMode > 2) dispatch({ type: "SET_MODE", payload: 0 });
|
|
|
|
|
else dispatch({ type: "SET_MODE", payload: newMode });
|
2025-12-22 12:19:00 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const updateSize = () => {
|
2025-12-22 14:09:30 +00:00
|
|
|
const width = window.innerWidth * 0.48;
|
|
|
|
|
const height = (width * 2) / 3;
|
2025-12-22 12:19:00 +00:00
|
|
|
setSize({ width, height });
|
|
|
|
|
};
|
|
|
|
|
updateSize();
|
|
|
|
|
window.addEventListener("resize", updateSize);
|
|
|
|
|
return () => window.removeEventListener("resize", updateSize);
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
if (isLoading) return <>Loading...</>;
|
2025-12-19 16:04:06 +00:00
|
|
|
|
|
|
|
|
return (
|
2025-12-22 12:19:00 +00:00
|
|
|
<div className="w-[70%] mt-[2%]">
|
|
|
|
|
<Stage width={size.width} height={size.height} onClick={() => handleModeChange(mode + 1)}>
|
|
|
|
|
<Layer>
|
|
|
|
|
<Image
|
|
|
|
|
image={image}
|
|
|
|
|
height={size.height}
|
|
|
|
|
width={size.width}
|
|
|
|
|
onMouseEnter={(e) => {
|
|
|
|
|
const container = e.target.getStage()?.container();
|
|
|
|
|
if (container) container.style.cursor = "pointer";
|
|
|
|
|
}}
|
|
|
|
|
onMouseLeave={(e) => {
|
|
|
|
|
const container = e.target.getStage()?.container();
|
|
|
|
|
if (container) container.style.cursor = "default";
|
|
|
|
|
}}
|
|
|
|
|
cornerRadius={10}
|
|
|
|
|
/>
|
|
|
|
|
</Layer>
|
|
|
|
|
{plateRect && mode === 1 && (
|
|
|
|
|
<Layer>
|
|
|
|
|
<Rect
|
|
|
|
|
x={plateRect?.[0] * size.width}
|
|
|
|
|
y={plateRect?.[1] * size.height}
|
|
|
|
|
width={plateRect?.[2] * size.width}
|
|
|
|
|
height={plateRect?.[3] * size.height}
|
|
|
|
|
stroke="blue"
|
|
|
|
|
strokeWidth={4}
|
2025-12-22 14:09:30 +00:00
|
|
|
cornerRadius={5}
|
2025-12-22 12:19:00 +00:00
|
|
|
/>
|
|
|
|
|
</Layer>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{plateTrack && mode === 2 && (
|
|
|
|
|
<Layer>
|
|
|
|
|
{plateTrack.map((rect, index) => (
|
|
|
|
|
<Rect
|
|
|
|
|
key={index}
|
|
|
|
|
x={rect[0] * size.width}
|
|
|
|
|
y={rect[1] * size.height}
|
|
|
|
|
width={rect[2] * size.width}
|
|
|
|
|
height={rect[3] * size.height}
|
|
|
|
|
stroke="red"
|
|
|
|
|
strokeWidth={2}
|
2025-12-22 14:09:30 +00:00
|
|
|
cornerRadius={5}
|
2025-12-22 12:19:00 +00:00
|
|
|
/>
|
|
|
|
|
))}
|
|
|
|
|
</Layer>
|
|
|
|
|
)}
|
|
|
|
|
</Stage>
|
2025-12-19 16:04:06 +00:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default VideoFeed;
|