Add video feed feature with related components and hooks

- Implemented VideoFeed component to display sightings.
- Created useSightingList and useVideoFeed hooks for data fetching and state management.
- Added AppProviders for context management.
- Updated Dashboard to include VideoFeed.
- Introduced types for sightings in utils/types.tsx.
- Added Header and Footer components for layout.
- Configured React Query for data handling.
This commit is contained in:
2025-12-19 16:04:06 +00:00
parent c38e3439e2
commit 276dcd26ed
18 changed files with 194 additions and 5 deletions

View File

@@ -0,0 +1,12 @@
import VideoFeed from "./components/videoFeed/VideoFeed";
const Dashboard = () => {
return (
<div>
Dashboard
<VideoFeed />
</div>
);
};
export default Dashboard;

View File

@@ -0,0 +1,18 @@
import { useSightingList } from "../../hooks/useSightingList";
const VideoFeed = () => {
const { sightingList } = useSightingList();
console.log(sightingList);
return (
<div>
{sightingList.map((sighting) => (
<div key={sighting.ref} className="border p-2 mb-2">
<div>Ref: {sighting.ref}</div>
<div>vrm: {sighting.vrm}</div>
</div>
))}
</div>
);
};
export default VideoFeed;

View File

@@ -0,0 +1,26 @@
import { useEffect, useRef, useState } from "react";
import { useVideoFeed } from "./useVideoFeed";
import type { SightingType } from "../../../utils/types";
export const useSightingList = () => {
const [sightingList, setSightingList] = useState<SightingType[]>([]);
const { videoFeedQuery } = useVideoFeed();
const latestSighting = videoFeedQuery?.data;
const lastProcessedRef = useRef<number>(-1);
useEffect(() => {
if (!latestSighting || latestSighting.ref === undefined || latestSighting.ref === -1) return;
if (latestSighting.ref !== lastProcessedRef.current) {
lastProcessedRef.current = latestSighting.ref;
// eslint-disable-next-line react-hooks/set-state-in-effect
setSightingList((prevList) => {
if (prevList[0]?.ref === latestSighting.ref) return prevList;
const dedupPrev = prevList.filter((s) => s.ref !== latestSighting.ref);
return [latestSighting, ...dedupPrev].slice(0, 10);
});
}
}, [latestSighting, latestSighting?.ref]);
return { sightingList };
};

View File

@@ -0,0 +1,32 @@
import { useQuery } from "@tanstack/react-query";
import { cambase } from "../../../app/config";
import { useEffect, useRef } from "react";
const fetchVideoFeed = async (refId: number) => {
const response = await fetch(`${cambase}/mergedHistory/sightingSummary?mostRecentRef=${refId}`);
if (!response.ok) {
throw new Error("Network response was not ok");
}
return response.json();
};
export const useVideoFeed = () => {
const currentRefId = useRef<number>(-1);
const videoFeedQuery = useQuery({
queryKey: ["videoFeed"],
queryFn: () => fetchVideoFeed(currentRefId.current),
refetchInterval: 1000,
refetchOnWindowFocus: false,
retry: false,
staleTime: 0,
});
useEffect(() => {
if (videoFeedQuery.data?.ref !== -1) {
currentRefId.current = videoFeedQuery?.data?.ref;
}
}, [videoFeedQuery.data]);
return { videoFeedQuery };
};