forked from Github/frigate
WebUI Improvements and fixes (#9613)
* Show toast instead of text for success and errors * Show correct times * Start playing next hour when current hour ends * Fix refreshing camera image * Fix timeline
This commit is contained in:
@@ -24,6 +24,9 @@ export default function DynamicCameraImage({
|
||||
aspect,
|
||||
}: DynamicCameraImageProps) {
|
||||
const [key, setKey] = useState(Date.now());
|
||||
const [timeoutId, setTimeoutId] = useState<NodeJS.Timeout | undefined>(
|
||||
undefined
|
||||
);
|
||||
const [activeObjects, setActiveObjects] = useState<string[]>([]);
|
||||
const hasActiveObjects = useMemo(
|
||||
() => activeObjects.length > 0,
|
||||
@@ -58,6 +61,8 @@ export default function DynamicCameraImage({
|
||||
if (eventIndex == -1) {
|
||||
const newActiveObjects = [...activeObjects, event.after.id];
|
||||
setActiveObjects(newActiveObjects);
|
||||
clearTimeout(timeoutId);
|
||||
setKey(Date.now());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -69,12 +74,13 @@ export default function DynamicCameraImage({
|
||||
? INTERVAL_ACTIVE_MS
|
||||
: INTERVAL_INACTIVE_MS;
|
||||
|
||||
setTimeout(
|
||||
const tId = setTimeout(
|
||||
() => {
|
||||
setKey(Date.now());
|
||||
},
|
||||
loadTime > loadInterval ? 1 : loadInterval
|
||||
);
|
||||
setTimeoutId(tId);
|
||||
}, [key]);
|
||||
|
||||
return (
|
||||
|
||||
@@ -228,6 +228,7 @@ export default function DynamicVideoPlayer({
|
||||
player.on("timeupdate", () => {
|
||||
controller.updateProgress(player.currentTime() || 0);
|
||||
});
|
||||
player.on("ended", () => controller.fireClipEndEvent());
|
||||
|
||||
if (onControllerReady) {
|
||||
onControllerReady(controller);
|
||||
@@ -284,6 +285,7 @@ export class DynamicVideoController {
|
||||
// playback
|
||||
private recordings: Recording[] = [];
|
||||
private onPlaybackTimestamp: ((time: number) => void) | undefined = undefined;
|
||||
private onClipEnded: (() => void) | undefined = undefined;
|
||||
private annotationOffset: number;
|
||||
private timeToStart: number | undefined = undefined;
|
||||
|
||||
@@ -393,6 +395,16 @@ export class DynamicVideoController {
|
||||
this.onPlaybackTimestamp = listener;
|
||||
}
|
||||
|
||||
onClipEndedEvent(listener: () => void) {
|
||||
this.onClipEnded = listener;
|
||||
}
|
||||
|
||||
fireClipEndEvent() {
|
||||
if (this.onClipEnded) {
|
||||
this.onClipEnded();
|
||||
}
|
||||
}
|
||||
|
||||
scrubToTimestamp(time: number) {
|
||||
if (this.playerMode != "scrubbing") {
|
||||
this.playerMode = "scrubbing";
|
||||
|
||||
32
web/src/components/ui/sonner.tsx
Normal file
32
web/src/components/ui/sonner.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import { useTheme } from "next-themes";
|
||||
import { Toaster as Sonner } from "sonner";
|
||||
|
||||
type ToasterProps = React.ComponentProps<typeof Sonner>;
|
||||
|
||||
const Toaster = ({ ...props }: ToasterProps) => {
|
||||
const { theme = "system" } = useTheme();
|
||||
|
||||
return (
|
||||
<Sonner
|
||||
theme={theme as ToasterProps["theme"]}
|
||||
className="toaster group"
|
||||
toastOptions={{
|
||||
classNames: {
|
||||
toast:
|
||||
"group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg",
|
||||
description: "group-[.toast]:text-muted-foreground",
|
||||
actionButton:
|
||||
"group-[.toast]:bg-primary group-[.toast]:text-primary-foreground",
|
||||
cancelButton:
|
||||
"group-[.toast]:bg-muted group-[.toast]:text-muted-foreground",
|
||||
success:
|
||||
"group toast group-[.toaster]:bg-success group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg",
|
||||
error: "group toast group-[.toaster]:bg-danger group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg",
|
||||
},
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export { Toaster };
|
||||
Reference in New Issue
Block a user