2025-12-23 12:41:25 +00:00
|
|
|
import { Stage, Layer, Image, Rect, Text } from "react-konva";
|
2025-12-22 12:19:00 +00:00
|
|
|
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;
|
2025-12-23 10:37:02 +00:00
|
|
|
size: { width: number; height: number };
|
|
|
|
|
modeSetting?: number;
|
|
|
|
|
isModal?: boolean;
|
2025-12-22 12:19:00 +00:00
|
|
|
};
|
|
|
|
|
|
2025-12-23 10:37:02 +00:00
|
|
|
const VideoFeed = ({ mostRecentSighting, isLoading, size, modeSetting, isModal = false }: VideoFeedProps) => {
|
2025-12-22 16:10:34 +00:00
|
|
|
const { state: cameraSettings, dispatch } = useCameraSettingsContext();
|
2025-12-23 10:37:02 +00:00
|
|
|
const contextMode = cameraSettings.mode;
|
|
|
|
|
const [localMode, setLocalMode] = useState(0);
|
|
|
|
|
const mode = isModal ? localMode : contextMode;
|
2025-12-22 12:19:00 +00:00
|
|
|
const { image, plateRect, plateTrack } = useCreateVideoSnapshot(mostRecentSighting);
|
|
|
|
|
|
|
|
|
|
const handleModeChange = (newMode: number) => {
|
2025-12-23 10:37:02 +00:00
|
|
|
if (modeSetting) return;
|
|
|
|
|
|
|
|
|
|
const nextMode = newMode > 2 ? 0 : newMode;
|
|
|
|
|
|
|
|
|
|
if (isModal) {
|
|
|
|
|
setLocalMode(nextMode);
|
|
|
|
|
} else {
|
|
|
|
|
dispatch({ type: "SET_MODE", payload: nextMode });
|
|
|
|
|
}
|
2025-12-22 12:19:00 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const updateSize = () => {
|
2026-01-07 16:18:14 +00:00
|
|
|
const width = window.innerWidth * 0.57;
|
2025-12-22 14:09:30 +00:00
|
|
|
const height = (width * 2) / 3;
|
2025-12-23 10:37:02 +00:00
|
|
|
dispatch({ type: "SET_IMAGE_SIZE", payload: { width, height } });
|
2025-12-22 12:19:00 +00:00
|
|
|
};
|
|
|
|
|
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>
|
2025-12-23 10:37:02 +00:00
|
|
|
{image && (
|
|
|
|
|
<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}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
2025-12-22 12:19:00 +00:00
|
|
|
</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>
|
|
|
|
|
)}
|
2025-12-23 12:41:25 +00:00
|
|
|
<Layer>
|
|
|
|
|
<Rect
|
|
|
|
|
x={5}
|
|
|
|
|
y={0.955 * size.height}
|
|
|
|
|
width={size.width * 0.35}
|
|
|
|
|
height={30}
|
|
|
|
|
fill="rgba(255, 255, 255, 0.45)"
|
|
|
|
|
cornerRadius={5}
|
|
|
|
|
/>
|
|
|
|
|
<Text
|
|
|
|
|
text={`Overlay Mode: ${mode === 0 ? "None" : mode === 1 ? "Plate Highlight" : "Plate Track"}`}
|
|
|
|
|
x={10}
|
|
|
|
|
y={0.96 * size.height}
|
|
|
|
|
fontSize={16}
|
|
|
|
|
fill="#000000"
|
|
|
|
|
/>
|
|
|
|
|
</Layer>
|
2025-12-22 12:19:00 +00:00
|
|
|
</Stage>
|
2025-12-19 16:04:06 +00:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default VideoFeed;
|