* Copyright (C) 2018 Red Hat, Inc.
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
import cockpit from "cockpit";
import React from "react";
import { Flex } from "@patternfly/react-core/dist/esm/layouts/Flex/index.js";
import { Spinner } from "@patternfly/react-core/dist/esm/components/Spinner/index.js";
import { WarningTriangleIcon } from "@patternfly/react-icons";
import { show_modal_dialog } from "cockpit-components-dialog.jsx";
import { getPackageManager, InstallProgressType } from "packagemanager";
import "cockpit-components-install-dialog.css";
import { fmt_to_fragments } from "utils";
const _ = cockpit.gettext;
* install the given package.
*
* The install_dialog function returns a promise that is fulfilled when the dialog closes after
* a successful installation. The promise is rejected when the user cancels the dialog.
*
* If the package is already installed before the dialog opens, we still go
* through all the motions and the dialog closes successfully without doing
* anything when the use hits "Install".
*
* You shouldn't call install_dialog unless you know that PackageKit is available.
* (If you do anyway, the resulting D-Bus errors will be shown to the user.)
*/
export async function install_dialog(pkg, options) {
let data = null;
let error_message = null;
let progress_message = null;
let cancel = null;
let done = null;
if (!Array.isArray(pkg))
pkg = [pkg];
options = options || { };
const package_manager = await getPackageManager();
const prom = new Promise((resolve, reject) => { done = f => { if (f) resolve(); else reject(); } });
let dialog = null;
function update() {
let extra_details = null;
let remove_details = null;
let footer_message = null;
const missing_name = <strong>{pkg.join(", ")}</strong>;
if (data && data.extra_names.length > 0)
extra_details = (
<div className="scale-up-ct">
{_("Additional packages:")}
<ul className="package-list-ct">{data.extra_names.map(id => <li key={id}>{id}</li>)}</ul>
</div>
);
if (data && data.remove_names.length > 0)
remove_details = (
<div className="scale-up-ct">
<WarningTriangleIcon /> {_("Removals:")}
<ul className="package-list">{data.remove_names.map(id => <li key={id}>{id}</li>)}</ul>
</div>
);
if (progress_message)
footer_message = (
<Flex spaceItems={{ default: 'spaceItemsSm' }} alignItems={{ default: 'alignItemsCenter' }}>
<span>{ progress_message }</span>
<Spinner size="sm" />
</Flex>
);
else if (data?.download_size) {
footer_message = (
<div>
{ fmt_to_fragments(_("Total size: $0"), <strong>{cockpit.format_bytes(data.download_size)}</strong>) }
</div>
);
}
const body = {
id: "dialog",
title: options.title || _("Install software"),
body: (
<div className="scroll">
<p>{ fmt_to_fragments(options.text || _("$0 will be installed."), missing_name) }</p>
{ remove_details }
{ extra_details }
</div>
),
static_error: error_message,
};
const footer = {
actions: [
{
caption: _("Install"),
style: "primary",
clicked: install_missing,
disabled: data == null
}
],
idle_message: footer_message,
dialog_done: f => { if (!f && cancel) cancel(); done(f) }
};
if (dialog) {
dialog.setProps(body);
dialog.setFooterProps(footer);
} else {
dialog = show_modal_dialog(body, footer);
}
}
function check_missing() {
package_manager.check_missing_packages(pkg,
p => {
cancel = p.cancel;
let pm = null;
if (p.waiting)
pm = _("Waiting for other software management operations to finish");
else
pm = _("Checking installed software");
if (pm !== progress_message) {
progress_message = pm;
update();
}
})
.then(d => {
if (d.unavailable_names.length > 0)
error_message = cockpit.format(_("$0 is not available from any repository."),
d.unavailable_names[0]);
else
data = d;
progress_message = null;
cancel = null;
update();
})
.catch(e => {
progress_message = null;
cancel = null;
error_message = e.toString();
update();
});
}
function install_missing(progress_cb) {
return package_manager.install_missing_packages(data,
p => {
let text = null;
if (p.waiting) {
text = _("Waiting for other software management operations to finish");
} else if (p.package) {
let fmt;
if (p.info === InstallProgressType.DOWNLOADING)
fmt = _("Downloading $0");
else if (p.info === InstallProgressType.REMOVING)
fmt = _("Removing $0");
else
fmt = _("Installing $0");
text = fmt_to_fragments(fmt, <strong>{p.package}</strong>);
}
progress_cb(text, p.cancel);
});
}
update();
check_missing();
return prom;
}