fix roles

add state for useAdmin
add new request to cameras
add BlobImage
This commit is contained in:
NlightN22 2024-02-28 03:45:36 +07:00
parent 4ed2ac9e2e
commit 505dd09b43
12 changed files with 104 additions and 43 deletions

View File

@ -1,20 +1,31 @@
import { useQuery } from "@tanstack/react-query";
import { frigateQueryKeys, frigateApi } from "../services/frigate.proxy/frigate.api";
import { useRealmAccessRoles } from "./useRealmAccessRoles";
import { useEffect, useState } from "react";
export const useAdminRole = () => {
const { data: adminConfig, isError, isPending } = useQuery({
const { data: adminConfig, isError, isFetching } = useQuery({
queryKey: [frigateQueryKeys.getAdminRole],
queryFn: frigateApi.getAdminRole
queryFn: frigateApi.getAdminRole,
staleTime: 1000 * 60 * 60,
gcTime: 1000 * 60 * 60 * 24,
})
const roles = useRealmAccessRoles()
const [initialized, setInitialized] = useState(false)
const isLoading = isFetching || roles === undefined
if (isPending) return { isAdmin: undefined, isLoading: true }
if (isError) return { isAdmin: false, isError: true }
if (!adminConfig) return { isAdmin: true }
if (adminConfig && !adminConfig.value) return { isAdmin: true }
useEffect(() => {
if (!isLoading) {
setInitialized(true);
}
}, [isLoading]);
if (!initialized || isLoading) return { isAdmin: undefined, isLoading: true }
if (isError) return { isAdmin: false, isError: true, isLoading: false }
if (!adminConfig) return { isAdmin: true, isLoading: false }
if (adminConfig && !adminConfig.value) return { isAdmin: true, isLoading: false }
const isAdmin = roles.some(role => role === adminConfig.value)
return { isAdmin }
return { isAdmin, isLoading: false }
}

View File

@ -1,12 +1,11 @@
import React, { useCallback, useContext, useEffect, useRef } from 'react';
import { useParams } from 'react-router-dom';
import { Context } from '..';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { useQuery } from '@tanstack/react-query';
import { frigateApi, frigateQueryKeys, mapHostToHostname, proxyApi } from '../services/frigate.proxy/frigate.api';
import { Button, Flex, Text, useMantineTheme } from '@mantine/core';
import { Button, Flex, useMantineTheme } from '@mantine/core';
import { useClipboard } from '@mantine/hooks';
import { configureMonacoYaml } from "monaco-yaml";
import Editor, { DiffEditor, useMonaco, loader, Monaco } from '@monaco-editor/react'
import Editor, { Monaco } from '@monaco-editor/react'
import * as monaco from "monaco-editor";
import CenterLoader from '../shared/components/loaders/CenterLoader';
import RetryErrorPage from './RetryErrorPage';

View File

@ -1,13 +1,11 @@
import axios from "axios"
import { proxyURL } from "../../shared/env.const"
import { z } from "zod"
import {
GetConfig, DeleteFrigateHost, GetFrigateHost, PutConfig, PutFrigateHost,
GetFrigateHostWithCameras, GetCameraWHost, GetCameraWHostWConfig, GetRole,
GetUserByRole, GetRoleWCameras, GetExportedFile
GetCameraWHostWConfig, GetRole,
GetRoleWCameras, GetExportedFile
} from "./frigate.schema";
import { FrigateConfig } from "../../types/frigateConfig";
import { url } from "inspector";
import { RecordSummary } from "../../types/record";
import { EventFrigate } from "../../types/event";
import { keycloakConfig } from "../..";
@ -42,12 +40,11 @@ export const frigateApi = {
getHosts: () => instanceApi.get<GetFrigateHost[]>('apiv1/frigate-hosts').then(res => {
return res.data
}),
// getHostsWithCameras: () => instanceApi.get<GetFrigateHostWithCameras[]>('apiv1/frigate-hosts', { params: { include: 'cameras' } }).then(res => {
// return res.data
// }),
getHost: (id: string) => instanceApi.get<GetFrigateHostWithCameras>(`apiv1/frigate-hosts/${id}`).then(res => {
// TODO change request to cameras
getHost: (id: string) => instanceApi.get<GetFrigateHost>(`apiv1/frigate-hosts/${id}`).then(res => {
return res.data
}),
getCamerasByHostId: (hostId: string) => instanceApi.get<GetCameraWHostWConfig[]>(`apiv1/cameras/host/${hostId}`).then(res => res.data),
getCamerasWHost: () => instanceApi.get<GetCameraWHostWConfig[]>(`apiv1/cameras`).then(res => res.data),
getCameraWHost: (id: string) => instanceApi.get<GetCameraWHostWConfig>(`apiv1/cameras/${id}`).then(res => { return res.data }),
putHosts: (hosts: PutFrigateHost[]) => instanceApi.put<GetFrigateHost[]>('apiv1/frigate-hosts', hosts).then(res => {
@ -181,6 +178,7 @@ export const frigateQueryKeys = {
getFrigateHost: 'frigate-host',
getCamerasWHost: 'cameras-frigate-host',
getCameraWHost: 'camera-frigate-host',
getCameraByHostId: 'camera-by-hostId',
getHostConfig: 'host-config',
getRecordingsSummary: 'recordings-frigate-summary',
getRecordings: 'recordings-frigate',

View File

@ -108,7 +108,7 @@ export const getExpotedFile = z.object({
export type GetConfig = z.infer<typeof getConfigSchema>
export type PutConfig = z.infer<typeof putConfigSchema>
export type GetFrigateHost = z.infer<typeof getFrigateHostSchema>
export type GetFrigateHostWithCameras = z.infer<typeof getFrigateHostWithCamerasSchema>
// export type GetFrigateHostWithCameras = z.infer<typeof getFrigateHostWithCamerasSchema>
export type GetFrigateHostWConfig = GetFrigateHost & { config: FrigateConfig }
export type GetCameraWHost = z.infer<typeof getCameraWithHostSchema>
export type GetCameraWHostWConfig = GetCameraWHost & { config?: CameraConfig }

View File

@ -6,6 +6,7 @@ import { strings } from '../../strings/strings';
import { unixTimeToDate, getDurationFromTimestamps } from '../../utils/dateUtil';
import VideoPlayer from '../players/VideoPlayer';
import { EventFrigate } from '../../../types/event';
import BlobImage from '../images/BlobImage';
interface EventPanelProps {
event: EventFrigate
@ -25,7 +26,7 @@ const EventPanel = ({
{playedValue === event.id && playerUrl ? <VideoPlayer videoUrl={playerUrl} /> : <></>}
<Flex w='100%' justify='space-between'>
{!hostName ? <></> :
<Image
<BlobImage
maw={200}
fit="contain"
withPlaceholder
@ -48,7 +49,9 @@ const EventPanel = ({
<Text>{strings.player.object}: {event.label}</Text>
<Text>{strings.player.startTime}: {unixTimeToDate(event.start_time)}</Text>
<Text>{strings.player.duration}: {getDurationFromTimestamps(event.start_time, event.end_time)}</Text>
{!event.data?.score? <></> :
<Text>{strings.player.rating}: {(event.data.score * 100).toFixed(2)}%</Text>
}
</Flex>
</Flex>
</>

View File

@ -19,15 +19,15 @@ const CameraSelectFilter = ({
const { recordingsStore: recStore } = useContext(Context)
const { data, isError, isPending, isSuccess, refetch } = useQuery({
queryKey: [frigateQueryKeys.getFrigateHost, selectedHostId],
queryFn: () => frigateApi.getHost(selectedHostId)
queryKey: [frigateQueryKeys.getCameraByHostId, selectedHostId],
queryFn: () => frigateApi.getCamerasByHostId(selectedHostId)
})
useEffect(() => {
if (!data) return
if (recStore.cameraIdParam) {
console.log('change camera by param')
recStore.filteredCamera = data.cameras.find( camera => camera.id === recStore.cameraIdParam)
recStore.filteredCamera = data.find( camera => camera.id === recStore.cameraIdParam)
recStore.cameraIdParam = undefined
}
}, [isSuccess])
@ -36,10 +36,10 @@ const CameraSelectFilter = ({
if (isError) return <RetryError onRetry={refetch}/>
if (!data) return null
const camerasItems: OneSelectItem[] = data.cameras.map(camera => ({ value: camera.id, label: camera.name }))
const camerasItems: OneSelectItem[] = data.map(camera => ({ value: camera.id, label: camera.name }))
const handleSelect = (value: string) => {
const camera = data.cameras.find(camera => camera.id === value)
const camera = data.find(camera => camera.id === value)
if (!camera) {
recStore.filteredCamera = undefined
return

View File

@ -0,0 +1,51 @@
import { Image, ImageProps, Loader } from '@mantine/core';
import React, { useEffect, useState } from 'react';
import { proxyApi } from '../../../services/frigate.proxy/frigate.api';
import { useIntersection } from '@mantine/hooks';
import { useQuery } from '@tanstack/react-query';
import RetryError from '../RetryError';
interface BlobImageProps extends ImageProps {
src: string
}
const BlobImage = ({
src,
...rest
}: BlobImageProps) => {
const [imageSrc, setImageSrc] = useState<string | null>(null);
const { ref, entry } = useIntersection({ threshold: 0.1, })
const isVisible = entry?.isIntersecting
const { data: imageBlob, refetch, isPending, isError } = useQuery({
queryKey: [src],
queryFn: () => proxyApi.getImageFrigate(src),
staleTime: 60 * 1000,
gcTime: Infinity,
refetchInterval: isVisible ? 30 * 1000 : undefined,
});
useEffect(() => {
if (imageBlob && imageBlob instanceof Blob) {
const objectURL = URL.createObjectURL(imageBlob);
setImageSrc(objectURL);
return () => {
if (objectURL) {
URL.revokeObjectURL(objectURL);
}
}
}
}, [imageBlob])
if (isPending || !imageSrc) return <Loader />
if (isError) return <RetryError onRetry={refetch} />
return (
<Image src={imageSrc} {...rest} />
);
};
export default BlobImage;

View File

@ -2,8 +2,8 @@ import { ImageProps, Image, Center } from '@mantine/core';
import { IconPhotoOff } from '@tabler/icons-react';
import React from 'react';
const ImageWithPlaceHolder = (props: ImageProps & React.RefAttributes<HTMLDivElement>) => {
if (props.src) return (
const ImageWithPlaceHolder = (props: ImageProps) => {
if (props.src && (typeof props.src === 'string')) return (
<Image {...props} />
)
return (

View File

@ -1,6 +1,6 @@
import { makeAutoObservable } from "mobx"
import { z } from "zod"
import { GetCameraWHostWConfig, GetFrigateHost, GetFrigateHostWithCameras } from "../../services/frigate.proxy/frigate.schema"
import { GetCameraWHostWConfig, GetFrigateHost } from "../../services/frigate.proxy/frigate.schema"
export type RecordForPlay = {
hostName?: string // format 'localhost:4000'

View File

@ -13,9 +13,9 @@ export interface EventFrigate {
retain_indefinitely: boolean;
plus_id?: string;
model_hash?: string;
data: {
top_score: number;
score: number;
data?: {
top_score?: number;
score?: number;
sub_label_score?: number;
region: number[];
box: number[];

View File

@ -26,7 +26,6 @@ const FrigateHostsTable = ({ data, showAddButton = false, saveCallback, changedC
const [sortedName, setSortedName] = useState<string | null>(null)
useEffect(() => {
console.log('data changed')
setTableData(data)
}, [data])

View File

@ -21,19 +21,19 @@ const SelectedHostList = ({
const { recordingsStore: recStore } = useContext(Context)
const [openCameraId, setOpenCameraId] = useState<string | null>(null)
const { data: host, isPending: hostPending, isError: hostError, refetch: hostRefetch } = useQuery({
queryKey: [frigateQueryKeys.getFrigateHost, hostId],
const { data: camerasQuery, isPending: hostPending, isError: hostError, refetch: hostRefetch } = useQuery({
queryKey: [frigateQueryKeys.getCameraByHostId, hostId],
queryFn: async () => {
if (hostId) {
return frigateApi.getHost(hostId)
return frigateApi.getCamerasByHostId(hostId)
}
return null
return []
}
})
const handleOnChange = (cameraId: string | null) => {
setOpenCameraId(openCameraId === cameraId ? null : cameraId)
recStore.openedCamera = host?.cameras.find( camera => camera.id === cameraId)
recStore.openedCamera = camerasQuery?.find( camera => camera.id === cameraId)
}
const handleRetry = () => {
@ -43,9 +43,9 @@ const SelectedHostList = ({
if (hostPending) return <CenterLoader />
if (hostError) return <RetryErrorPage onRetry={handleRetry} />
if (!host || host.cameras.length < 1) return null
if (!camerasQuery || camerasQuery.length < 1) return null
const cameras = host.cameras.map(camera => {
const camerasItems = camerasQuery.map(camera => {
return (
<Accordion.Item key={camera.id + 'Item'} value={camera.id}>
<Accordion.Control key={camera.id + 'Control'}>{strings.camera}: {camera.name}</Accordion.Control>
@ -62,13 +62,13 @@ const SelectedHostList = ({
return (
<Flex w='100%' h='100%' direction='column' align='center'>
<Text>{strings.host}: {host.name}</Text>
<Text>{strings.host}: {camerasQuery[0].frigateHost?.name}</Text>
<Accordion
mt='1rem'
variant='separated'
radius="md" w='100%'
onChange={(value) => handleOnChange(value)}>
{cameras}
{camerasItems}
</Accordion>
</Flex>
)