Merge branch 'dev' of https://github.com/blakeblackshear/frigate into mobile-timeline

This commit is contained in:
Josh Hawkins 2024-02-28 09:08:24 -06:00
commit 6680a99a74
8 changed files with 45 additions and 38 deletions

View File

@ -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>

View File

@ -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
); );

View File

@ -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);

View File

@ -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

View File

@ -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"
); );

View File

@ -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

View File

@ -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;

View File

@ -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">