mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-04-10 09:07:37 +03:00
Allow clicking on a recording
This commit is contained in:
parent
04908301ec
commit
8c09103841
@ -114,6 +114,33 @@ export default function Events() {
|
||||
false,
|
||||
);
|
||||
|
||||
// Wrapper to update URL with review ID for deep linking when opening a recording.
|
||||
// This does a single atomic navigation to avoid creating multiple history entries
|
||||
// which would cause issues with the back button.
|
||||
const onOpenRecording = useCallback(
|
||||
(recordingInfo: RecordingStartingPoint) => {
|
||||
const updated = new URLSearchParams(searchParams);
|
||||
if (recordingInfo.reviewId) {
|
||||
updated.set("id", recordingInfo.reviewId);
|
||||
}
|
||||
const search = updated.toString();
|
||||
|
||||
// Single navigation that updates both URL params and location state
|
||||
navigate(
|
||||
{
|
||||
pathname: location.pathname,
|
||||
search: search ? `?${search}` : "",
|
||||
hash: location.hash,
|
||||
},
|
||||
{
|
||||
replace: true,
|
||||
state: { ...location.state, recording: recordingInfo },
|
||||
},
|
||||
);
|
||||
},
|
||||
[searchParams, navigate, location.pathname, location.hash, location.state],
|
||||
);
|
||||
|
||||
const [notificationTab, setNotificationTab] =
|
||||
useState<TimelineType>("timeline");
|
||||
|
||||
@ -125,6 +152,13 @@ export default function Events() {
|
||||
});
|
||||
|
||||
useSearchEffect("id", (reviewId: string) => {
|
||||
// If recording is already set (e.g., from clicking a review), don't re-fetch.
|
||||
// Return false to keep the id param in the URL for deep linking.
|
||||
if (recording) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fresh deep link - fetch review data and open recording
|
||||
axios
|
||||
.get(`review/${reviewId}`)
|
||||
.then((resp) => {
|
||||
@ -142,6 +176,7 @@ export default function Events() {
|
||||
startTime,
|
||||
severity: resp.data.severity,
|
||||
timelineType: notificationTab,
|
||||
reviewId,
|
||||
},
|
||||
true,
|
||||
);
|
||||
@ -149,7 +184,8 @@ export default function Events() {
|
||||
})
|
||||
.catch(() => {});
|
||||
|
||||
return true;
|
||||
// Return false to keep the id param in the URL for deep linking
|
||||
return false;
|
||||
});
|
||||
|
||||
const [startTime, setStartTime] = useState<number>();
|
||||
@ -560,7 +596,7 @@ export default function Events() {
|
||||
setSeverity={setSeverity}
|
||||
markItemAsReviewed={markItemAsReviewed}
|
||||
markAllItemsAsReviewed={markAllItemsAsReviewed}
|
||||
onOpenRecording={setRecording}
|
||||
onOpenRecording={onOpenRecording}
|
||||
pullLatestData={reloadData}
|
||||
updateFilter={onUpdateFilter}
|
||||
/>
|
||||
|
||||
@ -39,6 +39,8 @@ export type RecordingStartingPoint = {
|
||||
startTime: number;
|
||||
severity: ReviewSeverity;
|
||||
timelineType?: TimelineType;
|
||||
// Optional review ID for deep linking to specific review segments
|
||||
reviewId?: string;
|
||||
};
|
||||
|
||||
export type RecordingPlayerError = "stalled" | "startup";
|
||||
|
||||
@ -168,6 +168,7 @@ export default function EventView({
|
||||
startTime: effectiveStartTime - REVIEW_PADDING,
|
||||
severity: review.severity,
|
||||
timelineType: detail ? "detail" : undefined,
|
||||
reviewId: review.id,
|
||||
});
|
||||
|
||||
review.has_been_reviewed = true;
|
||||
|
||||
@ -35,7 +35,7 @@ import {
|
||||
} from "react";
|
||||
import { isDesktop, isMobile } from "react-device-detect";
|
||||
import { IoMdArrowRoundBack } from "react-icons/io";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useLocation, useNavigate, useSearchParams } from "react-router-dom";
|
||||
import { Toaster } from "@/components/ui/sonner";
|
||||
import useSWR from "swr";
|
||||
import { TimeRange, TimelineType } from "@/types/timeline";
|
||||
@ -97,8 +97,26 @@ export function RecordingView({
|
||||
const { t } = useTranslation(["views/events"]);
|
||||
const { data: config } = useSWR<FrigateConfig>("config");
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const [searchParams] = useSearchParams();
|
||||
const contentRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
// Navigate back while clearing the review id param to prevent
|
||||
// useSearchEffect from re-opening the recording
|
||||
const handleBack = useCallback(() => {
|
||||
const updated = new URLSearchParams(searchParams);
|
||||
updated.delete("id");
|
||||
const search = updated.toString();
|
||||
navigate(
|
||||
{
|
||||
pathname: location.pathname,
|
||||
search: search ? `?${search}` : "",
|
||||
hash: location.hash,
|
||||
},
|
||||
{ replace: true },
|
||||
);
|
||||
}, [searchParams, navigate, location.pathname, location.hash]);
|
||||
|
||||
// recordings summary
|
||||
|
||||
const timezone = useTimezone(config);
|
||||
@ -541,7 +559,7 @@ export function RecordingView({
|
||||
className="flex items-center gap-2.5 rounded-lg"
|
||||
aria-label={t("label.back", { ns: "common" })}
|
||||
size="sm"
|
||||
onClick={() => navigate(-1)}
|
||||
onClick={handleBack}
|
||||
>
|
||||
<IoMdArrowRoundBack className="size-5 text-secondary-foreground" />
|
||||
{isDesktop && (
|
||||
|
||||
Loading…
Reference in New Issue
Block a user