/* * Copyright (C) 2021 Red Hat, Inc. * SPDX-License-Identifier: LGPL-2.1-or-later */ import cockpit from "cockpit"; import React, { useCallback, useEffect, useRef, useState } from "react"; import { Page, PageGroup, PageSection, PageSectionTypes } from "@patternfly/react-core/dist/esm/components/Page/index.js"; import { BossClient } from "../apis/boss.js"; import { initialState, reducer, useReducerWithThunk } from "../reducer.js"; import { getInstallerConfValue, parseAnacondaConfBool, readConf } from "../helpers/conf.js"; import { isExiting } from "../helpers/exit.js"; import { debug } from "../helpers/log.js"; import { getAnacondaUIVersion, getAnacondaVersion } from "../helpers/product.js"; import { MainContextWrapper } from "../contexts/Common.jsx"; import { EmptyStatePanel } from "cockpit-components-empty-state"; import { read_os_release as readOsRelease } from "os-release.js"; import { AnacondaHeader } from "./AnacondaHeader.jsx"; import { AnacondaWizard } from "./AnacondaWizard.jsx"; import { ErrorBoundary } from "./Error.jsx"; const _ = cockpit.gettext; const N_ = cockpit.noop; export const ApplicationLoading = () => ( ); const ApplicationCompleted = () => ( ); export const Application = ({ conf, dispatch, installationStatus, isFetching, onCritFail, osRelease, reportLinkURL, setShowStorage, showStorage }) => { const [storeInitialized, setStoreInitialized] = useState(false); const [currentStepId, setCurrentStepId] = useState(); const address = useAddress(onCritFail); const automatedInstall = parseAnacondaConfBool(getInstallerConfValue(conf, "Runtime", "automated_install")); const pauseAtSummary = parseAnacondaConfBool(getInstallerConfValue(conf, "Runtime", "pause_at_summary")); useEffect(() => { if (!address) { return; } // Before unload ask the user for verification const preventExit = () => { return ""; }; // Attach the beforeunload event handler window.addEventListener("beforeunload", preventExit); // Function to temporarily disable beforeunload const allowExternalNavigation = (event) => { const link = event.target.closest("a"); if (link && (link.href.startsWith("extlink:") || link.href.startsWith("anaconda-gnome-control-center:"))) { window.removeEventListener("beforeunload", preventExit); setTimeout(() => { window.addEventListener("beforeunload", preventExit); }, 1000); // Re-enable after 1 second } }; // Attach a click event listener to detect external link clicks document.addEventListener("click", allowExternalNavigation); new BossClient(address, dispatch).init({ automatedInstall, conf }) .then(() => { setStoreInitialized(true); }, onCritFail({ context: N_("Reading information about the computer failed.") })); }, [address, automatedInstall, conf, dispatch, onCritFail]); // Postpone rendering anything until we read the dbus address and the default configuration if (!address || !storeInitialized || !installationStatus) { if (isExiting()) { return ; } debug("Loading initial data..."); return ; } // On live media rebooting the system will actually shut it off const title = cockpit.format(_("$0 installation"), osRelease.PRETTY_NAME); return ( <> ); }; const useConf = ({ onCritFail }) => { const [conf, setConf] = useState(); useEffect(() => { readConf().then(setConf, onCritFail({ context: N_("Reading installer configuration failed.") })); }, [onCritFail]); return conf; }; const useOsRelease = ({ onCritFail }) => { const [osRelease, setOsRelease] = useState(); useEffect(() => { readOsRelease().then(setOsRelease, onCritFail({ context: N_("Reading information about the OS failed.") })); }, [onCritFail]); return osRelease; }; const useAddress = (onCritFail) => { const [backendReady, setBackendReady] = useState(false); const [address, setAddress] = useState(); useEffect(() => { if (!backendReady) { return; } cockpit.file("/run/anaconda/bus.address").watch(address => { setAddress(address); }); }, [backendReady]); const wasReadyRef = useRef(false); useEffect(() => { cockpit.file("/run/anaconda/backend_ready").watch( (content) => { const isReady = content !== null; if (isReady) { wasReadyRef.current = true; } setBackendReady(isReady); if (!isReady && wasReadyRef.current && onCritFail && !isExiting()) { onCritFail()({ message: _("The Anaconda installation has stopped unexpectedly."), }); } } ); }, [onCritFail]); return address; }; const useAppVersion = () => { const initialState = { webui: getAnacondaUIVersion(), }; const [appVersion, setAppVersion] = useState(initialState); useEffect(() => { getAnacondaVersion().then(value => setAppVersion(obj => ({ ...obj, backend: value }))); }, []); return appVersion; }; export const ApplicationWithErrorBoundary = () => { const [showStorage, setShowStorage] = useState(false); const [state, dispatch] = useReducerWithThunk(reducer, initialState); const [errorBeforeBoundary, setErrorBeforeBoundary] = useState(); const onCritFail = useCallback( (context) => (exc) => setErrorBeforeBoundary({ contextData: { context }, ...exc }), [] ); const conf = useConf({ onCritFail }); const osRelease = useOsRelease({ onCritFail }); const appVersion = useAppVersion(); if (!conf || !osRelease) { return ; } return ( ); };