diff --git a/interface/package.json b/interface/package.json index e95c95621..7e91ddcc7 100644 --- a/interface/package.json +++ b/interface/package.json @@ -22,14 +22,13 @@ "lint": "eslint . --fix" }, "dependencies": { - "@alova/adapter-xhr": "1.0.6", - "@alova/scene-react": "1.6.2", + "@alova/adapter-xhr": "2.0.4", "@emotion/react": "^11.13.0", "@emotion/styled": "^11.13.0", "@mui/icons-material": "^5.16.7", "@mui/material": "^5.16.7", "@table-library/react-table-library": "4.1.7", - "alova": "2.21.4", + "alova": "3.0.6", "async-validator": "^4.2.5", "jwt-decode": "^4.0.0", "mime-types": "^2.1.35", diff --git a/interface/src/SignIn.tsx b/interface/src/SignIn.tsx index 9be23b693..409cb36e2 100644 --- a/interface/src/SignIn.tsx +++ b/interface/src/SignIn.tsx @@ -5,8 +5,7 @@ import ForwardIcon from '@mui/icons-material/Forward'; import { Box, Button, Paper, Typography } from '@mui/material'; import * as AuthenticationApi from 'components/routing/authentication'; -// import { useRequest } from 'alova/client' // TODO replace when Alova 3 is released -import { useRequest } from 'alova'; +import { useRequest } from 'alova/client'; import type { ValidateFieldsError } from 'async-validator'; import { LanguageSelector, @@ -32,26 +31,12 @@ const SignIn = () => { const [processing, setProcessing] = useState(false); const [fieldErrors, setFieldErrors] = useState(); - // TODO ALova 3 code... - // const { send: callSignIn } = useRequest( - // (request: SignInRequest) => AuthenticationApi.signIn(request), - // { - // immediate: false - // } - // ).onSuccess((response) => { - // if (response.data) { - // authenticationContext.signIn(response.data.access_token); - // } - // }); - - const { send: callSignIn, onSuccess } = useRequest( + const { send: callSignIn } = useRequest( (request: SignInRequest) => AuthenticationApi.signIn(request), { immediate: false } - ); - - onSuccess((response) => { + ).onSuccess((response) => { if (response.data) { authenticationContext.signIn(response.data.access_token); } diff --git a/interface/src/api/app.ts b/interface/src/api/app.ts index 3d7228d6a..3e605e656 100644 --- a/interface/src/api/app.ts +++ b/interface/src/api/app.ts @@ -65,8 +65,7 @@ export const readDeviceEntities = (id: number) => alovaInstance.Get(`/rest/deviceEntities`, { params: { id }, responseType: 'arraybuffer', - // transform(data) { // TODO alova 3 - transformData(data) { + transform(data) { return (data as DeviceEntity[]).map((de: DeviceEntity) => ({ ...de, o_m: de.m, @@ -89,8 +88,7 @@ export const writeDeviceName = (data: { id: number; name: string }) => // SettingsScheduler export const readSchedule = () => alovaInstance.Get('/rest/schedule', { - // transform(data) { // TODO alova 3 - transformData(data) { + transform(data) { return (data as Schedule).schedule.map((si: ScheduleItem) => ({ ...si, o_id: si.id, @@ -110,8 +108,7 @@ export const writeSchedule = (data: Schedule) => // Modules export const readModules = () => alovaInstance.Get('/rest/modules', { - // transform(data) { // TODO alova 3 - transformData(data) { + transform(data) { return (data as Modules).modules.map((mi: ModuleItem) => ({ ...mi, o_enabled: mi.enabled, @@ -128,8 +125,7 @@ export const writeModules = (data: { // SettingsEntities export const readCustomEntities = () => alovaInstance.Get('/rest/customEntities', { - // transform(data) { // TODO alova 3 - transformData(data) { + transform(data) { return (data as Entities).entities.map((ei: EntityItem) => ({ ...ei, o_id: ei.id, diff --git a/interface/src/api/endpoints.ts b/interface/src/api/endpoints.ts index e104f1ee6..817063538 100644 --- a/interface/src/api/endpoints.ts +++ b/interface/src/api/endpoints.ts @@ -1,4 +1,4 @@ -import { xhrRequestAdapter } from '@alova/adapter-xhr'; +import { type AlovaXHRResponse, xhrRequestAdapter } from '@alova/adapter-xhr'; import { createAlova } from 'alova'; import ReactHook from 'alova/react'; @@ -8,10 +8,13 @@ export const ACCESS_TOKEN = 'access_token'; export const alovaInstance = createAlova({ statesHook: ReactHook, - timeout: 3000, // 3 seconds but throwing a timeout error - localCache: null, - // TODO Alova v3 code... - // cacheFor: null, + timeout: 3000, // 3 seconds before throwing a timeout error + cacheFor: { + GET: { + mode: 'memory', + expire: 60 * 10 * 1000 // 60 seconds in cache + } + }, requestAdapter: xhrRequestAdapter(), beforeRequest(method) { if (localStorage.getItem(ACCESS_TOKEN)) { @@ -21,7 +24,7 @@ export const alovaInstance = createAlova({ }, responded: { - onSuccess: async (response) => { + onSuccess: async (response: AlovaXHRResponse) => { // if (response.status === 202) { // throw new Error('Wait'); // wifi scan in progress // } else diff --git a/interface/src/api/system.ts b/interface/src/api/system.ts index 963e7ca6d..beabe0125 100644 --- a/interface/src/api/system.ts +++ b/interface/src/api/system.ts @@ -24,29 +24,15 @@ export const fetchLog = () => alovaInstance.Post('/rest/fetchLog'); export const fetchLogES = () => alovaInstance.Get('/es/log'); // Get versions from github - -// Alova 3 code... -// export const getStableVersion = () => -// alovaInstanceGH.Get('latest', { -// transform(response: { data: { name: string } }) { -// return response.data.name.substring(1); -// } -// }); -// export const getDevVersion = () => -// alovaInstanceGH.Get('tags/latest', { -// transform(response: { data: { name: string } }) { -// return response.data.name.split(/\s+/).splice(-1)[0].substring(1); -// } -// }); export const getStableVersion = () => alovaInstanceGH.Get('latest', { - transformData(response: { data: { name: string } }) { + transform(response: { data: { name: string } }) { return response.data.name.substring(1); } }); export const getDevVersion = () => alovaInstanceGH.Get('tags/latest', { - transformData(response: { data: { name: string } }) { + transform(response: { data: { name: string } }) { return response.data.name.split(/\s+/).splice(-1)[0].substring(1); } }); diff --git a/interface/src/app/main/CustomEntities.tsx b/interface/src/app/main/CustomEntities.tsx index b3000f3f4..bbaaa0903 100644 --- a/interface/src/app/main/CustomEntities.tsx +++ b/interface/src/app/main/CustomEntities.tsx @@ -19,8 +19,7 @@ import { Table } from '@table-library/react-table-library/table'; import { useTheme } from '@table-library/react-table-library/theme'; -import { updateState, useRequest } from 'alova'; -// import { updateState, useRequest } from 'alova/client'; // TODO replace when Alova 3 is released +import { updateState, useRequest } from 'alova/client'; import { BlockNavigation, ButtonRow, diff --git a/interface/src/app/main/Customizations.tsx b/interface/src/app/main/Customizations.tsx index 7c68e60b4..5dc5214bc 100644 --- a/interface/src/app/main/Customizations.tsx +++ b/interface/src/app/main/Customizations.tsx @@ -39,8 +39,7 @@ import { } from '@table-library/react-table-library/table'; import { useTheme } from '@table-library/react-table-library/theme'; import { dialogStyle } from 'CustomTheme'; -import { useRequest } from 'alova'; -// import { useRequest } from 'alova/client' // TODO replace when Alova 3 is released +import { useRequest } from 'alova/client'; import RestartMonitor from 'app/status/RestartMonitor'; import { BlockNavigation, @@ -111,25 +110,13 @@ const Customizations = () => { } ); - // TODO Alova 3 code... - // const { send: sendDeviceEntities } = useRequest( - // (data: number) => readDeviceEntities(data), - // { - // initialData: [], - // immediate: false - // } - // ).onSuccess((event) => { - // setOriginalSettings(event.data); - // }); - - const { send: sendDeviceEntities, onSuccess } = useRequest( + const { send: sendDeviceEntities } = useRequest( (data: number) => readDeviceEntities(data), { initialData: [], immediate: false } - ); - onSuccess((event) => { + ).onSuccess((event) => { setOriginalSettings(event.data); }); diff --git a/interface/src/app/main/Devices.tsx b/interface/src/app/main/Devices.tsx index 3cfeb7bc7..4d13c09ed 100644 --- a/interface/src/app/main/Devices.tsx +++ b/interface/src/app/main/Devices.tsx @@ -56,8 +56,7 @@ import { import { useTheme } from '@table-library/react-table-library/theme'; import type { Action, State } from '@table-library/react-table-library/types/common'; import { dialogStyle } from 'CustomTheme'; -// import { useRequest } from 'alova/client' // TODO replace when Alova 3 is released -import { useRequest } from 'alova'; +import { useRequest } from 'alova/client'; import { ButtonRow, MessageBox, SectionContent, useLayoutTitle } from 'components'; import { AuthenticatedContext } from 'contexts/authentication'; import { useI18nContext } from 'i18n/i18n-react'; diff --git a/interface/src/app/main/Help.tsx b/interface/src/app/main/Help.tsx index 843335ef5..47ff6d10c 100644 --- a/interface/src/app/main/Help.tsx +++ b/interface/src/app/main/Help.tsx @@ -17,8 +17,7 @@ import { Typography } from '@mui/material'; -// import { useRequest } from 'alova/client' // TODO replace when Alova 3 is released -import { useRequest } from 'alova'; +import { useRequest } from 'alova/client'; import { SectionContent, useLayoutTitle } from 'components'; import { useI18nContext } from 'i18n/i18n-react'; @@ -29,44 +28,23 @@ const Help = () => { const { LL } = useI18nContext(); useLayoutTitle(LL.HELP_OF('')); - const { send: getAPI, onSuccess: onGetAPI } = useRequest( - (data: APIcall) => API(data), - { - immediate: false - } - ); - onGetAPI((event) => { + const { send: getAPI } = useRequest((data: APIcall) => API(data), { + immediate: false + }).onSuccess((event) => { const anchor = document.createElement('a'); anchor.href = URL.createObjectURL( new Blob([JSON.stringify(event.data, null, 2)], { type: 'text/plain' }) ); + anchor.download = - 'emsesp_' + event.sendArgs[0].device + '_' + event.sendArgs[0].entity + '.txt'; + 'emsesp_' + event.args[0].device + '_' + event.args[0].entity + '.txt'; anchor.click(); URL.revokeObjectURL(anchor.href); toast.info(LL.DOWNLOAD_SUCCESSFUL()); }); - // Alova 3 code... - // const { send: getAPI } = useRequest((data: APIcall) => API(data), { - // immediate: false - // }).onSuccess((event) => { - // const anchor = document.createElement('a'); - // anchor.href = URL.createObjectURL( - // new Blob([JSON.stringify(event.data, null, 2)], { - // type: 'text/plain' - // }) - // ); - // - // anchor.download = - // 'emsesp_' + event.args[0].device + '_' + event.args[0].entity + '.txt'; - // anchor.click(); - // URL.revokeObjectURL(anchor.href); - // toast.info(LL.DOWNLOAD_SUCCESSFUL()); - // }); - const callAPI = async (device: string, entity: string) => { await getAPI({ device, entity, id: 0 }).catch((error: Error) => { toast.error(error.message); diff --git a/interface/src/app/main/Modules.tsx b/interface/src/app/main/Modules.tsx index 8fba532c6..cb366715f 100644 --- a/interface/src/app/main/Modules.tsx +++ b/interface/src/app/main/Modules.tsx @@ -17,8 +17,7 @@ import { Table } from '@table-library/react-table-library/table'; import { useTheme } from '@table-library/react-table-library/theme'; -import { updateState, useRequest } from 'alova'; -// import { updateState, useRequest } from 'alova/client'; // TODO replace when Alova 3 is released +import { updateState, useRequest } from 'alova/client'; import { BlockNavigation, ButtonRow, diff --git a/interface/src/app/main/Scheduler.tsx b/interface/src/app/main/Scheduler.tsx index 1594beaf4..b2a526560 100644 --- a/interface/src/app/main/Scheduler.tsx +++ b/interface/src/app/main/Scheduler.tsx @@ -18,8 +18,7 @@ import { Table } from '@table-library/react-table-library/table'; import { useTheme } from '@table-library/react-table-library/theme'; -// import { updateState, useRequest } from 'alova/client'; // TODO replace when Alova 3 is released -import { updateState, useRequest } from 'alova'; +import { updateState, useRequest } from 'alova/client'; import { BlockNavigation, ButtonRow, diff --git a/interface/src/app/main/Sensors.tsx b/interface/src/app/main/Sensors.tsx index cc19b6e90..58cdd02c6 100644 --- a/interface/src/app/main/Sensors.tsx +++ b/interface/src/app/main/Sensors.tsx @@ -20,8 +20,7 @@ import { } from '@table-library/react-table-library/table'; import { useTheme } from '@table-library/react-table-library/theme'; import type { State } from '@table-library/react-table-library/types/common'; -// import { useRequest } from 'alova/client' // TODO replace when Alova 3 is released -import { useRequest } from 'alova'; +import { useRequest } from 'alova/client'; import { ButtonRow, SectionContent, useLayoutTitle } from 'components'; import { AuthenticatedContext } from 'contexts/authentication'; import { useI18nContext } from 'i18n/i18n-react'; diff --git a/interface/src/app/settings/ApplicationSettings.tsx b/interface/src/app/settings/ApplicationSettings.tsx index 1e5b4766b..23dbc4ec5 100644 --- a/interface/src/app/settings/ApplicationSettings.tsx +++ b/interface/src/app/settings/ApplicationSettings.tsx @@ -18,8 +18,7 @@ import { import { readHardwareStatus, restart } from 'api/system'; -// import { useRequest } from 'alova/client' // TODO replace when Alova 3 is released -import { useRequest } from 'alova'; +import { useRequest } from 'alova/client'; import RestartMonitor from 'app/status/RestartMonitor'; import type { ValidateFieldsError } from 'async-validator'; import { @@ -82,15 +81,12 @@ const ApplicationSettings = () => { const [fieldErrors, setFieldErrors] = useState(); - const { - loading: processingBoard, - send: readBoardProfile, - onSuccess: onSuccessBoardProfile - } = useRequest((boardProfile: string) => getBoardProfile(boardProfile), { - immediate: false - }); - - onSuccessBoardProfile((event) => { + const { loading: processingBoard, send: readBoardProfile } = useRequest( + (boardProfile: string) => getBoardProfile(boardProfile), + { + immediate: false + } + ).onSuccess((event) => { const response = event.data as Settings; updateDataValue({ ...data, @@ -107,29 +103,6 @@ const ApplicationSettings = () => { }); }); - // TODO replace with Alova 3 - // const { loading: processingBoard, send: readBoardProfile } = useRequest( - // (boardProfile: string) => getBoardProfile(boardProfile), - // { - // immediate: false - // } - // ).onSuccess((event) => { - // const response = event.data as Settings; - // updateDataValue({ - // ...data, - // board_profile: response.board_profile, - // led_gpio: response.led_gpio, - // dallas_gpio: response.dallas_gpio, - // rx_gpio: response.rx_gpio, - // tx_gpio: response.tx_gpio, - // pbutton_gpio: response.pbutton_gpio, - // phy_type: response.phy_type, - // eth_power: response.eth_power, - // eth_phy_addr: response.eth_phy_addr, - // eth_clock_mode: response.eth_clock_mode - // }); - // }); - const { send: restartCommand } = useRequest(restart(), { immediate: false }); diff --git a/interface/src/app/settings/NTPSettings.tsx b/interface/src/app/settings/NTPSettings.tsx index aa16dd486..9843ccc0d 100644 --- a/interface/src/app/settings/NTPSettings.tsx +++ b/interface/src/app/settings/NTPSettings.tsx @@ -7,8 +7,7 @@ import { Button, Checkbox, MenuItem } from '@mui/material'; import * as NTPApi from 'api/ntp'; import { readNTPSettings } from 'api/ntp'; -// import { updateState } from 'alova/client'; // TODO replace when Alova 3 is released -import { updateState } from 'alova'; +import { updateState } from 'alova/client'; import type { ValidateFieldsError } from 'async-validator'; import { BlockFormControlLabel, diff --git a/interface/src/app/settings/Settings.tsx b/interface/src/app/settings/Settings.tsx index e5fc5f7ef..2ccb1b4e6 100644 --- a/interface/src/app/settings/Settings.tsx +++ b/interface/src/app/settings/Settings.tsx @@ -24,8 +24,7 @@ import { import * as SystemApi from 'api/system'; import { dialogStyle } from 'CustomTheme'; -// import { useRequest } from 'alova/client' // TODO replace when Alova 3 is released -import { useRequest } from 'alova'; +import { useRequest } from 'alova/client'; import { SectionContent, useLayoutTitle } from 'components'; import ListMenuItem from 'components/layout/ListMenuItem'; import { useI18nContext } from 'i18n/i18n-react'; diff --git a/interface/src/app/settings/UploadDownload.tsx b/interface/src/app/settings/UploadDownload.tsx index 9ee2e0b34..306a340a8 100644 --- a/interface/src/app/settings/UploadDownload.tsx +++ b/interface/src/app/settings/UploadDownload.tsx @@ -14,8 +14,7 @@ import { } from 'api/app'; import { getDevVersion, getStableVersion } from 'api/system'; -// import { useRequest } from 'alova/client' // TODO replace when Alova 3 is released -import { useRequest } from 'alova'; +import { useRequest } from 'alova/client'; import type { APIcall } from 'app/main/types'; import { FormLoader, @@ -32,92 +31,22 @@ const UploadDownload = () => { const [restarting, setRestarting] = useState(); const [md5, setMd5] = useState(); - const { send: sendSettings, onSuccess: onSuccessGetSettings } = useRequest( - getSettings(), - { - immediate: false - } - ); - const { send: sendCustomizations, onSuccess: onSuccessGetCustomizations } = - useRequest(getCustomizations(), { - immediate: false - }); - const { send: sendEntities, onSuccess: onSuccessGetEntities } = useRequest( - getEntities(), - { - immediate: false - } - ); - const { send: sendSchedule, onSuccess: onSuccessGetSchedule } = useRequest( - getSchedule(), - { - immediate: false - } - ); - const { send: getAPI, onSuccess: onGetAPI } = useRequest( - (data: APIcall) => API(data), - { - immediate: false - } - ); - - // Alova 3 code... - // const { - // loading: isUploading, - // uploading: progress, - // send: sendUpload, - // abort: cancelUpload - // } = useRequest(SystemApi.uploadFile, { - // immediate: false - // }).onSuccess(({ data }) => { - // if (data) { - // setMd5(data.md5); - // toast.success(LL.UPLOAD() + ' MD5 ' + LL.SUCCESSFUL()); - // } else { - // setRestarting(true); - // } - // }); const { loading: isUploading, uploading: progress, send: sendUpload, - onSuccess: onSuccessUpload, abort: cancelUpload } = useRequest(SystemApi.uploadFile, { immediate: false - }); - - onSuccessUpload(({ data }) => { + }).onSuccess(({ data }) => { if (data) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - setMd5(data.md5); + setMd5(data.md5 as string); toast.success(LL.UPLOAD() + ' MD5 ' + LL.SUCCESSFUL()); } else { setRestarting(true); } }); - onSuccessGetSettings((event) => { - saveFile(event.data, 'settings.json'); - }); - onSuccessGetCustomizations((event) => { - saveFile(event.data, 'customizations.json'); - }); - onSuccessGetEntities((event) => { - saveFile(event.data, 'entities.json'); - }); - onSuccessGetSchedule((event) => { - saveFile(event.data, 'schedule.json'); - }); - onGetAPI((event) => { - saveFile( - event.data, - event.sendArgs[0].device + '_' + event.sendArgs[0].entity + '.txt' - ); - }); - // TODO Alova 3 code... - /* - const { send: sendSettings } = useRequest(getSettings(), { immediate: false }).onSuccess((event) => { @@ -151,8 +80,6 @@ const UploadDownload = () => { ); }); - */ - const { data: data, send: loadData, diff --git a/interface/src/app/settings/network/NetworkSettings.tsx b/interface/src/app/settings/network/NetworkSettings.tsx index c0e8b459e..4fc22b358 100644 --- a/interface/src/app/settings/network/NetworkSettings.tsx +++ b/interface/src/app/settings/network/NetworkSettings.tsx @@ -25,8 +25,7 @@ import { import * as NetworkApi from 'api/network'; import * as SystemApi from 'api/system'; -import { updateState, useRequest } from 'alova'; -// import { updateState, useRequest } from 'alova/client'; // TODO replace when Alova 3 is released +import { updateState, useRequest } from 'alova/client'; import type { ValidateFieldsError } from 'async-validator'; import { BlockFormControlLabel, diff --git a/interface/src/app/settings/network/WiFiNetworkScanner.tsx b/interface/src/app/settings/network/WiFiNetworkScanner.tsx index cff76dc9a..a73cdea7a 100644 --- a/interface/src/app/settings/network/WiFiNetworkScanner.tsx +++ b/interface/src/app/settings/network/WiFiNetworkScanner.tsx @@ -5,8 +5,7 @@ import { Button } from '@mui/material'; import * as NetworkApi from 'api/network'; -// import { updateState, useRequest } from 'alova/client'; // TODO replace when Alova 3 is released -import { updateState, useRequest } from 'alova'; +import { updateState, useRequest } from 'alova/client'; import { ButtonRow, FormLoader, SectionContent } from 'components'; import { useI18nContext } from 'i18n/i18n-react'; @@ -21,18 +20,22 @@ const WiFiNetworkScanner = () => { const [errorMessage, setErrorMessage] = useState(); // is called on page load to start network scan - - const { send: scanNetworks, onComplete: onCompleteScanNetworks } = useRequest( - NetworkApi.scanNetworks + const { send: scanNetworks } = useRequest(NetworkApi.scanNetworks).onComplete( + () => { + pollCount.current = 0; + setErrorMessage(undefined); + void updateState(NetworkApi.listNetworks(), () => undefined); + void getNetworkList(); + } ); - const { - data: networkList, - send: getNetworkList, - onSuccess: onSuccessNetworkList - } = useRequest(NetworkApi.listNetworks, { - immediate: false - }); - onSuccessNetworkList((event) => { + + const { data: networkList, send: getNetworkList } = useRequest( + NetworkApi.listNetworks, + { + immediate: false + } + ).onSuccess((event) => { + // is called when network scan is completed if (!event.data) { const completedPollCount = pollCount.current + 1; if (completedPollCount < NUM_POLLS) { @@ -45,42 +48,6 @@ const WiFiNetworkScanner = () => { } }); - onCompleteScanNetworks(() => { - pollCount.current = 0; - setErrorMessage(undefined); - updateState('listNetworks', () => undefined); - void getNetworkList(); - }); - - // Alova 3 code... - // const { send: scanNetworks } = useRequest(NetworkApi.scanNetworks).onComplete( - // () => { - // pollCount.current = 0; - // setErrorMessage(undefined); - // void updateState(NetworkApi.listNetworks(), () => undefined); - // void getNetworkList(); - // } - // ); - - // const { data: networkList, send: getNetworkList } = useRequest( - // NetworkApi.listNetworks, - // { - // immediate: false - // } - // ).onSuccess((event) => { - // // is called when network scan is completed - // if (!event.data) { - // const completedPollCount = pollCount.current + 1; - // if (completedPollCount < NUM_POLLS) { - // pollCount.current = completedPollCount; - // setTimeout(getNetworkList, POLLING_FREQUENCY); - // } else { - // setErrorMessage(LL.PROBLEM_LOADING()); - // pollCount.current = 0; - // } - // } - // }); - const renderNetworkScanner = () => { if (!networkList) { return ( diff --git a/interface/src/app/settings/security/GenerateToken.tsx b/interface/src/app/settings/security/GenerateToken.tsx index b0292a4a5..aed035276 100644 --- a/interface/src/app/settings/security/GenerateToken.tsx +++ b/interface/src/app/settings/security/GenerateToken.tsx @@ -16,8 +16,7 @@ import { import * as SecurityApi from 'api/security'; import { dialogStyle } from 'CustomTheme'; -// import { useRequest } from 'alova/client' // TODO replace when Alova 3 is released -import { useRequest } from 'alova'; +import { useRequest } from 'alova/client'; import { MessageBox } from 'components'; import { useI18nContext } from 'i18n/i18n-react'; diff --git a/interface/src/app/status/APStatus.tsx b/interface/src/app/status/APStatus.tsx index ebdd61bdd..44aa25e2e 100644 --- a/interface/src/app/status/APStatus.tsx +++ b/interface/src/app/status/APStatus.tsx @@ -16,8 +16,7 @@ import type { Theme } from '@mui/material'; import * as APApi from 'api/ap'; -// import { useRequest } from 'alova/client' // TODO replace when Alova 3 is released -import { useRequest } from 'alova'; +import { useRequest } from 'alova/client'; import { ButtonRow, FormLoader, SectionContent, useLayoutTitle } from 'components'; import { useI18nContext } from 'i18n/i18n-react'; import type { APStatusType } from 'types'; diff --git a/interface/src/app/status/Activity.tsx b/interface/src/app/status/Activity.tsx index 993af9d3a..ae43f3d51 100644 --- a/interface/src/app/status/Activity.tsx +++ b/interface/src/app/status/Activity.tsx @@ -13,8 +13,7 @@ import { Table } from '@table-library/react-table-library/table'; import { useTheme as tableTheme } from '@table-library/react-table-library/theme'; -// import { useRequest } from 'alova/client' // TODO replace when Alova 3 is released -import { useRequest } from 'alova'; +import { useRequest } from 'alova/client'; import { ButtonRow, FormLoader, SectionContent, useLayoutTitle } from 'components'; import { useI18nContext } from 'i18n/i18n-react'; import type { Translation } from 'i18n/i18n-types'; diff --git a/interface/src/app/status/HardwareStatus.tsx b/interface/src/app/status/HardwareStatus.tsx index 6818abc58..32c440d5f 100644 --- a/interface/src/app/status/HardwareStatus.tsx +++ b/interface/src/app/status/HardwareStatus.tsx @@ -20,8 +20,7 @@ import { import * as SystemApi from 'api/system'; -// import { useRequest } from 'alova/client' // TODO replace when Alova 3 is released -import { useRequest } from 'alova'; +import { useRequest } from 'alova/client'; import { ButtonRow, FormLoader, SectionContent, useLayoutTitle } from 'components'; import { useI18nContext } from 'i18n/i18n-react'; diff --git a/interface/src/app/status/MqttStatus.tsx b/interface/src/app/status/MqttStatus.tsx index 1af738f54..462d17130 100644 --- a/interface/src/app/status/MqttStatus.tsx +++ b/interface/src/app/status/MqttStatus.tsx @@ -17,8 +17,7 @@ import type { Theme } from '@mui/material'; import * as MqttApi from 'api/mqtt'; -// import { useRequest } from 'alova/client' // TODO replace when Alova 3 is released -import { useRequest } from 'alova'; +import { useRequest } from 'alova/client'; import { ButtonRow, FormLoader, SectionContent, useLayoutTitle } from 'components'; import { useI18nContext } from 'i18n/i18n-react'; import type { MqttStatusType } from 'types'; diff --git a/interface/src/app/status/NTPStatus.tsx b/interface/src/app/status/NTPStatus.tsx index 3375dc073..13b7841fe 100644 --- a/interface/src/app/status/NTPStatus.tsx +++ b/interface/src/app/status/NTPStatus.tsx @@ -29,8 +29,7 @@ import type { Theme } from '@mui/material'; import * as NTPApi from 'api/ntp'; import { dialogStyle } from 'CustomTheme'; -// import { useRequest } from 'alova/client' // TODO replace when Alova 3 is released -import { useRequest } from 'alova'; +import { useRequest } from 'alova/client'; import { ButtonRow, FormLoader, SectionContent, useLayoutTitle } from 'components'; import { useI18nContext } from 'i18n/i18n-react'; import type { NTPStatusType, Time } from 'types'; diff --git a/interface/src/app/status/NetworkStatus.tsx b/interface/src/app/status/NetworkStatus.tsx index 48a29f378..201193cb2 100644 --- a/interface/src/app/status/NetworkStatus.tsx +++ b/interface/src/app/status/NetworkStatus.tsx @@ -20,8 +20,7 @@ import type { Theme } from '@mui/material'; import * as NetworkApi from 'api/network'; -// import { useRequest } from 'alova/client' // TODO replace when Alova 3 is released -import { useRequest } from 'alova'; +import { useRequest } from 'alova/client'; import { ButtonRow, FormLoader, SectionContent, useLayoutTitle } from 'components'; import { useI18nContext } from 'i18n/i18n-react'; import type { NetworkStatusType } from 'types'; diff --git a/interface/src/app/status/RestartMonitor.tsx b/interface/src/app/status/RestartMonitor.tsx index 7c59dcf87..a8cb12774 100644 --- a/interface/src/app/status/RestartMonitor.tsx +++ b/interface/src/app/status/RestartMonitor.tsx @@ -2,8 +2,7 @@ import { useEffect, useRef, useState } from 'react'; import * as SystemApi from 'api/system'; -// import { useRequest } from 'alova/client' // TODO replace when Alova 3 is released -import { useRequest } from 'alova'; +import { useRequest } from 'alova/client'; import { FormLoader } from 'components'; import { useI18nContext } from 'i18n/i18n-react'; diff --git a/interface/src/app/status/Status.tsx b/interface/src/app/status/Status.tsx index 2de7361e8..a639ebbc1 100644 --- a/interface/src/app/status/Status.tsx +++ b/interface/src/app/status/Status.tsx @@ -34,8 +34,7 @@ import { import * as SystemApi from 'api/system'; import { dialogStyle } from 'CustomTheme'; -// import { useRequest } from 'alova/client' // TODO replace when Alova 3 is released -import { useRequest } from 'alova'; +import { useRequest } from 'alova/client'; import { busConnectionStatus } from 'app/main/types'; import { FormLoader, SectionContent, useLayoutTitle } from 'components'; import ListMenuItem from 'components/layout/ListMenuItem'; diff --git a/interface/src/app/status/SystemLog.tsx b/interface/src/app/status/SystemLog.tsx index 71e92e8a5..32d649e6e 100644 --- a/interface/src/app/status/SystemLog.tsx +++ b/interface/src/app/status/SystemLog.tsx @@ -16,9 +16,7 @@ import { import * as SystemApi from 'api/system'; import { fetchLogES } from 'api/system'; -// import { useRequest, useSSE } from 'alova/client'; // TODO replace when Alova 3 is released -import { useSSE } from '@alova/scene-react'; -import { useRequest } from 'alova'; +import { useRequest, useSSE } from 'alova/client'; import { BlockFormControlLabel, BlockNavigation, diff --git a/interface/src/contexts/authentication/Authentication.tsx b/interface/src/contexts/authentication/Authentication.tsx index e49fb7562..632b0cc3b 100644 --- a/interface/src/contexts/authentication/Authentication.tsx +++ b/interface/src/contexts/authentication/Authentication.tsx @@ -6,8 +6,7 @@ import { toast } from 'react-toastify'; import { ACCESS_TOKEN } from 'api/endpoints'; import * as AuthenticationApi from 'components/routing/authentication'; -// import { useRequest } from 'alova/client' // TODO replace when Alova 3 is released -import { useRequest } from 'alova'; +import { useRequest } from 'alova/client'; import { LoadingSpinner } from 'components'; import { verifyAuthorization } from 'components/routing/authentication'; import { useI18nContext } from 'i18n/i18n-react'; @@ -42,7 +41,6 @@ const Authentication: FC = ({ children }) => { AuthenticationApi.clearAccessToken(); setMe(undefined); if (doRedirect) { - // navigate('/'); redirect('/'); } }; diff --git a/interface/src/utils/useRest.ts b/interface/src/utils/useRest.ts index 183f5ee48..02e42dc40 100644 --- a/interface/src/utils/useRest.ts +++ b/interface/src/utils/useRest.ts @@ -1,127 +1,43 @@ -// TODO Alova 3 code.... -// import { useState } from 'react'; -// import { useBlocker } from 'react-router-dom'; -// import { toast } from 'react-toastify'; -// import type { AlovaGenerics, Method } from 'alova'; -// import { useRequest } from 'alova/client'; -// import { useI18nContext } from 'i18n/i18n-react'; -// export interface RestRequestOptions { -// read: () => Method; -// update: (value: D) => Method; -// } -// export const useRest = ({ read, update }: RestRequestOptions) => { -// const { LL } = useI18nContext(); -// const [errorMessage, setErrorMessage] = useState(); -// const [restartNeeded, setRestartNeeded] = useState(false); -// const [origData, setOrigData] = useState(); -// const [dirtyFlags, setDirtyFlags] = useState([]); -// const blocker = useBlocker(dirtyFlags.length !== 0); -// const { -// data, -// send: readData, -// update: updateData -// } = useRequest(read()).onComplete((event) => { -// setOrigData(event.data as D); -// }); -// const { loading: saving, send: writeData } = useRequest( -// (newData: D) => update(newData), -// { immediate: false } -// ).onSuccess(() => { -// toast.success(LL.UPDATED_OF(LL.SETTINGS(1))); -// setDirtyFlags([]); -// }); -// const updateDataValue = (new_data: D) => { -// updateData({ data: new_data }); -// }; -// const loadData = async () => { -// setDirtyFlags([]); -// setErrorMessage(undefined); -// await readData().catch((error: Error) => { -// toast.error(error.message); -// setErrorMessage(error.message); -// }); -// }; -// const saveData = async () => { -// if (!data) { -// return; -// } -// setRestartNeeded(false); -// setErrorMessage(undefined); -// setDirtyFlags([]); -// setOrigData(data as D); -// await writeData(data as D).catch((error: Error) => { -// if (error.message === 'Reboot required') { -// setRestartNeeded(true); -// } else { -// toast.error(error.message); -// setErrorMessage(error.message); -// } -// }); -// }; -// return { -// loadData, -// saveData, -// saving: saving as boolean, -// updateDataValue, -// data: data as D, // Explicitly define the type of 'data' -// origData: origData as D, // Explicitly define the type of 'origData' to 'D' -// dirtyFlags, -// setDirtyFlags, -// setOrigData, -// blocker, -// errorMessage, -// restartNeeded -// } as const; -// }; import { useState } from 'react'; import { useBlocker } from 'react-router-dom'; import { toast } from 'react-toastify'; -import { type Method, useRequest } from 'alova'; +import type { AlovaGenerics, Method } from 'alova'; +import { useRequest } from 'alova/client'; import { useI18nContext } from 'i18n/i18n-react'; export interface RestRequestOptions { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - read: () => Method; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - update: (value: D) => Method; + read: () => Method; + update: (value: D) => Method; } export const useRest = ({ read, update }: RestRequestOptions) => { const { LL } = useI18nContext(); - const [errorMessage, setErrorMessage] = useState(); const [restartNeeded, setRestartNeeded] = useState(false); const [origData, setOrigData] = useState(); - const [dirtyFlags, setDirtyFlags] = useState([]); const blocker = useBlocker(dirtyFlags.length !== 0); const { data, send: readData, - update: updateData, - onComplete: onReadComplete - } = useRequest(read()); + update: updateData + } = useRequest(read()).onComplete((event) => { + setOrigData(event.data as D); + }); - const { - loading: saving, - send: writeData, - onSuccess: onWriteSuccess - } = useRequest((newData: D) => update(newData), { immediate: false }); - - const updateDataValue = (new_data: D) => { - updateData({ data: new_data }); - }; - - onWriteSuccess(() => { + const { loading: saving, send: writeData } = useRequest( + (newData: D) => update(newData), + { immediate: false } + ).onSuccess(() => { toast.success(LL.UPDATED_OF(LL.SETTINGS(1))); setDirtyFlags([]); }); - onReadComplete((event) => { - setOrigData(event.data as D); - }); + const updateDataValue = (new_data: D) => { + updateData({ data: new_data }); + }; const loadData = async () => { setDirtyFlags([]); @@ -149,7 +65,6 @@ export const useRest = ({ read, update }: RestRequestOptions) => { } }); }; - return { loadData, saveData, diff --git a/interface/yarn.lock b/interface/yarn.lock index c3cd78222..3ef512dad 100644 --- a/interface/yarn.lock +++ b/interface/yarn.lock @@ -5,17 +5,21 @@ __metadata: version: 8 cacheKey: 10c0 -"@alova/adapter-xhr@npm:1.0.6": - version: 1.0.6 - resolution: "@alova/adapter-xhr@npm:1.0.6" - checksum: 10c0/1cd4f2da4f1c58865e14999d9cf367635f72f8e6bd41497366c25681154ffc7a4dedd70f234610c5e028c01418992dab709c777d986a6432d7d9783beeeb150e +"@alova/adapter-xhr@npm:2.0.4": + version: 2.0.4 + resolution: "@alova/adapter-xhr@npm:2.0.4" + dependencies: + "@alova/shared": "npm:^1.0.4" + peerDependencies: + alova: ^3.0.5 + checksum: 10c0/c68f51b83c75844cf2cddc47d6e08fa7a6f0af83cc6213b60d0d8ac32988f8e7b44f321b1f926fa282a2cda02757ce3cbfe94e86f140fd025ab22f0dfee9bed9 languageName: node linkType: hard -"@alova/scene-react@npm:1.6.2": - version: 1.6.2 - resolution: "@alova/scene-react@npm:1.6.2" - checksum: 10c0/dac01605df824e66dba92c5d7073e33f98ad47367a7180bd0ddc78c01f1e672ac665c1ff0445df7299be1426ca92e325cf0b9fa459984125d6391e9e9eb9d439 +"@alova/shared@npm:^1.0.4": + version: 1.0.4 + resolution: "@alova/shared@npm:1.0.4" + checksum: 10c0/308b3163ee81fe98bb8372bd8a7df2a053be508167c86c6cfa2b3610c79389621e9a3466c391210e3f058799733465cf7dd153fcae536b03ea58dabff9d6eab4 languageName: node linkType: hard @@ -1651,8 +1655,7 @@ __metadata: version: 0.0.0-use.local resolution: "EMS-ESP@workspace:." dependencies: - "@alova/adapter-xhr": "npm:1.0.6" - "@alova/scene-react": "npm:1.6.2" + "@alova/adapter-xhr": "npm:2.0.4" "@babel/core": "npm:^7.25.2" "@emotion/react": "npm:^11.13.0" "@emotion/styled": "npm:^11.13.0" @@ -1668,7 +1671,7 @@ __metadata: "@types/react": "npm:^18.3.3" "@types/react-dom": "npm:^18.3.0" "@types/react-router-dom": "npm:^5.3.3" - alova: "npm:2.21.4" + alova: "npm:3.0.6" async-validator: "npm:^4.2.5" concurrently: "npm:^8.2.2" eslint: "npm:^9.9.0" @@ -1750,10 +1753,13 @@ __metadata: languageName: node linkType: hard -"alova@npm:2.21.4": - version: 2.21.4 - resolution: "alova@npm:2.21.4" - checksum: 10c0/6600826747eecc85f750904cd5a9f75361bd5b542615b027fae072cafe72cb27b9f7b260a834063c0734a9b41a7aa8b15396a190f8ce9a46713d01b073296a50 +"alova@npm:3.0.6": + version: 3.0.6 + resolution: "alova@npm:3.0.6" + dependencies: + "@alova/shared": "npm:^1.0.4" + rate-limiter-flexible: "npm:^5.0.3" + checksum: 10c0/dec1ceabaedc4c3c42237e03e4fdcd41ada8ad8aeb856ab2a1d428a1daf4559125767a9bd2d20f193ae013063baa59641a6c34dddf7794f7bba1a31e7258968f languageName: node linkType: hard @@ -5717,6 +5723,13 @@ __metadata: languageName: node linkType: hard +"rate-limiter-flexible@npm:^5.0.3": + version: 5.0.3 + resolution: "rate-limiter-flexible@npm:5.0.3" + checksum: 10c0/4ae8290cf57cbd9bc8dfd9829ed821ffad2bc6d4699170608b4b85b010059981e591803a4b1974f25755f1cbf25ff8208795cc325d5bed9d2315717865e25697 + languageName: node + linkType: hard + "react-dom@npm:^18.3.1": version: 18.3.1 resolution: "react-dom@npm:18.3.1"