mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-09 04:35:25 +03:00
Merge branch 'dev' of https://github.com/blakeblackshear/frigate into mobile-timeline
This commit is contained in:
commit
6680a99a74
@ -22,7 +22,7 @@ export default function DebugCameraImage({
|
|||||||
cameraConfig,
|
cameraConfig,
|
||||||
}: DebugCameraImageProps) {
|
}: DebugCameraImageProps) {
|
||||||
const [showSettings, setShowSettings] = useState(false);
|
const [showSettings, setShowSettings] = useState(false);
|
||||||
const [options, setOptions] = usePersistence(
|
const [options, setOptions] = usePersistence<Options>(
|
||||||
`${cameraConfig?.name}-feed`,
|
`${cameraConfig?.name}-feed`,
|
||||||
emptyObject
|
emptyObject
|
||||||
);
|
);
|
||||||
@ -36,7 +36,7 @@ export default function DebugCameraImage({
|
|||||||
const searchParams = useMemo(
|
const searchParams = useMemo(
|
||||||
() =>
|
() =>
|
||||||
new URLSearchParams(
|
new URLSearchParams(
|
||||||
Object.keys(options).reduce((memo, key) => {
|
Object.keys(options || {}).reduce((memo, key) => {
|
||||||
//@ts-ignore we know this is correct
|
//@ts-ignore we know this is correct
|
||||||
memo.push([key, options[key] === true ? "1" : "0"]);
|
memo.push([key, options[key] === true ? "1" : "0"]);
|
||||||
return memo;
|
return memo;
|
||||||
@ -68,7 +68,7 @@ export default function DebugCameraImage({
|
|||||||
<CardContent>
|
<CardContent>
|
||||||
<DebugSettings
|
<DebugSettings
|
||||||
handleSetOption={handleSetOption}
|
handleSetOption={handleSetOption}
|
||||||
options={options}
|
options={options || {}}
|
||||||
/>
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import { LivePlayerMode } from "@/types/live";
|
|||||||
export default function useCameraLiveMode(
|
export default function useCameraLiveMode(
|
||||||
cameraConfig: CameraConfig,
|
cameraConfig: CameraConfig,
|
||||||
preferredMode?: string
|
preferredMode?: string
|
||||||
): LivePlayerMode {
|
): LivePlayerMode | undefined {
|
||||||
const { data: config } = useSWR<FrigateConfig>("config");
|
const { data: config } = useSWR<FrigateConfig>("config");
|
||||||
|
|
||||||
const restreamEnabled = useMemo(() => {
|
const restreamEnabled = useMemo(() => {
|
||||||
@ -22,10 +22,10 @@ export default function useCameraLiveMode(
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
}, [config, cameraConfig]);
|
}, [config, cameraConfig]);
|
||||||
const defaultLiveMode = useMemo(() => {
|
const defaultLiveMode = useMemo<LivePlayerMode | undefined>(() => {
|
||||||
if (config && cameraConfig) {
|
if (config && cameraConfig) {
|
||||||
if (restreamEnabled) {
|
if (restreamEnabled) {
|
||||||
return cameraConfig.ui.live_mode || config?.ui.live_mode;
|
return cameraConfig.ui.live_mode || config.ui.live_mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
return "jsmpeg";
|
return "jsmpeg";
|
||||||
@ -33,7 +33,7 @@ export default function useCameraLiveMode(
|
|||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}, [cameraConfig, restreamEnabled]);
|
}, [cameraConfig, restreamEnabled]);
|
||||||
const [viewSource] = usePersistence(
|
const [viewSource] = usePersistence<LivePlayerMode>(
|
||||||
`${cameraConfig.name}-source`,
|
`${cameraConfig.name}-source`,
|
||||||
defaultLiveMode
|
defaultLiveMode
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,21 +1,21 @@
|
|||||||
import { useEffect, useState, useCallback } from "react";
|
import { useEffect, useState, useCallback } from "react";
|
||||||
import { get as getData, set as setData } from "idb-keyval";
|
import { get as getData, set as setData } from "idb-keyval";
|
||||||
|
|
||||||
type usePersistenceReturn = [
|
type usePersistenceReturn<S> = [
|
||||||
value: any | undefined,
|
value: S | undefined,
|
||||||
setValue: (value: string | boolean) => void,
|
setValue: (value: S) => void,
|
||||||
loaded: boolean,
|
loaded: boolean,
|
||||||
];
|
];
|
||||||
|
|
||||||
export function usePersistence(
|
export function usePersistence<S>(
|
||||||
key: string,
|
key: string,
|
||||||
defaultValue: any | undefined = undefined
|
defaultValue: S | undefined = undefined
|
||||||
): usePersistenceReturn {
|
): usePersistenceReturn<S> {
|
||||||
const [value, setInternalValue] = useState<any | undefined>(defaultValue);
|
const [value, setInternalValue] = useState<S | undefined>(defaultValue);
|
||||||
const [loaded, setLoaded] = useState<boolean>(false);
|
const [loaded, setLoaded] = useState<boolean>(false);
|
||||||
|
|
||||||
const setValue = useCallback(
|
const setValue = useCallback(
|
||||||
(value: string | boolean) => {
|
(value: S) => {
|
||||||
setInternalValue(value);
|
setInternalValue(value);
|
||||||
async function update() {
|
async function update() {
|
||||||
await setData(key, value);
|
await setData(key, value);
|
||||||
|
|||||||
@ -28,9 +28,10 @@ export default function Events() {
|
|||||||
|
|
||||||
// review paging
|
// review paging
|
||||||
|
|
||||||
|
const [beforeTs, setBeforeTs] = useState(Date.now() / 1000);
|
||||||
const last24Hours = useMemo(() => {
|
const last24Hours = useMemo(() => {
|
||||||
return { before: Date.now() / 1000, after: getHoursAgo(24) };
|
return { before: beforeTs, after: getHoursAgo(24) };
|
||||||
}, []);
|
}, [beforeTs]);
|
||||||
const selectedTimeRange = useMemo(() => {
|
const selectedTimeRange = useMemo(() => {
|
||||||
if (reviewSearchParams["after"] == undefined) {
|
if (reviewSearchParams["after"] == undefined) {
|
||||||
return last24Hours;
|
return last24Hours;
|
||||||
@ -73,7 +74,7 @@ export default function Events() {
|
|||||||
};
|
};
|
||||||
return ["review", params];
|
return ["review", params];
|
||||||
},
|
},
|
||||||
[reviewSearchParams]
|
[reviewSearchParams, last24Hours]
|
||||||
);
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -96,10 +97,7 @@ export default function Events() {
|
|||||||
setSize(size + 1);
|
setSize(size + 1);
|
||||||
}, [size]);
|
}, [size]);
|
||||||
|
|
||||||
const reloadData = useCallback(() => {
|
const reloadData = useCallback(() => setBeforeTs(Date.now() / 1000), []);
|
||||||
setSize(1);
|
|
||||||
updateSegments();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// preview videos
|
// preview videos
|
||||||
|
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import LivePlayer from "@/components/player/LivePlayer";
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
|
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
|
||||||
import { TooltipProvider } from "@/components/ui/tooltip";
|
import { TooltipProvider } from "@/components/ui/tooltip";
|
||||||
|
import { usePersistence } from "@/hooks/use-persistence";
|
||||||
import { Event as FrigateEvent } from "@/types/event";
|
import { Event as FrigateEvent } from "@/types/event";
|
||||||
import { FrigateConfig } from "@/types/frigateConfig";
|
import { FrigateConfig } from "@/types/frigateConfig";
|
||||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
@ -17,7 +18,8 @@ function Live() {
|
|||||||
|
|
||||||
// layout
|
// layout
|
||||||
|
|
||||||
const [layout, setLayout] = useState<"grid" | "list">(
|
const [layout, setLayout] = usePersistence<"grid" | "list">(
|
||||||
|
"live-layout",
|
||||||
isDesktop ? "grid" : "list"
|
isDesktop ? "grid" : "list"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -97,8 +97,9 @@ function Logs() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{!endVisible && (
|
{!endVisible && (
|
||||||
<div
|
<Button
|
||||||
className="absolute bottom-8 left-[50%] -translate-x-[50%] rounded-xl bg-accent-foreground text-white z-20 p-2"
|
className="absolute bottom-8 left-[50%] -translate-x-[50%] rounded-xl bg-accent-foreground text-white bg-gray-400 z-20 p-2"
|
||||||
|
variant="secondary"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
contentRef.current?.scrollTo({
|
contentRef.current?.scrollTo({
|
||||||
top: contentRef.current?.scrollHeight,
|
top: contentRef.current?.scrollHeight,
|
||||||
@ -107,7 +108,7 @@ function Logs() {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
Jump to Bottom
|
Jump to Bottom
|
||||||
</div>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
|
import { LivePlayerMode } from "./live";
|
||||||
|
|
||||||
export interface UiConfig {
|
export interface UiConfig {
|
||||||
timezone?: string;
|
timezone?: string;
|
||||||
time_format?: "browser" | "12hour" | "24hour";
|
time_format?: "browser" | "12hour" | "24hour";
|
||||||
date_style?: "full" | "long" | "medium" | "short";
|
date_style?: "full" | "long" | "medium" | "short";
|
||||||
time_style?: "full" | "long" | "medium" | "short";
|
time_style?: "full" | "long" | "medium" | "short";
|
||||||
strftime_fmt?: string;
|
strftime_fmt?: string;
|
||||||
live_mode?: string;
|
live_mode?: LivePlayerMode;
|
||||||
use_experimental?: boolean;
|
use_experimental?: boolean;
|
||||||
dashboard: boolean;
|
dashboard: boolean;
|
||||||
order: number;
|
order: number;
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import { useEventUtils } from "@/hooks/use-event-utils";
|
|||||||
import { FrigateConfig } from "@/types/frigateConfig";
|
import { FrigateConfig } from "@/types/frigateConfig";
|
||||||
import { ReviewFilter, ReviewSegment, ReviewSeverity } from "@/types/review";
|
import { ReviewFilter, ReviewSegment, ReviewSeverity } from "@/types/review";
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { isDesktop } from "react-device-detect";
|
import { isDesktop, isMobile } from "react-device-detect";
|
||||||
import { LuFolderCheck } from "react-icons/lu";
|
import { LuFolderCheck } from "react-icons/lu";
|
||||||
import { MdCircle } from "react-icons/md";
|
import { MdCircle } from "react-icons/md";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
@ -191,7 +191,9 @@ export default function EventView({
|
|||||||
return (
|
return (
|
||||||
<div className="flex flex-col size-full">
|
<div className="flex flex-col size-full">
|
||||||
<div className="relative flex justify-between mb-2">
|
<div className="relative flex justify-between mb-2">
|
||||||
<Logo className="absolute inset-y-0 inset-x-1/2 -translate-x-1/2 h-8" />
|
{isMobile && (
|
||||||
|
<Logo className="absolute inset-y-0 inset-x-1/2 -translate-x-1/2 h-8" />
|
||||||
|
)}
|
||||||
<ToggleGroup
|
<ToggleGroup
|
||||||
className="*:px-3 *:py4 *:rounded-2xl"
|
className="*:px-3 *:py4 *:rounded-2xl"
|
||||||
type="single"
|
type="single"
|
||||||
@ -234,14 +236,16 @@ export default function EventView({
|
|||||||
ref={contentRef}
|
ref={contentRef}
|
||||||
className="flex flex-1 flex-wrap content-start gap-2 overflow-y-auto no-scrollbar"
|
className="flex flex-1 flex-wrap content-start gap-2 overflow-y-auto no-scrollbar"
|
||||||
>
|
>
|
||||||
<NewReviewData
|
{filter?.before == undefined && (
|
||||||
className="absolute w-full z-30"
|
<NewReviewData
|
||||||
contentRef={contentRef}
|
className="absolute w-full z-30"
|
||||||
severity={severity}
|
contentRef={contentRef}
|
||||||
pullLatestData={pullLatestData}
|
severity={severity}
|
||||||
/>
|
pullLatestData={pullLatestData}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{reachedEnd && currentItems == null && (
|
{!isValidating && currentItems == null && (
|
||||||
<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} items to review
|
There are no {severity} items to review
|
||||||
@ -287,9 +291,9 @@ export default function EventView({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
) : (
|
) : severity != "alert" ? (
|
||||||
<div ref={lastReviewRef} />
|
<div ref={lastReviewRef} />
|
||||||
)}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-[55px] md:w-[100px] mt-2 overflow-y-auto no-scrollbar">
|
<div className="w-[55px] md:w-[100px] mt-2 overflow-y-auto no-scrollbar">
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user