From 6dcce1fff8f6706c813b728000d49c8dcdb1812d Mon Sep 17 00:00:00 2001 From: Juntao Wang Date: Wed, 26 Oct 2022 11:56:22 -0400 Subject: [PATCH 1/5] refresh notebook status and fetch all status periodically --- .../pages/projects/ProjectDetailsContext.tsx | 7 +++- .../notebook/NotebookStatusToggle.tsx | 6 +-- .../notebook/useRefreshNotebookUntilStart.ts | 38 ------------------- frontend/src/pages/projects/notebook/utils.ts | 5 ++- .../screens/detail/ProjectDetails.tsx | 4 +- .../{NotebooksList.tsx => NotebookList.tsx} | 13 ++++++- 6 files changed, 24 insertions(+), 49 deletions(-) delete mode 100644 frontend/src/pages/projects/notebook/useRefreshNotebookUntilStart.ts rename frontend/src/pages/projects/screens/detail/notebooks/{NotebooksList.tsx => NotebookList.tsx} (77%) diff --git a/frontend/src/pages/projects/ProjectDetailsContext.tsx b/frontend/src/pages/projects/ProjectDetailsContext.tsx index 1a2656a38e..cfabed6906 100644 --- a/frontend/src/pages/projects/ProjectDetailsContext.tsx +++ b/frontend/src/pages/projects/ProjectDetailsContext.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { PersistentVolumeClaimKind, ProjectKind, SecretKind } from '../../k8sTypes'; +import { PersistentVolumeClaimKind, ProjectKind } from '../../k8sTypes'; import { Outlet, useParams } from 'react-router-dom'; import { Bullseye, @@ -18,6 +18,7 @@ import useProjectPvcs from './screens/detail/storage/useProjectPvcs'; import useDataConnections from './screens/detail/data-connections/useDataConnections'; import { DataConnection } from './types'; import { NotebookState } from './notebook/types'; +import { POLL_INTERVAL } from '../../utilities/const'; type ContextResourceData = { data: T[]; @@ -42,6 +43,10 @@ const DEFAULT_DATA: ContextResourceData = { const useContextResourceData = (resourceData): ContextResourceData => { const [values, loaded, error, refresh] = resourceData; + React.useEffect(() => { + const timer = setInterval(() => refresh(), POLL_INTERVAL); + return () => clearInterval(timer); + }, [refresh]); return React.useMemo( () => ({ data: values, diff --git a/frontend/src/pages/projects/notebook/NotebookStatusToggle.tsx b/frontend/src/pages/projects/notebook/NotebookStatusToggle.tsx index 30b1002353..ec99930f38 100644 --- a/frontend/src/pages/projects/notebook/NotebookStatusToggle.tsx +++ b/frontend/src/pages/projects/notebook/NotebookStatusToggle.tsx @@ -2,7 +2,6 @@ import * as React from 'react'; import { Switch } from '@patternfly/react-core'; import { startNotebook, stopNotebook } from '../../../api'; import { NotebookState } from './types'; -import useRefreshNotebookUntilStart from './useRefreshNotebookUntilStart'; import StopNotebookConfirmModal from './StopNotebookConfirmModal'; import useStopNotebookModalAvailability from './useStopNotebookModalAvailability'; import NotebookStatusPopover from './NotebookStatusPopover'; @@ -16,7 +15,6 @@ const NotebookStatusToggle: React.FC = ({ notebookSta const [isOpenConfirm, setOpenConfirm] = React.useState(false); const [inProgress, setInProgress] = React.useState(false); const [isPopoverVisible, setPopoverVisible] = React.useState(false); - const listenToNotebookStart = useRefreshNotebookUntilStart(notebookState); const [dontShowModalValue] = useStopNotebookModalAvailability(); const notebookName = notebook.metadata.name; const notebookNamespace = notebook.metadata.namespace; @@ -38,9 +36,8 @@ const NotebookStatusToggle: React.FC = ({ notebookSta setInProgress(true); stopNotebook(notebookName, notebookNamespace).then(() => { refresh().then(() => setInProgress(false)); - listenToNotebookStart(false); }); - }, [notebookName, notebookNamespace, refresh, listenToNotebookStart]); + }, [notebookName, notebookNamespace, refresh]); return ( = ({ notebookSta setInProgress(true); startNotebook(notebookName, notebookNamespace).then(() => { refresh().then(() => setInProgress(false)); - listenToNotebookStart(true); }); } }} diff --git a/frontend/src/pages/projects/notebook/useRefreshNotebookUntilStart.ts b/frontend/src/pages/projects/notebook/useRefreshNotebookUntilStart.ts deleted file mode 100644 index 14e7c1fc7c..0000000000 --- a/frontend/src/pages/projects/notebook/useRefreshNotebookUntilStart.ts +++ /dev/null @@ -1,38 +0,0 @@ -import * as React from 'react'; -import { FAST_POLL_INTERVAL } from '../../../utilities/const'; -import { NotebookState } from './types'; - -const useRefreshNotebookUntilStart = ( - notebookState: NotebookState, -): ((listen: boolean) => void) => { - const [watchingForNotebook, setWatchingForNotebook] = React.useState(false); - const lastNotebookState = React.useRef(notebookState); - lastNotebookState.current = notebookState; - - React.useEffect(() => { - let interval; - if (watchingForNotebook) { - interval = setInterval(() => { - const { isRunning, refresh } = lastNotebookState.current; - if (!isRunning) { - refresh().catch((e) => { - console.error('Error refreshing, stopping notebook refresh', e); - setWatchingForNotebook(false); - }); - } else { - setWatchingForNotebook(false); - } - }, FAST_POLL_INTERVAL); - } - - return () => { - clearInterval(interval); - }; - }, [watchingForNotebook]); - - return React.useCallback((listen: boolean) => { - setWatchingForNotebook(listen); - }, []); -}; - -export default useRefreshNotebookUntilStart; diff --git a/frontend/src/pages/projects/notebook/utils.ts b/frontend/src/pages/projects/notebook/utils.ts index 96e26b3b14..3e9a1f9c48 100644 --- a/frontend/src/pages/projects/notebook/utils.ts +++ b/frontend/src/pages/projects/notebook/utils.ts @@ -3,7 +3,10 @@ import { EventStatus, NotebookStatus } from '../../../types'; import { useWatchNotebookEvents } from './useWatchNotebookEvents'; export const hasStopAnnotation = (notebook: NotebookKind): boolean => { - return !!notebook.metadata.annotations?.['kubeflow-resource-stopped']; + return !!( + notebook.metadata.annotations?.['kubeflow-resource-stopped'] && + notebook.metadata.annotations['kubeflow-resource-stopped'] !== 'odh-notebook-controller-lock' + ); }; export const getNotebookMountPaths = (notebook?: NotebookKind): string[] => { diff --git a/frontend/src/pages/projects/screens/detail/ProjectDetails.tsx b/frontend/src/pages/projects/screens/detail/ProjectDetails.tsx index aadee9124b..e59f40162c 100644 --- a/frontend/src/pages/projects/screens/detail/ProjectDetails.tsx +++ b/frontend/src/pages/projects/screens/detail/ProjectDetails.tsx @@ -7,7 +7,7 @@ import DataConnectionsList from './data-connections/DataConnectionsList'; import GenericSidebar from '../../components/GenericSidebar'; import StorageList from './storage/StorageList'; import { ProjectSectionID } from './types'; -import NotebooksList from './notebooks/NotebooksList'; +import NotebookList from './notebooks/NotebookList'; import { ProjectDetailsContext } from '../../ProjectDetailsContext'; import { getProjectDescription, getProjectDisplayName } from '../../utils'; @@ -23,7 +23,7 @@ const ProjectDetails: React.FC = () => { const scrollableSelectorID = 'project-details-list'; const sections: SectionType[] = [ - { id: ProjectSectionID.WORKBENCHES, component: }, + { id: ProjectSectionID.WORKBENCHES, component: }, { id: ProjectSectionID.STORAGES, component: }, { id: ProjectSectionID.DATA_CONNECTIONS, component: }, ]; diff --git a/frontend/src/pages/projects/screens/detail/notebooks/NotebooksList.tsx b/frontend/src/pages/projects/screens/detail/notebooks/NotebookList.tsx similarity index 77% rename from frontend/src/pages/projects/screens/detail/notebooks/NotebooksList.tsx rename to frontend/src/pages/projects/screens/detail/notebooks/NotebookList.tsx index 56e460303d..81f48a831a 100644 --- a/frontend/src/pages/projects/screens/detail/notebooks/NotebooksList.tsx +++ b/frontend/src/pages/projects/screens/detail/notebooks/NotebookList.tsx @@ -7,8 +7,9 @@ import { ProjectSectionTitles } from '../const'; import { ProjectDetailsContext } from '../../../ProjectDetailsContext'; import { useNavigate } from 'react-router-dom'; import NotebookTable from './NotebookTable'; +import { FAST_POLL_INTERVAL } from '../../../../../utilities/const'; -const NotebooksList: React.FC = () => { +const NotebookList: React.FC = () => { const { currentProject, notebooks: { data: notebookStates, loaded, error: loadError, refresh: refreshNotebooks }, @@ -16,6 +17,14 @@ const NotebooksList: React.FC = () => { const navigate = useNavigate(); const projectName = currentProject.metadata.name; + React.useEffect(() => { + let interval; + if (notebookStates.some((notebookState) => notebookState.isStarting)) { + interval = setInterval(() => refreshNotebooks(), FAST_POLL_INTERVAL); + } + return () => clearInterval(interval); + }, [notebookStates, refreshNotebooks]); + return ( { ); }; -export default NotebooksList; +export default NotebookList; From 0fcde376211e5e72974f75cd726815af72c5d709 Mon Sep 17 00:00:00 2001 From: Juntao Wang Date: Wed, 26 Oct 2022 12:12:07 -0400 Subject: [PATCH 2/5] small fix for listening notebook --- .../projects/notebook/ListNotebookState.tsx | 2 +- .../notebook/NotebookStatusToggle.tsx | 9 ++++- .../notebook/useRefreshNotebookUntilStart.ts | 39 +++++++++++++++++++ .../detail/notebooks/NotebookTableRow.tsx | 2 +- 4 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 frontend/src/pages/projects/notebook/useRefreshNotebookUntilStart.ts diff --git a/frontend/src/pages/projects/notebook/ListNotebookState.tsx b/frontend/src/pages/projects/notebook/ListNotebookState.tsx index 832d4d02c6..526b564d64 100644 --- a/frontend/src/pages/projects/notebook/ListNotebookState.tsx +++ b/frontend/src/pages/projects/notebook/ListNotebookState.tsx @@ -37,7 +37,7 @@ const ListNotebookState: React.FC = ({ case 'notebook': return ; case 'status': - return ; + return ; default: console.error('Unknown show type', show); return <>-; diff --git a/frontend/src/pages/projects/notebook/NotebookStatusToggle.tsx b/frontend/src/pages/projects/notebook/NotebookStatusToggle.tsx index ec99930f38..81567c14a5 100644 --- a/frontend/src/pages/projects/notebook/NotebookStatusToggle.tsx +++ b/frontend/src/pages/projects/notebook/NotebookStatusToggle.tsx @@ -2,19 +2,22 @@ import * as React from 'react'; import { Switch } from '@patternfly/react-core'; import { startNotebook, stopNotebook } from '../../../api'; import { NotebookState } from './types'; +import useRefreshNotebookUntilStart from './useRefreshNotebookUntilStart'; import StopNotebookConfirmModal from './StopNotebookConfirmModal'; import useStopNotebookModalAvailability from './useStopNotebookModalAvailability'; import NotebookStatusPopover from './NotebookStatusPopover'; type NotebookStatusToggleProps = { notebookState: NotebookState; + doListen: boolean; }; -const NotebookStatusToggle: React.FC = ({ notebookState }) => { +const NotebookStatusToggle: React.FC = ({ notebookState, doListen }) => { const { notebook, isStarting, isRunning, refresh } = notebookState; const [isOpenConfirm, setOpenConfirm] = React.useState(false); const [inProgress, setInProgress] = React.useState(false); const [isPopoverVisible, setPopoverVisible] = React.useState(false); + const listenToNotebookStart = useRefreshNotebookUntilStart(notebookState, doListen); const [dontShowModalValue] = useStopNotebookModalAvailability(); const notebookName = notebook.metadata.name; const notebookNamespace = notebook.metadata.namespace; @@ -36,8 +39,9 @@ const NotebookStatusToggle: React.FC = ({ notebookSta setInProgress(true); stopNotebook(notebookName, notebookNamespace).then(() => { refresh().then(() => setInProgress(false)); + listenToNotebookStart(false); }); - }, [notebookName, notebookNamespace, refresh]); + }, [notebookName, notebookNamespace, refresh, listenToNotebookStart]); return ( = ({ notebookSta setInProgress(true); startNotebook(notebookName, notebookNamespace).then(() => { refresh().then(() => setInProgress(false)); + listenToNotebookStart(true); }); } }} diff --git a/frontend/src/pages/projects/notebook/useRefreshNotebookUntilStart.ts b/frontend/src/pages/projects/notebook/useRefreshNotebookUntilStart.ts new file mode 100644 index 0000000000..3943e412c3 --- /dev/null +++ b/frontend/src/pages/projects/notebook/useRefreshNotebookUntilStart.ts @@ -0,0 +1,39 @@ +import * as React from 'react'; +import { FAST_POLL_INTERVAL } from '../../../utilities/const'; +import { NotebookState } from './types'; + +const useRefreshNotebookUntilStart = ( + notebookState: NotebookState, + doListen: boolean, +): ((listen: boolean) => void) => { + const [watchingForNotebook, setWatchingForNotebook] = React.useState(false); + const lastNotebookState = React.useRef(notebookState); + lastNotebookState.current = notebookState; + + React.useEffect(() => { + let interval; + if (watchingForNotebook && doListen) { + interval = setInterval(() => { + const { isRunning, refresh } = lastNotebookState.current; + if (!isRunning) { + refresh().catch((e) => { + console.error('Error refreshing, stopping notebook refresh', e); + setWatchingForNotebook(false); + }); + } else { + setWatchingForNotebook(false); + } + }, FAST_POLL_INTERVAL); + } + + return () => { + clearInterval(interval); + }; + }, [watchingForNotebook, doListen]); + + return React.useCallback((listen: boolean) => { + setWatchingForNotebook(listen); + }, []); +}; + +export default useRefreshNotebookUntilStart; diff --git a/frontend/src/pages/projects/screens/detail/notebooks/NotebookTableRow.tsx b/frontend/src/pages/projects/screens/detail/notebooks/NotebookTableRow.tsx index f9ccf36fc2..87e7d465d2 100644 --- a/frontend/src/pages/projects/screens/detail/notebooks/NotebookTableRow.tsx +++ b/frontend/src/pages/projects/screens/detail/notebooks/NotebookTableRow.tsx @@ -47,7 +47,7 @@ const NotebookTableRow: React.FC = ({ {notebookSize?.name ?? 'Unknown'} - + From f3dbca85d6269f5f7a4fba0bca012b1e22ffa703 Mon Sep 17 00:00:00 2001 From: Juntao Wang Date: Wed, 26 Oct 2022 12:51:59 -0400 Subject: [PATCH 3/5] some rename and clean up --- .../projects/dataConnections/AWSField.tsx | 1 + .../screens/detail/ProjectDetails.tsx | 2 +- .../pages/projects/screens/detail/const.ts | 2 +- .../screens/detail/storage/StorageList.tsx | 8 +- .../pages/projects/screens/detail/types.ts | 2 +- .../projects/screens/spawner/SpawnerPage.tsx | 20 ++- .../pages/projects/screens/spawner/const.ts | 2 +- .../deploymentSize/ContainerSizeSelector.tsx | 2 +- .../screens/spawner/storage/StorageField.tsx | 121 +++++++++--------- .../pages/projects/screens/spawner/types.ts | 2 +- 10 files changed, 84 insertions(+), 78 deletions(-) diff --git a/frontend/src/pages/projects/dataConnections/AWSField.tsx b/frontend/src/pages/projects/dataConnections/AWSField.tsx index 3cc8e8d823..44659152ca 100644 --- a/frontend/src/pages/projects/dataConnections/AWSField.tsx +++ b/frontend/src/pages/projects/dataConnections/AWSField.tsx @@ -19,6 +19,7 @@ const AWSField: React.FC = ({ values, onUpdate }) => { {Object.values(AWS_KEYS).map((value: AWS_KEYS) => ( { const scrollableSelectorID = 'project-details-list'; const sections: SectionType[] = [ { id: ProjectSectionID.WORKBENCHES, component: }, - { id: ProjectSectionID.STORAGES, component: }, + { id: ProjectSectionID.CLUSTER_STORAGES, component: }, { id: ProjectSectionID.DATA_CONNECTIONS, component: }, ]; diff --git a/frontend/src/pages/projects/screens/detail/const.ts b/frontend/src/pages/projects/screens/detail/const.ts index 9073afb3eb..e40e7baa49 100644 --- a/frontend/src/pages/projects/screens/detail/const.ts +++ b/frontend/src/pages/projects/screens/detail/const.ts @@ -2,6 +2,6 @@ import { ProjectSectionID, ProjectSectionTitlesType } from './types'; export const ProjectSectionTitles: ProjectSectionTitlesType = { [ProjectSectionID.WORKBENCHES]: 'Workbenches', - [ProjectSectionID.STORAGES]: 'Storages', + [ProjectSectionID.CLUSTER_STORAGES]: 'Cluster storages', [ProjectSectionID.DATA_CONNECTIONS]: 'Data connections', }; diff --git a/frontend/src/pages/projects/screens/detail/storage/StorageList.tsx b/frontend/src/pages/projects/screens/detail/storage/StorageList.tsx index 0f5bdf42a1..412d12c99e 100644 --- a/frontend/src/pages/projects/screens/detail/storage/StorageList.tsx +++ b/frontend/src/pages/projects/screens/detail/storage/StorageList.tsx @@ -17,15 +17,15 @@ const StorageList: React.FC = () => { return ( <> setOpen(true)} - key={`action-${ProjectSectionID.STORAGES}`} + key={`action-${ProjectSectionID.CLUSTER_STORAGES}`} variant="secondary" > - Add storage + Add cluster storage , ]} isLoading={!loaded} diff --git a/frontend/src/pages/projects/screens/detail/types.ts b/frontend/src/pages/projects/screens/detail/types.ts index 04babea968..2043c8ca40 100644 --- a/frontend/src/pages/projects/screens/detail/types.ts +++ b/frontend/src/pages/projects/screens/detail/types.ts @@ -1,6 +1,6 @@ export enum ProjectSectionID { WORKBENCHES = 'workbenches', - STORAGES = 'storages', + CLUSTER_STORAGES = 'cluster-storages', DATA_CONNECTIONS = 'data-connections', } diff --git a/frontend/src/pages/projects/screens/spawner/SpawnerPage.tsx b/frontend/src/pages/projects/screens/spawner/SpawnerPage.tsx index 0a45d8f279..cdf21050ad 100644 --- a/frontend/src/pages/projects/screens/spawner/SpawnerPage.tsx +++ b/frontend/src/pages/projects/screens/spawner/SpawnerPage.tsx @@ -72,7 +72,10 @@ const SpawnerPage: React.FC = () => { /> - + { > - + + + diff --git a/frontend/src/pages/projects/screens/spawner/const.ts b/frontend/src/pages/projects/screens/spawner/const.ts index ebae8b71e0..5c86060507 100644 --- a/frontend/src/pages/projects/screens/spawner/const.ts +++ b/frontend/src/pages/projects/screens/spawner/const.ts @@ -6,7 +6,7 @@ export const SpawnerPageSectionTitles: SpawnerPageSectionTitlesType = { [SpawnerPageSectionID.NOTEBOOK_IMAGE]: 'Notebook image', [SpawnerPageSectionID.DEPLOYMENT_SIZE]: 'Deployment size', [SpawnerPageSectionID.ENVIRONMENT_VARIABLES]: 'Environment variables', - [SpawnerPageSectionID.STORAGE]: 'Storage', + [SpawnerPageSectionID.CLUSTER_STORAGE]: 'Cluster storage', }; export const ScrollableSelectorID = 'workbench-spawner-page'; diff --git a/frontend/src/pages/projects/screens/spawner/deploymentSize/ContainerSizeSelector.tsx b/frontend/src/pages/projects/screens/spawner/deploymentSize/ContainerSizeSelector.tsx index 0ddc85ce00..93badc42d2 100644 --- a/frontend/src/pages/projects/screens/spawner/deploymentSize/ContainerSizeSelector.tsx +++ b/frontend/src/pages/projects/screens/spawner/deploymentSize/ContainerSizeSelector.tsx @@ -25,7 +25,7 @@ const ContainerSizeSelector: React.FC = ({ return ( = ({ const { storageType, creating, existing } = storageData; return ( - - - - - setStorageData('storageType', StorageType.EPHEMERAL)} - /> - - - setStorageData('storageType', StorageType.NEW_PVC)} - body={ - storageType === StorageType.NEW_PVC && ( - - setStorageData('creating', { ...creating, [key]: value }) - } - availableSize={availableSize} - /> - ) - } - /> - - - setStorageData('storageType', StorageType.EXISTING_PVC)} - body={ - storageType === StorageType.EXISTING_PVC && ( - setStorageData('existing', data)} - selectDirection="up" - menuAppendTo={getDashboardMainContainer()} - /> - ) - } - /> - - - - + + + + setStorageData('storageType', StorageType.EPHEMERAL)} + /> + + + setStorageData('storageType', StorageType.NEW_PVC)} + body={ + storageType === StorageType.NEW_PVC && ( + + setStorageData('creating', { ...creating, [key]: value }) + } + availableSize={availableSize} + /> + ) + } + /> + + + setStorageData('storageType', StorageType.EXISTING_PVC)} + body={ + storageType === StorageType.EXISTING_PVC && ( + setStorageData('existing', data)} + selectDirection="up" + menuAppendTo={getDashboardMainContainer()} + /> + ) + } + /> + + + ); }; diff --git a/frontend/src/pages/projects/screens/spawner/types.ts b/frontend/src/pages/projects/screens/spawner/types.ts index 0af8a7c2b1..8d9578ca92 100644 --- a/frontend/src/pages/projects/screens/spawner/types.ts +++ b/frontend/src/pages/projects/screens/spawner/types.ts @@ -5,7 +5,7 @@ export enum SpawnerPageSectionID { NOTEBOOK_IMAGE = 'notebook-image', DEPLOYMENT_SIZE = 'deployment-size', ENVIRONMENT_VARIABLES = 'environment-variables', - STORAGE = 'storage', + CLUSTER_STORAGE = 'cluster-storage', } export type SpawnerPageSectionTitlesType = { From cea65f2a9113c65f4ef9bb5fc30fa1b22ab6ec25 Mon Sep 17 00:00:00 2001 From: Juntao Wang Date: Wed, 26 Oct 2022 13:04:04 -0400 Subject: [PATCH 4/5] add input group for mount path and change placeholder text --- .../src/pages/projects/pvc/MountPathField.tsx | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/frontend/src/pages/projects/pvc/MountPathField.tsx b/frontend/src/pages/projects/pvc/MountPathField.tsx index 64d915380c..7d5c7925ed 100644 --- a/frontend/src/pages/projects/pvc/MountPathField.tsx +++ b/frontend/src/pages/projects/pvc/MountPathField.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { FormGroup, TextInput } from '@patternfly/react-core'; +import { FormGroup, InputGroup, InputGroupText, TextInput } from '@patternfly/react-core'; import { MountPath } from '../types'; type MountPathFieldProps = { @@ -21,24 +21,27 @@ const MountPathField: React.FC = ({ label="Mount folder" validated={mountPath.error ? 'error' : 'success'} > - { - let error = ''; - if (value.length === 0) { - error = 'Required'; - } else if (!/^[a-z-]+$/.test(value)) { - error = 'Must only consist of lower case letters and dashes'; - } else if (inUseMountPaths.includes(value)) { - error = 'Mount folder is already in use for this workbench'; - } - setMountPath({ value, error }); - }} - /> + + / + { + let error = ''; + if (value.length === 0) { + error = 'Required'; + } else if (!/^[a-z-]+$/.test(value)) { + error = 'Must only consist of lower case letters and dashes'; + } else if (inUseMountPaths.includes(value)) { + error = 'Mount folder is already in use for this workbench'; + } + setMountPath({ value, error }); + }} + /> + ); }; From 9d30f85744f6d3538021eb82c4291d2783da8120 Mon Sep 17 00:00:00 2001 From: Juntao Wang Date: Wed, 26 Oct 2022 13:59:54 -0400 Subject: [PATCH 5/5] refactor useRelatedNotebooks --- .../projects/notebook/useRelatedNotebooks.ts | 63 ++++++++++--------- .../storage/ExistingConnectedNotebooks.tsx | 37 +++-------- 2 files changed, 44 insertions(+), 56 deletions(-) diff --git a/frontend/src/pages/projects/notebook/useRelatedNotebooks.ts b/frontend/src/pages/projects/notebook/useRelatedNotebooks.ts index d14d5f1c92..412a81ae59 100644 --- a/frontend/src/pages/projects/notebook/useRelatedNotebooks.ts +++ b/frontend/src/pages/projects/notebook/useRelatedNotebooks.ts @@ -16,35 +16,40 @@ const useRelatedNotebooks = ( notebooks: { data, loaded, error }, } = React.useContext(ProjectDetailsContext); - if (!resourceName) { - return { connectedNotebooks: [], loaded: false, error }; - } - - let connectedNotebooks: NotebookKind[]; - switch (context) { - case ConnectedNotebookContext.PVC: - connectedNotebooks = data.reduce((acc, { notebook }) => { - const relatedPVCNames = getNotebookPVCNames(notebook); - if (!relatedPVCNames.includes(resourceName)) { - return acc; - } - - return [...acc, notebook]; - }, []); - break; - case ConnectedNotebookContext.DATA_CONNECTION: - connectedNotebooks = data.reduce((acc, { notebook }) => { - const relatedEnvs = getNotebookSecretNames(notebook); - if (!relatedEnvs.includes(resourceName)) { - return acc; - } - - return [...acc, notebook]; - }, []); - break; - default: - connectedNotebooks = []; - } + const [connectedNotebooks, setConnectedNotebooks] = React.useState([]); + + React.useEffect(() => { + if (resourceName) { + switch (context) { + case ConnectedNotebookContext.PVC: + setConnectedNotebooks( + data.reduce((acc, { notebook }) => { + const relatedPVCNames = getNotebookPVCNames(notebook); + if (!relatedPVCNames.includes(resourceName)) { + return acc; + } + + return [...acc, notebook]; + }, []), + ); + break; + case ConnectedNotebookContext.DATA_CONNECTION: + setConnectedNotebooks( + data.reduce((acc, { notebook }) => { + const relatedEnvs = getNotebookSecretNames(notebook); + if (!relatedEnvs.includes(resourceName)) { + return acc; + } + + return [...acc, notebook]; + }, []), + ); + break; + default: + setConnectedNotebooks([]); + } + } + }, [context, resourceName, data]); return { connectedNotebooks, diff --git a/frontend/src/pages/projects/screens/detail/storage/ExistingConnectedNotebooks.tsx b/frontend/src/pages/projects/screens/detail/storage/ExistingConnectedNotebooks.tsx index 6e10efb593..955fa40e0a 100644 --- a/frontend/src/pages/projects/screens/detail/storage/ExistingConnectedNotebooks.tsx +++ b/frontend/src/pages/projects/screens/detail/storage/ExistingConnectedNotebooks.tsx @@ -1,7 +1,5 @@ import * as React from 'react'; -import { NotebookKind } from '../../../../../k8sTypes'; import { Alert, Chip, ChipGroup, FormGroup, Spinner, Text } from '@patternfly/react-core'; -import { getNotebook } from '../../../../../api'; import { ProjectDetailsContext } from '../../../ProjectDetailsContext'; import { getNotebookDisplayName } from '../../../utils'; @@ -14,30 +12,9 @@ const ExistingConnectedNotebooks: React.FC = ({ existingNotebooks, setExistingNotebooks, }) => { - const [notebookMap, setNotebookMap] = React.useState<{ [name: string]: NotebookKind }>({}); - const [loaded, setLoaded] = React.useState(false); - const [error, setError] = React.useState(); - - const { currentProject } = React.useContext(ProjectDetailsContext); - const namespace = currentProject.metadata.name; - - React.useEffect(() => { - if (existingNotebooks.length > 0) { - Promise.all(existingNotebooks.map((name) => getNotebook(name, namespace))) - .then((fetchedNotebooks) => { - setNotebookMap( - fetchedNotebooks.reduce( - (acc, notebook) => ({ ...acc, [notebook.metadata.name]: notebook }), - {}, - ), - ); - setLoaded(true); - }) - .catch((e) => { - setError(e); - }); - } - }, [existingNotebooks, namespace]); + const { + notebooks: { data: allNotebooks, loaded, error }, + } = React.useContext(ProjectDetailsContext); let content: React.ReactNode; if (error) { @@ -54,7 +31,13 @@ const ExistingConnectedNotebooks: React.FC = ({ content = ( {existingNotebooks.map((notebookName) => { - const notebookDisplayName = getNotebookDisplayName(notebookMap[notebookName]); + const foundNotebook = allNotebooks.find( + (notebook) => notebook.notebook.metadata.name === notebookName, + ); + if (!foundNotebook) { + return null; + } + const notebookDisplayName = getNotebookDisplayName(foundNotebook.notebook); return (