VMS-frontend/src/pages/EditCameraPage.tsx
2024-03-23 01:18:54 +07:00

194 lines
7.7 KiB
TypeScript

import { Button, Center, Flex, Text } from '@mantine/core';
import { notifications } from '@mantine/notifications';
import { IconAlertCircle, IconCircleCheck } from '@tabler/icons-react';
import { useMutation, useQuery } from '@tanstack/react-query';
import { observer } from 'mobx-react-lite';
import { useContext, useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useParams } from 'react-router-dom';
import { Context } from '..';
import { useAdminRole } from '../hooks/useAdminRole';
import { frigateApi, frigateQueryKeys, mapHostToHostname, proxyApi } from '../services/frigate.proxy/frigate.api';
import MaskSelect, { MaskItem, MaskType } from '../shared/components/filters/MaskSelect';
import CenterLoader from '../shared/components/loaders/CenterLoader';
import { Point, extractMaskNumber } from '../shared/utils/maskPoint';
import CameraMaskDrawer from '../widgets/CameraMaskDrawer';
import CameraPageHeader from '../widgets/header/CameraPageHeader';
import Forbidden from './403';
import RetryErrorPage from './RetryErrorPage';
const EditCameraPage = () => {
const { t } = useTranslation()
const executed = useRef(false)
let { id: cameraId } = useParams<'id'>()
if (!cameraId) throw Error(t('editCameraPage.cameraIdNotExist'))
const [selectedMask, setSelectedMask] = useState<MaskItem>()
const [points, setPoints] = useState<Point[]>()
const { data: camera, isPending, isError, refetch } = useQuery({
queryKey: [frigateQueryKeys.getCameraWHost, cameraId],
queryFn: () => frigateApi.getCameraWHost(cameraId!)
})
const { mutate } = useMutation({
mutationFn: () => {
if (!selectedMask || !points || !camera) return Promise.reject(t('editCameraPage.errorAtPut'))
if (points.length < 3) return Promise.reject(t('editCameraPage.errorAtPut'))
const hostName = mapHostToHostname(camera.frigateHost)
if (!hostName) return Promise.reject(t('editCameraPage.errorAtPut'))
switch (selectedMask.type) {
case MaskType.Motion: {
const index = extractMaskNumber(selectedMask.id)
if (index === null) return Promise.reject(t('editCameraPage.errorAtPut'))
return proxyApi.putMotionMask(hostName, camera.name, index, Point.arrayToRequest(points))
.catch(error => {
if (error.response && error.response.data) {
return Promise.reject(error.response.data)
}
return Promise.reject(error)
})
}
case MaskType.Object: {
let maskName = selectedMask.id
if (selectedMask.id.startsWith('add_new_')) {
maskName = selectedMask.id.replace('add_new_', '')
}
return proxyApi.putZoneMask(hostName, camera.name, maskName, Point.arrayToRequest(points))
.catch(error => {
if (error.response && error.response.data) {
return Promise.reject(error.response.data)
}
return Promise.reject(error)
})
}
case MaskType.Zone: {
const index = extractMaskNumber(selectedMask.id)
if (index === null) return Promise.reject(t('editCameraPage.errorAtPut'))
let maskName = selectedMask.id
if (selectedMask.id.startsWith('add_new_')) {
maskName = selectedMask.id.replace('add_new_', '')
}
maskName = maskName.split('_')[0]
return proxyApi.putObjectMask(hostName, camera.name, maskName, index, Point.arrayToRequest(points))
.catch(error => {
if (error.response && error.response.data) {
return Promise.reject(error.response.data)
}
return Promise.reject(error)
})
}
}
},
onSuccess: (data) => {
notifications.show({
id: data?.message,
withCloseButton: true,
autoClose: 5000,
title: `Sucess: ${data?.success}`,
message: data?.message,
color: 'green',
icon: <IconCircleCheck />
})
},
onError: (e) => {
notifications.show({
id: e.message,
withCloseButton: true,
autoClose: false,
title: "Error",
message: e.message,
color: 'red',
icon: <IconAlertCircle />,
})
},
})
const { isAdmin, isLoading: adminLoading } = useAdminRole()
const { sideBarsStore } = useContext(Context)
useEffect(() => {
if (!executed.current) {
sideBarsStore.rightVisible = false
sideBarsStore.setLeftChildren(null)
sideBarsStore.setRightChildren(null)
executed.current = true
}
}, [sideBarsStore])
if (isPending || adminLoading) return <CenterLoader />
if (!isAdmin) return <Forbidden />
if (isError) return <RetryErrorPage onRetry={refetch} />
const hostName = mapHostToHostname(camera.frigateHost)
if (!hostName) return (
<Center>
<Text>{t('editCameraPage.notFrigateCamera')}</Text>
</Center>
)
if (!camera.config) return (
<Center>
<Text>{t('editCameraPage.cameraConfigNotExist')}</Text>
</Center>
)
const handleSelectMask = (mask?: MaskItem) => {
setSelectedMask(mask)
setPoints(mask?.coordinates)
}
const handleChangePoints = (points: Point[]) => {
setPoints(points)
}
const handleSave = () => {
if (!selectedMask || !points) return
console.log('type', selectedMask?.type)
console.log('save', points)
mutate()
}
const handleReset = () => {
setPoints(selectedMask?.coordinates)
}
const handleClear = () => {
setPoints([])
}
return (
<Flex w='100%' h='100%' direction='column'>
<CameraPageHeader camera={camera} configButton/>
{!camera.config ? null :
<Flex w='100%' justify='center' mb='1rem'>
<MaskSelect
miw='50%'
cameraConfig={camera.config}
onSelect={handleSelectMask}
/>
</Flex>
}
{!points ? null :
<Flex justify='center' align='center' mb='1rem'>
<Text mr='1rem'>{t('editCameraPage.points')}: {points.map(point => `(x: ${point.x}, y: ${point.y}) `)}</Text>
<Flex>
<Button onClick={handleSave} ml='0.5rem'>Save</Button>
<Button onClick={handleReset} ml='0.5rem'>Reset</Button>
<Button onClick={handleClear} ml='0.5rem'>Clear</Button>
</Flex>
</Flex>
}
{!points || !camera.config ? null :
<CameraMaskDrawer
cameraWidth={camera.config.detect.width}
cameraHeight={camera.config.detect.height}
imageUrl={proxyApi.cameraImageURL(hostName, camera.name)}
inPoints={points}
onChange={handleChangePoints} />
}
</Flex>
);
};
export default observer(EditCameraPage);