/* * Copyright (C) 2017 Red Hat, Inc. * SPDX-License-Identifier: LGPL-2.1-or-later */ import cockpit from "cockpit"; import React, { useState } from "react"; import { Alert, AlertActionCloseButton, AlertActionLink } from "@patternfly/react-core/dist/esm/components/Alert/index.js"; import { Button } from "@patternfly/react-core/dist/esm/components/Button/index.js"; import { DataList, DataListAction, DataListCell, DataListItem, DataListItemCells, DataListItemRow } from "@patternfly/react-core/dist/esm/components/DataList/index.js"; import { Flex, FlexItem } from "@patternfly/react-core/dist/esm/layouts/Flex/index.js"; import { Page, PageSection } from "@patternfly/react-core/dist/esm/components/Page/index.js"; import { Stack, StackItem } from "@patternfly/react-core/dist/esm/layouts/Stack/index.js"; import { RebootingIcon } from "@patternfly/react-icons"; import { get_manifest_config_matchlist } from "utils"; import { read_os_release } from "os-release"; import { EmptyStatePanel } from "cockpit-components-empty-state.jsx"; import { useInit } from "hooks"; import { ProgressReporter, icon_url, show_error, launch, ProgressBar, CancelButton } from "./utils"; import { ActionButton } from "./application.jsx"; import { getPackageManager } from "packagemanager.js"; import { Title, TitleSizes } from "@patternfly/react-core/dist/esm/components/Title"; const _ = cockpit.gettext; async function refresh_appstream_metadata(origin_files, config_packages, data_packages, progress_cb) { /* In addition to refreshing the repository metadata, we also * update all packages that contain AppStream collection metadata. * * AppStream collection metadata is arguably part of the * repository metadata and should be updated during a regular * refresh of the repositories. On some distributions this is * what happens, but on others (such as Fedora), the collection * metadata is delivered in packages. We find them and update * them explicitly. * * Also, we have two explicit lists of packages, and we make sure * that they are installed. The first list contains packages that * configure the system to retrieve AppStream data as part of * repository metadata, and the second list contains packages that * contain AppStream data themselves. */ const packagemanager = await getPackageManager(); const progress = new ProgressReporter(0, 1, progress_cb); if (config_packages.length > 0) { progress.base = 0; progress.range = 5; await packagemanager.install_packages(config_packages, progress.progress_reporter); } const origin_pkgs = new Set(await packagemanager.find_file_packages(origin_files, progress.progress_reporter)); progress.base = 6; progress.range = 69; await packagemanager.refresh(true, progress.progress_reporter); progress.base = 75; progress.range = 5; const updates = await packagemanager.get_updates(false, progress.progress_reporter); const filtered_updates = []; for (const update of updates) { if (origin_pkgs.has(update.name)) filtered_updates.push(update); } progress.base = 80; progress.range = 15; if (filtered_updates.length > 0) return packagemanager.update_packages(filtered_updates, { on_package: () => {}, on_notify: (data) => progress.progress_reporter({ percentage: data.percentage, waiting: false, cancel: data.cancel, }), }); if (data_packages.length > 0) { progress.range = 95; await packagemanager.install_packages(data_packages, progress.progress_reporter); } } const ApplicationRow = ({ comp, progress, progress_title, action }) => { const [error, setError] = useState(); const name = ( ); let summary_or_progress; if (progress) { summary_or_progress = ( {progress_title} ); } else { if (error) { summary_or_progress = (
{comp.summary} setError(null)} />} title={error} />
); } else { summary_or_progress = comp.summary; } } return ( {cockpit.format(_("icon , {name} , {summary_or_progress} , ]} /> ); }; export const ApplicationList = ({ metainfo_db, appProgress, appProgressTitle, action }) => { const [progress, setProgress] = useState(false); const [dataPackagesInstalled, setDataPackagesInstalled] = useState(null); const comps = []; for (const id in metainfo_db.components) comps.push(metainfo_db.components[id]); comps.sort((a, b) => a.name.localeCompare(b.name)); async function check_missing_data(packages) { try { const packagemanager = await getPackageManager(); setDataPackagesInstalled(await packagemanager.is_installed(packages)); } catch (e) { console.warn("Failed to check missing AppStream metadata packages:", e.toString()); } } async function get_packages() { const os_release = await read_os_release(); // ID is a single value, ID_LIKE is a list const os_list = [os_release?.ID, ...(os_release?.ID_LIKE || "").split(/\s+/)]; const configPackages = get_manifest_config_matchlist('apps', 'appstream_config_packages', [], os_list); const dataPackages = get_manifest_config_matchlist('apps', 'appstream_data_packages', [], os_list); return [configPackages, dataPackages]; } useInit(async () => { const [config, data] = await get_packages(); await check_missing_data([...config, ...data]); }); async function refresh() { const [configPackages, dataPackages] = await get_packages(); try { await refresh_appstream_metadata(metainfo_db.origin_files, configPackages, dataPackages, setProgress); } catch (e) { show_error(e); } finally { await check_missing_data([...dataPackages, ...configPackages]); setProgress(false); } } let refresh_progress; let refresh_button; let tbody; if (progress) { refresh_progress = ; refresh_button = ; } else { refresh_progress = null; refresh_button = (