api refactoring

This commit is contained in:
proddy
2024-08-09 14:35:33 +02:00
parent 93ed502e57
commit 251b0ea287
24 changed files with 134 additions and 150 deletions

View File

@@ -25,13 +25,12 @@
"@alova/adapter-xhr": "^2.0.4", "@alova/adapter-xhr": "^2.0.4",
"@emotion/react": "^11.13.0", "@emotion/react": "^11.13.0",
"@emotion/styled": "^11.13.0", "@emotion/styled": "^11.13.0",
"@mui/icons-material": "^5.16.6", "@mui/icons-material": "^5.16.7",
"@mui/material": "^5.16.6", "@mui/material": "^5.16.7",
"@table-library/react-table-library": "4.1.7", "@table-library/react-table-library": "4.1.7",
"alova": "^3.0.5", "alova": "^3.0.5",
"async-validator": "^4.2.5", "async-validator": "^4.2.5",
"jwt-decode": "^4.0.0", "jwt-decode": "^4.0.0",
"lodash-es": "^4.17.21",
"mime-types": "^2.1.35", "mime-types": "^2.1.35",
"preact": "^10.23.1", "preact": "^10.23.1",
"react": "^18.3.1", "react": "^18.3.1",
@@ -50,7 +49,6 @@
"@preact/preset-vite": "^2.9.0", "@preact/preset-vite": "^2.9.0",
"@trivago/prettier-plugin-sort-imports": "^4.3.0", "@trivago/prettier-plugin-sort-imports": "^4.3.0",
"@types/babel__core": "^7", "@types/babel__core": "^7",
"@types/lodash-es": "^4.17.12",
"@types/node": "^22.1.0", "@types/node": "^22.1.0",
"@types/react": "^18.3.3", "@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0", "@types/react-dom": "^18.3.0",

View File

@@ -1,5 +1,5 @@
import { useContext, useEffect } from 'react'; import { useContext, useEffect } from 'react';
import { Navigate, Route, Routes, useLocation } from 'react-router-dom'; import { Navigate, Route, Routes } from 'react-router-dom';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import AuthenticatedRouting from 'AuthenticatedRouting'; import AuthenticatedRouting from 'AuthenticatedRouting';

View File

@@ -4,9 +4,7 @@ import { toast } from 'react-toastify';
import ForwardIcon from '@mui/icons-material/Forward'; import ForwardIcon from '@mui/icons-material/Forward';
import { Box, Button, Paper, Typography } from '@mui/material'; import { Box, Button, Paper, Typography } from '@mui/material';
import * as AuthenticationApi from 'api/authentication'; import * as AuthenticationApi from 'components/routing/authentication';
import { PROJECT_NAME } from 'api/env';
import { useRequest } from 'alova/client'; import { useRequest } from 'alova/client';
import type { ValidateFieldsError } from 'async-validator'; import type { ValidateFieldsError } from 'async-validator';
import { import {
@@ -15,6 +13,7 @@ import {
ValidatedTextField ValidatedTextField
} from 'components'; } from 'components';
import { AuthenticationContext } from 'contexts/authentication'; import { AuthenticationContext } from 'contexts/authentication';
import { PROJECT_NAME } from 'env';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import type { SignInRequest } from 'types'; import type { SignInRequest } from 'types';
import { onEnterCallback, updateValue } from 'utils'; import { onEnterCallback, updateValue } from 'utils';

View File

@@ -17,7 +17,7 @@ import type {
Settings, Settings,
WriteAnalogSensor, WriteAnalogSensor,
WriteTemperatureSensor WriteTemperatureSensor
} from './types'; } from '../app/main/types';
// DashboardDevices // DashboardDevices
export const readCoreData = () => alovaInstance.Get<CoreData>(`/rest/coreData`); export const readCoreData = () => alovaInstance.Get<CoreData>(`/rest/coreData`);

View File

@@ -2,7 +2,7 @@ import { xhrRequestAdapter } from '@alova/adapter-xhr';
import { createAlova } from 'alova'; import { createAlova } from 'alova';
import ReactHook from 'alova/react'; import ReactHook from 'alova/react';
import { unpack } from '../api/unpack'; import { unpack } from './unpack';
export const ACCESS_TOKEN = 'access_token'; export const ACCESS_TOKEN = 'access_token';

View File

@@ -29,8 +29,8 @@ import {
} from 'components'; } from 'components';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import { readCustomEntities, writeCustomEntities } from '../../api/app';
import SettingsCustomEntitiesDialog from './CustomEntitiesDialog'; import SettingsCustomEntitiesDialog from './CustomEntitiesDialog';
import { readCustomEntities, writeCustomEntities } from './api';
import { DeviceValueTypeNames, DeviceValueUOM_s } from './types'; import { DeviceValueTypeNames, DeviceValueUOM_s } from './types';
import type { Entities, EntityItem } from './types'; import type { Entities, EntityItem } from './types';
import { entityItemValidation } from './validators'; import { entityItemValidation } from './validators';

View File

@@ -26,7 +26,7 @@ import {
Typography Typography
} from '@mui/material'; } from '@mui/material';
import * as SystemApi from 'api/system'; import { restart } from 'api/system';
import { import {
Body, Body,
@@ -50,7 +50,13 @@ import {
} from 'components'; } from 'components';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import * as EMSESP from './api'; import {
readDeviceEntities,
readDevices,
resetCustomizations,
writeCustomizationEntities,
writeDeviceName
} from '../../api/app';
import SettingsCustomizationsDialog from './CustomizationsDialog'; import SettingsCustomizationsDialog from './CustomizationsDialog';
import EntityMaskToggle from './EntityMaskToggle'; import EntityMaskToggle from './EntityMaskToggle';
import OptionIcon from './OptionIcon'; import OptionIcon from './OptionIcon';
@@ -77,7 +83,7 @@ const Customizations = () => {
useLayoutTitle(LL.CUSTOMIZATIONS()); useLayoutTitle(LL.CUSTOMIZATIONS());
// fetch devices first // fetch devices first
const { data: devices, send: fetchDevices } = useRequest(EMSESP.readDevices); const { data: devices, send: fetchDevices } = useRequest(readDevices);
const [selectedDevice, setSelectedDevice] = useState<number>( const [selectedDevice, setSelectedDevice] = useState<number>(
Number(useLocation().state) || -1 Number(useLocation().state) || -1
@@ -86,27 +92,26 @@ const Customizations = () => {
useState<string>(''); // needed for API URL useState<string>(''); // needed for API URL
const [selectedDeviceName, setSelectedDeviceName] = useState<string>(''); const [selectedDeviceName, setSelectedDeviceName] = useState<string>('');
const { send: resetCustomizations } = useRequest(EMSESP.resetCustomizations(), { const { send: sendResetCustomizations } = useRequest(resetCustomizations(), {
immediate: false immediate: false
}); });
const { send: writeDeviceName } = useRequest( const { send: sendDeviceName } = useRequest(
(data: { id: number; name: string }) => EMSESP.writeDeviceName(data), (data: { id: number; name: string }) => writeDeviceName(data),
{ {
immediate: false immediate: false
} }
); );
const { send: writeCustomizationEntities } = useRequest( const { send: sendCustomizationEntities } = useRequest(
(data: { id: number; entity_ids: string[] }) => (data: { id: number; entity_ids: string[] }) => writeCustomizationEntities(data),
EMSESP.writeCustomizationEntities(data),
{ {
immediate: false immediate: false
} }
); );
const { send: readDeviceEntities } = useRequest( const { send: sendDeviceEntities } = useRequest(
(data: number) => EMSESP.readDeviceEntities(data), (data: number) => readDeviceEntities(data),
{ {
initialData: [], initialData: [],
immediate: false immediate: false
@@ -127,7 +132,7 @@ const Customizations = () => {
); );
}; };
const { send: restartCommand } = useRequest(SystemApi.restart(), { const { send: sendRestart } = useRequest(restart(), {
immediate: false immediate: false
}); });
@@ -228,7 +233,7 @@ const Customizations = () => {
useEffect(() => { useEffect(() => {
if (devices && selectedDevice !== -1) { if (devices && selectedDevice !== -1) {
void readDeviceEntities(selectedDevice); void sendDeviceEntities(selectedDevice);
const id = devices.devices.findIndex((d) => d.i === selectedDevice); const id = devices.devices.findIndex((d) => d.i === selectedDevice);
if (id === -1) { if (id === -1) {
setSelectedDevice(-1); setSelectedDevice(-1);
@@ -242,8 +247,8 @@ const Customizations = () => {
} }
}, [devices, selectedDevice]); }, [devices, selectedDevice]);
const restart = async () => { const doRestart = async () => {
await restartCommand().catch((error: Error) => { await sendRestart().catch((error: Error) => {
toast.error(error.message); toast.error(error.message);
}); });
setRestarting(true); setRestarting(true);
@@ -324,7 +329,7 @@ const Customizations = () => {
const resetCustomization = async () => { const resetCustomization = async () => {
try { try {
await resetCustomizations(); await sendResetCustomizations();
toast.info(LL.CUSTOMIZATIONS_RESTART()); toast.info(LL.CUSTOMIZATIONS_RESTART());
} catch (error) { } catch (error) {
toast.error((error as Error).message); toast.error((error as Error).message);
@@ -384,7 +389,7 @@ const Customizations = () => {
return; return;
} }
await writeCustomizationEntities({ await sendCustomizationEntities({
id: selectedDevice, id: selectedDevice,
entity_ids: masked_entities entity_ids: masked_entities
}).catch((error: Error) => { }).catch((error: Error) => {
@@ -399,7 +404,7 @@ const Customizations = () => {
}; };
const renameDevice = async () => { const renameDevice = async () => {
await writeDeviceName({ id: selectedDevice, name: selectedDeviceName }) await sendDeviceName({ id: selectedDevice, name: selectedDeviceName })
.then(() => { .then(() => {
toast.success(LL.UPDATED_OF(LL.NAME(1))); toast.success(LL.UPDATED_OF(LL.NAME(1)));
}) })
@@ -673,7 +678,7 @@ const Customizations = () => {
startIcon={<PowerSettingsNewIcon />} startIcon={<PowerSettingsNewIcon />}
variant="contained" variant="contained"
color="error" color="error"
onClick={restart} onClick={doRestart}
> >
{LL.RESTART()} {LL.RESTART()}
</Button> </Button>
@@ -688,7 +693,7 @@ const Customizations = () => {
startIcon={<CancelIcon />} startIcon={<CancelIcon />}
variant="outlined" variant="outlined"
color="secondary" color="secondary"
onClick={() => devices && readDeviceEntities(selectedDevice)} onClick={() => devices && sendDeviceEntities(selectedDevice)}
> >
{LL.CANCEL()} {LL.CANCEL()}
</Button> </Button>

View File

@@ -61,7 +61,7 @@ import { ButtonRow, MessageBox, SectionContent, useLayoutTitle } from 'component
import { AuthenticatedContext } from 'contexts/authentication'; import { AuthenticatedContext } from 'contexts/authentication';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import * as EMSESP from './api'; import { readCoreData, readDeviceData, writeDeviceValue } from '../../api/app';
import DeviceIcon from './DeviceIcon'; import DeviceIcon from './DeviceIcon';
import DashboardDevicesDialog from './DevicesDialog'; import DashboardDevicesDialog from './DevicesDialog';
import { formatValue } from './deviceValue'; import { formatValue } from './deviceValue';
@@ -84,18 +84,15 @@ const Devices = () => {
useLayoutTitle(LL.DEVICES()); useLayoutTitle(LL.DEVICES());
const { data: coreData, send: readCoreData } = useRequest( const { data: coreData, send: sendCoreData } = useRequest(() => readCoreData(), {
() => EMSESP.readCoreData(),
{
initialData: { initialData: {
connected: true, connected: true,
devices: [] devices: []
} }
} });
);
const { data: deviceData, send: readDeviceData } = useRequest( const { data: deviceData, send: sendDeviceData } = useRequest(
(id: number) => EMSESP.readDeviceData(id), (id: number) => readDeviceData(id),
{ {
initialData: { initialData: {
data: [] data: []
@@ -104,8 +101,8 @@ const Devices = () => {
} }
); );
const { loading: submitting, send: writeDeviceValue } = useRequest( const { loading: submitting, send: sendDeviceValue } = useRequest(
(data: { id: number; c: string; v: unknown }) => EMSESP.writeDeviceValue(data), (data: { id: number; c: string; v: unknown }) => writeDeviceValue(data),
{ {
immediate: false immediate: false
} }
@@ -287,7 +284,7 @@ const Devices = () => {
async function onSelectChange(action: Action, state: State) { async function onSelectChange(action: Action, state: State) {
setSelectedDevice(state.id as number); setSelectedDevice(state.id as number);
if (action.type === 'ADD_BY_ID_EXCLUSIVELY') { if (action.type === 'ADD_BY_ID_EXCLUSIVELY') {
await readDeviceData(state.id as number); await sendDeviceData(state.id as number);
} }
} }
@@ -322,7 +319,7 @@ const Devices = () => {
const refreshData = () => { const refreshData = () => {
if (!deviceValueDialogOpen) { if (!deviceValueDialogOpen) {
selectedDevice ? void readDeviceData(selectedDevice) : void readCoreData(); selectedDevice ? void sendDeviceData(selectedDevice) : void sendCoreData();
} }
}; };
@@ -444,7 +441,7 @@ const Devices = () => {
const deviceValueDialogSave = async (devicevalue: DeviceValue) => { const deviceValueDialogSave = async (devicevalue: DeviceValue) => {
const id = Number(device_select.state.id); const id = Number(device_select.state.id);
await writeDeviceValue({ id, c: devicevalue.c ?? '', v: devicevalue.v }) await sendDeviceValue({ id, c: devicevalue.c ?? '', v: devicevalue.v })
.then(() => { .then(() => {
toast.success(LL.WRITE_CMD_SENT()); toast.success(LL.WRITE_CMD_SENT());
}) })
@@ -453,7 +450,7 @@ const Devices = () => {
}) })
.finally(async () => { .finally(async () => {
setDeviceValueDialogOpen(false); setDeviceValueDialogOpen(false);
await readDeviceData(id); await sendDeviceData(id);
setSelectedDeviceValue(undefined); setSelectedDeviceValue(undefined);
}); });
}; };

View File

@@ -17,20 +17,20 @@ import {
Typography Typography
} from '@mui/material'; } from '@mui/material';
import * as SystemApi from 'api/system'; import { readSystemStatus } from 'api/system';
import * as EMSESP from 'app/main/api';
import { useRequest } from 'alova/client'; import { useRequest } from 'alova/client';
import { SectionContent, useLayoutTitle } from 'components'; import { SectionContent, useLayoutTitle } from 'components';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import { API } from '../../api/app';
import type { APIcall } from './types'; import type { APIcall } from './types';
const Help = () => { const Help = () => {
const { LL } = useI18nContext(); const { LL } = useI18nContext();
useLayoutTitle(LL.HELP_OF('')); useLayoutTitle(LL.HELP_OF(''));
const { send: getAPI } = useRequest((data: APIcall) => EMSESP.API(data), { const { send: getAPI } = useRequest((data: APIcall) => API(data), {
immediate: false immediate: false
}).onSuccess((event) => { }).onSuccess((event) => {
const anchor = document.createElement('a'); const anchor = document.createElement('a');
@@ -47,7 +47,7 @@ const Help = () => {
toast.info(LL.DOWNLOAD_SUCCESSFUL()); toast.info(LL.DOWNLOAD_SUCCESSFUL());
}); });
const { data, loading } = useRequest(SystemApi.readSystemStatus); const { data, loading } = useRequest(readSystemStatus);
const callAPI = async (device: string, entity: string) => { const callAPI = async (device: string, entity: string) => {
await getAPI({ device, entity, id: 0 }).catch((error: Error) => { await getAPI({ device, entity, id: 0 }).catch((error: Error) => {

View File

@@ -27,8 +27,8 @@ import {
} from 'components'; } from 'components';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import { readModules, writeModules } from '../../api/app';
import ModulesDialog from './ModulesDialog'; import ModulesDialog from './ModulesDialog';
import { readModules, writeModules } from './api';
import type { ModuleItem, Modules } from './types'; import type { ModuleItem, Modules } from './types';
const Modules = () => { const Modules = () => {

View File

@@ -28,8 +28,8 @@ import {
} from 'components'; } from 'components';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import { readSchedule, writeSchedule } from '../../api/app';
import SettingsSchedulerDialog from './SchedulerDialog'; import SettingsSchedulerDialog from './SchedulerDialog';
import { readSchedule, writeSchedule } from './api';
import { ScheduleFlag } from './types'; import { ScheduleFlag } from './types';
import type { Schedule, ScheduleItem } from './types'; import type { Schedule, ScheduleItem } from './types';
import { schedulerItemValidation } from './validators'; import { schedulerItemValidation } from './validators';

View File

@@ -25,7 +25,11 @@ import { ButtonRow, SectionContent, useLayoutTitle } from 'components';
import { AuthenticatedContext } from 'contexts/authentication'; import { AuthenticatedContext } from 'contexts/authentication';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import * as EMSESP from './api'; import {
readSensorData,
writeAnalogSensor,
writeTemperatureSensor
} from '../../api/app';
import DashboardSensorsAnalogDialog from './SensorsAnalogDialog'; import DashboardSensorsAnalogDialog from './SensorsAnalogDialog';
import DashboardSensorsTemperatureDialog from './SensorsTemperatureDialog'; import DashboardSensorsTemperatureDialog from './SensorsTemperatureDialog';
import { import {
@@ -56,8 +60,8 @@ const Sensors = () => {
const [analogDialogOpen, setAnalogDialogOpen] = useState<boolean>(false); const [analogDialogOpen, setAnalogDialogOpen] = useState<boolean>(false);
const [creating, setCreating] = useState<boolean>(false); const [creating, setCreating] = useState<boolean>(false);
const { data: sensorData, send: fetchSensorData } = useRequest( const { data: sensorData, send: sendSensorData } = useRequest(
() => EMSESP.readSensorData(), () => readSensorData(),
{ {
initialData: { initialData: {
ts: [], ts: [],
@@ -68,15 +72,15 @@ const Sensors = () => {
} }
); );
const { send: writeTemperatureSensor } = useRequest( const { send: sendTemperatureSensor } = useRequest(
(data: WriteTemperatureSensor) => EMSESP.writeTemperatureSensor(data), (data: WriteTemperatureSensor) => writeTemperatureSensor(data),
{ {
immediate: false immediate: false
} }
); );
const { send: writeAnalogSensor } = useRequest( const { send: sendAnalogSensor } = useRequest(
(data: WriteAnalogSensor) => EMSESP.writeAnalogSensor(data), (data: WriteAnalogSensor) => writeAnalogSensor(data),
{ {
immediate: false immediate: false
} }
@@ -195,7 +199,7 @@ const Sensors = () => {
); );
useEffect(() => { useEffect(() => {
const timer = setInterval(() => fetchSensorData(), 30000); const timer = setInterval(() => sendSensorData(), 30000);
return () => { return () => {
clearInterval(timer); clearInterval(timer);
}; };
@@ -265,7 +269,7 @@ const Sensors = () => {
}; };
const onTemperatureDialogSave = async (ts: TemperatureSensor) => { const onTemperatureDialogSave = async (ts: TemperatureSensor) => {
await writeTemperatureSensor({ id: ts.id, name: ts.n, offset: ts.o }) await sendTemperatureSensor({ id: ts.id, name: ts.n, offset: ts.o })
.then(() => { .then(() => {
toast.success(LL.UPDATED_OF(LL.SENSOR(1))); toast.success(LL.UPDATED_OF(LL.SENSOR(1)));
}) })
@@ -275,7 +279,7 @@ const Sensors = () => {
.finally(async () => { .finally(async () => {
setTemperatureDialogOpen(false); setTemperatureDialogOpen(false);
setSelectedTemperatureSensor(undefined); setSelectedTemperatureSensor(undefined);
await fetchSensorData(); await sendSensorData();
}); });
}; };
@@ -310,7 +314,7 @@ const Sensors = () => {
}; };
const onAnalogDialogSave = async (as: AnalogSensor) => { const onAnalogDialogSave = async (as: AnalogSensor) => {
await writeAnalogSensor({ await sendAnalogSensor({
id: as.id, id: as.id,
gpio: as.g, gpio: as.g,
name: as.n, name: as.n,
@@ -329,7 +333,7 @@ const Sensors = () => {
.finally(async () => { .finally(async () => {
setAnalogDialogOpen(false); setAnalogDialogOpen(false);
setSelectedAnalogSensor(undefined); setSelectedAnalogSensor(undefined);
await fetchSensorData(); await sendSensorData();
}); });
}; };
@@ -503,7 +507,7 @@ const Sensors = () => {
startIcon={<RefreshIcon />} startIcon={<RefreshIcon />}
variant="outlined" variant="outlined"
color="secondary" color="secondary"
onClick={fetchSensorData} onClick={sendSensorData}
> >
{LL.REFRESH()} {LL.REFRESH()}
</Button> </Button>

View File

@@ -18,7 +18,6 @@ import {
useLayoutTitle useLayoutTitle
} from 'components'; } from 'components';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import { range } from 'lodash-es';
import type { APSettingsType } from 'types'; import type { APSettingsType } from 'types';
import { APProvisionMode } from 'types'; import { APProvisionMode } from 'types';
import { numberValue, updateValueDirty, useRest } from 'utils'; import { numberValue, updateValueDirty, useRest } from 'utils';
@@ -73,6 +72,11 @@ const APSettings = () => {
} }
}; };
// no lodash - https://asleepace.com/blog/typescript-range-without-a-loop/
function range(a: number, b: number): number[] {
return a < b ? [a, ...range(a + 1, b)] : [b];
}
return ( return (
<> <>
<ValidatedTextField <ValidatedTextField

View File

@@ -16,7 +16,7 @@ import {
Typography Typography
} from '@mui/material'; } from '@mui/material';
import * as SystemApi from 'api/system'; import { readHardwareStatus, restart } from 'api/system';
import { useRequest } from 'alova/client'; import { useRequest } from 'alova/client';
import RestartMonitor from 'app/status/RestartMonitor'; import RestartMonitor from 'app/status/RestartMonitor';
@@ -35,7 +35,7 @@ import { useI18nContext } from 'i18n/i18n-react';
import { numberValue, updateValueDirty, useRest } from 'utils'; import { numberValue, updateValueDirty, useRest } from 'utils';
import { validate } from 'validators'; import { validate } from 'validators';
import * as EMSESP from '../main/api'; import { getBoardProfile, readSettings, writeSettings } from '../../api/app';
import { BOARD_PROFILES } from '../main/types'; import { BOARD_PROFILES } from '../main/types';
import type { Settings } from '../main/types'; import type { Settings } from '../main/types';
import { createSettingsValidator } from '../main/validators'; import { createSettingsValidator } from '../main/validators';
@@ -49,7 +49,7 @@ export function boardProfileSelectItems() {
} }
const ApplicationSettings = () => { const ApplicationSettings = () => {
const { data: hardwareData } = useRequest(SystemApi.readHardwareStatus, { const { data: hardwareData } = useRequest(readHardwareStatus, {
initialData: { psram: false } initialData: { psram: false }
}); });
@@ -66,8 +66,8 @@ const ApplicationSettings = () => {
errorMessage, errorMessage,
restartNeeded restartNeeded
} = useRest<Settings>({ } = useRest<Settings>({
read: EMSESP.readSettings, read: readSettings,
update: EMSESP.writeSettings update: writeSettings
}); });
const [restarting, setRestarting] = useState<boolean>(); const [restarting, setRestarting] = useState<boolean>();
@@ -84,7 +84,7 @@ const ApplicationSettings = () => {
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>(); const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
const { loading: processingBoard, send: readBoardProfile } = useRequest( const { loading: processingBoard, send: readBoardProfile } = useRequest(
(boardProfile: string) => EMSESP.getBoardProfile(boardProfile), (boardProfile: string) => getBoardProfile(boardProfile),
{ {
immediate: false immediate: false
} }
@@ -105,7 +105,7 @@ const ApplicationSettings = () => {
}); });
}); });
const { send: restartCommand } = useRequest(SystemApi.restart(), { const { send: restartCommand } = useRequest(restart(), {
immediate: false immediate: false
}); });

View File

@@ -5,8 +5,15 @@ import DownloadIcon from '@mui/icons-material/GetApp';
import { Box, Button, Divider, Link, Typography } from '@mui/material'; import { Box, Button, Divider, Link, Typography } from '@mui/material';
import * as SystemApi from 'api/system'; import * as SystemApi from 'api/system';
import {
API,
getCustomizations,
getEntities,
getSchedule,
getSettings
} from 'api/app';
import { getDevVersion, getStableVersion } from 'api/system';
import * as EMSESP from 'app/main/api';
import { useRequest } from 'alova/client'; import { useRequest } from 'alova/client';
import type { APIcall } from 'app/main/types'; import type { APIcall } from 'app/main/types';
import { import {
@@ -24,31 +31,31 @@ const UploadDownload = () => {
const [restarting, setRestarting] = useState<boolean>(); const [restarting, setRestarting] = useState<boolean>();
const [md5, setMd5] = useState<string>(); const [md5, setMd5] = useState<string>();
const { send: getSettings } = useRequest(EMSESP.getSettings(), { const { send: sendSettings } = useRequest(getSettings(), {
immediate: false immediate: false
}).onSuccess((event) => { }).onSuccess((event) => {
saveFile(event.data, 'settings.json'); saveFile(event.data, 'settings.json');
}); });
const { send: getCustomizations } = useRequest(EMSESP.getCustomizations(), { const { send: sendCustomizations } = useRequest(getCustomizations(), {
immediate: false immediate: false
}).onSuccess((event) => { }).onSuccess((event) => {
saveFile(event.data, 'customizations.json'); saveFile(event.data, 'customizations.json');
}); });
const { send: getEntities } = useRequest(EMSESP.getEntities(), { const { send: sendEntities } = useRequest(getEntities(), {
immediate: false immediate: false
}).onSuccess((event) => { }).onSuccess((event) => {
saveFile(event.data, 'entities.json'); saveFile(event.data, 'entities.json');
}); });
const { send: getSchedule } = useRequest(EMSESP.getSchedule(), { const { send: sendSchedule } = useRequest(getSchedule(), {
immediate: false immediate: false
}).onSuccess((event) => { }).onSuccess((event) => {
saveFile(event.data, 'schedule.json'); saveFile(event.data, 'schedule.json');
}); });
const { send: getAPI } = useRequest((data: APIcall) => EMSESP.API(data), { const { send: getAPI } = useRequest((data: APIcall) => API(data), {
immediate: false immediate: false
}).onSuccess((event) => { }).onSuccess((event) => {
saveFile( saveFile(
@@ -64,8 +71,8 @@ const UploadDownload = () => {
} = useRequest(SystemApi.readHardwareStatus); } = useRequest(SystemApi.readHardwareStatus);
// called immediately to get the latest version, on page load // called immediately to get the latest version, on page load
const { data: latestVersion } = useRequest(SystemApi.getStableVersion); const { data: latestVersion } = useRequest(getStableVersion);
const { data: latestDevVersion } = useRequest(SystemApi.getDevVersion); const { data: latestDevVersion } = useRequest(getDevVersion);
const STABLE_URL = 'https://github.com/emsesp/EMS-ESP32/releases/download/'; const STABLE_URL = 'https://github.com/emsesp/EMS-ESP32/releases/download/';
const STABLE_RELNOTES_URL = const STABLE_RELNOTES_URL =
@@ -131,25 +138,25 @@ const UploadDownload = () => {
}; };
const downloadSettings = async () => { const downloadSettings = async () => {
await getSettings().catch((error: Error) => { await sendSettings().catch((error: Error) => {
toast.error(error.message); toast.error(error.message);
}); });
}; };
const downloadCustomizations = async () => { const downloadCustomizations = async () => {
await getCustomizations().catch((error: Error) => { await sendCustomizations().catch((error: Error) => {
toast.error(error.message); toast.error(error.message);
}); });
}; };
const downloadEntities = async () => { const downloadEntities = async () => {
await getEntities().catch((error: Error) => { await sendEntities().catch((error: Error) => {
toast.error(error.message); toast.error(error.message);
}); });
}; };
const downloadSchedule = async () => { const downloadSchedule = async () => {
await getSchedule().catch((error: Error) => { await sendSchedule().catch((error: Error) => {
toast.error(error.message); toast.error(error.message);
}); });
}; };

View File

@@ -18,7 +18,7 @@ import { ButtonRow, FormLoader, SectionContent, useLayoutTitle } from 'component
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import type { Translation } from 'i18n/i18n-types'; import type { Translation } from 'i18n/i18n-types';
import * as EMSESP from '../main/api'; import * as EMSESP from '../../api/app';
import type { Stat } from '../main/types'; import type { Stat } from '../main/types';
const SystemActivity = () => { const SystemActivity = () => {

View File

@@ -4,8 +4,7 @@ import { useLocation } from 'react-router-dom';
import { Box, Toolbar } from '@mui/material'; import { Box, Toolbar } from '@mui/material';
import { PROJECT_NAME } from 'api/env'; import { PROJECT_NAME } from 'env';
import type { RequiredChildrenProps } from 'utils'; import type { RequiredChildrenProps } from 'utils';
import LayoutAppBar from './LayoutAppBar'; import LayoutAppBar from './LayoutAppBar';

View File

@@ -1,6 +1,6 @@
import { Box, Divider, Drawer, Toolbar, Typography, styled } from '@mui/material'; import { Box, Divider, Drawer, Toolbar, Typography, styled } from '@mui/material';
import { PROJECT_NAME } from 'api/env'; import { PROJECT_NAME } from 'env';
import { DRAWER_WIDTH } from './Layout'; import { DRAWER_WIDTH } from './Layout';
import LayoutMenu from './LayoutMenu'; import LayoutMenu from './LayoutMenu';

View File

@@ -2,8 +2,7 @@ import { useContext, useEffect } from 'react';
import type { FC } from 'react'; import type { FC } from 'react';
import { Navigate, useLocation } from 'react-router-dom'; import { Navigate, useLocation } from 'react-router-dom';
import { storeLoginRedirect } from 'api/authentication'; import { storeLoginRedirect } from 'components/routing/authentication';
import type { AuthenticatedContextValue } from 'contexts/authentication/context'; import type { AuthenticatedContextValue } from 'contexts/authentication/context';
import { import {
AuthenticatedContext, AuthenticatedContext,

View File

@@ -2,8 +2,7 @@ import { useContext } from 'react';
import type { FC } from 'react'; import type { FC } from 'react';
import { Navigate } from 'react-router-dom'; import { Navigate } from 'react-router-dom';
import * as AuthenticationApi from 'api/authentication'; import { fetchLoginRedirect } from 'components/routing/authentication';
import { AuthenticationContext } from 'contexts/authentication'; import { AuthenticationContext } from 'contexts/authentication';
import type { RequiredChildrenProps } from 'utils'; import type { RequiredChildrenProps } from 'utils';
@@ -11,7 +10,7 @@ const RequireUnauthenticated: FC<RequiredChildrenProps> = ({ children }) => {
const authenticationContext = useContext(AuthenticationContext); const authenticationContext = useContext(AuthenticationContext);
return authenticationContext.me ? ( return authenticationContext.me ? (
<Navigate to={AuthenticationApi.fetchLoginRedirect()} /> <Navigate to={fetchLoginRedirect()} />
) : ( ) : (
<>{children}</> <>{children}</>
); );

View File

@@ -4,7 +4,7 @@ import type * as H from 'history';
import { jwtDecode } from 'jwt-decode'; import { jwtDecode } from 'jwt-decode';
import type { Me, SignInRequest, SignInResponse } from 'types'; import type { Me, SignInRequest, SignInResponse } from 'types';
import { ACCESS_TOKEN, alovaInstance } from './endpoints'; import { ACCESS_TOKEN, alovaInstance } from '../../api/endpoints';
export const SIGN_IN_PATHNAME = 'loginPathname'; export const SIGN_IN_PATHNAME = 'loginPathname';
export const SIGN_IN_SEARCH = 'loginSearch'; export const SIGN_IN_SEARCH = 'loginSearch';

View File

@@ -3,11 +3,12 @@ import type { FC } from 'react';
import { redirect } from 'react-router-dom'; import { redirect } from 'react-router-dom';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import * as AuthenticationApi from 'api/authentication';
import { ACCESS_TOKEN } from 'api/endpoints'; import { ACCESS_TOKEN } from 'api/endpoints';
import * as AuthenticationApi from 'components/routing/authentication';
import { useRequest } from 'alova/client'; import { useRequest } from 'alova/client';
import { LoadingSpinner } from 'components'; import { LoadingSpinner } from 'components';
import { verifyAuthorization } from 'components/routing/authentication';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import type { Me } from 'types'; import type { Me } from 'types';
import type { RequiredChildrenProps } from 'utils'; import type { RequiredChildrenProps } from 'utils';
@@ -20,12 +21,9 @@ const Authentication: FC<RequiredChildrenProps> = ({ children }) => {
const [initialized, setInitialized] = useState<boolean>(false); const [initialized, setInitialized] = useState<boolean>(false);
const [me, setMe] = useState<Me>(); const [me, setMe] = useState<Me>();
const { send: verifyAuthorization } = useRequest( const { send: sendVerifyAuthorization } = useRequest(verifyAuthorization(), {
AuthenticationApi.verifyAuthorization(),
{
immediate: false immediate: false
} });
);
const signIn = (accessToken: string) => { const signIn = (accessToken: string) => {
try { try {
@@ -51,7 +49,7 @@ const Authentication: FC<RequiredChildrenProps> = ({ children }) => {
const refresh = useCallback(async () => { const refresh = useCallback(async () => {
const accessToken = AuthenticationApi.getStorage().getItem(ACCESS_TOKEN); const accessToken = AuthenticationApi.getStorage().getItem(ACCESS_TOKEN);
if (accessToken) { if (accessToken) {
await verifyAuthorization() await sendVerifyAuthorization()
.then(() => { .then(() => {
setMe(AuthenticationApi.decodeMeJWT(accessToken)); setMe(AuthenticationApi.decodeMeJWT(accessToken));
setInitialized(true); setInitialized(true);

View File

@@ -815,16 +815,16 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@mui/core-downloads-tracker@npm:^5.16.6": "@mui/core-downloads-tracker@npm:^5.16.7":
version: 5.16.6 version: 5.16.7
resolution: "@mui/core-downloads-tracker@npm:5.16.6" resolution: "@mui/core-downloads-tracker@npm:5.16.7"
checksum: 10c0/ee7655eda56e8eeb18ae600b24182c10caab4468d33179de3e76a783a827be9577afe114750835f7c9c7f555c0de0845c0500e91ed2e669b16399a1ffe41c33d checksum: 10c0/4644c850160d01232c1abdbed141e4fa70e155891a9c68f0c2cc3054b4a3cdc1d28cf2d6665366fd8c725b2b091db677e11831552889a4e4e14f1e44450cf654
languageName: node languageName: node
linkType: hard linkType: hard
"@mui/icons-material@npm:^5.16.6": "@mui/icons-material@npm:^5.16.7":
version: 5.16.6 version: 5.16.7
resolution: "@mui/icons-material@npm:5.16.6" resolution: "@mui/icons-material@npm:5.16.7"
dependencies: dependencies:
"@babel/runtime": "npm:^7.23.9" "@babel/runtime": "npm:^7.23.9"
peerDependencies: peerDependencies:
@@ -834,17 +834,17 @@ __metadata:
peerDependenciesMeta: peerDependenciesMeta:
"@types/react": "@types/react":
optional: true optional: true
checksum: 10c0/9d24d95e8b4606b5fa2a2e40319255e2e6da52845db50d2d3ca6fd526102be3c498ec1aa9be8d33aad52cf531749b15d490abca5f23af92afc86cf45dc62ce24 checksum: 10c0/49bab1754334798acaf93187d27200cf90d7c50b6a019531594aeac9e5ced9168281fec70bb040792dc86c8bc0d3bf9a876f22cfbf86ad07941ca6bc6c564921
languageName: node languageName: node
linkType: hard linkType: hard
"@mui/material@npm:^5.16.6": "@mui/material@npm:^5.16.7":
version: 5.16.6 version: 5.16.7
resolution: "@mui/material@npm:5.16.6" resolution: "@mui/material@npm:5.16.7"
dependencies: dependencies:
"@babel/runtime": "npm:^7.23.9" "@babel/runtime": "npm:^7.23.9"
"@mui/core-downloads-tracker": "npm:^5.16.6" "@mui/core-downloads-tracker": "npm:^5.16.7"
"@mui/system": "npm:^5.16.6" "@mui/system": "npm:^5.16.7"
"@mui/types": "npm:^7.2.15" "@mui/types": "npm:^7.2.15"
"@mui/utils": "npm:^5.16.6" "@mui/utils": "npm:^5.16.6"
"@popperjs/core": "npm:^2.11.8" "@popperjs/core": "npm:^2.11.8"
@@ -867,7 +867,7 @@ __metadata:
optional: true optional: true
"@types/react": "@types/react":
optional: true optional: true
checksum: 10c0/52cbffd87a36b9f8e16ba59030e89501e680ddea4d3dcf3db5d8fb7b86205444b00162e656c79571824a74769f879f39df2e7671d1e7ca8bc23602a7bf588dc5 checksum: 10c0/b11419c1a77835413471f9352586fed65fb5de19c6737e121669da0484c441c7dd9939aa73fdad779482c30efaa694fb9fdcf18dcf418af07881e60eaff92b4f
languageName: node languageName: node
linkType: hard linkType: hard
@@ -909,9 +909,9 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@mui/system@npm:^5.16.6": "@mui/system@npm:^5.16.7":
version: 5.16.6 version: 5.16.7
resolution: "@mui/system@npm:5.16.6" resolution: "@mui/system@npm:5.16.7"
dependencies: dependencies:
"@babel/runtime": "npm:^7.23.9" "@babel/runtime": "npm:^7.23.9"
"@mui/private-theming": "npm:^5.16.6" "@mui/private-theming": "npm:^5.16.6"
@@ -933,7 +933,7 @@ __metadata:
optional: true optional: true
"@types/react": "@types/react":
optional: true optional: true
checksum: 10c0/493900594f51d1fc84994042e5cfa000b2d7664f86c8c0d62b87dbbb51cf7e789a700512f98e18c82e605beab38c20d0714ec25c46e2f6a5024f79f16db743f7 checksum: 10c0/c07479c0728433847c1e3d7f57b96d9e0770cc814dfd1c9e070304955984a0b706832703b22388eb83906d1a01691f37047e2bac6a5e5c083e8c29a54302d476
languageName: node languageName: node
linkType: hard linkType: hard
@@ -1429,22 +1429,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@types/lodash-es@npm:^4.17.12":
version: 4.17.12
resolution: "@types/lodash-es@npm:4.17.12"
dependencies:
"@types/lodash": "npm:*"
checksum: 10c0/5d12d2cede07f07ab067541371ed1b838a33edb3c35cb81b73284e93c6fd0c4bbeaefee984e69294bffb53f62d7272c5d679fdba8e595ff71e11d00f2601dde0
languageName: node
linkType: hard
"@types/lodash@npm:*":
version: 4.17.7
resolution: "@types/lodash@npm:4.17.7"
checksum: 10c0/40c965b5ffdcf7ff5c9105307ee08b782da228c01b5c0529122c554c64f6b7168fc8f11dc79aa7bae4e67e17efafaba685dc3a47e294dbf52a65ed2b67100561
languageName: node
linkType: hard
"@types/minimatch@npm:*": "@types/minimatch@npm:*":
version: 5.1.2 version: 5.1.2
resolution: "@types/minimatch@npm:5.1.2" resolution: "@types/minimatch@npm:5.1.2"
@@ -1676,14 +1660,13 @@ __metadata:
"@emotion/react": "npm:^11.13.0" "@emotion/react": "npm:^11.13.0"
"@emotion/styled": "npm:^11.13.0" "@emotion/styled": "npm:^11.13.0"
"@eslint/js": "npm:^9.8.0" "@eslint/js": "npm:^9.8.0"
"@mui/icons-material": "npm:^5.16.6" "@mui/icons-material": "npm:^5.16.7"
"@mui/material": "npm:^5.16.6" "@mui/material": "npm:^5.16.7"
"@preact/compat": "npm:^17.1.2" "@preact/compat": "npm:^17.1.2"
"@preact/preset-vite": "npm:^2.9.0" "@preact/preset-vite": "npm:^2.9.0"
"@table-library/react-table-library": "npm:4.1.7" "@table-library/react-table-library": "npm:4.1.7"
"@trivago/prettier-plugin-sort-imports": "npm:^4.3.0" "@trivago/prettier-plugin-sort-imports": "npm:^4.3.0"
"@types/babel__core": "npm:^7" "@types/babel__core": "npm:^7"
"@types/lodash-es": "npm:^4.17.12"
"@types/node": "npm:^22.1.0" "@types/node": "npm:^22.1.0"
"@types/react": "npm:^18.3.3" "@types/react": "npm:^18.3.3"
"@types/react-dom": "npm:^18.3.0" "@types/react-dom": "npm:^18.3.0"
@@ -1694,7 +1677,6 @@ __metadata:
eslint: "npm:^9.8.0" eslint: "npm:^9.8.0"
eslint-config-prettier: "npm:^9.1.0" eslint-config-prettier: "npm:^9.1.0"
jwt-decode: "npm:^4.0.0" jwt-decode: "npm:^4.0.0"
lodash-es: "npm:^4.17.21"
mime-types: "npm:^2.1.35" mime-types: "npm:^2.1.35"
preact: "npm:^10.23.1" preact: "npm:^10.23.1"
prettier: "npm:^3.3.3" prettier: "npm:^3.3.3"
@@ -4695,13 +4677,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"lodash-es@npm:^4.17.21":
version: 4.17.21
resolution: "lodash-es@npm:4.17.21"
checksum: 10c0/fb407355f7e6cd523a9383e76e6b455321f0f153a6c9625e21a8827d10c54c2a2341bd2ae8d034358b60e07325e1330c14c224ff582d04612a46a4f0479ff2f2
languageName: node
linkType: hard
"lodash.merge@npm:^4.6.2": "lodash.merge@npm:^4.6.2":
version: 4.6.2 version: 4.6.2
resolution: "lodash.merge@npm:4.6.2" resolution: "lodash.merge@npm:4.6.2"