From 39883a5aba8370fb01b73069dd3bfe62eff09732 Mon Sep 17 00:00:00 2001 From: Katerina Koukiou Date: Mon, 22 Jan 2024 13:56:43 +0100 Subject: [PATCH 1/4] Enhance Wizard Navigation Logic In preparation for an upcoming commit introducing a new 'page' for editing storage, this change refines the logic for the initial step when opening the wizard. The goal is to enable users to seamlessly return to their last visited step within the wizard, when briefly stepping out of the flow. This commit achieves the desired behavior by always selecting the last visited step if the wizard is not opening for the first time. --- src/components/AnacondaWizard.jsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/AnacondaWizard.jsx b/src/components/AnacondaWizard.jsx index d6abeec7ec..9885a557da 100644 --- a/src/components/AnacondaWizard.jsx +++ b/src/components/AnacondaWizard.jsx @@ -273,14 +273,18 @@ export const AnacondaWizard = ({ dispatch, storageData, localizationData, runtim ); } - const firstVisibleStepIndex = steps.findIndex(step => !step.props.isHidden) + 1; + const startIndex = steps.findIndex(step => { + // Find the first step that is not hidden if the Wizard is opening for the first time. + // Otherwise, find the first step that was last visited. + return currentStepId ? step.props.id === currentStepId : !step.props.isHidden; + }) + 1; return ( Date: Mon, 22 Jan 2024 14:29:55 +0100 Subject: [PATCH 2/4] Create helper file with custom hooks for disk data --- src/actions/storage-actions.js | 5 +- src/components/AnacondaWizard.jsx | 14 +- src/components/storage/Common.jsx | 121 ++++++++++++++++++ src/components/storage/InstallationMethod.jsx | 2 + .../storage/InstallationScenario.jsx | 66 ++-------- src/components/storage/MountPointMapping.jsx | 5 +- src/reducer.js | 3 +- 7 files changed, 142 insertions(+), 74 deletions(-) create mode 100644 src/components/storage/Common.jsx diff --git a/src/actions/storage-actions.js b/src/actions/storage-actions.js index 0fe525b227..fb5f18acb2 100644 --- a/src/actions/storage-actions.js +++ b/src/actions/storage-actions.js @@ -60,7 +60,10 @@ export const getDevicesAction = () => { return dispatch({ type: "GET_DEVICES_DATA", - payload: { devices: devicesData.reduce((acc, curr) => ({ ...acc, ...curr }), {}) } + payload: { + deviceNames: devices, + devices: devicesData.reduce((acc, curr) => ({ ...acc, ...curr }), {}), + } }); } catch (error) { return dispatch(setCriticalErrorAction(error)); diff --git a/src/components/AnacondaWizard.jsx b/src/components/AnacondaWizard.jsx index 9885a557da..932d6db475 100644 --- a/src/components/AnacondaWizard.jsx +++ b/src/components/AnacondaWizard.jsx @@ -42,9 +42,6 @@ import { Accounts, getPageProps as getAccountsProps, getAccountsState, applyAcco import { InstallationProgress } from "./installation/InstallationProgress.jsx"; import { ReviewConfiguration, ReviewConfigurationConfirmModal, getPageProps as getReviewConfigurationProps } from "./review/ReviewConfiguration.jsx"; import { exitGui } from "../helpers/exit.js"; -import { - getMountPointConstraints, -} from "../apis/storage_devicetree.js"; import { applyStorage, resetPartitioning, @@ -57,7 +54,6 @@ const N_ = cockpit.noop; export const AnacondaWizard = ({ dispatch, storageData, localizationData, runtimeData, onCritFail, title, conf }) => { const [isFormDisabled, setIsFormDisabled] = useState(false); const [isFormValid, setIsFormValid] = useState(false); - const [mountPointConstraints, setMountPointConstraints] = useState(); const [reusePartitioning, setReusePartitioning] = useState(false); const [stepNotification, setStepNotification] = useState(); const [storageEncryption, setStorageEncryption] = useState(getStorageEncryptionState()); @@ -72,14 +68,6 @@ export const AnacondaWizard = ({ dispatch, storageData, localizationData, runtim return Object.keys(storageData.devices); }, [storageData.devices]); - useEffect(() => { - const updateMountPointConstraints = async () => { - const mountPointConstraints = await getMountPointConstraints().catch(console.error); - setMountPointConstraints(mountPointConstraints); - }; - updateMountPointConstraints(); - }, []); - useEffect(() => { if (!currentStepId) { return; @@ -115,6 +103,7 @@ export const AnacondaWizard = ({ dispatch, storageData, localizationData, runtim component: InstallationMethod, data: { deviceData: storageData.devices, + deviceNames: storageData.deviceNames, diskSelection: storageData.diskSelection, dispatch, storageScenarioId, @@ -135,7 +124,6 @@ export const AnacondaWizard = ({ dispatch, storageData, localizationData, runtim diskSelection: storageData.diskSelection, dispatch, partitioningData: storageData.partitioning, - mountPointConstraints, reusePartitioning, setReusePartitioning, }, diff --git a/src/components/storage/Common.jsx b/src/components/storage/Common.jsx new file mode 100644 index 0000000000..8eff0cc498 --- /dev/null +++ b/src/components/storage/Common.jsx @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2024 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with This program; If not, see . + */ +import { useEffect, useState } from "react"; + +import { + getDiskFreeSpace, + getDiskTotalSpace, + getMountPointConstraints, + getRequiredDeviceSize, +} from "../../apis/storage_devicetree.js"; +import { + getRequiredSpace +} from "../../apis/payloads.js"; +import { findDuplicatesInArray } from "../../helpers/utils.js"; + +export const useDiskTotalSpace = ({ selectedDisks, devices }) => { + const [diskTotalSpace, setDiskTotalSpace] = useState(); + + useEffect(() => { + const update = async () => { + const diskTotalSpace = await getDiskTotalSpace({ diskNames: selectedDisks }); + + setDiskTotalSpace(diskTotalSpace); + }; + update(); + }, [selectedDisks, devices]); + + return diskTotalSpace; +}; + +export const useDiskFreeSpace = ({ selectedDisks, devices }) => { + const [diskFreeSpace, setDiskFreeSpace] = useState(); + + useEffect(() => { + const update = async () => { + const diskFreeSpace = await getDiskFreeSpace({ diskNames: selectedDisks }); + + setDiskFreeSpace(diskFreeSpace); + }; + update(); + }, [selectedDisks, devices]); + + return diskFreeSpace; +}; + +export const useDuplicateDeviceNames = ({ deviceNames }) => { + const [duplicateDeviceNames, setDuplicateDeviceNames] = useState([]); + + useEffect(() => { + const update = async () => { + const _duplicateDeviceNames = findDuplicatesInArray(deviceNames); + + setDuplicateDeviceNames(_duplicateDeviceNames); + }; + update(); + }, [deviceNames]); + + return duplicateDeviceNames; +}; + +export const useHasFilesystems = ({ selectedDisks, devices }) => { + const [hasFilesystems, setHasFilesystems] = useState(); + + useEffect(() => { + const _hasFilesystems = ( + selectedDisks.some(device => ( + devices[device]?.children.v.some(child => ( + devices[child]?.formatData.mountable.v || devices[child]?.formatData.type.v === "luks") + ) + )) + ); + + setHasFilesystems(_hasFilesystems); + }, [selectedDisks, devices]); + + return hasFilesystems; +}; + +export const useRequiredSize = () => { + const [requiredSize, setRequiredSize] = useState(); + + useEffect(() => { + const update = async () => { + const requiredSpace = await getRequiredSpace().catch(console.error); + const requiredSize = await getRequiredDeviceSize({ requiredSpace }).catch(console.error); + + setRequiredSize(requiredSize); + }; + update(); + }, []); + + return requiredSize; +}; + +export const useMountPointConstraints = () => { + const [mountPointConstraints, setMountPointConstraints] = useState(); + + useEffect(() => { + const update = async () => { + const mountPointConstraints = await getMountPointConstraints().catch(console.error); + setMountPointConstraints(mountPointConstraints); + }; + update(); + }, []); + + return mountPointConstraints; +}; diff --git a/src/components/storage/InstallationMethod.jsx b/src/components/storage/InstallationMethod.jsx index 756f81345c..3beb992737 100644 --- a/src/components/storage/InstallationMethod.jsx +++ b/src/components/storage/InstallationMethod.jsx @@ -30,6 +30,7 @@ const _ = cockpit.gettext; export const InstallationMethod = ({ deviceData, + deviceNames, diskSelection, dispatch, idPrefix, @@ -58,6 +59,7 @@ export const InstallationMethod = ({ />