diff --git a/src/pages/403.tsx b/src/pages/403.tsx
index 9bbf3d4..5f1e2b3 100644
--- a/src/pages/403.tsx
+++ b/src/pages/403.tsx
@@ -1,6 +1,6 @@
import { Button, Flex, Text } from '@mantine/core';
import React, { useContext, useEffect, useState } from 'react';
-import CogWheelWithText from '../shared/components/CogWheelWithText';
+import CogWheelWithText from '../shared/components/loaders/CogWheelWithText';
import { strings } from '../shared/strings/strings';
import { redirect, useNavigate } from 'react-router-dom';
import { routesPath } from '../router/routes.path';
diff --git a/src/pages/404.tsx b/src/pages/404.tsx
index 98c7c13..dcf3e64 100644
--- a/src/pages/404.tsx
+++ b/src/pages/404.tsx
@@ -1,6 +1,6 @@
import { Button, Flex, Text } from '@mantine/core';
import React, { useContext, useEffect, useState } from 'react';
-import CogWheelWithText from '../shared/components/CogWheelWithText';
+import CogWheelWithText from '../shared/components/loaders/CogWheelWithText';
import { strings } from '../shared/strings/strings';
import { redirect, useNavigate } from 'react-router-dom';
import { routesPath } from '../router/routes.path';
diff --git a/src/pages/FrigateHostsPage.tsx b/src/pages/FrigateHostsPage.tsx
index 777d60b..31a2684 100644
--- a/src/pages/FrigateHostsPage.tsx
+++ b/src/pages/FrigateHostsPage.tsx
@@ -4,7 +4,7 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { frigateApi, frigateQueryKeys } from '../services/frigate.proxy/frigate.api';
import { deleteFrigateHostSchema, GetFrigateHost, putFrigateHostSchema} from '../services/frigate.proxy/frigate.schema';
import CenterLoader from '../shared/components/CenterLoader';
-import RetryError from './RetryError';
+import RetryErrorPage from './RetryErrorPage';
import { Context } from '..';
import { strings } from '../shared/strings/strings';
import { Button, Flex } from '@mantine/core';
@@ -71,7 +71,7 @@ const FrigateHostsPage = observer(() => {
}
if (hostsPending) return
- if (hostsError) return
+ if (hostsError) return
return (
{
diff --git a/src/pages/HostConfigPage.tsx b/src/pages/HostConfigPage.tsx
index 153ad9f..18ce3d7 100644
--- a/src/pages/HostConfigPage.tsx
+++ b/src/pages/HostConfigPage.tsx
@@ -9,7 +9,7 @@ import { configureMonacoYaml } from "monaco-yaml";
import Editor, { DiffEditor, useMonaco, loader, Monaco } from '@monaco-editor/react'
import * as monaco from "monaco-editor";
import CenterLoader from '../shared/components/CenterLoader';
-import RetryError from './RetryError';
+import RetryErrorPage from './RetryErrorPage';
const HostConfigPage = () => {
@@ -74,7 +74,7 @@ const HostConfigPage = () => {
if (configPending) return
- if (configError) return
+ if (configError) return
return (
diff --git a/src/pages/LiveCameraPage.tsx b/src/pages/LiveCameraPage.tsx
index 5aaa799..a6b5c36 100644
--- a/src/pages/LiveCameraPage.tsx
+++ b/src/pages/LiveCameraPage.tsx
@@ -5,8 +5,8 @@ import { useParams } from 'react-router-dom';
import { frigateApi, frigateQueryKeys } from '../services/frigate.proxy/frigate.api';
import { useQuery } from '@tanstack/react-query';
import CenterLoader from '../shared/components/CenterLoader';
-import RetryError from './RetryError';
-import Player from '../shared/components/frigate/Player';
+import RetryErrorPage from './RetryErrorPage';
+import Player from '../widgets/Player';
import { Flex } from '@mantine/core';
const LiveCameraPage = observer(() => {
@@ -28,7 +28,7 @@ const LiveCameraPage = observer(() => {
if (isPending) return
- if (isError) return
+ if (isError) return
return (
diff --git a/src/pages/MainBody.tsx b/src/pages/MainPage.tsx
similarity index 84%
rename from src/pages/MainBody.tsx
rename to src/pages/MainPage.tsx
index ae450bc..c5bbb0c 100644
--- a/src/pages/MainBody.tsx
+++ b/src/pages/MainPage.tsx
@@ -8,11 +8,12 @@ import { observer } from 'mobx-react-lite'
import CenterLoader from '../shared/components/CenterLoader';
import { useQuery } from '@tanstack/react-query';
import { frigateApi, frigateQueryKeys } from '../services/frigate.proxy/frigate.api';
-import RetryError from './RetryError';
+import RetryErrorPage from './RetryErrorPage';
import CameraCard from '../shared/components/CameraCard';
-const MainBody = observer(() => {
+const MainPage = () => {
const { sideBarsStore } = useContext(Context)
+
useEffect(() => {
sideBarsStore.rightVisible = false
sideBarsStore.setLeftChildren(null)
@@ -32,11 +33,11 @@ const MainBody = observer(() => {
if (isPending) return
- if (isError) return
+ if (isError) return
const cards = () => {
- // return cameras.filter(cam => cam.frigateHost?.host.includes('5001')).slice(0,1).map(camera => (
- return cameras.map(camera => (
+ return cameras.filter(cam => cam.frigateHost?.host.includes('5000')).slice(0,25).map(camera => (
+ // return cameras.map(camera => (
{
}
return (
-
+
{
-
+
{cards()}
);
-})
+}
-export default MainBody;
\ No newline at end of file
+export default observer(MainPage);
\ No newline at end of file
diff --git a/src/pages/RecordingsPage.tsx b/src/pages/RecordingsPage.tsx
index 2573105..ee47501 100644
--- a/src/pages/RecordingsPage.tsx
+++ b/src/pages/RecordingsPage.tsx
@@ -9,12 +9,18 @@ import SelectedCameraList from '../widgets/SelectedCameraList';
import SelectedHostList from '../widgets/SelectedHostList';
import { useQuery } from '@tanstack/react-query';
import { frigateApi, frigateQueryKeys } from '../services/frigate.proxy/frigate.api';
+import { dateToQueryString, parseQueryDateToDate } from '../shared/utils/dateUtil';
+import SelecteDayList from '../widgets/SelecteDayList';
+import { useDebouncedValue } from '@mantine/hooks';
+import CogwheelLoader from '../shared/components/loaders/CogwheelLoader';
+import CenterLoader from '../shared/components/CenterLoader';
-const recordingsQuery = {
+export const recordingsPageQuery = {
hostId: 'hostId',
cameraId: 'cameraId',
- date: 'date',
+ startDay: 'startDay',
+ endDay: 'endDay',
hour: 'hour',
}
@@ -24,13 +30,16 @@ const RecordingsPage = observer(() => {
const location = useLocation()
const navigate = useNavigate()
const queryParams = new URLSearchParams(location.search)
- const paramHostId = queryParams.get(recordingsQuery.hostId)
- const paramCameraId = queryParams.get(recordingsQuery.cameraId);
- const paramDate = queryParams.get(recordingsQuery.date);
- const paramTime = queryParams.get(recordingsQuery.hour);
+ const paramHostId = queryParams.get(recordingsPageQuery.hostId)
+ const paramCameraId = queryParams.get(recordingsPageQuery.cameraId);
+ const paramStartDay = queryParams.get(recordingsPageQuery.startDay);
+ const paramEndDay = queryParams.get(recordingsPageQuery.endDay);
+ const paramTime = queryParams.get(recordingsPageQuery.hour);
const [hostId, setHostId] = useState('')
const [cameraId, setCameraId] = useState('')
+ const [period, setPeriod] = useState<[Date | null, Date | null]>([null, null])
+ const [firstRender, setFirstRender] = useState(false)
useEffect(() => {
sideBarsStore.rightVisible = true
@@ -39,15 +48,21 @@ const RecordingsPage = observer(() => {
)
if (paramHostId) recStore.hostIdParam = paramHostId
if (paramCameraId) recStore.cameraIdParam = paramCameraId
+ if (paramStartDay && paramEndDay) {
+ const parsedStartDay = parseQueryDateToDate(paramStartDay)
+ const parsedEndDay = parseQueryDateToDate(paramEndDay)
+ recStore.selectedRange = [parsedStartDay, parsedEndDay]
+ }
+ setFirstRender(true)
return () => sideBarsStore.setRightChildren(null)
}, [])
useEffect(() => {
setHostId(recStore.selectedHost?.id || '')
if (recStore.selectedHost) {
- queryParams.set(recordingsQuery.hostId, recStore.selectedHost.id)
+ queryParams.set(recordingsPageQuery.hostId, recStore.selectedHost.id)
} else {
- queryParams.delete(recordingsQuery.hostId)
+ queryParams.delete(recordingsPageQuery.hostId)
}
navigate({ pathname: location.pathname, search: queryParams.toString() });
}, [recStore.selectedHost])
@@ -55,26 +70,61 @@ const RecordingsPage = observer(() => {
useEffect(() => {
setCameraId(recStore.selectedCamera?.id || '')
if (recStore.selectedCamera) {
- queryParams.set(recordingsQuery.cameraId, recStore.selectedCamera?.id)
+ queryParams.set(recordingsPageQuery.cameraId, recStore.selectedCamera?.id)
} else {
- console.log('delete recordingsQuery.cameraId')
- queryParams.delete(recordingsQuery.cameraId)
+ queryParams.delete(recordingsPageQuery.cameraId)
}
navigate({ pathname: location.pathname, search: queryParams.toString() });
}, [recStore.selectedCamera])
- if (cameraId) {
- return
+ useEffect(() => {
+ setPeriod(recStore.selectedRange)
+ const [startDay, endDay] = recStore.selectedRange
+ if (startDay && endDay) {
+ const startQuery = dateToQueryString(startDay)
+ const endQuery = dateToQueryString(endDay)
+ queryParams.set(recordingsPageQuery.startDay, startQuery)
+ queryParams.set(recordingsPageQuery.endDay, endQuery)
+ } else {
+ queryParams.delete(recordingsPageQuery.startDay)
+ queryParams.delete(recordingsPageQuery.endDay)
+ }
+ navigate({ pathname: location.pathname, search: queryParams.toString() });
+ }, [recStore.selectedRange])
+
+ console.log('RecordingsPage rendered')
+
+ if (!firstRender) return
+
+ const [startDay, endDay] = period
+ if (startDay && endDay) {
+ if (startDay.getDate() === endDay.getDate()) { // if select only one day
+ return
+ }
}
- if (hostId) {
+ if (cameraId && paramCameraId) {
+ // console.log('cameraId', cameraId)
+ // console.log('paramCameraId', paramCameraId)
+ if ((startDay && endDay) || (!startDay && !endDay)) {
+ return
+ // return
+ }
+ }
+
+ if (hostId && paramHostId && !cameraId) {
return
}
- console.log('RecordingsPage rendered')
return (
- Please select host
+ {!hostId ?
+ Please select host
+ : <>>}
+ {hostId && !(startDay && endDay) ?
+ Please select date
+ : <>>
+ }
)
})
diff --git a/src/pages/RetryError.tsx b/src/pages/RetryErrorPage.tsx
similarity index 92%
rename from src/pages/RetryError.tsx
rename to src/pages/RetryErrorPage.tsx
index 9a081c3..5967487 100644
--- a/src/pages/RetryError.tsx
+++ b/src/pages/RetryErrorPage.tsx
@@ -6,11 +6,11 @@ import { useNavigate } from 'react-router-dom';
import { ExclamationCogWheel } from '../shared/components/svg/ExclamationCogWheel';
import { Context } from '..';
-interface RetryErrorProps {
+interface RetryErrorPageProps {
onRetry?: () => void
}
-const RetryError = ({ onRetry }: RetryErrorProps) => {
+const RetryErrorPage = ({ onRetry }: RetryErrorPageProps) => {
const navigate = useNavigate()
const { sideBarsStore } = useContext(Context)
@@ -48,4 +48,4 @@ const RetryError = ({ onRetry }: RetryErrorProps) => {
);
};
-export default RetryError;
\ No newline at end of file
+export default RetryErrorPage;
\ No newline at end of file
diff --git a/src/pages/SettingsPage.tsx b/src/pages/SettingsPage.tsx
index 568da83..10246e0 100644
--- a/src/pages/SettingsPage.tsx
+++ b/src/pages/SettingsPage.tsx
@@ -6,7 +6,7 @@ import {
} from '@tanstack/react-query'
import { frigateApi, frigateQueryKeys } from '../services/frigate.proxy/frigate.api';
import CenterLoader from '../shared/components/CenterLoader';
-import RetryError from './RetryError';
+import RetryErrorPage from './RetryErrorPage';
import { Button, Flex, Space } from '@mantine/core';
import { FloatingLabelInput } from '../shared/components/FloatingLabelInput';
import { strings } from '../shared/strings/strings';
@@ -83,7 +83,7 @@ const SettingsPage = () => {
if (configPending) return
- if (configError) return
+ if (configError) return
return (
diff --git a/src/pages/Test.tsx b/src/pages/Test.tsx
deleted file mode 100644
index c90a119..0000000
--- a/src/pages/Test.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-import React, { Fragment, useContext, useEffect } from 'react';
-import { observer } from 'mobx-react-lite';
-import JSMpegPlayer from '../shared/components/frigate/JSMpegPlayer';
-import { frigateApi } from '../services/frigate.proxy/frigate.api';
-import { Flex } from '@mantine/core';
-import AutoUpdatingCameraImage from '../shared/components/frigate/AutoUpdatingCameraImage';
-
-const Test = observer(() => {
- // const test = {
- // camera: 'Buhgalteria',
- // host: 'localhost:5000',
- // width: 800,
- // height: 600,
- // url : function() { return frigateApi.cameraWsURL(this.host, this.camera)},
- // }
-
- // return (
- //
- //
- //
- // );
-
- return (
-
-
-
- );
-})
-
-export default Test;
\ No newline at end of file
diff --git a/src/pages/TestItem.tsx b/src/pages/TestItem.tsx
new file mode 100644
index 0000000..cc6ae19
--- /dev/null
+++ b/src/pages/TestItem.tsx
@@ -0,0 +1,18 @@
+import { Paper } from '@mantine/core';
+import { useIntersection } from '@mantine/hooks';
+import React from 'react';
+
+const TestItem = () => {
+
+ const { ref, entry } = useIntersection({threshold: 0.1,})
+
+ return ( ({
+ backgroundColor: entry?.isIntersecting ? theme.colors.green[9] : theme.colors.red[9],
+ })}
+ />)
+};
+
+export default TestItem;
\ No newline at end of file
diff --git a/src/pages/TestPage.tsx b/src/pages/TestPage.tsx
new file mode 100644
index 0000000..741bbd1
--- /dev/null
+++ b/src/pages/TestPage.tsx
@@ -0,0 +1,59 @@
+import React, { Fragment, useContext, useEffect, useRef, useState } from 'react';
+import { observer } from 'mobx-react-lite';
+import { Button, Flex, Grid, Group, Indicator, Paper, Skeleton } from '@mantine/core';
+import RetryError from '../shared/components/RetryError';
+import { DatePickerInput } from '@mantine/dates';
+import HeadSearch from '../shared/components/HeadSearch';
+import ViewSelector from '../shared/components/ViewSelector';
+import { useIntersection } from '@mantine/hooks';
+import TestItem from './TestItem';
+
+const Test = observer(() => {
+ const [value, setValue] = useState<[Date | null, Date | null]>([null, null])
+
+ useEffect(() => {
+ console.log('value', value)
+ }, [value])
+
+ const handleClick = () => {
+ const startOfDay = new Date();
+ startOfDay.setHours(0, 0, 0, 0);
+ setValue([startOfDay, startOfDay])
+ }
+
+ const cards = (qty: number) => {
+ let items = []
+ for (let i = 0; i < qty; i++) {
+ items.push()
+ }
+ return items
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ {cards(60)}
+
+
+
+ );
+})
+
+export default Test;
\ No newline at end of file
diff --git a/src/router/routes.tsx b/src/router/routes.tsx
index 43cca09..ca2ece1 100644
--- a/src/router/routes.tsx
+++ b/src/router/routes.tsx
@@ -1,8 +1,8 @@
import {JSX} from "react";
-import Test from "../pages/Test"
-import MainBody from "../pages/MainBody";
+import Test from "../pages/TestPage"
+import MainPage from "../pages/MainPage";
import {routesPath} from "./routes.path";
-import RetryError from "../pages/RetryError";
+import RetryErrorPage from "../pages/RetryErrorPage";
import Forbidden from "../pages/403";
import NotFound from "../pages/404";
import SettingsPage from "../pages/SettingsPage";
@@ -53,11 +53,11 @@ export const routes: IRoute[] = [
},
{
path: routesPath.MAIN_PATH,
- component: ,
+ component: ,
},
{
path: routesPath.RETRY_ERROR_PATH,
- component: ,
+ component: ,
},
{
path: routesPath.FORBIDDEN_ERROR_PATH,
diff --git a/src/shared/components/frigate/CameraImage.tsx b/src/shared/components/AutoUpdatedImage.tsx
similarity index 51%
rename from src/shared/components/frigate/CameraImage.tsx
rename to src/shared/components/AutoUpdatedImage.tsx
index 956d426..8258949 100644
--- a/src/shared/components/frigate/CameraImage.tsx
+++ b/src/shared/components/AutoUpdatedImage.tsx
@@ -1,12 +1,13 @@
import { useEffect, useRef } from "react";
-import { CameraConfig } from "../../../types/frigateConfig";
-import { AspectRatio, Flex, createStyles, Text } from "@mantine/core";
+import { CameraConfig } from "../../types/frigateConfig";
+import { Flex, Text } from "@mantine/core";
import { useQuery } from "@tanstack/react-query";
-import CenterLoader from "../CenterLoader";
-import axios from "axios";
-import { frigateApi, proxyApi } from "../../../services/frigate.proxy/frigate.api";
+import CenterLoader from "./CenterLoader";
+import { frigateApi, proxyApi } from "../../services/frigate.proxy/frigate.api";
+import { useIntersection } from "@mantine/hooks";
+import CogwheelLoader from "./loaders/CogwheelLoader";
-interface CameraImageProps extends React.ImgHTMLAttributes {
+interface AutoUpdatedImageProps extends React.ImgHTMLAttributes {
className?: string;
cameraConfig?: CameraConfig;
onload?: () => void;
@@ -18,26 +19,29 @@ const AutoUpdatedImage = ({
imageUrl,
enabled,
...rest
-}: CameraImageProps) => {
+}: AutoUpdatedImageProps) => {
+ const { ref, entry } = useIntersection({threshold: 0.1,})
+ const isVisible = entry?.isIntersecting
+
const { data: imageBlob, refetch, isPending, isError } = useQuery({
queryKey: ['image', imageUrl],
queryFn: () => proxyApi.getImageFrigate(imageUrl),
staleTime: 60 * 1000,
gcTime: Infinity,
- refetchInterval: 60 * 1000,
+ refetchInterval: isVisible ? 30 * 1000 : undefined,
});
useEffect(() => {
- const intervalId = setInterval(() => {
- refetch();
- }, 60 * 1000);
+ if (isVisible) {
+ console.log('imageUrl is visible')
+ const intervalId = setInterval(() => {
+ refetch();
+ }, 60 * 1000);
+ return () => clearInterval(intervalId);
+ }
+ }, [refetch, isVisible]);
- return () => clearInterval(intervalId);
- }, [refetch]);
-
-
-
- if (isPending) return
+ if (isPending) return
if (isError) return (
@@ -51,7 +55,7 @@ const AutoUpdatedImage = ({
return (
<>
- {enabled ?
+ {enabled ?
:
Camera is disabled in config, no stream or snapshot available!
diff --git a/src/shared/components/CameraCard.tsx b/src/shared/components/CameraCard.tsx
index acbc99a..2e17559 100644
--- a/src/shared/components/CameraCard.tsx
+++ b/src/shared/components/CameraCard.tsx
@@ -1,12 +1,12 @@
import React from 'react';
import { CameraConfig } from '../../types/frigateConfig';
import { AspectRatio, Button, Card, Flex, Grid, Group, Space, Text, createStyles, useMantineTheme } from '@mantine/core';
-import AutoUpdatingCameraImage from './frigate/AutoUpdatingCameraImage';
import { useNavigate } from 'react-router-dom';
import { routesPath } from '../../router/routes.path';
import { GetCameraWHostWConfig, GetFrigateHost } from '../../services/frigate.proxy/frigate.schema';
import { frigateApi, mapHostToHostname, proxyApi } from '../../services/frigate.proxy/frigate.api';
-import AutoUpdatedImage from './frigate/CameraImage';
+import AutoUpdatedImage from './AutoUpdatedImage';
+import { recordingsPageQuery } from '../../pages/RecordingsPage';
const useStyles = createStyles((theme) => ({
@@ -50,7 +50,8 @@ const CameraCard = ({
navigate(url)
}
const handleOpenRecordings = () => {
- throw Error('Not yet implemented')
+ const url = `${routesPath.RECORDINGS_PATH}?${recordingsPageQuery.hostId}=${camera.frigateHost?.id}&${recordingsPageQuery.cameraId}=${camera.id}`
+ navigate(url)
}
return (
diff --git a/src/shared/components/FullProductModal.tsx b/src/shared/components/FullProductModal.tsx
index 9851c66..cd8aec0 100644
--- a/src/shared/components/FullProductModal.tsx
+++ b/src/shared/components/FullProductModal.tsx
@@ -7,7 +7,7 @@ import { Modal, createStyles, getStylesRef, rem, Text, Box, Flex, Grid, Divider,
import { useMediaQuery } from '@mantine/hooks';
import { dimensions } from '../dimensions/dimensions';
import { observer } from 'mobx-react-lite';
-import KomponentLoader from './CogwheelLoader';
+import KomponentLoader from './loaders/CogwheelLoader';
import ProductParameter from './ProductParameter';
import { productString } from '../strings/product.strings';
import { IconArrowBadgeLeft, IconArrowBadgeRight } from '@tabler/icons-react';
diff --git a/src/shared/components/RetryError.tsx b/src/shared/components/RetryError.tsx
new file mode 100644
index 0000000..79438be
--- /dev/null
+++ b/src/shared/components/RetryError.tsx
@@ -0,0 +1,25 @@
+import { Center, Text, ActionIcon } from '@mantine/core';
+import { IconRotateClockwise } from '@tabler/icons-react';
+import React from 'react';
+
+interface RetryErrorProps {
+ onRetry?: () => void
+}
+
+const RetryError = ({
+ onRetry
+}: RetryErrorProps) => {
+ const handleClick = () => {
+ if (onRetry) onRetry()
+ }
+ return (
+
+ Loading error
+
+
+
+
+ );
+};
+
+export default RetryError;
\ No newline at end of file
diff --git a/src/shared/components/accordion/CameraAccordion.tsx b/src/shared/components/accordion/CameraAccordion.tsx
index 8f1ca5a..dd95b8d 100644
--- a/src/shared/components/accordion/CameraAccordion.tsx
+++ b/src/shared/components/accordion/CameraAccordion.tsx
@@ -1,4 +1,4 @@
-import { Accordion, Center, Text } from '@mantine/core';
+import { Accordion, Center, Loader, Text } from '@mantine/core';
import React, { useContext, useEffect, useState } from 'react';
import { GetCameraWHostWConfig, GetFrigateHost } from '../../../services/frigate.proxy/frigate.schema';
import { useQuery } from '@tanstack/react-query';
@@ -6,20 +6,23 @@ import { frigateQueryKeys, mapHostToHostname, proxyApi } from '../../../services
import DayAccordion from './DayAccordion';
import { observer } from 'mobx-react-lite';
import { Context } from '../../..';
-import { getResolvedTimeZone } from '../frigate/dateUtil';
+import { getResolvedTimeZone, parseQueryDateToDate } from '../../utils/dateUtil';
+import RetryError from '../RetryError';
+import { strings } from '../../strings/strings';
+import { RecordSummary } from '../../../types/record';
interface CameraAccordionProps {
camera: GetCameraWHostWConfig,
host: GetFrigateHost
}
-const CameraAccordion = observer(({
+const CameraAccordion = ({
camera,
host
}: CameraAccordionProps) => {
const { recordingsStore: recStore } = useContext(Context)
- const { data, isPending, isError } = useQuery({
+ const { data, isPending, isError, refetch } = useQuery({
queryKey: [frigateQueryKeys.getRecordingsSummary, camera?.id],
queryFn: () => {
if (camera && host) {
@@ -44,29 +47,46 @@ const CameraAccordion = observer(({
setOpenedDay(value)
}
- if (isPending) return Loading...
- if (isError) return Loading error
+ if (isPending) return
+ if (isError) return
if (!data || !camera) return null
-
- const days = data.slice(0, 2).map(rec => (
-
- {rec.day}
-
-
+ const recodItem = (record: RecordSummary) => (
+
+ {strings.day}: {record.day}
+
+
+ )
- ))
+ const days = () => {
+ const [startDate, endDate] = recStore.selectedRange
+ if (startDate && endDate) {
+ return data
+ .filter(rec => {
+ const parsedRecDate = parseQueryDateToDate(rec.day)
+ if (parsedRecDate) {
+ return parsedRecDate >= startDate && parsedRecDate <= endDate
+ }
+ return false
+ })
+ .map(rec => recodItem(rec))
+ }
+ if ((startDate && endDate) || (!startDate && !endDate)) {
+ return data.map(rec => recodItem(rec))
+ }
+ return []
+ }
console.log('CameraAccordion rendered')
return (
- {days}
+ {days()}
)
-})
+}
-export default CameraAccordion;
\ No newline at end of file
+export default observer(CameraAccordion);
\ No newline at end of file
diff --git a/src/shared/components/accordion/DayAccordion.tsx b/src/shared/components/accordion/DayAccordion.tsx
index c29c330..edc14fb 100644
--- a/src/shared/components/accordion/DayAccordion.tsx
+++ b/src/shared/components/accordion/DayAccordion.tsx
@@ -1,25 +1,23 @@
-import { Accordion, Center, Flex, Group, Text } from '@mantine/core';
+import { Accordion, Center, Group, Text } from '@mantine/core';
import React, { useContext, useEffect, useState } from 'react';
-import { RecordHour, RecordSummary, Recording } from '../../../types/record';
-import Button from '../frigate/Button';
-import { IconPlayerPause, IconPlayerPlay, IconPlayerStop } from '@tabler/icons-react';
+import { RecordSummary } from '../../../types/record';
import { observer } from 'mobx-react-lite';
import PlayControl from './PlayControl';
-import { frigateApi, proxyApi } from '../../../services/frigate.proxy/frigate.api';
+import { proxyApi } from '../../../services/frigate.proxy/frigate.api';
import { Context } from '../../..';
-import VideoPlayer from '../frigate/VideoPlayer';
-import { getResolvedTimeZone } from '../frigate/dateUtil';
-import EventsAccordion from './EventsAccordion';
+import VideoPlayer from '../players/VideoPlayer';
+import { getResolvedTimeZone } from '../../utils/dateUtil';
import DayEventsAccordion from './DayEventsAccordion';
+import { strings } from '../../strings/strings';
interface RecordingAccordionProps {
recordSummary?: RecordSummary
}
-const DayAccordion = observer(({
+const DayAccordion = ({
recordSummary
}: RecordingAccordionProps) => {
- const { recordingsStore } = useContext(Context)
+ const { recordingsStore: recStore } = useContext(Context)
const [openVideoPlayer, setOpenVideoPlayer] = useState()
const [openedValue, setOpenedValue] = useState()
const [playerUrl, setPlayerUrl] = useState()
@@ -28,11 +26,11 @@ const DayAccordion = observer(({
if (openVideoPlayer) {
console.log('openVideoPlayer', openVideoPlayer)
if (openVideoPlayer) {
- recordingsStore.recordToPlay.day = recordSummary?.day
- recordingsStore.recordToPlay.hour = openVideoPlayer
- recordingsStore.recordToPlay.timezone = getResolvedTimeZone().replace('/', ',')
- const parsed = recordingsStore.getFullRecordForPlay(recordingsStore.recordToPlay)
- console.log('recordingsStore.playedRecord: ', recordingsStore.recordToPlay)
+ recStore.recordToPlay.day = recordSummary?.day
+ recStore.recordToPlay.hour = openVideoPlayer
+ recStore.recordToPlay.timezone = getResolvedTimeZone().replace('/', ',')
+ const parsed = recStore.getFullRecordForPlay(recStore.recordToPlay)
+ console.log('recordingsStore.playedRecord: ', recStore.recordToPlay)
if (parsed.success) {
const url = proxyApi.recordingURL(
parsed.data.hostName,
@@ -50,10 +48,10 @@ const DayAccordion = observer(({
}
}, [openVideoPlayer])
- if (!recordSummary || recordSummary.hours.length < 1) return (Not have record at that day)
+ if (!recordSummary ) return Not have record at that day
+ if (recordSummary.hours.length < 1) return Not have record at that day
const handleOpenPlayer = (hour: string) => {
- // console.log(`openVideoPlayer day:${recordSummary.day} hour:${hour}`)
if (openVideoPlayer !== hour) {
setOpenedValue(hour)
setOpenVideoPlayer(hour)
@@ -73,6 +71,17 @@ const DayAccordion = observer(({
console.log('DayAccordion rendered')
+ const hourLabel = (hour: string, eventsQty: number) => (
+
+ {strings.hour}: {hour}:00
+ {eventsQty > 0 ?
+ {strings.events}: {eventsQty}
+ :
+ {strings.notHaveEvents}
+ }
+
+ )
+
return (
- {recordSummary.hours.slice(0, 5).map(hour => (
+ {recordSummary.hours.map(hour => (
-
+
{openVideoPlayer === hour.hour && playerUrl ? : <>>}
{hour.events > 0 ?
:
- Not have events
+ {strings.notHaveEvents}
}
))}
)
-})
+}
-export default DayAccordion;
\ No newline at end of file
+export default observer(DayAccordion);
\ No newline at end of file
diff --git a/src/shared/components/accordion/DayEventsAccordion.tsx b/src/shared/components/accordion/DayEventsAccordion.tsx
index 05ea739..c673e4b 100644
--- a/src/shared/components/accordion/DayEventsAccordion.tsx
+++ b/src/shared/components/accordion/DayEventsAccordion.tsx
@@ -1,5 +1,6 @@
import { Accordion, Text } from '@mantine/core';
import React, { Suspense, lazy, useState } from 'react';
+import { strings } from '../../strings/strings';
const EventsAccordion = lazy(() => import('./EventsAccordion'))
interface DayEventsAccordionProps {
@@ -21,7 +22,7 @@ const DayEventsAccordion = ({
return (
- Events {qty}
+ {strings.events}: {qty}
{openedItem === hour ?
diff --git a/src/shared/components/accordion/EventsAccordion.tsx b/src/shared/components/accordion/EventsAccordion.tsx
index 0e94e65..fc714c7 100644
--- a/src/shared/components/accordion/EventsAccordion.tsx
+++ b/src/shared/components/accordion/EventsAccordion.tsx
@@ -1,13 +1,16 @@
-import { Accordion, Center, Text } from '@mantine/core';
+import { Accordion, Center, Group, Loader, Text } from '@mantine/core';
import { observer } from 'mobx-react-lite';
import React, { useContext, useEffect, useState } from 'react';
import { Context } from '../../..';
import { useQuery } from '@tanstack/react-query';
-import { frigateQueryKeys, proxyApi } from '../../../services/frigate.proxy/frigate.api';
+import { frigateQueryKeys, mapHostToHostname, proxyApi } from '../../../services/frigate.proxy/frigate.api';
import { getEventsQuerySchema } from '../../../services/frigate.proxy/frigate.schema';
import PlayControl from './PlayControl';
-import VideoPlayer from '../frigate/VideoPlayer';
-import { formatUnixTimestampToDateTime, getDurationFromTimestamps, getUnixTime, unixTimeToDate } from '../frigate/dateUtil';
+import VideoPlayer from '../players/VideoPlayer';
+import { getDurationFromTimestamps, getUnixTime, unixTimeToDate } from '../../utils/dateUtil';
+import RetryError from '../RetryError';
+import { strings } from '../../strings/strings';
+import { EventFrigate } from '../../../types/event';
/**
* @param day frigate format, e.g day: 2024-02-23
@@ -31,8 +34,6 @@ interface EventsAccordionProps {
const EventsAccordion = observer(({
day,
hour,
- cameraName,
- hostName,
// TODO labels, score
}: EventsAccordionProps) => {
const { recordingsStore: recStore } = useContext(Context)
@@ -40,17 +41,17 @@ const EventsAccordion = observer(({
const [openedValue, setOpenedValue] = useState()
const [playerUrl, setPlayerUrl] = useState()
- const inHostName = hostName || recStore.recordToPlay.hostName
- const inCameraName = cameraName || recStore.recordToPlay.cameraName
- const isRequiredParams = inCameraName && inHostName
+ const inHost = recStore.selectedHost
+ const inCamera = recStore.selectedCamera
+ const isRequiredParams = inHost && inCamera
const { data, isPending, isError, refetch } = useQuery({
- queryKey: [frigateQueryKeys.getEvents, day, hour, inCameraName, inHostName],
+ queryKey: [frigateQueryKeys.getEvents, inHost, inCamera, day, hour],
queryFn: () => {
if (!isRequiredParams) return null
const [startTime, endTime] = getUnixTime(day, hour)
const parsed = getEventsQuerySchema.safeParse({
- hostName: inHostName,
- camerasName: [inCameraName],
+ hostName: mapHostToHostname(inHost),
+ camerasName: [inCamera.name],
after: startTime,
before: endTime,
hasClip: true,
@@ -78,8 +79,8 @@ const EventsAccordion = observer(({
useEffect(() => {
if (openVideoPlayer) {
console.log('openVideoPlayer', openVideoPlayer)
- if (openVideoPlayer && inHostName) {
- const url = proxyApi.eventURL(inHostName, openVideoPlayer)
+ if (openVideoPlayer && inHost) {
+ const url = proxyApi.eventURL(mapHostToHostname(inHost), openVideoPlayer)
console.log('GET EVENT URL: ', url)
setPlayerUrl(url)
}
@@ -88,8 +89,8 @@ const EventsAccordion = observer(({
}
}, [openVideoPlayer])
- if (isPending) return Loading...
- if (isError) return Loading error
+ if (isPending) return
+ if (isError) return
if (!data || data.length < 1) return Not have events at that period
const handleOpenPlayer = (eventId: string) => {
@@ -111,6 +112,20 @@ const EventsAccordion = observer(({
setOpenVideoPlayer(undefined)
}
+ const eventLabel = (event: EventFrigate) => {
+ const time = unixTimeToDate(event.start_time)
+ const duration = getDurationFromTimestamps(event.start_time, event.end_time)
+ return (
+
+ {strings.player.object}: {event.label}
+ {time}
+ {duration ?
+ {duration}
+ : <>>}
+
+ )
+ }
+
return (
- {data.slice(0, 5).map(event => (
+ {data.map(event => (
{openVideoPlayer === event.id && playerUrl ? : <>>}
- Camera: {event.camera}
- Label: {event.label}
- Start: {unixTimeToDate(event.start_time)}
- Duration: {getDurationFromTimestamps(event.start_time, event.end_time)}
+
+ {strings.camera}: {event.camera}
+ {strings.player.object}: {event.label}
+
+
+ {strings.player.startTime}: {unixTimeToDate(event.start_time)}
+ {strings.player.duration}: {getDurationFromTimestamps(event.start_time, event.end_time)}
+
))}
diff --git a/src/shared/components/accordion/PlayControl.tsx b/src/shared/components/accordion/PlayControl.tsx
index 0208329..bad8324 100644
--- a/src/shared/components/accordion/PlayControl.tsx
+++ b/src/shared/components/accordion/PlayControl.tsx
@@ -1,9 +1,33 @@
-import { Flex, Group, Text } from '@mantine/core';
-import { IconPlayerPlay, IconPlayerStop } from '@tabler/icons-react';
import React from 'react';
+import { Flex, Group, Text, createStyles } from '@mantine/core';
+import { IconPlayerPlay, IconPlayerPlayFilled, IconPlayerStop, IconPlayerStopFilled } from '@tabler/icons-react';
+import { strings } from '../../strings/strings';
+
+
+const useStyles = createStyles((theme) => ({
+ group: {
+ backgroundColor: theme.colors.blue[7],
+ borderRadius: '1rem',
+ paddingLeft: '0.5rem',
+ paddingRight: '0.3rem',
+ '&:hover': {
+ backgroundColor: theme.fn.darken(theme.colors.blue[7], 0.2),
+ },
+ },
+ text: {
+ color: theme.white,
+ fontWeight: 'normal'
+ },
+ iconStop: {
+ color: theme.colors.red[5]
+ },
+ iconPlay: {
+ color: theme.colors.green[5]
+ }
+}))
interface PlayControlProps {
- label: string,
+ label: string | JSX.Element,
value: string,
openVideoPlayer?: string,
onClick?: (value: string) => void
@@ -15,26 +39,38 @@ const PlayControl = ({
openVideoPlayer,
onClick
}: PlayControlProps) => {
+ const { classes } = useStyles();
+
const handleClick = (value: string) => {
if (onClick) onClick(value)
}
return (
{label}
-
- {
+ {
+ event.stopPropagation()
+ handleClick(value)
+ }}
+ >
+ {
event.stopPropagation()
handleClick(value)
}}>
- {openVideoPlayer === value ? 'Stop Video' : 'Play Video'}
+ {openVideoPlayer === value ? strings.player.stopVideo : strings.player.startVideo}
{openVideoPlayer === value ?
- {
+ {
event.stopPropagation()
handleClick(value)
}} />
:
- {
+ {
event.stopPropagation()
handleClick(value)
}} />
diff --git a/src/shared/components/accordion/TestAccordion.tsx b/src/shared/components/accordion/TestAccordion.tsx
deleted file mode 100644
index a690bda..0000000
--- a/src/shared/components/accordion/TestAccordion.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import React from 'react';
-
-const TestAccordion = ({ camera }: { camera: any }) => {
- return (
-
- TEST ACCORDION {camera.name}
-
- );
-};
-
-export default TestAccordion;
\ No newline at end of file
diff --git a/src/shared/components/filters.aps/CameraSelectFilter.tsx b/src/shared/components/filters.aps/CameraSelectFilter.tsx
index 08c75db..768dcc6 100644
--- a/src/shared/components/filters.aps/CameraSelectFilter.tsx
+++ b/src/shared/components/filters.aps/CameraSelectFilter.tsx
@@ -3,9 +3,11 @@ import React, { useContext, useEffect } from 'react';
import { Context } from '../../..';
import { useQuery } from '@tanstack/react-query';
import { frigateApi, frigateQueryKeys } from '../../../services/frigate.proxy/frigate.api';
-import CogwheelLoader from '../CogwheelLoader';
-import { Center, Text } from '@mantine/core';
+import CogwheelLoader from '../loaders/CogwheelLoader';
+import { Center, Loader, Text } from '@mantine/core';
import OneSelectFilter, { OneSelectItem } from './OneSelectFilter';
+import { strings } from '../../strings/strings';
+import RetryError from '../RetryError';
interface CameraSelectFilterProps {
selectedHostId: string,
@@ -16,7 +18,7 @@ const CameraSelectFilter = ({
}: CameraSelectFilterProps) => {
const { recordingsStore: recStore } = useContext(Context)
- const { data, isError, isPending, isSuccess } = useQuery({
+ const { data, isError, isPending, isSuccess, refetch } = useQuery({
queryKey: [frigateQueryKeys.getFrigateHost, selectedHostId],
queryFn: () => frigateApi.getHost(selectedHostId)
})
@@ -30,8 +32,8 @@ const CameraSelectFilter = ({
}
}, [isSuccess])
- if (isPending) return
- if (isError) return Loading error!
+ if (isPending) return
+ if (isError) return
if (!data) return null
const camerasItems: OneSelectItem[] = data.cameras.map(camera => ({ value: camera.id, label: camera.name }))
@@ -51,7 +53,7 @@ const CameraSelectFilter = ({
return (
{
+ const { recordingsStore: recStore } = useContext(Context)
+
+ const handlePick = (value: [Date | null, Date | null]) => {
+ console.log('handlePick',value)
+ recStore.selectedRange = value
+ }
+
+ console.log('DateRangeSelectFilter rendered')
+ return (
+
+
+ {strings.selectRange}
+
+ {
+ const day = date.getDate();
+ const now = new Date().getDate()
+ return (
+
+ {day}
+
+ );
+ }}
+ />
+
+ );
+};
+
+
+
+export default observer(DateRangeSelectFilter);
\ No newline at end of file
diff --git a/src/shared/components/filters.aps/HostSelectFilter.tsx b/src/shared/components/filters.aps/HostSelectFilter.tsx
new file mode 100644
index 0000000..03d1402
--- /dev/null
+++ b/src/shared/components/filters.aps/HostSelectFilter.tsx
@@ -0,0 +1,63 @@
+import { Center, Text } from '@mantine/core';
+import { useQuery } from '@tanstack/react-query';
+import { observer } from 'mobx-react-lite';
+import React, { useContext, useEffect } from 'react';
+import { Context } from '../../..';
+import { frigateQueryKeys, frigateApi } from '../../../services/frigate.proxy/frigate.api';
+import { strings } from '../../strings/strings';
+import CogwheelLoader from '../loaders/CogwheelLoader';
+import OneSelectFilter, { OneSelectItem } from './OneSelectFilter';
+import RetryError from '../RetryError';
+
+const HostSelectFilter = () => {
+ const { recordingsStore: recStore } = useContext(Context)
+
+ const { data: hosts, isError, isPending, isSuccess, refetch } = useQuery({
+ queryKey: [frigateQueryKeys.getFrigateHosts],
+ queryFn: frigateApi.getHosts
+ })
+
+ useEffect(() => {
+ if (!hosts) return
+ if (recStore.hostIdParam) {
+ recStore.selectedHost = hosts.find(host => host.id === recStore.hostIdParam)
+ recStore.hostIdParam = undefined
+ }
+ }, [isSuccess])
+
+ if (isPending) return
+ if (isError) return
+
+ if (!hosts || hosts.length < 1) return null
+
+ const hostItems: OneSelectItem[] = hosts
+ .filter(host => host.enabled)
+ .map(host => ({ value: host.id, label: host.name }))
+
+ const handleSelect = (id: string, value: string) => {
+ const host = hosts?.find(host => host.id === value)
+ if (!host) {
+ recStore.selectedHost = undefined
+ recStore.selectedCamera = undefined
+ return
+ }
+ if (recStore.selectedHost?.id !== host.id) {
+ recStore.selectedCamera = undefined
+ }
+ recStore.selectedHost = host
+ }
+
+ return (
+
+ );
+};
+
+export default observer(HostSelectFilter);
\ No newline at end of file
diff --git a/src/shared/components/frigate/AutoUpdatingCameraImage.tsx b/src/shared/components/frigate/AutoUpdatingCameraImage.tsx
index 5630c54..75f3e80 100644
--- a/src/shared/components/frigate/AutoUpdatingCameraImage.tsx
+++ b/src/shared/components/frigate/AutoUpdatingCameraImage.tsx
@@ -1,97 +1,99 @@
-import { useCallback, useEffect, useMemo, useState } from "react";
-import CameraImage from "./CameraImage";
-import { CameraConfig } from "../../../types/frigateConfig";
-import { useDocumentVisibility } from "@mantine/hooks";
-import { AspectRatio, Flex } from "@mantine/core";
+// import { useCallback, useEffect, useMemo, useState } from "react";
+// import CameraImage from "./CameraImage";
+// import { CameraConfig } from "../../../types/frigateConfig";
+// import { useDocumentVisibility } from "@mantine/hooks";
+// import { AspectRatio, Flex } from "@mantine/core";
-interface AutoUpdatingCameraImageProps extends React.ImgHTMLAttributes {
- cameraConfig?: CameraConfig
- searchParams?: {};
- showFps?: boolean;
- className?: string;
- url: string
-};
+export {}
-// TODO Delete
-export default function AutoUpdatingCameraImage({
- cameraConfig,
- searchParams = "",
- showFps = true,
- className,
- url,
- ...rest
-}: AutoUpdatingCameraImageProps) {
- const [key, setKey] = useState(Date.now());
- const [fps, setFps] = useState("0");
- const [timeoutId, setTimeoutId] = useState();
+// interface AutoUpdatingCameraImageProps extends React.ImgHTMLAttributes {
+// cameraConfig?: CameraConfig
+// searchParams?: {};
+// showFps?: boolean;
+// className?: string;
+// url: string
+// };
- const windowVisible = useDocumentVisibility()
+// // TODO Delete
+// export default function AutoUpdatingCameraImage({
+// cameraConfig,
+// searchParams = "",
+// showFps = true,
+// className,
+// url,
+// ...rest
+// }: AutoUpdatingCameraImageProps) {
+// const [key, setKey] = useState(Date.now());
+// const [fps, setFps] = useState("0");
+// const [timeoutId, setTimeoutId] = useState();
+
+// const windowVisible = useDocumentVisibility()
- const reloadInterval = useMemo(() => {
- if (windowVisible === "hidden") {
- return -1; // no reason to update the image when the window is not visible
- }
+// const reloadInterval = useMemo(() => {
+// if (windowVisible === "hidden") {
+// return -1; // no reason to update the image when the window is not visible
+// }
- // if (liveReady) {
- // return 60000;
- // }
+// // if (liveReady) {
+// // return 60000;
+// // }
- // if (cameraActive) {
- // return 200;
- // }
+// // if (cameraActive) {
+// // return 200;
+// // }
- return 30000;
- }, [windowVisible]);
+// return 30000;
+// }, [windowVisible]);
- useEffect(() => {
- if (reloadInterval == -1) {
- return;
- }
+// useEffect(() => {
+// if (reloadInterval == -1) {
+// return;
+// }
- setKey(Date.now());
+// setKey(Date.now());
- return () => {
- if (timeoutId) {
- clearTimeout(timeoutId);
- setTimeoutId(undefined);
- }
- };
- }, [reloadInterval]);
+// return () => {
+// if (timeoutId) {
+// clearTimeout(timeoutId);
+// setTimeoutId(undefined);
+// }
+// };
+// }, [reloadInterval]);
- const handleLoad = useCallback(() => {
- if (reloadInterval == -1) {
- return;
- }
+// const handleLoad = useCallback(() => {
+// if (reloadInterval == -1) {
+// return;
+// }
- const loadTime = Date.now() - key;
+// const loadTime = Date.now() - key;
- if (showFps) {
- setFps((1000 / Math.max(loadTime, reloadInterval)).toFixed(1));
- }
+// if (showFps) {
+// setFps((1000 / Math.max(loadTime, reloadInterval)).toFixed(1));
+// }
- setTimeoutId(
- setTimeout(
- () => {
- setKey(Date.now());
- },
- loadTime > reloadInterval ? 1 : reloadInterval
- )
- );
- }, [key, setFps]);
+// setTimeoutId(
+// setTimeout(
+// () => {
+// setKey(Date.now());
+// },
+// loadTime > reloadInterval ? 1 : reloadInterval
+// )
+// );
+// }, [key, setFps]);
- return (
- //
-
- {/* */}
- {showFps ? Displaying at {fps}fps : null}
-
- //
- );
-}
+// return (
+// //
+//
+// {/* */}
+// {showFps ? Displaying at {fps}fps : null}
+//
+// //
+// );
+// }
diff --git a/src/shared/components/frigate/Button.jsx b/src/shared/components/frigate/Button.jsx
index f422e40..87d6b3d 100644
--- a/src/shared/components/frigate/Button.jsx
+++ b/src/shared/components/frigate/Button.jsx
@@ -1,115 +1,117 @@
-import Tooltip from './Tooltip';
-import { Fragment, useCallback, useRef, useState } from 'react';
+// import Tooltip from './Tooltip';
+// import { Fragment, useCallback, useRef, useState } from 'react';
-const ButtonColors = {
- blue: {
- contained: 'bg-blue-500 focus:bg-blue-400 active:bg-blue-600 ring-blue-300',
- outlined:
- 'text-blue-500 border-2 border-blue-500 hover:bg-blue-500 hover:bg-opacity-20 focus:bg-blue-500 focus:bg-opacity-40 active:bg-blue-500 active:bg-opacity-40',
- text: 'text-blue-500 hover:bg-blue-500 hover:bg-opacity-20 focus:bg-blue-500 focus:bg-opacity-40 active:bg-blue-500 active:bg-opacity-40',
- iconOnly: 'text-blue-500 hover:text-blue-200',
- },
- red: {
- contained: 'bg-red-500 focus:bg-red-400 active:bg-red-600 ring-red-300',
- outlined:
- 'text-red-500 border-2 border-red-500 hover:bg-red-500 hover:bg-opacity-20 focus:bg-red-500 focus:bg-opacity-40 active:bg-red-500 active:bg-opacity-40',
- text: 'text-red-500 hover:bg-red-500 hover:bg-opacity-20 focus:bg-red-500 focus:bg-opacity-40 active:bg-red-500 active:bg-opacity-40',
- iconOnly: 'text-red-500 hover:text-red-200',
- },
- yellow: {
- contained: 'bg-yellow-500 focus:bg-yellow-400 active:bg-yellow-600 ring-yellow-300',
- outlined:
- 'text-yellow-500 border-2 border-yellow-500 hover:bg-yellow-500 hover:bg-opacity-20 focus:bg-yellow-500 focus:bg-opacity-40 active:bg-yellow-500 active:bg-opacity-40',
- text: 'text-yellow-500 hover:bg-yellow-500 hover:bg-opacity-20 focus:bg-yellow-500 focus:bg-opacity-40 active:bg-yellow-500 active:bg-opacity-40',
- iconOnly: 'text-yellow-500 hover:text-yellow-200',
- },
- green: {
- contained: 'bg-green-500 focus:bg-green-400 active:bg-green-600 ring-green-300',
- outlined:
- 'text-green-500 border-2 border-green-500 hover:bg-green-500 hover:bg-opacity-20 focus:bg-green-500 focus:bg-opacity-40 active:bg-green-500 active:bg-opacity-40',
- text: 'text-green-500 hover:bg-green-500 hover:bg-opacity-20 focus:bg-green-500 focus:bg-opacity-40 active:bg-green-500 active:bg-opacity-40',
- iconOnly: 'text-green-500 hover:text-green-200',
- },
- gray: {
- contained: 'bg-gray-500 focus:bg-gray-400 active:bg-gray-600 ring-gray-300',
- outlined:
- 'text-gray-500 border-2 border-gray-500 hover:bg-gray-500 hover:bg-opacity-20 focus:bg-gray-500 focus:bg-opacity-40 active:bg-gray-500 active:bg-opacity-40',
- text: 'text-gray-500 hover:bg-gray-500 hover:bg-opacity-20 focus:bg-gray-500 focus:bg-opacity-40 active:bg-gray-500 active:bg-opacity-40',
- iconOnly: 'text-gray-500 hover:text-gray-200',
- },
- disabled: {
- contained: 'bg-gray-400',
- outlined:
- 'text-gray-500 border-2 border-gray-500 hover:bg-gray-500 hover:bg-opacity-20 focus:bg-gray-500 focus:bg-opacity-40 active:bg-gray-500 active:bg-opacity-40',
- text: 'text-gray-500 hover:bg-gray-500 hover:bg-opacity-20 focus:bg-gray-500 focus:bg-opacity-40 active:bg-gray-500 active:bg-opacity-40',
- iconOnly: 'text-gray-500 hover:text-gray-200',
- },
- black: {
- contained: '',
- outlined: '',
- text: 'text-black dark:text-white',
- iconOnly: '',
- },
-};
+export {}
-const ButtonTypes = {
- contained: 'text-white shadow focus:shadow-xl hover:shadow-md',
- outlined: '',
- text: 'transition-opacity',
- iconOnly: 'transition-opacity',
-};
+// const ButtonColors = {
+// blue: {
+// contained: 'bg-blue-500 focus:bg-blue-400 active:bg-blue-600 ring-blue-300',
+// outlined:
+// 'text-blue-500 border-2 border-blue-500 hover:bg-blue-500 hover:bg-opacity-20 focus:bg-blue-500 focus:bg-opacity-40 active:bg-blue-500 active:bg-opacity-40',
+// text: 'text-blue-500 hover:bg-blue-500 hover:bg-opacity-20 focus:bg-blue-500 focus:bg-opacity-40 active:bg-blue-500 active:bg-opacity-40',
+// iconOnly: 'text-blue-500 hover:text-blue-200',
+// },
+// red: {
+// contained: 'bg-red-500 focus:bg-red-400 active:bg-red-600 ring-red-300',
+// outlined:
+// 'text-red-500 border-2 border-red-500 hover:bg-red-500 hover:bg-opacity-20 focus:bg-red-500 focus:bg-opacity-40 active:bg-red-500 active:bg-opacity-40',
+// text: 'text-red-500 hover:bg-red-500 hover:bg-opacity-20 focus:bg-red-500 focus:bg-opacity-40 active:bg-red-500 active:bg-opacity-40',
+// iconOnly: 'text-red-500 hover:text-red-200',
+// },
+// yellow: {
+// contained: 'bg-yellow-500 focus:bg-yellow-400 active:bg-yellow-600 ring-yellow-300',
+// outlined:
+// 'text-yellow-500 border-2 border-yellow-500 hover:bg-yellow-500 hover:bg-opacity-20 focus:bg-yellow-500 focus:bg-opacity-40 active:bg-yellow-500 active:bg-opacity-40',
+// text: 'text-yellow-500 hover:bg-yellow-500 hover:bg-opacity-20 focus:bg-yellow-500 focus:bg-opacity-40 active:bg-yellow-500 active:bg-opacity-40',
+// iconOnly: 'text-yellow-500 hover:text-yellow-200',
+// },
+// green: {
+// contained: 'bg-green-500 focus:bg-green-400 active:bg-green-600 ring-green-300',
+// outlined:
+// 'text-green-500 border-2 border-green-500 hover:bg-green-500 hover:bg-opacity-20 focus:bg-green-500 focus:bg-opacity-40 active:bg-green-500 active:bg-opacity-40',
+// text: 'text-green-500 hover:bg-green-500 hover:bg-opacity-20 focus:bg-green-500 focus:bg-opacity-40 active:bg-green-500 active:bg-opacity-40',
+// iconOnly: 'text-green-500 hover:text-green-200',
+// },
+// gray: {
+// contained: 'bg-gray-500 focus:bg-gray-400 active:bg-gray-600 ring-gray-300',
+// outlined:
+// 'text-gray-500 border-2 border-gray-500 hover:bg-gray-500 hover:bg-opacity-20 focus:bg-gray-500 focus:bg-opacity-40 active:bg-gray-500 active:bg-opacity-40',
+// text: 'text-gray-500 hover:bg-gray-500 hover:bg-opacity-20 focus:bg-gray-500 focus:bg-opacity-40 active:bg-gray-500 active:bg-opacity-40',
+// iconOnly: 'text-gray-500 hover:text-gray-200',
+// },
+// disabled: {
+// contained: 'bg-gray-400',
+// outlined:
+// 'text-gray-500 border-2 border-gray-500 hover:bg-gray-500 hover:bg-opacity-20 focus:bg-gray-500 focus:bg-opacity-40 active:bg-gray-500 active:bg-opacity-40',
+// text: 'text-gray-500 hover:bg-gray-500 hover:bg-opacity-20 focus:bg-gray-500 focus:bg-opacity-40 active:bg-gray-500 active:bg-opacity-40',
+// iconOnly: 'text-gray-500 hover:text-gray-200',
+// },
+// black: {
+// contained: '',
+// outlined: '',
+// text: 'text-black dark:text-white',
+// iconOnly: '',
+// },
+// };
-export default function Button({
- children,
- className = '',
- color = 'blue',
- disabled = false,
- ariaCapitalize = false,
- href,
- target,
- type = 'contained',
- ...attrs
-}) {
- const [hovered, setHovered] = useState(false);
- const ref = useRef();
+// const ButtonTypes = {
+// contained: 'text-white shadow focus:shadow-xl hover:shadow-md',
+// outlined: '',
+// text: 'transition-opacity',
+// iconOnly: 'transition-opacity',
+// };
- let classes = `whitespace-nowrap flex items-center space-x-1 ${className} ${ButtonTypes[type]} ${
- ButtonColors[disabled ? 'disabled' : color][type]
- } font-sans inline-flex font-bold uppercase text-xs px-1.5 md:px-2 py-2 rounded outline-none focus:outline-none ring-opacity-50 transition-shadow transition-colors ${
- disabled ? 'cursor-not-allowed' : `${type == 'iconOnly' ? '' : 'focus:ring-2'} cursor-pointer`
- }`;
+// export default function Button({
+// children,
+// className = '',
+// color = 'blue',
+// disabled = false,
+// ariaCapitalize = false,
+// href,
+// target,
+// type = 'contained',
+// ...attrs
+// }) {
+// const [hovered, setHovered] = useState(false);
+// const ref = useRef();
- if (disabled) {
- classes = classes.replace(/(?:focus|active|hover):[^ ]+/g, '');
- }
+// let classes = `whitespace-nowrap flex items-center space-x-1 ${className} ${ButtonTypes[type]} ${
+// ButtonColors[disabled ? 'disabled' : color][type]
+// } font-sans inline-flex font-bold uppercase text-xs px-1.5 md:px-2 py-2 rounded outline-none focus:outline-none ring-opacity-50 transition-shadow transition-colors ${
+// disabled ? 'cursor-not-allowed' : `${type == 'iconOnly' ? '' : 'focus:ring-2'} cursor-pointer`
+// }`;
- const handleMousenter = useCallback(() => {
- setHovered(true);
- }, []);
+// if (disabled) {
+// classes = classes.replace(/(?:focus|active|hover):[^ ]+/g, '');
+// }
- const handleMouseleave = useCallback(() => {
- setHovered(false);
- }, []);
+// const handleMousenter = useCallback(() => {
+// setHovered(true);
+// }, []);
- const Element = href ? 'a' : 'div';
+// const handleMouseleave = useCallback(() => {
+// setHovered(false);
+// }, []);
- return (
-
-
- {children}
-
- {hovered && attrs['aria-label'] ? : null}
-
- );
-}
+// const Element = href ? 'a' : 'div';
+
+// return (
+//
+//
+// {children}
+//
+// {hovered && attrs['aria-label'] ? : null}
+//
+// );
+// }
diff --git a/src/shared/components/frigate/DebugCameraImage.tsx b/src/shared/components/frigate/DebugCameraImage.tsx
index e129d06..0367a4a 100644
--- a/src/shared/components/frigate/DebugCameraImage.tsx
+++ b/src/shared/components/frigate/DebugCameraImage.tsx
@@ -1,159 +1,161 @@
-import { useCallback, useMemo, useState } from "react";
-import AutoUpdatingCameraImage from "./AutoUpdatingCameraImage";
-import { CameraConfig } from "../../../types/frigateConfig";
-import { usePersistence } from "../../../hooks/use-persistence";
-import { Button, Switch, Text } from "@mantine/core";
-import { Card, CardContent, CardHeader, CardTitle } from "./card";
-import { IconSettings } from "@tabler/icons-react";
+// import { useCallback, useMemo, useState } from "react";
+// import AutoUpdatingCameraImage from "./AutoUpdatingCameraImage";
+// import { CameraConfig } from "../../../types/frigateConfig";
+// import { usePersistence } from "../../../hooks/use-persistence";
+// import { Button, Switch, Text } from "@mantine/core";
+// import { Card, CardContent, CardHeader, CardTitle } from "./card";
+// import { IconSettings } from "@tabler/icons-react";
-type Options = { [key: string]: boolean };
+export {}
-const emptyObject = Object.freeze({});
+// type Options = { [key: string]: boolean };
-type DebugCameraImageProps = {
- className?: string;
- cameraConfig: CameraConfig
- url: string
-};
+// const emptyObject = Object.freeze({});
-export default function DebugCameraImage({
- className,
- cameraConfig,
- url,
-}: DebugCameraImageProps) {
- const [showSettings, setShowSettings] = useState(false);
- const [options, setOptions] = usePersistence(
- `${cameraConfig?.name}-feed`,
- emptyObject
- );
- const handleSetOption = useCallback(
- (id: string, value: boolean) => {
- const newOptions = { ...options, [id]: value };
- setOptions(newOptions);
- },
- [options]
- );
- const searchParams = useMemo(
- () =>
- new URLSearchParams(
- Object.keys(options).reduce((memo, key) => {
- //@ts-ignore we know this is correct
- memo.push([key, options[key] === true ? "1" : "0"]);
- return memo;
- }, [])
- ),
- [options]
- );
- const handleToggleSettings = useCallback(() => {
- setShowSettings(!showSettings);
- }, [showSettings]);
+// type DebugCameraImageProps = {
+// className?: string;
+// cameraConfig: CameraConfig
+// url: string
+// };
- return (
-
-
-
- {showSettings ? (
-
-
- Options
-
-
-
-
-
- ) : null}
-
- );
-}
+// export default function DebugCameraImage({
+// className,
+// cameraConfig,
+// url,
+// }: DebugCameraImageProps) {
+// const [showSettings, setShowSettings] = useState(false);
+// const [options, setOptions] = usePersistence(
+// `${cameraConfig?.name}-feed`,
+// emptyObject
+// );
+// const handleSetOption = useCallback(
+// (id: string, value: boolean) => {
+// const newOptions = { ...options, [id]: value };
+// setOptions(newOptions);
+// },
+// [options]
+// );
+// const searchParams = useMemo(
+// () =>
+// new URLSearchParams(
+// Object.keys(options).reduce((memo, key) => {
+// //@ts-ignore we know this is correct
+// memo.push([key, options[key] === true ? "1" : "0"]);
+// return memo;
+// }, [])
+// ),
+// [options]
+// );
+// const handleToggleSettings = useCallback(() => {
+// setShowSettings(!showSettings);
+// }, [showSettings]);
-type DebugSettingsProps = {
- handleSetOption: (id: string, value: boolean) => void;
- options: Options;
-};
+// return (
+//
+//
+//
+// {showSettings ? (
+//
+//
+// Options
+//
+//
+//
+//
+//
+// ) : null}
+//
+// );
+// }
-function DebugSettings({ handleSetOption, options }: DebugSettingsProps) {
- return (
-
-
- { }}
- // onCheckedChange={(isChecked) => {
- // handleSetOption("bbox", isChecked);
- // }}
- />
- {/* */}
- Bounding Box
-
-
- {
- // handleSetOption("timestamp", isChecked);
- // }}
- />
- {/* */}
- Timestamp
-
-
- {
- // handleSetOption("zones", isChecked);
- // }}
- />
- {/* */}
- Zones
-
-
- {
- // handleSetOption("mask", isChecked);
- // }}
- />
- {/* */}
- Mask
-
-
- {
- // handleSetOption("motion", isChecked);
- // }}
- />
- {/* */}
- Motion
-
-
- {
- // handleSetOption("regions", isChecked);
- // }}
- />
- {/* */}
- Regions
-
-
- );
-}
+// type DebugSettingsProps = {
+// handleSetOption: (id: string, value: boolean) => void;
+// options: Options;
+// };
+
+// function DebugSettings({ handleSetOption, options }: DebugSettingsProps) {
+// return (
+//
+//
+// { }}
+// // onCheckedChange={(isChecked) => {
+// // handleSetOption("bbox", isChecked);
+// // }}
+// />
+// {/* */}
+// Bounding Box
+//
+//
+// {
+// // handleSetOption("timestamp", isChecked);
+// // }}
+// />
+// {/* */}
+// Timestamp
+//
+//
+// {
+// // handleSetOption("zones", isChecked);
+// // }}
+// />
+// {/* */}
+// Zones
+//
+//
+// {
+// // handleSetOption("mask", isChecked);
+// // }}
+// />
+// {/* */}
+// Mask
+//
+//
+// {
+// // handleSetOption("motion", isChecked);
+// // }}
+// />
+// {/* */}
+// Motion
+//
+//
+// {
+// // handleSetOption("regions", isChecked);
+// // }}
+// />
+// {/* */}
+// Regions
+//
+//
+// );
+// }
diff --git a/src/shared/components/frigate/Dialog.jsx b/src/shared/components/frigate/Dialog.jsx
index 6bf9e31..fc5cc79 100644
--- a/src/shared/components/frigate/Dialog.jsx
+++ b/src/shared/components/frigate/Dialog.jsx
@@ -1,35 +1,37 @@
-import { h, Fragment } from 'preact';
-import { createPortal } from 'preact/compat';
-import { useState, useEffect } from 'preact/hooks';
+// import { h, Fragment } from 'preact';
+// import { createPortal } from 'preact/compat';
+// import { useState, useEffect } from 'preact/hooks';
-export default function Dialog({ children, portalRootID = 'dialogs' }) {
- const portalRoot = portalRootID && document.getElementById(portalRootID);
- const [show, setShow] = useState(false);
+export {}
- useEffect(() => {
- window.requestAnimationFrame(() => {
- setShow(true);
- });
- }, []);
+// export default function Dialog({ children, portalRootID = 'dialogs' }) {
+// const portalRoot = portalRootID && document.getElementById(portalRootID);
+// const [show, setShow] = useState(false);
- const dialog = (
-
-
-
- );
+// useEffect(() => {
+// window.requestAnimationFrame(() => {
+// setShow(true);
+// });
+// }, []);
- return portalRoot ? createPortal(dialog, portalRoot) : dialog;
-}
+// const dialog = (
+//
+//
+//
+// {children}
+//
+//
+//
+// );
+
+// return portalRoot ? createPortal(dialog, portalRoot) : dialog;
+// }
diff --git a/src/shared/components/frigate/Link.jsx b/src/shared/components/frigate/Link.jsx
index 3547996..c8aa803 100644
--- a/src/shared/components/frigate/Link.jsx
+++ b/src/shared/components/frigate/Link.jsx
@@ -1,16 +1,18 @@
-import { h } from 'preact';
-import { Link as RouterLink } from 'preact-router/match';
+// import { h } from 'preact';
+// import { Link as RouterLink } from 'preact-router/match';
-export default function Link({
- activeClassName = '',
- className = 'text-blue-500 hover:underline',
- children,
- href,
- ...props
-}) {
- return (
-
- {children}
-
- );
-}
+export {}
+
+// export default function Link({
+// activeClassName = '',
+// className = 'text-blue-500 hover:underline',
+// children,
+// href,
+// ...props
+// }) {
+// return (
+//
+// {children}
+//
+// );
+// }
diff --git a/src/shared/components/frigate/Menu.jsx b/src/shared/components/frigate/Menu.jsx
index 34ff203..e3bb19a 100644
--- a/src/shared/components/frigate/Menu.jsx
+++ b/src/shared/components/frigate/Menu.jsx
@@ -1,48 +1,50 @@
-import { h } from 'preact';
-import RelativeModal from './RelativeModal';
-import { useCallback } from 'preact/hooks';
+// import { h } from 'preact';
+// import RelativeModal from './RelativeModal';
+// import { useCallback } from 'preact/hooks';
-export default function Menu({ className, children, onDismiss, relativeTo, widthRelative }) {
- return relativeTo ? (
-
- ) : null;
-}
+export {}
-export function MenuItem({ focus, icon: Icon, label, href, onSelect, value, ...attrs }) {
- const handleClick = useCallback(() => {
- onSelect && onSelect(value, label);
- }, [onSelect, value, label]);
+// export default function Menu({ className, children, onDismiss, relativeTo, widthRelative }) {
+// return relativeTo ? (
+//
+// ) : null;
+// }
- const Element = href ? 'a' : 'div';
+// export function MenuItem({ focus, icon: Icon, label, href, onSelect, value, ...attrs }) {
+// const handleClick = useCallback(() => {
+// onSelect && onSelect(value, label);
+// }, [onSelect, value, label]);
- return (
-
- {Icon ? (
-
-
-
- ) : null}
- {label}
-
- );
-}
+// const Element = href ? 'a' : 'div';
-export function MenuSeparator() {
- return ;
-}
+// return (
+//
+// {Icon ? (
+//
+//
+//
+// ) : null}
+// {label}
+//
+// );
+// }
+
+// export function MenuSeparator() {
+// return ;
+// }
diff --git a/src/shared/components/frigate/MultiSelect.jsx b/src/shared/components/frigate/MultiSelect.jsx
index 5c706fd..93bb154 100644
--- a/src/shared/components/frigate/MultiSelect.jsx
+++ b/src/shared/components/frigate/MultiSelect.jsx
@@ -1,70 +1,72 @@
-import { h } from 'preact';
-import { useRef, useState } from 'preact/hooks';
-import Menu from './Menu';
-import { ArrowDropdown } from '../icons/ArrowDropdown';
-import Heading from './Heading';
-import Button from './Button';
-import SelectOnlyIcon from '../icons/SelectOnly';
+// import { h } from 'preact';
+// import { useRef, useState } from 'preact/hooks';
+// import Menu from './Menu';
+// import { ArrowDropdown } from '../icons/ArrowDropdown';
+// import Heading from './Heading';
+// import Button from './Button';
+// import SelectOnlyIcon from '../icons/SelectOnly';
-export default function MultiSelect({ className, title, options, selection, onToggle, onShowAll, onSelectSingle }) {
- const popupRef = useRef(null);
+export {}
- const [state, setState] = useState({
- showMenu: false,
- });
+// export default function MultiSelect({ className, title, options, selection, onToggle, onShowAll, onSelectSingle }) {
+// const popupRef = useRef(null);
- const isOptionSelected = (item) => {
- return selection == 'all' || selection.split(',').indexOf(item) > -1;
- };
+// const [state, setState] = useState({
+// showMenu: false,
+// });
- const menuHeight = Math.round(window.innerHeight * 0.55);
- return (
-
-
setState({ showMenu: true })}>
-
-
-
- {state.showMenu ? (
-
- ) : null}
-
- );
-}
+// const isOptionSelected = (item) => {
+// return selection == 'all' || selection.split(',').indexOf(item) > -1;
+// };
+
+// const menuHeight = Math.round(window.innerHeight * 0.55);
+// return (
+//
+//
setState({ showMenu: true })}>
+//
+//
+//
+// {state.showMenu ? (
+//
+// ) : null}
+//
+// );
+// }
diff --git a/src/shared/components/frigate/Tabs.jsx b/src/shared/components/frigate/Tabs.jsx
index 2e14227..dd7a098 100644
--- a/src/shared/components/frigate/Tabs.jsx
+++ b/src/shared/components/frigate/Tabs.jsx
@@ -1,41 +1,43 @@
-import { h } from 'preact';
-import { useCallback, useState } from 'preact/hooks';
+// import { h } from 'preact';
+// import { useCallback, useState } from 'preact/hooks';
-export function Tabs({ children, selectedIndex: selectedIndexProp, onChange, className }) {
- const [selectedIndex, setSelectedIndex] = useState(selectedIndexProp);
+export {}
- const handleSelected = useCallback(
- (index) => () => {
- setSelectedIndex(index);
- onChange && onChange(index);
- },
- [onChange]
- );
+// export function Tabs({ children, selectedIndex: selectedIndexProp, onChange, className }) {
+// const [selectedIndex, setSelectedIndex] = useState(selectedIndexProp);
- const RenderChildren = useCallback(() => {
- return children.map((child, i) => {
- child.props.selected = i === selectedIndex;
- child.props.onClick = handleSelected(i);
- return child;
- });
- }, [selectedIndex, children, handleSelected]);
+// const handleSelected = useCallback(
+// (index) => () => {
+// setSelectedIndex(index);
+// onChange && onChange(index);
+// },
+// [onChange]
+// );
- return (
-
-
-
- );
-}
+// const RenderChildren = useCallback(() => {
+// return children.map((child, i) => {
+// child.props.selected = i === selectedIndex;
+// child.props.onClick = handleSelected(i);
+// return child;
+// });
+// }, [selectedIndex, children, handleSelected]);
-export function TextTab({ selected, text, onClick, disabled }) {
- const selectedStyle = disabled
- ? 'text-gray-400 dark:text-gray-600 bg-transparent'
- : selected
- ? 'text-white bg-blue-500 dark:text-black dark:bg-white'
- : 'text-black dark:text-white bg-transparent';
- return (
-
- );
-}
+// return (
+//
+//
+//
+// );
+// }
+
+// export function TextTab({ selected, text, onClick, disabled }) {
+// const selectedStyle = disabled
+// ? 'text-gray-400 dark:text-gray-600 bg-transparent'
+// : selected
+// ? 'text-white bg-blue-500 dark:text-black dark:bg-white'
+// : 'text-black dark:text-white bg-transparent';
+// return (
+//
+// );
+// }
diff --git a/src/shared/components/frigate/TimeAgo.tsx b/src/shared/components/frigate/TimeAgo.tsx
index 38c80c7..54a5b5e 100644
--- a/src/shared/components/frigate/TimeAgo.tsx
+++ b/src/shared/components/frigate/TimeAgo.tsx
@@ -1,83 +1,85 @@
-import { FunctionComponent, useEffect, useMemo, useState } from 'react';
+// import { FunctionComponent, useEffect, useMemo, useState } from 'react';
-interface IProp {
- /** The time to calculate time-ago from */
- time: Date;
- /** OPTIONAL: overwrite current time */
- currentTime?: Date;
- /** OPTIONAL: boolean that determines whether to show the time-ago text in dense format */
- dense?: boolean;
- /** OPTIONAL: set custom refresh interval in milliseconds, default 1000 (1 sec) */
- refreshInterval?: number;
-}
+// interface IProp {
+// /** The time to calculate time-ago from */
+// time: Date;
+// /** OPTIONAL: overwrite current time */
+// currentTime?: Date;
+// /** OPTIONAL: boolean that determines whether to show the time-ago text in dense format */
+// dense?: boolean;
+// /** OPTIONAL: set custom refresh interval in milliseconds, default 1000 (1 sec) */
+// refreshInterval?: number;
+// }
-type TimeUnit = {
- unit: string;
- full: string;
- value: number;
-};
+export {}
-const timeAgo = ({ time, currentTime = new Date(), dense = false }: IProp): string => {
- if (typeof time !== 'number' || time < 0) return 'Invalid Time Provided';
+// type TimeUnit = {
+// unit: string;
+// full: string;
+// value: number;
+// };
- const pastTime: Date = new Date(time);
- const elapsedTime: number = currentTime.getTime() - pastTime.getTime();
+// const timeAgo = ({ time, currentTime = new Date(), dense = false }: IProp): string => {
+// if (typeof time !== 'number' || time < 0) return 'Invalid Time Provided';
- const timeUnits: TimeUnit[] = [
- { unit: 'yr', full: 'year', value: 31536000 },
- { unit: 'mo', full: 'month', value: 0 },
- { unit: 'd', full: 'day', value: 86400 },
- { unit: 'h', full: 'hour', value: 3600 },
- { unit: 'm', full: 'minute', value: 60 },
- { unit: 's', full: 'second', value: 1 },
- ];
+// const pastTime: Date = new Date(time);
+// const elapsedTime: number = currentTime.getTime() - pastTime.getTime();
- const elapsed: number = elapsedTime / 1000;
- if (elapsed < 10) {
- return 'just now';
- }
+// const timeUnits: TimeUnit[] = [
+// { unit: 'yr', full: 'year', value: 31536000 },
+// { unit: 'mo', full: 'month', value: 0 },
+// { unit: 'd', full: 'day', value: 86400 },
+// { unit: 'h', full: 'hour', value: 3600 },
+// { unit: 'm', full: 'minute', value: 60 },
+// { unit: 's', full: 'second', value: 1 },
+// ];
- for (let i = 0; i < timeUnits.length; i++) {
- // if months
- if (i === 1) {
- // Get the month and year for the time provided
- const pastMonth = pastTime.getUTCMonth();
- const pastYear = pastTime.getUTCFullYear();
+// const elapsed: number = elapsedTime / 1000;
+// if (elapsed < 10) {
+// return 'just now';
+// }
- // get current month and year
- const currentMonth = currentTime.getUTCMonth();
- const currentYear = currentTime.getUTCFullYear();
+// for (let i = 0; i < timeUnits.length; i++) {
+// // if months
+// if (i === 1) {
+// // Get the month and year for the time provided
+// const pastMonth = pastTime.getUTCMonth();
+// const pastYear = pastTime.getUTCFullYear();
- let monthDiff = (currentYear - pastYear) * 12 + (currentMonth - pastMonth);
+// // get current month and year
+// const currentMonth = currentTime.getUTCMonth();
+// const currentYear = currentTime.getUTCFullYear();
- // check if the time provided is the previous month but not exceeded 1 month ago.
- if (currentTime.getUTCDate() < pastTime.getUTCDate()) {
- monthDiff--;
- }
+// let monthDiff = (currentYear - pastYear) * 12 + (currentMonth - pastMonth);
- if (monthDiff > 0) {
- const unitAmount = monthDiff;
- return `${unitAmount}${dense ? timeUnits[i].unit : ` ${timeUnits[i].full}`}${dense ? '' : 's'} ago`;
- }
- } else if (elapsed >= timeUnits[i].value) {
- const unitAmount: number = Math.floor(elapsed / timeUnits[i].value);
- return `${unitAmount}${dense ? timeUnits[i].unit : ` ${timeUnits[i].full}`}${dense ? '' : 's'} ago`;
- }
- }
- return 'Invalid Time';
-};
+// // check if the time provided is the previous month but not exceeded 1 month ago.
+// if (currentTime.getUTCDate() < pastTime.getUTCDate()) {
+// monthDiff--;
+// }
-const TimeAgo: FunctionComponent = ({ refreshInterval = 1000, ...rest }): JSX.Element => {
- const [currentTime, setCurrentTime] = useState(new Date());
- useEffect(() => {
- const intervalId: NodeJS.Timeout = setInterval(() => {
- setCurrentTime(new Date());
- }, refreshInterval);
- return () => clearInterval(intervalId);
- }, [refreshInterval]);
+// if (monthDiff > 0) {
+// const unitAmount = monthDiff;
+// return `${unitAmount}${dense ? timeUnits[i].unit : ` ${timeUnits[i].full}`}${dense ? '' : 's'} ago`;
+// }
+// } else if (elapsed >= timeUnits[i].value) {
+// const unitAmount: number = Math.floor(elapsed / timeUnits[i].value);
+// return `${unitAmount}${dense ? timeUnits[i].unit : ` ${timeUnits[i].full}`}${dense ? '' : 's'} ago`;
+// }
+// }
+// return 'Invalid Time';
+// };
- const timeAgoValue = useMemo(() => timeAgo({ currentTime, ...rest }), [currentTime, rest]);
+// const TimeAgo: FunctionComponent = ({ refreshInterval = 1000, ...rest }): JSX.Element => {
+// const [currentTime, setCurrentTime] = useState(new Date());
+// useEffect(() => {
+// const intervalId: NodeJS.Timeout = setInterval(() => {
+// setCurrentTime(new Date());
+// }, refreshInterval);
+// return () => clearInterval(intervalId);
+// }, [refreshInterval]);
- return {timeAgoValue};
-};
-export default TimeAgo;
+// const timeAgoValue = useMemo(() => timeAgo({ currentTime, ...rest }), [currentTime, rest]);
+
+// return {timeAgoValue};
+// };
+// export default TimeAgo;
diff --git a/src/shared/components/frigate/TimelineEventOverlay.jsx b/src/shared/components/frigate/TimelineEventOverlay.jsx
index 1cff550..c7458bb 100644
--- a/src/shared/components/frigate/TimelineEventOverlay.jsx
+++ b/src/shared/components/frigate/TimelineEventOverlay.jsx
@@ -1,65 +1,67 @@
-import { Fragment, h } from 'preact';
-import { useState } from 'preact/hooks';
+// import { Fragment, h } from 'preact';
+// import { useState } from 'preact/hooks';
-export default function TimelineEventOverlay({ eventOverlay, cameraConfig }) {
- const boxLeftEdge = Math.round(eventOverlay.data.box[0] * 100);
- const boxTopEdge = Math.round(eventOverlay.data.box[1] * 100);
- const boxRightEdge = Math.round((1 - eventOverlay.data.box[2] - eventOverlay.data.box[0]) * 100);
- const boxBottomEdge = Math.round((1 - eventOverlay.data.box[3] - eventOverlay.data.box[1]) * 100);
+export {}
- const [isHovering, setIsHovering] = useState(false);
- const getHoverStyle = () => {
- if (boxLeftEdge < 15) {
- // show object stats on right side
- return {
- left: `${boxLeftEdge + eventOverlay.data.box[2] * 100 + 1}%`,
- top: `${boxTopEdge}%`,
- };
- }
+// export default function TimelineEventOverlay({ eventOverlay, cameraConfig }) {
+// const boxLeftEdge = Math.round(eventOverlay.data.box[0] * 100);
+// const boxTopEdge = Math.round(eventOverlay.data.box[1] * 100);
+// const boxRightEdge = Math.round((1 - eventOverlay.data.box[2] - eventOverlay.data.box[0]) * 100);
+// const boxBottomEdge = Math.round((1 - eventOverlay.data.box[3] - eventOverlay.data.box[1]) * 100);
- return {
- right: `${boxRightEdge + eventOverlay.data.box[2] * 100 + 1}%`,
- top: `${boxTopEdge}%`,
- };
- };
+// const [isHovering, setIsHovering] = useState(false);
+// const getHoverStyle = () => {
+// if (boxLeftEdge < 15) {
+// // show object stats on right side
+// return {
+// left: `${boxLeftEdge + eventOverlay.data.box[2] * 100 + 1}%`,
+// top: `${boxTopEdge}%`,
+// };
+// }
- const getObjectArea = () => {
- const width = eventOverlay.data.box[2] * cameraConfig.detect.width;
- const height = eventOverlay.data.box[3] * cameraConfig.detect.height;
- return Math.round(width * height);
- };
+// return {
+// right: `${boxRightEdge + eventOverlay.data.box[2] * 100 + 1}%`,
+// top: `${boxTopEdge}%`,
+// };
+// };
- const getObjectRatio = () => {
- const width = eventOverlay.data.box[2] * cameraConfig.detect.width;
- const height = eventOverlay.data.box[3] * cameraConfig.detect.height;
- return Math.round(100 * (width / height)) / 100;
- };
+// const getObjectArea = () => {
+// const width = eventOverlay.data.box[2] * cameraConfig.detect.width;
+// const height = eventOverlay.data.box[3] * cameraConfig.detect.height;
+// return Math.round(width * height);
+// };
- return (
-
- setIsHovering(true)}
- onMouseLeave={() => setIsHovering(false)}
- onTouchStart={() => setIsHovering(true)}
- onTouchEnd={() => setIsHovering(false)}
- style={{
- left: `${boxLeftEdge}%`,
- top: `${boxTopEdge}%`,
- right: `${boxRightEdge}%`,
- bottom: `${boxBottomEdge}%`,
- }}
- >
- {eventOverlay.class_type == 'entered_zone' ? (
-
- ) : null}
-
- {isHovering && (
-
-
{`Area: ${getObjectArea()} px`}
-
{`Ratio: ${getObjectRatio()}`}
-
- )}
-
- );
-}
+// const getObjectRatio = () => {
+// const width = eventOverlay.data.box[2] * cameraConfig.detect.width;
+// const height = eventOverlay.data.box[3] * cameraConfig.detect.height;
+// return Math.round(100 * (width / height)) / 100;
+// };
+
+// return (
+//
+// setIsHovering(true)}
+// onMouseLeave={() => setIsHovering(false)}
+// onTouchStart={() => setIsHovering(true)}
+// onTouchEnd={() => setIsHovering(false)}
+// style={{
+// left: `${boxLeftEdge}%`,
+// top: `${boxTopEdge}%`,
+// right: `${boxRightEdge}%`,
+// bottom: `${boxBottomEdge}%`,
+// }}
+// >
+// {eventOverlay.class_type == 'entered_zone' ? (
+//
+// ) : null}
+//
+// {isHovering && (
+//
+//
{`Area: ${getObjectArea()} px`}
+//
{`Ratio: ${getObjectRatio()}`}
+//
+// )}
+//
+// );
+// }
diff --git a/src/shared/components/frigate/TimelineSummary.jsx b/src/shared/components/frigate/TimelineSummary.jsx
index 62e3b4c..dff8d9a 100644
--- a/src/shared/components/frigate/TimelineSummary.jsx
+++ b/src/shared/components/frigate/TimelineSummary.jsx
@@ -1,218 +1,220 @@
-import { h } from 'preact';
-import useSWR from 'swr';
-import ActivityIndicator from './ActivityIndicator';
-import { formatUnixTimestampToDateTime } from '../utils/dateUtil';
-import About from '../icons/About';
-import ActiveObjectIcon from '../icons/ActiveObject';
-import PlayIcon from '../icons/Play';
-import ExitIcon from '../icons/Exit';
-import StationaryObjectIcon from '../icons/StationaryObject';
-import FaceIcon from '../icons/Face';
-import LicensePlateIcon from '../icons/LicensePlate';
-import DeliveryTruckIcon from '../icons/DeliveryTruck';
-import ZoneIcon from '../icons/Zone';
-import { useMemo, useState } from 'preact/hooks';
-import Button from './Button';
+// import { h } from 'preact';
+// import useSWR from 'swr';
+// import ActivityIndicator from './ActivityIndicator';
+// import { formatUnixTimestampToDateTime } from '../utils/dateUtil';
+// import About from '../icons/About';
+// import ActiveObjectIcon from '../icons/ActiveObject';
+// import PlayIcon from '../icons/Play';
+// import ExitIcon from '../icons/Exit';
+// import StationaryObjectIcon from '../icons/StationaryObject';
+// import FaceIcon from '../icons/Face';
+// import LicensePlateIcon from '../icons/LicensePlate';
+// import DeliveryTruckIcon from '../icons/DeliveryTruck';
+// import ZoneIcon from '../icons/Zone';
+// import { useMemo, useState } from 'preact/hooks';
+// import Button from './Button';
-export default function TimelineSummary({ event, onFrameSelected }) {
- const { data: eventTimeline } = useSWR([
- 'timeline',
- {
- source_id: event.id,
- },
- ]);
+export {}
- const { data: config } = useSWR('config');
+// export default function TimelineSummary({ event, onFrameSelected }) {
+// const { data: eventTimeline } = useSWR([
+// 'timeline',
+// {
+// source_id: event.id,
+// },
+// ]);
- const annotationOffset = useMemo(() => {
- if (!config) {
- return 0;
- }
+// const { data: config } = useSWR('config');
- return (config.cameras[event.camera]?.detect?.annotation_offset || 0) / 1000;
- }, [config, event]);
+// const annotationOffset = useMemo(() => {
+// if (!config) {
+// return 0;
+// }
- const [timeIndex, setTimeIndex] = useState(-1);
+// return (config.cameras[event.camera]?.detect?.annotation_offset || 0) / 1000;
+// }, [config, event]);
- const recordingParams = useMemo(() => {
- if (!event.end_time) {
- return {
- after: event.start_time,
- };
- }
+// const [timeIndex, setTimeIndex] = useState(-1);
- return {
- before: event.end_time,
- after: event.start_time,
- };
- }, [event]);
- const { data: recordings } = useSWR([`${event.camera}/recordings`, recordingParams], { revalidateOnFocus: false });
+// const recordingParams = useMemo(() => {
+// if (!event.end_time) {
+// return {
+// after: event.start_time,
+// };
+// }
- // calculates the seek seconds by adding up all the seconds in the segments prior to the playback time
- const getSeekSeconds = (seekUnix) => {
- if (!recordings) {
- return 0;
- }
+// return {
+// before: event.end_time,
+// after: event.start_time,
+// };
+// }, [event]);
+// const { data: recordings } = useSWR([`${event.camera}/recordings`, recordingParams], { revalidateOnFocus: false });
- let seekSeconds = 0;
- recordings.every((segment) => {
- // if the next segment is past the desired time, stop calculating
- if (segment.start_time > seekUnix) {
- return false;
- }
+// // calculates the seek seconds by adding up all the seconds in the segments prior to the playback time
+// const getSeekSeconds = (seekUnix) => {
+// if (!recordings) {
+// return 0;
+// }
- if (segment.end_time < seekUnix) {
- seekSeconds += segment.end_time - segment.start_time;
- return true;
- }
+// let seekSeconds = 0;
+// recordings.every((segment) => {
+// // if the next segment is past the desired time, stop calculating
+// if (segment.start_time > seekUnix) {
+// return false;
+// }
- seekSeconds += segment.end_time - segment.start_time - (segment.end_time - seekUnix);
- return true;
- });
+// if (segment.end_time < seekUnix) {
+// seekSeconds += segment.end_time - segment.start_time;
+// return true;
+// }
- return seekSeconds;
- };
+// seekSeconds += segment.end_time - segment.start_time - (segment.end_time - seekUnix);
+// return true;
+// });
- const onSelectMoment = async (index) => {
- setTimeIndex(index);
- onFrameSelected(eventTimeline[index], getSeekSeconds(eventTimeline[index].timestamp + annotationOffset));
- };
+// return seekSeconds;
+// };
- if (!eventTimeline || !config) {
- return ;
- }
+// const onSelectMoment = async (index) => {
+// setTimeIndex(index);
+// onFrameSelected(eventTimeline[index], getSeekSeconds(eventTimeline[index].timestamp + annotationOffset));
+// };
- if (eventTimeline.length == 0) {
- return ;
- }
+// if (!eventTimeline || !config) {
+// return ;
+// }
- return (
-
-
-
- {eventTimeline.map((item, index) => (
-
- ))}
-
-
- {timeIndex >= 0 ? (
-
-
-
Bounding boxes may not align
-
-
-
- ) : null}
-
- );
-}
+// if (eventTimeline.length == 0) {
+// return ;
+// }
-function getTimelineIcon(timelineItem) {
- switch (timelineItem.class_type) {
- case 'visible':
- return ;
- case 'gone':
- return ;
- case 'active':
- return ;
- case 'stationary':
- return ;
- case 'entered_zone':
- return ;
- case 'attribute':
- switch (timelineItem.data.attribute) {
- case 'face':
- return ;
- case 'license_plate':
- return ;
- default:
- return ;
- }
- case 'sub_label':
- switch (timelineItem.data.label) {
- case 'person':
- return ;
- case 'car':
- return ;
- }
- }
-}
+// return (
+//
+//
+//
+// {eventTimeline.map((item, index) => (
+//
+// ))}
+//
+//
+// {timeIndex >= 0 ? (
+//
+//
+//
Bounding boxes may not align
+//
+//
+//
+// ) : null}
+//
+// );
+// }
-function getTimelineItemDescription(config, timelineItem, event) {
- switch (timelineItem.class_type) {
- case 'visible':
- return `${event.label} detected at ${formatUnixTimestampToDateTime(timelineItem.timestamp, {
- date_style: 'short',
- time_style: 'medium',
- time_format: config.ui.time_format,
- })}`;
- case 'entered_zone':
- return `${event.label.replaceAll('_', ' ')} entered ${timelineItem.data.zones
- .join(' and ')
- .replaceAll('_', ' ')} at ${formatUnixTimestampToDateTime(timelineItem.timestamp, {
- date_style: 'short',
- time_style: 'medium',
- time_format: config.ui.time_format,
- })}`;
- case 'active':
- return `${event.label} became active at ${formatUnixTimestampToDateTime(timelineItem.timestamp, {
- date_style: 'short',
- time_style: 'medium',
- time_format: config.ui.time_format,
- })}`;
- case 'stationary':
- return `${event.label} became stationary at ${formatUnixTimestampToDateTime(timelineItem.timestamp, {
- date_style: 'short',
- time_style: 'medium',
- time_format: config.ui.time_format,
- })}`;
- case 'attribute': {
- let title = "";
- if (timelineItem.data.attribute == 'face' || timelineItem.data.attribute == 'license_plate') {
- title = `${timelineItem.data.attribute.replaceAll("_", " ")} detected for ${event.label}`;
- } else {
- title = `${event.label} recognized as ${timelineItem.data.attribute.replaceAll("_", " ")}`
- }
- return `${title} at ${formatUnixTimestampToDateTime(
- timelineItem.timestamp,
- {
- date_style: 'short',
- time_style: 'medium',
- time_format: config.ui.time_format,
- }
- )}`;
- }
- case 'sub_label':
- return `${event.label} recognized as ${timelineItem.data.sub_label} at ${formatUnixTimestampToDateTime(
- timelineItem.timestamp,
- {
- date_style: 'short',
- time_style: 'medium',
- time_format: config.ui.time_format,
- }
- )}`;
- case 'gone':
- return `${event.label} left at ${formatUnixTimestampToDateTime(timelineItem.timestamp, {
- date_style: 'short',
- time_style: 'medium',
- time_format: config.ui.time_format,
- })}`;
- }
-}
+// function getTimelineIcon(timelineItem) {
+// switch (timelineItem.class_type) {
+// case 'visible':
+// return ;
+// case 'gone':
+// return ;
+// case 'active':
+// return ;
+// case 'stationary':
+// return ;
+// case 'entered_zone':
+// return ;
+// case 'attribute':
+// switch (timelineItem.data.attribute) {
+// case 'face':
+// return ;
+// case 'license_plate':
+// return ;
+// default:
+// return ;
+// }
+// case 'sub_label':
+// switch (timelineItem.data.label) {
+// case 'person':
+// return ;
+// case 'car':
+// return ;
+// }
+// }
+// }
+
+// function getTimelineItemDescription(config, timelineItem, event) {
+// switch (timelineItem.class_type) {
+// case 'visible':
+// return `${event.label} detected at ${formatUnixTimestampToDateTime(timelineItem.timestamp, {
+// date_style: 'short',
+// time_style: 'medium',
+// time_format: config.ui.time_format,
+// })}`;
+// case 'entered_zone':
+// return `${event.label.replaceAll('_', ' ')} entered ${timelineItem.data.zones
+// .join(' and ')
+// .replaceAll('_', ' ')} at ${formatUnixTimestampToDateTime(timelineItem.timestamp, {
+// date_style: 'short',
+// time_style: 'medium',
+// time_format: config.ui.time_format,
+// })}`;
+// case 'active':
+// return `${event.label} became active at ${formatUnixTimestampToDateTime(timelineItem.timestamp, {
+// date_style: 'short',
+// time_style: 'medium',
+// time_format: config.ui.time_format,
+// })}`;
+// case 'stationary':
+// return `${event.label} became stationary at ${formatUnixTimestampToDateTime(timelineItem.timestamp, {
+// date_style: 'short',
+// time_style: 'medium',
+// time_format: config.ui.time_format,
+// })}`;
+// case 'attribute': {
+// let title = "";
+// if (timelineItem.data.attribute == 'face' || timelineItem.data.attribute == 'license_plate') {
+// title = `${timelineItem.data.attribute.replaceAll("_", " ")} detected for ${event.label}`;
+// } else {
+// title = `${event.label} recognized as ${timelineItem.data.attribute.replaceAll("_", " ")}`
+// }
+// return `${title} at ${formatUnixTimestampToDateTime(
+// timelineItem.timestamp,
+// {
+// date_style: 'short',
+// time_style: 'medium',
+// time_format: config.ui.time_format,
+// }
+// )}`;
+// }
+// case 'sub_label':
+// return `${event.label} recognized as ${timelineItem.data.sub_label} at ${formatUnixTimestampToDateTime(
+// timelineItem.timestamp,
+// {
+// date_style: 'short',
+// time_style: 'medium',
+// time_format: config.ui.time_format,
+// }
+// )}`;
+// case 'gone':
+// return `${event.label} left at ${formatUnixTimestampToDateTime(timelineItem.timestamp, {
+// date_style: 'short',
+// time_style: 'medium',
+// time_format: config.ui.time_format,
+// })}`;
+// }
+// }
diff --git a/src/shared/components/frigate/Tooltip.jsx b/src/shared/components/frigate/Tooltip.jsx
index 626239b..a7f38ff 100644
--- a/src/shared/components/frigate/Tooltip.jsx
+++ b/src/shared/components/frigate/Tooltip.jsx
@@ -1,62 +1,64 @@
-import { createPortal } from 'react'; // TODO implement
-import { useLayoutEffect, useRef, useState } from 'react';
+// import { createPortal } from 'react'; // TODO implement
+// import { useLayoutEffect, useRef, useState } from 'react';
-const TIP_SPACE = 20;
+// const TIP_SPACE = 20;
-export default function Tooltip({ relativeTo, text, capitalize }) {
- const [position, setPosition] = useState({ top: -9999, left: -9999 });
- const portalRoot = document.getElementById('tooltips');
- const ref = useRef();
+export {}
- useLayoutEffect(() => {
- if (ref && ref.current && relativeTo && relativeTo.current) {
- const windowWidth = window.innerWidth;
- const {
- x: relativeToX,
- y: relativeToY,
- width: relativeToWidth,
- height: relativeToHeight,
- } = relativeTo.current.getBoundingClientRect();
- const { width: _tipWidth, height: _tipHeight } = ref.current.getBoundingClientRect();
- const tipWidth = _tipWidth * 1.1;
- const tipHeight = _tipHeight * 1.1;
+// export default function Tooltip({ relativeTo, text, capitalize }) {
+// const [position, setPosition] = useState({ top: -9999, left: -9999 });
+// const portalRoot = document.getElementById('tooltips');
+// const ref = useRef();
- const left = relativeToX + Math.round(relativeToWidth / 2) + window.scrollX;
- const top = relativeToY + Math.round(relativeToHeight / 2) + window.scrollY;
+// useLayoutEffect(() => {
+// if (ref && ref.current && relativeTo && relativeTo.current) {
+// const windowWidth = window.innerWidth;
+// const {
+// x: relativeToX,
+// y: relativeToY,
+// width: relativeToWidth,
+// height: relativeToHeight,
+// } = relativeTo.current.getBoundingClientRect();
+// const { width: _tipWidth, height: _tipHeight } = ref.current.getBoundingClientRect();
+// const tipWidth = _tipWidth * 1.1;
+// const tipHeight = _tipHeight * 1.1;
- let newTop = top - TIP_SPACE - tipHeight;
- let newLeft = left - Math.round(tipWidth / 2);
- // too far right
- if (newLeft + tipWidth + TIP_SPACE > windowWidth - window.scrollX) {
- newLeft = Math.max(0, left - tipWidth - TIP_SPACE);
- newTop = top - Math.round(tipHeight / 2);
- }
- // too far left
- else if (newLeft < TIP_SPACE + window.scrollX) {
- newLeft = left + TIP_SPACE;
- newTop = top - Math.round(tipHeight / 2);
- }
- // too close to top
- else if (newTop <= TIP_SPACE + window.scrollY) {
- newTop = top + tipHeight + TIP_SPACE;
- }
+// const left = relativeToX + Math.round(relativeToWidth / 2) + window.scrollX;
+// const top = relativeToY + Math.round(relativeToHeight / 2) + window.scrollY;
- setPosition({ left: newLeft, top: newTop });
- }
- }, [relativeTo, ref]);
+// let newTop = top - TIP_SPACE - tipHeight;
+// let newLeft = left - Math.round(tipWidth / 2);
+// // too far right
+// if (newLeft + tipWidth + TIP_SPACE > windowWidth - window.scrollX) {
+// newLeft = Math.max(0, left - tipWidth - TIP_SPACE);
+// newTop = top - Math.round(tipHeight / 2);
+// }
+// // too far left
+// else if (newLeft < TIP_SPACE + window.scrollX) {
+// newLeft = left + TIP_SPACE;
+// newTop = top - Math.round(tipHeight / 2);
+// }
+// // too close to top
+// else if (newTop <= TIP_SPACE + window.scrollY) {
+// newTop = top + tipHeight + TIP_SPACE;
+// }
- const tooltip = (
- = 0 ? 'opacity-100 scale-100' : ''}`}
- ref={ref}
- style={position}
- >
- {text}
-
- );
+// setPosition({ left: newLeft, top: newTop });
+// }
+// }, [relativeTo, ref]);
- return portalRoot ? createPortal(tooltip, portalRoot) : tooltip;
-}
+// const tooltip = (
+// = 0 ? 'opacity-100 scale-100' : ''}`}
+// ref={ref}
+// style={position}
+// >
+// {text}
+//
+// );
+
+// return portalRoot ? createPortal(tooltip, portalRoot) : tooltip;
+// }
diff --git a/src/shared/components/frigate/card.tsx b/src/shared/components/frigate/card.tsx
index e0d00ae..84e7869 100644
--- a/src/shared/components/frigate/card.tsx
+++ b/src/shared/components/frigate/card.tsx
@@ -1,80 +1,81 @@
-import * as React from "react"
+// import * as React from "react"
+export {}
-const Card = React.forwardRef<
- HTMLDivElement,
- React.HTMLAttributes
->(({ className, ...props }, ref) => (
-
-))
-Card.displayName = "Card"
+// const Card = React.forwardRef<
+// HTMLDivElement,
+// React.HTMLAttributes
+// >(({ className, ...props }, ref) => (
+//
+// ))
+// Card.displayName = "Card"
-const CardHeader = React.forwardRef<
- HTMLDivElement,
- React.HTMLAttributes
->(({ className, ...props }, ref) => (
-
-))
-CardHeader.displayName = "CardHeader"
+// const CardHeader = React.forwardRef<
+// HTMLDivElement,
+// React.HTMLAttributes
+// >(({ className, ...props }, ref) => (
+//
+// ))
+// CardHeader.displayName = "CardHeader"
-const CardTitle = React.forwardRef<
- HTMLParagraphElement,
- React.HTMLAttributes
->(({ className, ...props }, ref) => (
-
-))
-CardTitle.displayName = "CardTitle"
+// const CardTitle = React.forwardRef<
+// HTMLParagraphElement,
+// React.HTMLAttributes
+// >(({ className, ...props }, ref) => (
+//
+// ))
+// CardTitle.displayName = "CardTitle"
-const CardDescription = React.forwardRef<
- HTMLParagraphElement,
- React.HTMLAttributes
->(({ className, ...props }, ref) => (
-
-))
-CardDescription.displayName = "CardDescription"
+// const CardDescription = React.forwardRef<
+// HTMLParagraphElement,
+// React.HTMLAttributes
+// >(({ className, ...props }, ref) => (
+//
+// ))
+// CardDescription.displayName = "CardDescription"
-const CardContent = React.forwardRef<
- HTMLDivElement,
- React.HTMLAttributes
->(({ className, ...props }, ref) => (
-
-))
-CardContent.displayName = "CardContent"
+// const CardContent = React.forwardRef<
+// HTMLDivElement,
+// React.HTMLAttributes
+// >(({ className, ...props }, ref) => (
+//
+// ))
+// CardContent.displayName = "CardContent"
-const CardFooter = React.forwardRef<
- HTMLDivElement,
- React.HTMLAttributes
->(({ className, ...props }, ref) => (
-
-))
-CardFooter.displayName = "CardFooter"
+// const CardFooter = React.forwardRef<
+// HTMLDivElement,
+// React.HTMLAttributes
+// >(({ className, ...props }, ref) => (
+//
+// ))
+// CardFooter.displayName = "CardFooter"
-export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
+// export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
diff --git a/src/shared/components/frigate/icons/About.jsx b/src/shared/components/frigate/icons/About.jsx
deleted file mode 100644
index 6271b2d..0000000
--- a/src/shared/components/frigate/icons/About.jsx
+++ /dev/null
@@ -1,19 +0,0 @@
-import { h } from 'preact';
-import { memo } from 'preact/compat';
-
-export function About({ className = '' }) {
- return (
-
- );
-}
-
-export default memo(About);
diff --git a/src/shared/components/frigate/icons/CalendarIcon.jsx b/src/shared/components/frigate/icons/CalendarIcon.jsx
deleted file mode 100644
index 6558f1a..0000000
--- a/src/shared/components/frigate/icons/CalendarIcon.jsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { h } from 'preact';
-import { memo } from 'preact/compat';
-
-export function CalendarIcon({ className = 'h-6 w-6', stroke = 'currentColor', fill = 'none', onClick = () => {} }) {
- return (
-
- );
-}
-
-export default memo(CalendarIcon);
diff --git a/src/shared/components/frigate/icons/Camera.jsx b/src/shared/components/frigate/icons/Camera.jsx
deleted file mode 100644
index fd0fcd1..0000000
--- a/src/shared/components/frigate/icons/Camera.jsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { h } from 'preact';
-import { memo } from 'preact/compat';
-
-export function Camera({ className = 'h-6 w-6', stroke = 'currentColor', fill = 'none', onClick = () => {} }) {
- return (
-
- );
-}
-
-export default memo(Camera);
diff --git a/src/shared/components/frigate/icons/Clip.jsx b/src/shared/components/frigate/icons/Clip.jsx
deleted file mode 100644
index a7fd815..0000000
--- a/src/shared/components/frigate/icons/Clip.jsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { h } from 'preact';
-import { memo } from 'preact/compat';
-
-export function Clip({ className = 'h-6 w-6', stroke = 'currentColor', onClick = () => {} }) {
- return (
-
- );
-}
-
-export default memo(Clip);
diff --git a/src/shared/components/frigate/icons/Clock.jsx b/src/shared/components/frigate/icons/Clock.jsx
deleted file mode 100644
index e813e00..0000000
--- a/src/shared/components/frigate/icons/Clock.jsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { h } from 'preact';
-import { memo } from 'preact/compat';
-
-export function Clock({ className = 'h-6 w-6', stroke = 'currentColor', fill = 'none', onClick = () => {} }) {
- return (
-
- );
-}
-
-export default memo(Clock);
diff --git a/src/shared/components/frigate/icons/Delete.jsx b/src/shared/components/frigate/icons/Delete.jsx
deleted file mode 100644
index f00da15..0000000
--- a/src/shared/components/frigate/icons/Delete.jsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { h } from 'preact';
-import { memo } from 'preact/compat';
-
-export function Delete({ className = 'h-6 w-6', stroke = 'currentColor', fill = 'none', onClick = () => {} }) {
- return (
-
- );
-}
-
-export default memo(Delete);
diff --git a/src/shared/components/frigate/icons/Download.jsx b/src/shared/components/frigate/icons/Download.jsx
deleted file mode 100644
index 7ed2c57..0000000
--- a/src/shared/components/frigate/icons/Download.jsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import { memo } from 'react';
-
-
-
-
-export function Download({ className = 'h-6 w-6', stroke = 'currentColor', fill = 'none', onClick = () => void }) {
- return (
-
- );
-}
-
-export default memo(Download);
diff --git a/src/shared/components/frigate/icons/Menu.jsx b/src/shared/components/frigate/icons/Menu.jsx
deleted file mode 100644
index 76ea9dd..0000000
--- a/src/shared/components/frigate/icons/Menu.jsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import { h } from 'preact';
-import { memo } from 'preact/compat';
-
-export function Menu({ className = '' }) {
- return (
-
- );
-}
-
-export default memo(Menu);
diff --git a/src/shared/components/frigate/icons/MenuOpen.jsx b/src/shared/components/frigate/icons/MenuOpen.jsx
deleted file mode 100644
index c3b2830..0000000
--- a/src/shared/components/frigate/icons/MenuOpen.jsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import { h } from 'preact';
-import { memo } from 'preact/compat';
-
-export function MenuOpen({ className = '' }) {
- return (
-
- );
-}
-
-export default memo(MenuOpen);
diff --git a/src/shared/components/frigate/icons/Score.jsx b/src/shared/components/frigate/icons/Score.jsx
deleted file mode 100644
index 2abed4b..0000000
--- a/src/shared/components/frigate/icons/Score.jsx
+++ /dev/null
@@ -1,20 +0,0 @@
-import { h } from 'preact';
-import { memo } from 'preact/compat';
-
-export function Score({ className = 'h-6 w-6', stroke = 'currentColor', fill = 'currentColor', onClick = () => {} }) {
- return (
-
- );
-}
-
-export default memo(Score);
diff --git a/src/shared/components/frigate/icons/Snapshot.jsx b/src/shared/components/frigate/icons/Snapshot.jsx
deleted file mode 100644
index 696b080..0000000
--- a/src/shared/components/frigate/icons/Snapshot.jsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { h } from 'preact';
-import { memo } from 'preact/compat';
-
-export function Snapshot({ className = 'h-6 w-6', stroke = 'currentColor', onClick = () => {} }) {
- return (
-
- );
-}
-
-export default memo(Snapshot);
diff --git a/src/shared/components/frigate/icons/StarRecording.jsx b/src/shared/components/frigate/icons/StarRecording.jsx
deleted file mode 100644
index e4923b6..0000000
--- a/src/shared/components/frigate/icons/StarRecording.jsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { h } from 'preact';
-import { memo } from 'preact/compat';
-
-export function StarRecording({ className = 'h-6 w-6', stroke = 'currentColor', fill = 'none', onClick = () => {} }) {
- return (
-
- );
-}
-
-export default memo(StarRecording);
diff --git a/src/shared/components/frigate/icons/Submitted.jsx b/src/shared/components/frigate/icons/Submitted.jsx
deleted file mode 100644
index e7612a7..0000000
--- a/src/shared/components/frigate/icons/Submitted.jsx
+++ /dev/null
@@ -1,19 +0,0 @@
-import { h } from 'preact';
-import { memo } from 'preact/compat';
-
-export function Submitted({ className = 'h-6 w-6', inner_fill = 'none', outer_stroke = 'currentColor', onClick = () => {} }) {
- return (
-
- );
-}
-
-export default memo(Submitted);
diff --git a/src/shared/components/frigate/icons/UploadPlus.jsx b/src/shared/components/frigate/icons/UploadPlus.jsx
deleted file mode 100644
index 7fd2f88..0000000
--- a/src/shared/components/frigate/icons/UploadPlus.jsx
+++ /dev/null
@@ -1,23 +0,0 @@
-import { h } from 'preact';
-import { memo } from 'preact/compat';
-
-export function UploadPlus({ className = 'h-6 w-6', stroke = 'currentColor', onClick = () => {} }) {
- return (
-
- );
-}
-
-export default memo(UploadPlus);
diff --git a/src/shared/components/frigate/icons/Zone.jsx b/src/shared/components/frigate/icons/Zone.jsx
deleted file mode 100644
index b19205d..0000000
--- a/src/shared/components/frigate/icons/Zone.jsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import { h } from 'preact';
-import { memo } from 'preact/compat';
-
-export function Zone({ className = 'h-6 w-6', stroke = 'currentColor', fill = 'none', onClick = () => {} }) {
- return (
-
- );
-}
-
-export default memo(Zone);
diff --git a/src/shared/components/CogWheelWithText.tsx b/src/shared/components/loaders/CogWheelWithText.tsx
similarity index 100%
rename from src/shared/components/CogWheelWithText.tsx
rename to src/shared/components/loaders/CogWheelWithText.tsx
diff --git a/src/shared/components/CogwheelLoader.tsx b/src/shared/components/loaders/CogwheelLoader.tsx
similarity index 72%
rename from src/shared/components/CogwheelLoader.tsx
rename to src/shared/components/loaders/CogwheelLoader.tsx
index fe2ea4f..e008c5e 100644
--- a/src/shared/components/CogwheelLoader.tsx
+++ b/src/shared/components/loaders/CogwheelLoader.tsx
@@ -1,6 +1,6 @@
import React from 'react';
import { Center, DEFAULT_THEME } from '@mantine/core';
-import CogwheelSVG from './svg/CogwheelSVG';
+import CogwheelSVG from '../svg/CogwheelSVG';
const CogwheelLoader = () => {
diff --git a/src/shared/components/frigate/JSMpegPlayer.tsx b/src/shared/components/players/JSMpegPlayer.tsx
similarity index 100%
rename from src/shared/components/frigate/JSMpegPlayer.tsx
rename to src/shared/components/players/JSMpegPlayer.tsx
diff --git a/src/shared/components/frigate/MsePlayer.tsx b/src/shared/components/players/MsePlayer.tsx
similarity index 100%
rename from src/shared/components/frigate/MsePlayer.tsx
rename to src/shared/components/players/MsePlayer.tsx
diff --git a/src/shared/components/frigate/VideoPlayer.tsx b/src/shared/components/players/VideoPlayer.tsx
similarity index 100%
rename from src/shared/components/frigate/VideoPlayer.tsx
rename to src/shared/components/players/VideoPlayer.tsx
diff --git a/src/shared/components/frigate/WebRTCPlayer.tsx b/src/shared/components/players/WebRTCPlayer.tsx
similarity index 100%
rename from src/shared/components/frigate/WebRTCPlayer.tsx
rename to src/shared/components/players/WebRTCPlayer.tsx
diff --git a/src/shared/stores/recordings.store.ts b/src/shared/stores/recordings.store.ts
index ed5760b..0bbd0f9 100644
--- a/src/shared/stores/recordings.store.ts
+++ b/src/shared/stores/recordings.store.ts
@@ -15,7 +15,7 @@ export class RecordingsStore {
makeAutoObservable(this)
}
- recordingSchema = z.object({
+ private _recordingSchema = z.object({
hostName: z.string(),
cameraName: z.string(),
hour: z.string(),
@@ -31,7 +31,7 @@ export class RecordingsStore {
this._recordToPlay = value
}
getFullRecordForPlay(value: RecordForPlay) {
- return this.recordingSchema.safeParse(value)
+ return this._recordingSchema.safeParse(value)
}
private _hostIdParam: string | undefined
@@ -63,6 +63,11 @@ export class RecordingsStore {
public set selectedCamera(value: GetCameraWHostWConfig | undefined) {
this._selectedCamera = value
}
- selectedStartDay: string = ''
- selectedEndDay: string = ''
+ private _selectedRange: [Date | null, Date | null] = [null, null]
+ public get selectedRange(): [Date | null, Date | null] {
+ return this._selectedRange
+ }
+ public set selectedRange(value: [Date | null, Date | null]) {
+ this._selectedRange = value
+ }
}
\ No newline at end of file
diff --git a/src/shared/strings/strings.ts b/src/shared/strings/strings.ts
index 2aff05d..e0c06d5 100644
--- a/src/shared/strings/strings.ts
+++ b/src/shared/strings/strings.ts
@@ -1,10 +1,31 @@
export const strings = {
- host: {
+ host: 'Хост',
+ hostArr: {
name: 'Имя хоста',
url: 'Адрес',
enabled: 'Включен',
},
- // user section
+ player: {
+ startVideo: 'Вкл Видео',
+ stopVideo: 'Выкл Видео',
+ object: 'Объект',
+ duration: 'Длительность',
+ startTime: 'Начало',
+ endTime: 'Конец'
+ },
+ camera: 'Камера',
+ hour: 'Час',
+ minute: 'Минута',
+ second: 'Секунда',
+ events: 'События',
+ event: 'Событие',
+ notHaveEvents: 'Нет событий',
+ date: 'Дата',
+ day: 'День',
+ selectHost:'Выбери хост',
+ selectCamera: 'Выбери камеру',
+ selectDate: 'Выбери дату',
+ selectRange: 'Выбери период',
aboutMe: "Обо мне",
settings: "Настройки",
changeTheme: "Изменить тему",
@@ -32,14 +53,14 @@ export const strings = {
selectDeliveryMethod: "Выберите метод доставки:",
pickUpByMyself: "Я заберу самостоятельно",
courierDelivery: "Доставка курьером",
- deliveryPoint:"Адрес доставки",
+ deliveryPoint: "Адрес доставки",
selectYourDeliveryAddress: "Выберите свой адрес доставки:",
- deliveryDate:"Дата доставки",
+ deliveryDate: "Дата доставки",
selectDeliveryDate: "Выберите дату доставки:",
enterQuantity: "Введите количество:",
- quantity:"Количество",
- tooltip_close:"нажмите Enter",
- currency:"₽",
+ quantity: "Количество",
+ tooltip_close: "нажмите Enter",
+ currency: "₽",
category: "Категории:",
collapse: "Свернуть",
hide: "Скрыть",
diff --git a/src/shared/components/frigate/dateUtil.ts b/src/shared/utils/dateUtil.ts
similarity index 93%
rename from src/shared/components/frigate/dateUtil.ts
rename to src/shared/utils/dateUtil.ts
index ee7cd69..61a6761 100644
--- a/src/shared/components/frigate/dateUtil.ts
+++ b/src/shared/utils/dateUtil.ts
@@ -30,6 +30,33 @@ export const unixTimeToDate = (unixTime: number) => {
return formattedDate;
}
+/**
+ * @param date
+ * @returns string '2024-02-25'
+ */
+export const dateToQueryString = (date: Date): string => {
+ const year = date.getFullYear();
+ const month = date.getMonth() + 1;
+ const formattedMonth = month < 10 ? `0${month}` : month;
+ const day = date.getDate();
+ const formattedDay = day < 10 ? `0${day}` : day;
+
+ return `${year}-${formattedMonth}-${formattedDay}`;
+}
+
+export const parseQueryDateToDate = (dateQuery: string): Date | null => {
+ const match = dateQuery.match(/(\d{4})-(\d{2})-(\d{2})/);
+
+ if (match) {
+ const year = parseInt(match[1], 10);
+ const month = parseInt(match[2], 10) - 1;
+ const day = parseInt(match[3], 10);
+ return new Date(year, month, day);
+ }
+
+ return null;
+}
+
/**
* @param day frigate format, e.g day: 2024-02-23
* @param hour frigate format, e.g hour: 22
@@ -210,14 +237,14 @@ interface DurationToken {
* @param end_time: number|null - Unix timestamp for end time
* @returns string - duration or 'In Progress' if end time is not provided
*/
-export const getDurationFromTimestamps = (start_time: number, end_time: number | undefined): string => {
+export const getDurationFromTimestamps = (start_time: number, end_time: number | undefined): string | undefined => {
if (isNaN(start_time)) {
- return 'Invalid start time';
+ return
}
let duration = 'In Progress';
if (end_time) {
if (isNaN(end_time)) {
- return 'Invalid end time';
+ return
}
const start = fromUnixTime(start_time);
const end = fromUnixTime(end_time);
diff --git a/src/widgets/FrigateHostsTable.tsx b/src/widgets/FrigateHostsTable.tsx
index 5b7b03c..16df897 100644
--- a/src/widgets/FrigateHostsTable.tsx
+++ b/src/widgets/FrigateHostsTable.tsx
@@ -61,9 +61,9 @@ const FrigateHostsTable = ({ data, showAddButton = false, saveCallback, changedC
}
const headTitle = [
- { propertyName: 'name', title: strings.host.name },
- { propertyName: 'host', title: strings.host.url },
- { propertyName: 'enabled', title: strings.host.enabled },
+ { propertyName: 'name', title: strings.hostArr.name },
+ { propertyName: 'host', title: strings.hostArr.url },
+ { propertyName: 'enabled', title: strings.hostArr.enabled },
{ title: '', sorting: false },
]
diff --git a/src/shared/components/frigate/Player.tsx b/src/widgets/Player.tsx
similarity index 77%
rename from src/shared/components/frigate/Player.tsx
rename to src/widgets/Player.tsx
index f187883..ba09575 100644
--- a/src/shared/components/frigate/Player.tsx
+++ b/src/widgets/Player.tsx
@@ -1,14 +1,14 @@
import React, { useEffect, useMemo, useState } from 'react';
-import JSMpegPlayer from './JSMpegPlayer';
-import MSEPlayer from './MsePlayer';
-import { CameraConfig } from '../../../types/frigateConfig';
-import { LivePlayerMode } from '../../../types/live';
-import useCameraActivity from '../../../hooks/use-camera-activity';
-import useCameraLiveMode from '../../../hooks/use-camera-live-mode';
-import WebRtcPlayer from './WebRTCPlayer';
+import JSMpegPlayer from '../shared/components/players/JSMpegPlayer';
+import MSEPlayer from '../shared/components/players/MsePlayer';
+import { CameraConfig } from '../types/frigateConfig';
+import { LivePlayerMode } from '../types/live';
+import useCameraActivity from '../hooks/use-camera-activity';
+import useCameraLiveMode from '../hooks/use-camera-live-mode';
+import WebRtcPlayer from '../shared/components/players/WebRTCPlayer';
import { Flex } from '@mantine/core';
-import { frigateApi, proxyApi } from '../../../services/frigate.proxy/frigate.api';
-import { GetCameraWHostWConfig } from '../../../services/frigate.proxy/frigate.schema';
+import { frigateApi, proxyApi } from '../services/frigate.proxy/frigate.api';
+import { GetCameraWHostWConfig } from '../services/frigate.proxy/frigate.schema';
type LivePlayerProps = {
camera: GetCameraWHostWConfig;
@@ -70,7 +70,7 @@ const Player = ({
player = (
setLiveReady(true)}
wsUrl={wsUrl}
diff --git a/src/widgets/RecordingsFiltersRightSide.tsx b/src/widgets/RecordingsFiltersRightSide.tsx
index b0a8199..625cc5a 100644
--- a/src/widgets/RecordingsFiltersRightSide.tsx
+++ b/src/widgets/RecordingsFiltersRightSide.tsx
@@ -1,12 +1,9 @@
-import React, { useContext, useEffect } from 'react';
-import OneSelectFilter, { OneSelectItem } from '../shared/components/filters.aps/OneSelectFilter';
+import React, { useContext } from 'react';
import { observer } from 'mobx-react-lite';
import { Context } from '..';
-import { useQuery } from '@tanstack/react-query';
-import { frigateApi, frigateQueryKeys } from '../services/frigate.proxy/frigate.api';
-import { Center, Text } from '@mantine/core';
-import CogwheelLoader from '../shared/components/CogwheelLoader';
import CameraSelectFilter from '../shared/components/filters.aps/CameraSelectFilter';
+import DateRangeSelectFilter from '../shared/components/filters.aps/DateRangeSelectFilter';
+import HostSelectFilter from '../shared/components/filters.aps/HostSelectFilter';
interface RecordingsFiltersRightSideProps {
}
@@ -15,58 +12,18 @@ const RecordingsFiltersRightSide = ({
}: RecordingsFiltersRightSideProps) => {
const { recordingsStore: recStore } = useContext(Context)
- const { data: hosts, isError, isPending, isSuccess } = useQuery({
- queryKey: [frigateQueryKeys.getFrigateHosts],
- queryFn: frigateApi.getHosts
- })
-
- useEffect(() => {
- if (!hosts) return
- if (recStore.hostIdParam) {
- recStore.selectedHost = hosts.find(host => host.id === recStore.hostIdParam)
- recStore.hostIdParam = undefined
- }
- }, [isSuccess])
-
- if (isPending) return
- if (isError) return Loading error!
-
- if (!hosts || hosts.length < 1) return null
-
- const hostItems: OneSelectItem[] = hosts
- .filter(host => host.enabled)
- .map(host => ({ value: host.id, label: host.name }))
-
- const handleSelect = (id: string, value: string) => {
- const host = hosts?.find(host => host.id === value)
- if (!host) {
- recStore.selectedHost = undefined
- recStore.selectedCamera = undefined
- return
- }
- if (recStore.selectedHost?.id !== host.id) {
- recStore.selectedCamera = undefined
- }
- recStore.selectedHost = host
- }
-
console.log('RecordingsFiltersRightSide rendered')
return (
<>
-
+
{recStore.selectedHost ?
- :
- <>>
+ : <>>
+ }
+ {recStore.selectedCamera ?
+
+ : <>>
}
>
diff --git a/src/widgets/SelecteDayList.tsx b/src/widgets/SelecteDayList.tsx
new file mode 100644
index 0000000..f3f609c
--- /dev/null
+++ b/src/widgets/SelecteDayList.tsx
@@ -0,0 +1,53 @@
+import { useQuery } from '@tanstack/react-query';
+import React, { useContext } from 'react';
+import { frigateQueryKeys, mapHostToHostname, proxyApi } from '../services/frigate.proxy/frigate.api';
+import { dateToQueryString, getResolvedTimeZone } from '../shared/utils/dateUtil';
+import { Context } from '..';
+import { Flex, Text } from '@mantine/core';
+import RetryErrorPage from '../pages/RetryErrorPage';
+import CenterLoader from '../shared/components/CenterLoader';
+import { observer } from 'mobx-react-lite';
+import DayAccordion from '../shared/components/accordion/DayAccordion';
+
+interface SelecteDayListProps {
+ day: Date
+
+}
+
+const SelecteDayList = ({
+ day
+}: SelecteDayListProps) => {
+ const { recordingsStore: recStore } = useContext(Context)
+ const camera = recStore.selectedCamera
+ const host = recStore.selectedHost
+
+ const { data, isPending, isError, refetch } = useQuery({
+ queryKey: [frigateQueryKeys.getRecordingsSummary, recStore.selectedCamera?.id, day],
+ queryFn: async () => {
+ if (camera && host) {
+ const stringDay = dateToQueryString(day)
+ const hostName = mapHostToHostname(host)
+ const res = await proxyApi.getRecordingsSummary(hostName, camera.name, getResolvedTimeZone())
+ return res.find(record => record.day === stringDay)
+ }
+ return null
+ }
+ })
+
+ const handleRetry = () => {
+ if (recStore.selectedHost) refetch()
+ }
+
+ if (isPending) return
+ if (isError) return
+ if (!camera || !host || !data) return
+
+ return (
+
+ {host.name} / {camera.name} / {data.day}
+
+
+ );
+};
+
+export default observer(SelecteDayList);
\ No newline at end of file
diff --git a/src/widgets/SelectedCameraList.tsx b/src/widgets/SelectedCameraList.tsx
index fd2beec..4ad279a 100644
--- a/src/widgets/SelectedCameraList.tsx
+++ b/src/widgets/SelectedCameraList.tsx
@@ -6,24 +6,25 @@ import { useQuery } from '@tanstack/react-query';
import { Context } from '..';
import { frigateQueryKeys, frigateApi } from '../services/frigate.proxy/frigate.api';
import { host } from '../shared/env.const';
-import CogwheelLoader from '../shared/components/CogwheelLoader';
-import RetryError from '../pages/RetryError';
+import CogwheelLoader from '../shared/components/loaders/CogwheelLoader';
+import RetryErrorPage from '../pages/RetryErrorPage';
+import CenterLoader from '../shared/components/CenterLoader';
interface SelectedCameraListProps {
- cameraId: string,
+ // cameraId: string,
}
const SelectedCameraList = ({
- cameraId,
+ // cameraId,
}: SelectedCameraListProps) => {
const { recordingsStore: recStore } = useContext(Context)
const { data: camera, isPending: cameraPending, isError: cameraError, refetch: cameraRefetch } = useQuery({
- queryKey: [frigateQueryKeys.getCameraWHost, cameraId],
+ queryKey: [frigateQueryKeys.getCameraWHost, recStore.selectedCamera?.id],
queryFn: async () => {
- if (cameraId) {
- return frigateApi.getCameraWHost(cameraId)
+ if (recStore.selectedCamera) {
+ return frigateApi.getCameraWHost(recStore.selectedCamera.id)
}
return null
}
@@ -33,14 +34,18 @@ const SelectedCameraList = ({
cameraRefetch()
}
- if (cameraPending) return
- if (cameraError) return
+ if (cameraPending) return
+ if (cameraError) return
if (!camera?.frigateHost) return null
return (
{camera.frigateHost.name} / {camera.name}
+ {
+
+
+ }
diff --git a/src/widgets/SelectedHostList.tsx b/src/widgets/SelectedHostList.tsx
index c8021e5..cdd359b 100644
--- a/src/widgets/SelectedHostList.tsx
+++ b/src/widgets/SelectedHostList.tsx
@@ -5,7 +5,8 @@ import { useQuery } from '@tanstack/react-query';
import { frigateQueryKeys, frigateApi } from '../services/frigate.proxy/frigate.api';
import { Context } from '..';
import CenterLoader from '../shared/components/CenterLoader';
-import RetryError from '../pages/RetryError';
+import RetryErrorPage from '../pages/RetryErrorPage';
+import { strings } from '../shared/strings/strings';
const CameraAccordion = lazy(() => import('../shared/components/accordion/CameraAccordion'));
@@ -39,14 +40,14 @@ const SelectedHostList = ({
}
if (hostPending) return
- if (hostError) return
+ if (hostError) return
if (!host || host.cameras.length < 1) return null
const cameras = host.cameras.slice(0, 2).map(camera => {
return (
- {camera.name}
+ {strings.camera}: {camera.name}
{openCameraId === camera.id && (
@@ -60,7 +61,7 @@ const SelectedHostList = ({
return (
- {host.name}
+ {strings.host}: {host.name}