mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-08 20:25:26 +03:00
Rework scrolling to fix nested scrolling
This commit is contained in:
parent
6b0701c73c
commit
16d81de5e1
@ -28,11 +28,11 @@ function App() {
|
||||
<BrowserRouter>
|
||||
<Wrapper>
|
||||
<Header onToggleNavbar={toggleNavbar} />
|
||||
<div className="grid grid-cols-[auto,1fr] flex-grow-1 overflow-auto">
|
||||
<div className="w-full h-full pt-2 overflow-hidden">
|
||||
<Sidebar sheetOpen={sheetOpen} setSheetOpen={setSheetOpen} />
|
||||
<div
|
||||
id="pageRoot"
|
||||
className="overflow-x-hidden px-4 py-2 w-screen md:w-full"
|
||||
className="absolute left-0 md:left-16 top-16 md:top-0 right-0 bottom-0 overflow-hidden"
|
||||
>
|
||||
<Routes>
|
||||
<Route path="/" element={<Live />} />
|
||||
|
||||
@ -5,7 +5,7 @@ type TWrapperProps = {
|
||||
};
|
||||
|
||||
const Wrapper = ({ children }: TWrapperProps) => {
|
||||
return <main className="flex flex-col h-screen">{children}</main>;
|
||||
return <main className="w-screen h-screen overflow-hidden">{children}</main>;
|
||||
};
|
||||
|
||||
export default Wrapper;
|
||||
|
||||
@ -1,5 +1,11 @@
|
||||
import VideoPlayer from "./VideoPlayer";
|
||||
import React, { useCallback, useMemo, useRef, useState } from "react";
|
||||
import React, {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { useApiHost } from "@/api";
|
||||
import Player from "video.js/dist/types/player";
|
||||
import { formatUnixTimestampToDateTime } from "@/utils/dateUtil";
|
||||
@ -37,14 +43,34 @@ export default function PreviewThumbnailPlayer({
|
||||
const playerRef = useRef<Player | null>(null);
|
||||
|
||||
const [hoverTimeout, setHoverTimeout] = useState<NodeJS.Timeout | null>();
|
||||
const [hover, setHover] = useState(false);
|
||||
const [playback, setPlayback] = useState(false);
|
||||
const [progress, setProgress] = useState(0);
|
||||
|
||||
const playingBack = useMemo(
|
||||
() => relevantPreview && (hover || autoPlayback),
|
||||
[hover, autoPlayback, relevantPreview]
|
||||
() => relevantPreview && playback,
|
||||
[playback, autoPlayback, relevantPreview]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!autoPlayback) {
|
||||
setPlayback(false);
|
||||
|
||||
if (hoverTimeout) {
|
||||
clearTimeout(hoverTimeout);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const timeout = setTimeout(() => {
|
||||
setPlayback(true);
|
||||
setHoverTimeout(null);
|
||||
}, 500);
|
||||
|
||||
return () => {
|
||||
clearTimeout(timeout);
|
||||
};
|
||||
}, [autoPlayback]);
|
||||
|
||||
const onPlayback = useCallback(
|
||||
(isHovered: Boolean) => {
|
||||
if (!relevantPreview) {
|
||||
@ -54,7 +80,7 @@ export default function PreviewThumbnailPlayer({
|
||||
if (isHovered) {
|
||||
setHoverTimeout(
|
||||
setTimeout(() => {
|
||||
setHover(true);
|
||||
setPlayback(true);
|
||||
setHoverTimeout(null);
|
||||
}, 500)
|
||||
);
|
||||
@ -63,7 +89,7 @@ export default function PreviewThumbnailPlayer({
|
||||
clearTimeout(hoverTimeout);
|
||||
}
|
||||
|
||||
setHover(false);
|
||||
setPlayback(false);
|
||||
setProgress(0);
|
||||
|
||||
if (playerRef.current) {
|
||||
@ -203,7 +229,7 @@ function PreviewContent({
|
||||
(player.currentTime() || 0) - playerStartTime;
|
||||
|
||||
// end with a bit of padding
|
||||
const playerDuration = (review.end_time - review.start_time) + 8;
|
||||
const playerDuration = review.end_time - review.start_time + 8;
|
||||
const playerPercent = (playerProgress / playerDuration) * 100;
|
||||
|
||||
if (
|
||||
|
||||
@ -47,7 +47,7 @@ export function EventReviewTimeline({
|
||||
const currentTimeRef = useRef<HTMLDivElement>(null);
|
||||
const observer = useRef<ResizeObserver | null>(null);
|
||||
const timelineDuration = useMemo(
|
||||
() => timelineEnd - timelineStart,
|
||||
() => timelineStart - timelineEnd,
|
||||
[timelineEnd, timelineStart]
|
||||
);
|
||||
|
||||
|
||||
@ -6,9 +6,12 @@ export const useSegmentUtils = (
|
||||
events: ReviewSegment[],
|
||||
severityType: string
|
||||
) => {
|
||||
const getSegmentStart = useCallback((time: number): number => {
|
||||
return Math.floor(time / (segmentDuration)) * (segmentDuration);
|
||||
}, [segmentDuration]);
|
||||
const getSegmentStart = useCallback(
|
||||
(time: number): number => {
|
||||
return Math.floor(time / segmentDuration) * segmentDuration;
|
||||
},
|
||||
[segmentDuration]
|
||||
);
|
||||
|
||||
const getSegmentEnd = useCallback(
|
||||
(time: number | undefined): number => {
|
||||
@ -67,7 +70,8 @@ export const useSegmentUtils = (
|
||||
[events, getSegmentStart, getSegmentEnd, mapSeverityToNumber]
|
||||
);
|
||||
|
||||
const getReviewed = useCallback((time: number): boolean => {
|
||||
const getReviewed = useCallback(
|
||||
(time: number): boolean => {
|
||||
return events.some((event) => {
|
||||
const segmentStart = getSegmentStart(event.start_time);
|
||||
const segmentEnd = getSegmentEnd(event.end_time);
|
||||
@ -75,7 +79,9 @@ export const useSegmentUtils = (
|
||||
time >= segmentStart && time < segmentEnd && event.has_been_reviewed
|
||||
);
|
||||
});
|
||||
}, [events, getSegmentStart, getSegmentEnd]);
|
||||
},
|
||||
[events, getSegmentStart, getSegmentEnd]
|
||||
);
|
||||
|
||||
const shouldShowRoundedCorners = useCallback(
|
||||
(segmentTime: number): { roundTop: boolean; roundBottom: boolean } => {
|
||||
@ -150,5 +156,12 @@ export const useSegmentUtils = (
|
||||
[events, getSegmentStart, getSegmentEnd, segmentDuration, severityType]
|
||||
);
|
||||
|
||||
return { getSegmentStart, getSegmentEnd, getSeverity, displaySeverityType, getReviewed, shouldShowRoundedCorners };
|
||||
return {
|
||||
getSegmentStart,
|
||||
getSegmentEnd,
|
||||
getSeverity,
|
||||
displaySeverityType,
|
||||
getReviewed,
|
||||
shouldShowRoundedCorners,
|
||||
};
|
||||
};
|
||||
@ -79,7 +79,7 @@ function Live() {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="w-full h-full overflow-scroll">
|
||||
{events && events.length > 0 && (
|
||||
<ScrollArea>
|
||||
<TooltipProvider>
|
||||
@ -115,7 +115,7 @@ function Live() {
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -244,6 +244,8 @@ export default function DesktopEventView() {
|
||||
return <ActivityIndicator />;
|
||||
}
|
||||
|
||||
console.log("end of the timeline is " + after + " vs " + (Math.floor(Date.now() / 1000) + 2 * 60 * 60))
|
||||
|
||||
return (
|
||||
<div className="relative w-full h-full">
|
||||
<div className="absolute flex justify-between left-0 top-0 right-0">
|
||||
@ -329,23 +331,40 @@ export default function DesktopEventView() {
|
||||
})}
|
||||
</div>
|
||||
<div className="absolute top-12 right-0 bottom-0">
|
||||
<EventReviewTimeline
|
||||
{after != 0 && (<EventReviewTimeline
|
||||
segmentDuration={60}
|
||||
timestampSpread={15}
|
||||
timelineStart={Math.floor(Date.now() / 1000)}
|
||||
timelineDuration={24 * 60 * 60}
|
||||
timelineStart={Math.floor(Date.now() / 1000)} // start of the timeline - all times are numeric, not Date objects
|
||||
timelineEnd={after} // end of timeline - timestamp
|
||||
showMinimap
|
||||
minimapStartTime={minimapBounds.start}
|
||||
minimapEndTime={minimapBounds.end}
|
||||
events={reviewItems.all}
|
||||
severityType={severity}
|
||||
contentRef={contentRef}
|
||||
/>
|
||||
/>)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* <EventReviewTimeline
|
||||
segmentDuration={60} // seconds per segment
|
||||
timestampSpread={15} // minutes between each major timestamp
|
||||
timelineStart={Math.floor(Date.now() / 1000)} // start of the timeline - all times are numeric, not Date objects
|
||||
timelineEnd={Math.floor(Date.now() / 1000) + 2 * 60 * 60} // end of timeline - timestamp
|
||||
showHandlebar // show / hide the handlebar
|
||||
handlebarTime={Math.floor(Date.now() / 1000) - 27 * 60} // set the time of the handlebar
|
||||
showMinimap // show / hide the minimap
|
||||
minimapStartTime={Math.floor(Date.now() / 1000) - 35 * 60} // start time of the minimap - the earlier time (eg 1:00pm)
|
||||
minimapEndTime={Math.floor(Date.now() / 1000) - 21 * 60} // end of the minimap - the later time (eg 3:00pm)
|
||||
events={mockEvents} // events, including new has_been_reviewed and severity properties
|
||||
severityType={"alert"} // choose the severity type for the middle line - all other severity types are to the right
|
||||
contentRef={contentRef} // optional content ref where previews are, can be used for observing/scrolling later
|
||||
/>
|
||||
*/
|
||||
|
||||
function ReviewCalendarButton() {
|
||||
const disabledDates = useMemo(() => {
|
||||
const tomorrow = new Date();
|
||||
|
||||
@ -139,7 +139,6 @@ export default function MobileEventView() {
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
const start = (entry.target as HTMLElement).dataset.start;
|
||||
console.log(`${start} has been updated as intersect ${entry.isIntersecting}`)
|
||||
|
||||
if (!start) {
|
||||
return;
|
||||
@ -153,7 +152,8 @@ export default function MobileEventView() {
|
||||
|
||||
setMinimap([...visibleTimestamps]);
|
||||
});
|
||||
}, { threshold: 0.5 }
|
||||
},
|
||||
{ threshold: 0.5 }
|
||||
);
|
||||
|
||||
return () => {
|
||||
@ -186,8 +186,6 @@ export default function MobileEventView() {
|
||||
data.start = parseFloat(list[0]);
|
||||
}
|
||||
|
||||
console.log("the new times are " + JSON.stringify(data))
|
||||
|
||||
return data;
|
||||
}, [minimap]);
|
||||
|
||||
@ -237,7 +235,7 @@ export default function MobileEventView() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full h-full">
|
||||
<>
|
||||
<ToggleGroup
|
||||
type="single"
|
||||
defaultValue="alert"
|
||||
@ -278,7 +276,7 @@ export default function MobileEventView() {
|
||||
|
||||
<div
|
||||
ref={contentRef}
|
||||
className="grid grid-cols-1 sm:grid-cols-2 mt-2 gap-2 overflow-y-auto no-scrollbar"
|
||||
className="w-full h-full grid grid-cols-1 sm:grid-cols-2 mt-2 gap-2 overflow-y-auto"
|
||||
>
|
||||
{reviewItems[severity]?.map((value, segIdx) => {
|
||||
const lastRow = segIdx == reviewItems[severity].length - 1;
|
||||
@ -308,6 +306,6 @@ export default function MobileEventView() {
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user