mirror of
https://github.com/blakeblackshear/frigate.git
synced 2024-11-21 19:07:46 +01:00
Remove paging (#10310)
* Remove paging and use reviewed as an internal filter * Fix new reviews not going away
This commit is contained in:
parent
8bfc0c98dc
commit
b2931bcaa9
@ -26,7 +26,7 @@ def review():
|
|||||||
cameras = request.args.get("cameras", "all")
|
cameras = request.args.get("cameras", "all")
|
||||||
labels = request.args.get("labels", "all")
|
labels = request.args.get("labels", "all")
|
||||||
reviewed = request.args.get("reviewed", type=int, default=0)
|
reviewed = request.args.get("reviewed", type=int, default=0)
|
||||||
limit = request.args.get("limit", 100)
|
limit = request.args.get("limit", type=int, default=None)
|
||||||
severity = request.args.get("severity", None)
|
severity = request.args.get("severity", None)
|
||||||
|
|
||||||
before = request.args.get("before", type=float, default=datetime.now().timestamp())
|
before = request.args.get("before", type=float, default=datetime.now().timestamp())
|
||||||
|
@ -8,19 +8,22 @@ type NewReviewDataProps = {
|
|||||||
className: string;
|
className: string;
|
||||||
contentRef: MutableRefObject<HTMLDivElement | null>;
|
contentRef: MutableRefObject<HTMLDivElement | null>;
|
||||||
severity: ReviewSeverity;
|
severity: ReviewSeverity;
|
||||||
|
hasUpdate: boolean;
|
||||||
|
setHasUpdate: (update: boolean) => void;
|
||||||
pullLatestData: () => void;
|
pullLatestData: () => void;
|
||||||
};
|
};
|
||||||
export default function NewReviewData({
|
export default function NewReviewData({
|
||||||
className,
|
className,
|
||||||
contentRef,
|
contentRef,
|
||||||
severity,
|
severity,
|
||||||
|
hasUpdate,
|
||||||
|
setHasUpdate,
|
||||||
pullLatestData,
|
pullLatestData,
|
||||||
}: NewReviewDataProps) {
|
}: NewReviewDataProps) {
|
||||||
const { payload: review } = useFrigateReviews();
|
const { payload: review } = useFrigateReviews();
|
||||||
|
|
||||||
const startCheckTs = useMemo(() => Date.now() / 1000, []);
|
const startCheckTs = useMemo(() => Date.now() / 1000, []);
|
||||||
const [reviewTs, setReviewTs] = useState(startCheckTs);
|
const [reviewTs, setReviewTs] = useState(startCheckTs);
|
||||||
const [hasUpdate, setHasUpdate] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!review) {
|
if (!review) {
|
||||||
@ -36,7 +39,7 @@ export default function NewReviewData({
|
|||||||
if (reviewTs > startCheckTs) {
|
if (reviewTs > startCheckTs) {
|
||||||
setHasUpdate(true);
|
setHasUpdate(true);
|
||||||
}
|
}
|
||||||
}, [startCheckTs, reviewTs]);
|
}, [startCheckTs, reviewTs, setHasUpdate]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
|
@ -19,9 +19,6 @@ import axios from "axios";
|
|||||||
import { useCallback, useMemo, useState } from "react";
|
import { useCallback, useMemo, useState } from "react";
|
||||||
import { isMobile } from "react-device-detect";
|
import { isMobile } from "react-device-detect";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import useSWRInfinite from "swr/infinite";
|
|
||||||
|
|
||||||
const API_LIMIT = 100;
|
|
||||||
|
|
||||||
export default function Events() {
|
export default function Events() {
|
||||||
const { data: config } = useSWR<FrigateConfig>("config");
|
const { data: config } = useSWR<FrigateConfig>("config");
|
||||||
@ -41,7 +38,6 @@ export default function Events() {
|
|||||||
useApiFilter<ReviewFilter>();
|
useApiFilter<ReviewFilter>();
|
||||||
|
|
||||||
const onUpdateFilter = useCallback((newFilter: ReviewFilter) => {
|
const onUpdateFilter = useCallback((newFilter: ReviewFilter) => {
|
||||||
setSize(1);
|
|
||||||
setReviewFilter(newFilter);
|
setReviewFilter(newFilter);
|
||||||
// we don't want this updating
|
// we don't want this updating
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
@ -69,53 +65,25 @@ export default function Events() {
|
|||||||
return axios.get(path, { params }).then((res) => res.data);
|
return axios.get(path, { params }).then((res) => res.data);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const getKey = useCallback(
|
const getKey = useCallback(() => {
|
||||||
(index: number, prevData: ReviewSegment[]) => {
|
const params = {
|
||||||
if (index > 0) {
|
cameras: reviewSearchParams["cameras"],
|
||||||
const lastDate = prevData[prevData.length - 1].start_time;
|
labels: reviewSearchParams["labels"],
|
||||||
reviewSearchParams;
|
reviewed: 1,
|
||||||
const pagedParams = {
|
before: reviewSearchParams["before"] || last24Hours.before,
|
||||||
cameras: reviewSearchParams["cameras"],
|
after: reviewSearchParams["after"] || last24Hours.after,
|
||||||
labels: reviewSearchParams["labels"],
|
};
|
||||||
reviewed: reviewSearchParams["showReviewed"],
|
return ["review", params];
|
||||||
before: lastDate,
|
}, [reviewSearchParams, last24Hours]);
|
||||||
after: reviewSearchParams["after"] || last24Hours.after,
|
|
||||||
limit: API_LIMIT,
|
|
||||||
};
|
|
||||||
return ["review", pagedParams];
|
|
||||||
}
|
|
||||||
|
|
||||||
const params = {
|
const { data: reviews, mutate: updateSegments } = useSWR<ReviewSegment[]>(
|
||||||
cameras: reviewSearchParams["cameras"],
|
getKey,
|
||||||
labels: reviewSearchParams["labels"],
|
reviewSegmentFetcher,
|
||||||
reviewed: reviewSearchParams["showReviewed"],
|
{
|
||||||
limit: API_LIMIT,
|
revalidateOnFocus: false,
|
||||||
before: reviewSearchParams["before"] || last24Hours.before,
|
|
||||||
after: reviewSearchParams["after"] || last24Hours.after,
|
|
||||||
};
|
|
||||||
return ["review", params];
|
|
||||||
},
|
},
|
||||||
[reviewSearchParams, last24Hours],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const {
|
|
||||||
data: reviewPages,
|
|
||||||
mutate: updateSegments,
|
|
||||||
size,
|
|
||||||
setSize,
|
|
||||||
isValidating,
|
|
||||||
} = useSWRInfinite<ReviewSegment[]>(getKey, reviewSegmentFetcher, {
|
|
||||||
revalidateOnFocus: false,
|
|
||||||
persistSize: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const isDone = useMemo(
|
|
||||||
() => (reviewPages?.at(-1)?.length ?? 0) < API_LIMIT,
|
|
||||||
[reviewPages],
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadNextPage = useCallback(() => setSize(size + 1), [size, setSize]);
|
|
||||||
|
|
||||||
// review summary
|
// review summary
|
||||||
|
|
||||||
const { data: reviewSummary, mutate: updateSummary } = useSWR<ReviewSummary>([
|
const { data: reviewSummary, mutate: updateSummary } = useSWR<ReviewSummary>([
|
||||||
@ -136,24 +104,20 @@ export default function Events() {
|
|||||||
// preview videos
|
// preview videos
|
||||||
|
|
||||||
const previewTimes = useMemo(() => {
|
const previewTimes = useMemo(() => {
|
||||||
if (
|
if (!reviews || reviews.length == 0) {
|
||||||
!reviewPages ||
|
|
||||||
reviewPages.length == 0 ||
|
|
||||||
reviewPages.at(-1)?.length == 0
|
|
||||||
) {
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const startDate = new Date();
|
const startDate = new Date();
|
||||||
startDate.setMinutes(0, 0, 0);
|
startDate.setMinutes(0, 0, 0);
|
||||||
|
|
||||||
const endDate = new Date(reviewPages.at(-1)?.at(-1)?.end_time || 0);
|
const endDate = new Date(reviews.at(-1)?.end_time || 0);
|
||||||
endDate.setHours(0, 0, 0, 0);
|
endDate.setHours(0, 0, 0, 0);
|
||||||
return {
|
return {
|
||||||
start: startDate.getTime() / 1000,
|
start: startDate.getTime() / 1000,
|
||||||
end: endDate.getTime() / 1000,
|
end: endDate.getTime() / 1000,
|
||||||
};
|
};
|
||||||
}, [reviewPages]);
|
}, [reviews]);
|
||||||
const { data: allPreviews } = useSWR<Preview[]>(
|
const { data: allPreviews } = useSWR<Preview[]>(
|
||||||
previewTimes
|
previewTimes
|
||||||
? `preview/all/start/${previewTimes.start}/end/${previewTimes.end}`
|
? `preview/all/start/${previewTimes.start}/end/${previewTimes.end}`
|
||||||
@ -169,28 +133,21 @@ export default function Events() {
|
|||||||
|
|
||||||
if (resp.status == 200) {
|
if (resp.status == 200) {
|
||||||
updateSegments(
|
updateSegments(
|
||||||
(data: ReviewSegment[][] | undefined) => {
|
(data: ReviewSegment[] | undefined) => {
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
const newData: ReviewSegment[][] = [];
|
const reviewIndex = data.findIndex((item) => item.id == review.id);
|
||||||
|
if (reviewIndex == -1) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
data.forEach((page) => {
|
const newData = [
|
||||||
const reviewIndex = page.findIndex(
|
...data.slice(0, reviewIndex),
|
||||||
(item) => item.id == review.id,
|
{ ...data[reviewIndex], has_been_reviewed: true },
|
||||||
);
|
...data.slice(reviewIndex + 1),
|
||||||
|
];
|
||||||
if (reviewIndex == -1) {
|
|
||||||
newData.push([...page]);
|
|
||||||
} else {
|
|
||||||
newData.push([
|
|
||||||
...page.slice(0, reviewIndex),
|
|
||||||
{ ...page[reviewIndex], has_been_reviewed: true },
|
|
||||||
...page.slice(reviewIndex + 1),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return newData;
|
return newData;
|
||||||
},
|
},
|
||||||
@ -252,7 +209,7 @@ export default function Events() {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!reviewPages) {
|
if (!reviews) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,8 +219,6 @@ export default function Events() {
|
|||||||
|
|
||||||
const allCameras = reviewFilter?.cameras ?? Object.keys(config.cameras);
|
const allCameras = reviewFilter?.cameras ?? Object.keys(config.cameras);
|
||||||
|
|
||||||
const allReviews = reviewPages.flat();
|
|
||||||
|
|
||||||
if (selectedReviewId.startsWith("motion")) {
|
if (selectedReviewId.startsWith("motion")) {
|
||||||
const motionData = selectedReviewId.split(",");
|
const motionData = selectedReviewId.split(",");
|
||||||
// format is motion,camera,start_time
|
// format is motion,camera,start_time
|
||||||
@ -272,15 +227,13 @@ export default function Events() {
|
|||||||
severity: "significant_motion" as ReviewSeverity,
|
severity: "significant_motion" as ReviewSeverity,
|
||||||
start_time: parseFloat(motionData[2]),
|
start_time: parseFloat(motionData[2]),
|
||||||
allCameras: allCameras,
|
allCameras: allCameras,
|
||||||
cameraSegments: allReviews.filter((seg) =>
|
cameraSegments: reviews.filter((seg) =>
|
||||||
allCameras.includes(seg.camera),
|
allCameras.includes(seg.camera),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedReview = allReviews.find(
|
const selectedReview = reviews.find((item) => item.id == selectedReviewId);
|
||||||
(item) => item.id == selectedReviewId,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!selectedReview) {
|
if (!selectedReview) {
|
||||||
return undefined;
|
return undefined;
|
||||||
@ -296,14 +249,12 @@ export default function Events() {
|
|||||||
severity: selectedReview.severity,
|
severity: selectedReview.severity,
|
||||||
start_time: selectedReview.start_time,
|
start_time: selectedReview.start_time,
|
||||||
allCameras: allCameras,
|
allCameras: allCameras,
|
||||||
cameraSegments: allReviews.filter((seg) =>
|
cameraSegments: reviews.filter((seg) => allCameras.includes(seg.camera)),
|
||||||
allCameras.includes(seg.camera),
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// previews will not update after item is selected
|
// previews will not update after item is selected
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [selectedReviewId, reviewPages]);
|
}, [selectedReviewId, reviews]);
|
||||||
|
|
||||||
if (!timezone) {
|
if (!timezone) {
|
||||||
return <ActivityIndicator />;
|
return <ActivityIndicator />;
|
||||||
@ -335,16 +286,13 @@ export default function Events() {
|
|||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<EventView
|
<EventView
|
||||||
reviewPages={reviewPages}
|
reviews={reviews}
|
||||||
reviewSummary={reviewSummary}
|
reviewSummary={reviewSummary}
|
||||||
relevantPreviews={allPreviews}
|
relevantPreviews={allPreviews}
|
||||||
timeRange={selectedTimeRange}
|
timeRange={selectedTimeRange}
|
||||||
reachedEnd={isDone}
|
|
||||||
isValidating={isValidating}
|
|
||||||
filter={reviewFilter}
|
filter={reviewFilter}
|
||||||
severity={severity ?? "alert"}
|
severity={severity ?? "alert"}
|
||||||
setSeverity={setSeverity}
|
setSeverity={setSeverity}
|
||||||
loadNextPage={onLoadNextPage}
|
|
||||||
markItemAsReviewed={markItemAsReviewed}
|
markItemAsReviewed={markItemAsReviewed}
|
||||||
onOpenReview={setSelectedReviewId}
|
onOpenReview={setSelectedReviewId}
|
||||||
pullLatestData={reloadData}
|
pullLatestData={reloadData}
|
||||||
|
@ -38,32 +38,26 @@ import MotionReviewTimeline from "@/components/timeline/MotionReviewTimeline";
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
type EventViewProps = {
|
type EventViewProps = {
|
||||||
reviewPages?: ReviewSegment[][];
|
reviews?: ReviewSegment[];
|
||||||
reviewSummary?: ReviewSummary;
|
reviewSummary?: ReviewSummary;
|
||||||
relevantPreviews?: Preview[];
|
relevantPreviews?: Preview[];
|
||||||
timeRange: { before: number; after: number };
|
timeRange: { before: number; after: number };
|
||||||
reachedEnd: boolean;
|
|
||||||
isValidating: boolean;
|
|
||||||
filter?: ReviewFilter;
|
filter?: ReviewFilter;
|
||||||
severity: ReviewSeverity;
|
severity: ReviewSeverity;
|
||||||
setSeverity: (severity: ReviewSeverity) => void;
|
setSeverity: (severity: ReviewSeverity) => void;
|
||||||
loadNextPage: () => void;
|
|
||||||
markItemAsReviewed: (review: ReviewSegment) => void;
|
markItemAsReviewed: (review: ReviewSegment) => void;
|
||||||
onOpenReview: (reviewId: string) => void;
|
onOpenReview: (reviewId: string) => void;
|
||||||
pullLatestData: () => void;
|
pullLatestData: () => void;
|
||||||
updateFilter: (filter: ReviewFilter) => void;
|
updateFilter: (filter: ReviewFilter) => void;
|
||||||
};
|
};
|
||||||
export default function EventView({
|
export default function EventView({
|
||||||
reviewPages,
|
reviews,
|
||||||
reviewSummary,
|
reviewSummary,
|
||||||
relevantPreviews,
|
relevantPreviews,
|
||||||
timeRange,
|
timeRange,
|
||||||
reachedEnd,
|
|
||||||
isValidating,
|
|
||||||
filter,
|
filter,
|
||||||
severity,
|
severity,
|
||||||
setSeverity,
|
setSeverity,
|
||||||
loadNextPage,
|
|
||||||
markItemAsReviewed,
|
markItemAsReviewed,
|
||||||
onOpenReview,
|
onOpenReview,
|
||||||
pullLatestData,
|
pullLatestData,
|
||||||
@ -115,22 +109,20 @@ export default function EventView({
|
|||||||
const detections: ReviewSegment[] = [];
|
const detections: ReviewSegment[] = [];
|
||||||
const motion: ReviewSegment[] = [];
|
const motion: ReviewSegment[] = [];
|
||||||
|
|
||||||
reviewPages?.forEach((page) => {
|
reviews?.forEach((segment) => {
|
||||||
page.forEach((segment) => {
|
all.push(segment);
|
||||||
all.push(segment);
|
|
||||||
|
|
||||||
switch (segment.severity) {
|
switch (segment.severity) {
|
||||||
case "alert":
|
case "alert":
|
||||||
alerts.push(segment);
|
alerts.push(segment);
|
||||||
break;
|
break;
|
||||||
case "detection":
|
case "detection":
|
||||||
detections.push(segment);
|
detections.push(segment);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
motion.push(segment);
|
motion.push(segment);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -139,7 +131,7 @@ export default function EventView({
|
|||||||
detection: detections,
|
detection: detections,
|
||||||
significant_motion: motion,
|
significant_motion: motion,
|
||||||
};
|
};
|
||||||
}, [reviewPages]);
|
}, [reviews]);
|
||||||
|
|
||||||
const currentItems = useMemo(() => {
|
const currentItems = useMemo(() => {
|
||||||
const current = reviewItems[severity];
|
const current = reviewItems[severity];
|
||||||
@ -151,8 +143,6 @@ export default function EventView({
|
|||||||
return current;
|
return current;
|
||||||
}, [reviewItems, severity]);
|
}, [reviewItems, severity]);
|
||||||
|
|
||||||
const pagingObserver = useRef<IntersectionObserver | null>(null);
|
|
||||||
|
|
||||||
// review interaction
|
// review interaction
|
||||||
|
|
||||||
const [selectedReviews, setSelectedReviews] = useState<string[]>([]);
|
const [selectedReviews, setSelectedReviews] = useState<string[]>([]);
|
||||||
@ -267,14 +257,11 @@ export default function EventView({
|
|||||||
currentItems={currentItems}
|
currentItems={currentItems}
|
||||||
reviewItems={reviewItems}
|
reviewItems={reviewItems}
|
||||||
relevantPreviews={relevantPreviews}
|
relevantPreviews={relevantPreviews}
|
||||||
pagingObserver={pagingObserver}
|
|
||||||
selectedReviews={selectedReviews}
|
selectedReviews={selectedReviews}
|
||||||
|
itemsToReview={reviewCounts[severity]}
|
||||||
severity={severity}
|
severity={severity}
|
||||||
filter={filter}
|
filter={filter}
|
||||||
isValidating={isValidating}
|
|
||||||
reachedEnd={reachedEnd}
|
|
||||||
timeRange={timeRange}
|
timeRange={timeRange}
|
||||||
loadNextPage={loadNextPage}
|
|
||||||
markItemAsReviewed={markItemAsReviewed}
|
markItemAsReviewed={markItemAsReviewed}
|
||||||
onSelectReview={onSelectReview}
|
onSelectReview={onSelectReview}
|
||||||
pullLatestData={pullLatestData}
|
pullLatestData={pullLatestData}
|
||||||
@ -306,14 +293,10 @@ type DetectionReviewProps = {
|
|||||||
};
|
};
|
||||||
itemsToReview?: number;
|
itemsToReview?: number;
|
||||||
relevantPreviews?: Preview[];
|
relevantPreviews?: Preview[];
|
||||||
pagingObserver: MutableRefObject<IntersectionObserver | null>;
|
|
||||||
selectedReviews: string[];
|
selectedReviews: string[];
|
||||||
severity: ReviewSeverity;
|
severity: ReviewSeverity;
|
||||||
filter?: ReviewFilter;
|
filter?: ReviewFilter;
|
||||||
isValidating: boolean;
|
|
||||||
reachedEnd: boolean;
|
|
||||||
timeRange: { before: number; after: number };
|
timeRange: { before: number; after: number };
|
||||||
loadNextPage: () => void;
|
|
||||||
markItemAsReviewed: (review: ReviewSegment) => void;
|
markItemAsReviewed: (review: ReviewSegment) => void;
|
||||||
onSelectReview: (id: string, ctrl: boolean) => void;
|
onSelectReview: (id: string, ctrl: boolean) => void;
|
||||||
pullLatestData: () => void;
|
pullLatestData: () => void;
|
||||||
@ -324,14 +307,10 @@ function DetectionReview({
|
|||||||
reviewItems,
|
reviewItems,
|
||||||
itemsToReview,
|
itemsToReview,
|
||||||
relevantPreviews,
|
relevantPreviews,
|
||||||
pagingObserver,
|
|
||||||
selectedReviews,
|
selectedReviews,
|
||||||
severity,
|
severity,
|
||||||
filter,
|
filter,
|
||||||
isValidating,
|
|
||||||
reachedEnd,
|
|
||||||
timeRange,
|
timeRange,
|
||||||
loadNextPage,
|
|
||||||
markItemAsReviewed,
|
markItemAsReviewed,
|
||||||
onSelectReview,
|
onSelectReview,
|
||||||
pullLatestData,
|
pullLatestData,
|
||||||
@ -344,23 +323,7 @@ function DetectionReview({
|
|||||||
|
|
||||||
// review interaction
|
// review interaction
|
||||||
|
|
||||||
const lastReviewRef = useCallback(
|
const [hasUpdate, setHasUpdate] = useState(false);
|
||||||
(node: HTMLElement | null) => {
|
|
||||||
if (isValidating) return;
|
|
||||||
if (pagingObserver.current) pagingObserver.current.disconnect();
|
|
||||||
try {
|
|
||||||
pagingObserver.current = new IntersectionObserver((entries) => {
|
|
||||||
if (entries[0].isIntersecting && !reachedEnd) {
|
|
||||||
loadNextPage();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (node) pagingObserver.current.observe(node);
|
|
||||||
} catch (e) {
|
|
||||||
// no op
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[isValidating, pagingObserver, reachedEnd, loadNextPage],
|
|
||||||
);
|
|
||||||
|
|
||||||
const markAllReviewed = useCallback(async () => {
|
const markAllReviewed = useCallback(async () => {
|
||||||
if (!currentItems) {
|
if (!currentItems) {
|
||||||
@ -370,6 +333,7 @@ function DetectionReview({
|
|||||||
await axios.post(`reviews/viewed`, {
|
await axios.post(`reviews/viewed`, {
|
||||||
ids: currentItems?.map((seg) => seg.id),
|
ids: currentItems?.map((seg) => seg.id),
|
||||||
});
|
});
|
||||||
|
setHasUpdate(false);
|
||||||
pullLatestData();
|
pullLatestData();
|
||||||
}, [currentItems, pullLatestData]);
|
}, [currentItems, pullLatestData]);
|
||||||
|
|
||||||
@ -463,11 +427,13 @@ function DetectionReview({
|
|||||||
className="absolute w-full z-30"
|
className="absolute w-full z-30"
|
||||||
contentRef={contentRef}
|
contentRef={contentRef}
|
||||||
severity={severity}
|
severity={severity}
|
||||||
|
hasUpdate={hasUpdate}
|
||||||
|
setHasUpdate={setHasUpdate}
|
||||||
pullLatestData={pullLatestData}
|
pullLatestData={pullLatestData}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{(itemsToReview == 0 || (currentItems == null && !isValidating)) && (
|
{itemsToReview == 0 && (
|
||||||
<div className="size-full flex flex-col justify-center items-center">
|
<div className="size-full flex flex-col justify-center items-center">
|
||||||
<LuFolderCheck className="size-16" />
|
<LuFolderCheck className="size-16" />
|
||||||
There are no {severity.replace(/_/g, " ")} items to review
|
There are no {severity.replace(/_/g, " ")} items to review
|
||||||
@ -478,15 +444,18 @@ function DetectionReview({
|
|||||||
className="w-full m-2 grid sm:grid-cols-2 md:grid-cols-3 3xl:grid-cols-4 gap-2 md:gap-4"
|
className="w-full m-2 grid sm:grid-cols-2 md:grid-cols-3 3xl:grid-cols-4 gap-2 md:gap-4"
|
||||||
ref={contentRef}
|
ref={contentRef}
|
||||||
>
|
>
|
||||||
{currentItems ? (
|
{currentItems &&
|
||||||
currentItems.map((value, segIdx) => {
|
currentItems.map((value) => {
|
||||||
const lastRow = segIdx == currentItems.length - 1;
|
if (value.has_been_reviewed && filter?.showReviewed != 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const selected = selectedReviews.includes(value.id);
|
const selected = selectedReviews.includes(value.id);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={value.id}
|
key={value.id}
|
||||||
ref={lastRow ? lastReviewRef : minimapRef}
|
ref={minimapRef}
|
||||||
data-start={value.start_time}
|
data-start={value.start_time}
|
||||||
data-segment-start={
|
data-segment-start={
|
||||||
alignStartDateToTimeline(value.start_time) - segmentDuration
|
alignStartDateToTimeline(value.start_time) - segmentDuration
|
||||||
@ -505,23 +474,16 @@ function DetectionReview({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})
|
})}
|
||||||
) : itemsToReview != 0 ? (
|
{(itemsToReview ?? 0) > 0 && (
|
||||||
<div ref={lastReviewRef} />
|
|
||||||
) : null}
|
|
||||||
{currentItems && (
|
|
||||||
<div className="col-span-full flex justify-center items-center">
|
<div className="col-span-full flex justify-center items-center">
|
||||||
{reachedEnd ? (
|
<Button
|
||||||
<Button
|
className="text-white"
|
||||||
className="text-white"
|
variant="select"
|
||||||
variant="select"
|
onClick={markAllReviewed}
|
||||||
onClick={markAllReviewed}
|
>
|
||||||
>
|
Mark these items as reviewed
|
||||||
Mark these items as reviewed
|
</Button>
|
||||||
</Button>
|
|
||||||
) : (
|
|
||||||
<ActivityIndicator />
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user