fix roles
add state for useAdmin add new request to cameras add BlobImage
This commit is contained in:
parent
4ed2ac9e2e
commit
505dd09b43
@ -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 }
|
||||
const isAdmin = roles.some(role => role === adminConfig.value)
|
||||
return { isAdmin }
|
||||
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, isLoading: false }
|
||||
}
|
||||
@ -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';
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -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 }
|
||||
|
||||
@ -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>
|
||||
</>
|
||||
|
||||
@ -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
|
||||
|
||||
51
src/shared/components/images/BlobImage.tsx
Normal file
51
src/shared/components/images/BlobImage.tsx
Normal 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;
|
||||
@ -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 (
|
||||
|
||||
@ -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'
|
||||
|
||||
@ -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[];
|
||||
|
||||
@ -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])
|
||||
|
||||
|
||||
@ -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>
|
||||
)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user