fix frigate hosts infinity rendering after save
This commit is contained in:
parent
67c27ff614
commit
ea48a538a5
@ -51,7 +51,7 @@
|
||||
"zod": "^3.21.4"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"dev": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
|
||||
@ -1,16 +1,20 @@
|
||||
import React, { useContext, useEffect, useRef, useState } from 'react';
|
||||
import FrigateHostsTable from '../widgets/FrigateHostsTable';
|
||||
import { Button, Flex, Text } from '@mantine/core';
|
||||
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/loaders/CenterLoader';
|
||||
import RetryErrorPage from './RetryErrorPage';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useContext, useEffect, useRef, useState } from 'react';
|
||||
import { Context } from '..';
|
||||
import { strings } from '../shared/strings/strings';
|
||||
import { Button, Flex } from '@mantine/core';
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { useAdminRole } from '../hooks/useAdminRole';
|
||||
import { frigateApi, frigateQueryKeys } from '../services/frigate.proxy/frigate.api';
|
||||
import { GetFrigateHost, deleteFrigateHostSchema, putFrigateHostSchema } from '../services/frigate.proxy/frigate.schema';
|
||||
import CenterLoader from '../shared/components/loaders/CenterLoader';
|
||||
import { isProduction } from '../shared/env.const';
|
||||
import { strings } from '../shared/strings/strings';
|
||||
import FrigateHostsTable from '../widgets/FrigateHostsTable';
|
||||
import Forbidden from './403';
|
||||
import RetryErrorPage from './RetryErrorPage';
|
||||
import { notifications } from '@mantine/notifications';
|
||||
import { IconAlertCircle } from '@tabler/icons-react';
|
||||
|
||||
|
||||
const FrigateHostsPage = () => {
|
||||
const executed = useRef(false)
|
||||
@ -36,6 +40,10 @@ const FrigateHostsPage = () => {
|
||||
if (data) setPageData(data)
|
||||
}, [data])
|
||||
|
||||
useEffect(() => {
|
||||
if (!isProduction) console.log('pageData', pageData)
|
||||
}, [pageData])
|
||||
|
||||
const { mutate } = useMutation({
|
||||
mutationFn: (tableData: GetFrigateHost[]) => {
|
||||
let fetchPromises = []
|
||||
@ -48,16 +56,29 @@ const FrigateHostsPage = () => {
|
||||
const parsedChanged = putFrigateHostSchema.array().parse(tableData)
|
||||
fetchPromises.push(frigateApi.putHosts(parsedChanged))
|
||||
}
|
||||
return Promise.all(fetchPromises)
|
||||
return Promise.all(fetchPromises).catch(error => {
|
||||
if (error.response && error.response.data) {
|
||||
return Promise.reject(error.response.data);
|
||||
}
|
||||
return Promise.reject(error);
|
||||
})
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: [frigateQueryKeys.getFrigateHosts] })
|
||||
},
|
||||
onError: () => {
|
||||
onError: (e) => {
|
||||
if (e && e.message) {
|
||||
notifications.show({
|
||||
id: e.message,
|
||||
withCloseButton: true,
|
||||
autoClose: 5000,
|
||||
title: "Error",
|
||||
message: e.message,
|
||||
color: 'red',
|
||||
icon: <IconAlertCircle />,
|
||||
})
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: [frigateQueryKeys.getFrigateHosts] })
|
||||
},
|
||||
onSettled: () => {
|
||||
if (data) setPageData([...data])
|
||||
}
|
||||
})
|
||||
|
||||
@ -79,18 +100,16 @@ const FrigateHostsPage = () => {
|
||||
if (hostsPending || adminLoading) return <CenterLoader />
|
||||
if (!isAdmin) return <Forbidden />
|
||||
if (hostsError) return <RetryErrorPage />
|
||||
if (!pageData) return <Text>Empty server response</Text>
|
||||
|
||||
return (
|
||||
<div>
|
||||
{
|
||||
!pageData ? <></> :
|
||||
<FrigateHostsTable data={pageData} showAddButton changedCallback={handleChange} />
|
||||
}
|
||||
<Flex w='100%' h='100%' direction='column'>
|
||||
<FrigateHostsTable data={pageData} showAddButton changedCallback={handleChange} />
|
||||
<Flex justify='center'>
|
||||
<Button m='0.5rem' onClick={handleDiscard}>{strings.discard}</Button>
|
||||
<Button m='0.5rem' onClick={handleSave}>{strings.save}</Button>
|
||||
</Flex>
|
||||
</div>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -1,17 +1,16 @@
|
||||
import { Button, Flex, Table } from '@mantine/core';
|
||||
import { IconPlus, IconTrash } from '@tabler/icons-react';
|
||||
import ObjectId from 'bson-objectid';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { GetFrigateHost } from '../services/frigate.proxy/frigate.schema';
|
||||
import HostSettingsMenu from '../shared/components/menu/HostSettingsMenu';
|
||||
import SortedTh from '../shared/components/table.aps/SortedTh';
|
||||
import { isProduction } from '../shared/env.const';
|
||||
import { strings } from '../shared/strings/strings';
|
||||
import { debounce } from '../shared/utils/debounce';
|
||||
import StateCell from './hosts.table/StateCell';
|
||||
import SwitchCell from './hosts.table/SwitchCell';
|
||||
import TextInputCell from './hosts.table/TextInputCell';
|
||||
import { isProduction } from '../shared/env.const';
|
||||
|
||||
interface TableProps<T> {
|
||||
data: T[],
|
||||
@ -21,22 +20,22 @@ interface TableProps<T> {
|
||||
}
|
||||
|
||||
const FrigateHostsTable = ({ data, showAddButton = false, saveCallback, changedCallback }: TableProps<GetFrigateHost>) => {
|
||||
if (!isProduction) console.log('FrigateHostsTable rendered')
|
||||
const [tableData, setTableData] = useState(data)
|
||||
const [reversed, setReversed] = useState(false)
|
||||
const [sortedName, setSortedName] = useState<string | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
setTableData(data)
|
||||
}, [data])
|
||||
|
||||
const debouncedChanged = useCallback(debounce((tableData: GetFrigateHost[]) => {
|
||||
if (changedCallback) changedCallback(tableData)
|
||||
}, 200), [tableData])
|
||||
console.log('data', data)
|
||||
console.log('tableData', tableData)
|
||||
|
||||
useEffect(() => {
|
||||
debouncedChanged(tableData)
|
||||
}, [tableData, debouncedChanged])
|
||||
setTableData(data);
|
||||
}, [data]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isProduction) console.log('TableData', tableData)
|
||||
if (changedCallback)
|
||||
changedCallback(tableData)
|
||||
}, [tableData])
|
||||
|
||||
function sortByKey<T, K extends keyof T>(array: T[], key: K): T[] {
|
||||
return array.sort((a, b) => {
|
||||
@ -80,7 +79,7 @@ const FrigateHostsTable = ({ data, showAddButton = false, saveCallback, changedC
|
||||
)
|
||||
})
|
||||
|
||||
const handleTextChange = (id: string | number, propertyName: string, value: string,) => {
|
||||
const handleTextChange = (id: string | number, propertyName: string, value: string | number | boolean | undefined,) => {
|
||||
setTableData(tableData.map(item => {
|
||||
if (item.id === id) {
|
||||
return {
|
||||
@ -116,7 +115,7 @@ const FrigateHostsTable = ({ data, showAddButton = false, saveCallback, changedC
|
||||
name: '',
|
||||
enabled: true
|
||||
}
|
||||
setTableData(prevTableData => [...prevTableData, newHost])
|
||||
setTableData([...tableData, newHost])
|
||||
}
|
||||
|
||||
const rows = tableData.map(item => {
|
||||
@ -135,7 +134,7 @@ const FrigateHostsTable = ({ data, showAddButton = false, saveCallback, changedC
|
||||
</tr>
|
||||
)
|
||||
})
|
||||
|
||||
if (!isProduction) console.log('FrigateHostsTable rendered')
|
||||
return (
|
||||
<div>
|
||||
<Table >
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { Flex, Switch, useMantineTheme } from '@mantine/core';
|
||||
import { IconBulbFilled, IconBulbOff } from '@tabler/icons-react';
|
||||
import React from 'react';
|
||||
import { boolean } from 'zod';
|
||||
|
||||
interface SwithCellProps {
|
||||
value?: boolean,
|
||||
@ -14,7 +15,7 @@ interface SwithCellProps {
|
||||
export const SwitchCell = ( { value, defaultValue, width, id, propertyName, toggle }: SwithCellProps ) => {
|
||||
const theme = useMantineTheme();
|
||||
|
||||
if (!value && !defaultValue) value = defaultValue
|
||||
if (typeof value !== 'boolean') value = defaultValue
|
||||
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (id && toggle && propertyName) toggle(id, propertyName, event.target.value)
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { TextInput } from '@mantine/core';
|
||||
import React from 'react';
|
||||
import { useDebouncedValue } from '@mantine/hooks';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
interface TextImputCellProps {
|
||||
text?: string | number | boolean,
|
||||
@ -9,18 +10,25 @@ interface TextImputCellProps {
|
||||
onChange?: (
|
||||
id: string | number,
|
||||
propertyName: string,
|
||||
value: string,
|
||||
value?: string | number | boolean,
|
||||
) => void,
|
||||
}
|
||||
|
||||
const TextInputCell = ({ text, width, id, propertyName, onChange }: TextImputCellProps) => {
|
||||
const [value, setValue] = useState(text);
|
||||
const [debouncedValue] = useDebouncedValue(value, 300)
|
||||
useEffect(() => {
|
||||
if (debouncedValue !== text) {
|
||||
if (id && propertyName && onChange)
|
||||
onChange(id, propertyName, debouncedValue)
|
||||
}
|
||||
}, [debouncedValue, id, propertyName, onChange, text])
|
||||
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (id && propertyName && onChange)
|
||||
onChange(id, propertyName, event.currentTarget.value)
|
||||
setValue(event.currentTarget.value)
|
||||
}
|
||||
return (
|
||||
<td style={{ width: width, textAlign: 'center' }}>
|
||||
<TextInput onChange={handleChange} size='sm' value={String(text)} />
|
||||
<TextInput onChange={handleChange} size='sm' value={String(value)} />
|
||||
</td>
|
||||
)
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user