/* * Copyright (C) 2025 Red Hat, Inc. * SPDX-License-Identifier: LGPL-2.1-or-later */ import cockpit from "cockpit"; import React, { useContext, useEffect, useState } from "react"; import { Button } from "@patternfly/react-core/dist/esm/components/Button/index.js"; import { useWizardContext } from "@patternfly/react-core/dist/esm/components/Wizard/index.js"; import { applyStorage } from "../../../apis/storage_partitioning.js"; import { PageContext, StorageContext } from "../../../contexts/Common.jsx"; import { useFreeSpaceForSystem, useOriginalDevices, useRequiredSpace, } from "../../../hooks/Storage.jsx"; import { createStorageValidationNotification } from "../Common.jsx"; const _ = cockpit.gettext; const REVIEW_STEP_ID = "anaconda-screen-review"; const STORAGE_METHOD_STEP_ID = "anaconda-screen-method"; const MOUNT_POINT_MAPPING_STEP_ID = "anaconda-screen-mount-point-mapping"; /** Readiness of storage configuration for the review step */ const StorageCompleteStatus = Object.freeze({ INSUFFICIENT: "insufficient", OK: "ok", PENDING: "pending", }); /** * Storage readiness for the review screen (partitioning applied, scenario set, and enough free space). * ``status`` is one of ``StorageCompleteStatus``. */ export const useStorageComplete = () => { const { appliedPartitioning, storageScenarioId } = useContext(StorageContext); const freeSpace = useFreeSpaceForSystem(); const requiredSpace = useRequiredSpace(); const hasInsufficientSpace = requiredSpace != null && freeSpace != null && requiredSpace > freeSpace; const pending = appliedPartitioning === undefined || requiredSpace === undefined || freeSpace === undefined; if (pending) { return { freeSpace, requiredSpace, status: StorageCompleteStatus.PENDING }; } return { freeSpace, requiredSpace, status: hasInsufficientSpace || !appliedPartitioning || !storageScenarioId ? StorageCompleteStatus.INSUFFICIENT : StorageCompleteStatus.OK, }; }; const useApplyStorageOnReview = () => { const [validationPending, setValidationPending] = useState(false); const [validationReport, setValidationReport] = useState(null); const { appliedPartitioning, partitioning } = useContext(StorageContext); const { setStepNotification } = useContext(PageContext) ?? {}; const devices = useOriginalDevices(); const partitioningPath = partitioning?.path; useEffect(() => { if (appliedPartitioning || !partitioningPath) { return; } const step = REVIEW_STEP_ID; setValidationPending(true); (async () => { try { const { validationReport } = await applyStorage({ devices, partitioning: partitioningPath }); setValidationReport(validationReport); setStepNotification?.(createStorageValidationNotification(validationReport, step)); } catch (ex) { setStepNotification?.({ message: ex.message || String(ex), step }); } finally { setValidationPending(false); } })(); }, [appliedPartitioning, devices, partitioningPath, setStepNotification]); return { validationPending, validationReport }; }; const useStorageSpaceNotification = (status, freeSpace, requiredSpace) => { const { appliedPartitioning, storageScenarioId } = useContext(StorageContext); const { setStepNotification } = useContext(PageContext) ?? {}; const { goToStepById } = useWizardContext(); const fixupStepId = storageScenarioId === "mount-point-mapping" ? MOUNT_POINT_MAPPING_STEP_ID : STORAGE_METHOD_STEP_ID; useEffect(() => { if (!appliedPartitioning) { return; } if (status === StorageCompleteStatus.INSUFFICIENT) { const title = _("Not enough available free space"); const message = cockpit.format( _("$0 is required, but only $1 is available."), cockpit.format_bytes(requiredSpace), cockpit.format_bytes(freeSpace) ); const actionLinks = ( ); setStepNotification?.({ actionLinks, message, step: REVIEW_STEP_ID, title, }); } }, [ appliedPartitioning, fixupStepId, freeSpace, goToStepById, requiredSpace, setStepNotification, status, ]); }; /** * Storage completion check for the Review step * (1) run ``applyStorage`` on the current partitioning path when nothing is applied yet * (2) check for sufficient free space */ export const usePageComplete = () => { const spaceState = useStorageComplete(); const { validationPending } = useApplyStorageOnReview(); useStorageSpaceNotification(spaceState.status, spaceState.freeSpace, spaceState.requiredSpace); let complete; if (spaceState.status === StorageCompleteStatus.PENDING) { complete = undefined; } else if (spaceState.status === StorageCompleteStatus.INSUFFICIENT) { complete = false; } else { complete = true; } return { complete, validationPending }; };