Skip to content

Commit 3de4191

Browse files
mvollmermartinpitt
authored andcommitted
lib/cockpit-components-install-dialog: New
1 parent 5f210af commit 3de4191

File tree

2 files changed

+239
-0
lines changed

2 files changed

+239
-0
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
.package-list-ct {
2+
margin: 1em 0;
3+
padding: 0;
4+
max-width: 110rem;
5+
text-align: left;
6+
box-sizing: border-box;
7+
}
8+
9+
.package-list-ct li {
10+
text-align: left;
11+
box-sizing: border-box;
12+
width: 22rem;
13+
padding: 0 1ex;
14+
display: inline-block;
15+
overflow: hidden;
16+
white-space: nowrap;
17+
text-overflow: ellipsis;
18+
}
19+
20+
.scale-up-ct {
21+
animation: dialogScaleUpCt 1s;
22+
}
23+
24+
@keyframes dialogScaleUpCt {
25+
0% {
26+
opacity: 0;
27+
max-height: 0;
28+
}
29+
25% {
30+
opacity: 0;
31+
}
32+
50% {
33+
max-height: 100vh;
34+
}
35+
100% {
36+
opacity: 1;
37+
}
38+
}
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
/*
2+
* This file is part of Cockpit.
3+
*
4+
* Copyright (C) 2018 Red Hat, Inc.
5+
*
6+
* Cockpit is free software; you can redistribute it and/or modify it
7+
* under the terms of the GNU Lesser General Public License as published by
8+
* the Free Software Foundation; either version 2.1 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* Cockpit is distributed in the hope that it will be useful, but
12+
* WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with Cockpit; If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
import cockpit from "cockpit";
21+
import React from "react";
22+
23+
import { show_modal_dialog } from "cockpit-components-dialog.jsx";
24+
import * as PK from "packagekit.es6";
25+
26+
import "cockpit-components-install-dialog.css";
27+
28+
const _ = cockpit.gettext;
29+
30+
// TODO - generalize this to arbitrary number of arguments (when needed)
31+
function format_to_array(fmt, arg) {
32+
var index = fmt.indexOf("$0");
33+
if (index >= 0)
34+
return [ fmt.slice(0, index), arg, fmt.slice(index + 2) ];
35+
else
36+
return [ fmt ];
37+
}
38+
39+
/* Calling install_dialog will open a dialog that lets the user
40+
* install the given package.
41+
*
42+
* The install_dialog function returns a promise that is fulfilled when the dialog closes after
43+
* a successful installation. The promise is rejected when the user cancels the dialog.
44+
*
45+
* If the package is already installed before the dialog opens, we still go
46+
* through all the motions and the dialog closes successfully without doing
47+
* anything when the use hits "Install".
48+
*
49+
* You shouldn't call install_dialog unless you know that PackageKit is available.
50+
* (If you do anyway, the resulting D-Bus errors will be shown to the user.)
51+
*/
52+
53+
export function install_dialog(pkg) {
54+
var data = null;
55+
var error_message = null;
56+
var progress_message = null;
57+
var cancel = null;
58+
var done = null;
59+
60+
var prom = new Promise((resolve, reject) => { done = f => { if (f) resolve(); else reject(); }; });
61+
62+
var dialog = null;
63+
function update() {
64+
let extra_details = null;
65+
let remove_details = null;
66+
let footer_message = null;
67+
68+
let missing_name = <strong>{pkg}</strong>;
69+
70+
if (data && data.extra_names.length > 0)
71+
extra_details = (
72+
<p className="scale-up-ct">
73+
{_("Additional packages:")}
74+
<ul className="package-list-ct">{data.extra_names.map(id => <li>{id}</li>)}</ul>
75+
</p>
76+
);
77+
78+
if (data && data.remove_names.length > 0)
79+
remove_details = (
80+
<p className="scale-up-ct">
81+
<span className="pficon pficon-warning-triangle-o" /> {_("Removals:")}
82+
<ul className="package-list">{data.remove_names.map(id => <li>{id}</li>)}</ul>
83+
</p>
84+
);
85+
86+
if (progress_message)
87+
footer_message = (
88+
<div>
89+
<div className="spinner spinner-sm" />
90+
<span>{ progress_message }</span>
91+
</div>
92+
);
93+
else if (data && data.download_size) {
94+
footer_message = (
95+
<div>
96+
{ format_to_array(_("Total size: $0"), <strong>{cockpit.format_bytes(data.download_size)}</strong>) }
97+
</div>
98+
);
99+
}
100+
101+
let body = {
102+
id: "dialog",
103+
title: _("Install Software"),
104+
body: (
105+
<div className="modal-body">
106+
<p>{ format_to_array(_("$0 will be installed."), missing_name) }</p>
107+
{ remove_details }
108+
{ extra_details }
109+
</div>
110+
)
111+
};
112+
113+
let footer = {
114+
actions: [
115+
{ caption: _("Install"),
116+
style: "primary",
117+
clicked: install_missing,
118+
disabled: data == null
119+
}
120+
],
121+
static_error: error_message,
122+
idle_message: footer_message,
123+
dialog_done: f => { if (!f && cancel) cancel(); done(f); }
124+
};
125+
126+
if (dialog) {
127+
dialog.setProps(body);
128+
dialog.setFooterProps(footer);
129+
} else {
130+
dialog = show_modal_dialog(body, footer);
131+
}
132+
}
133+
134+
function check_missing() {
135+
PK.check_missing_packages([ pkg ],
136+
p => {
137+
cancel = p.cancel;
138+
var pm = null;
139+
if (p.waiting)
140+
pm = _("Waiting for other software management operations to finish");
141+
else
142+
pm = _("Checking installed software");
143+
if (pm != progress_message) {
144+
progress_message = pm;
145+
update();
146+
}
147+
})
148+
.then(d => {
149+
if (d.unavailable_names.length > 0)
150+
error_message = cockpit.format(_("$0 is not available from any repository."),
151+
d.unavailable_names[0]);
152+
else
153+
data = d;
154+
progress_message = null;
155+
cancel = null;
156+
update();
157+
})
158+
.catch(e => {
159+
progress_message = null;
160+
cancel = null;
161+
error_message = e.toString();
162+
update();
163+
});
164+
}
165+
166+
function install_missing() {
167+
// We need to return a Cockpit flavoured promise since we want
168+
// to use progress notifications.
169+
var dfd = cockpit.defer();
170+
171+
PK.install_missing_packages(data,
172+
p => {
173+
var text = null;
174+
if (p.waiting) {
175+
text = _("Waiting for other software management operations to finish");
176+
} else if (p.package) {
177+
var fmt;
178+
if (p.info == PK.Enum.INFO_DOWNLOADING)
179+
fmt = _("Downloading $0");
180+
else if (p.info == PK.Enum.INFO_REMOVING)
181+
fmt = _("Removing $0");
182+
else
183+
fmt = _("Installing $0");
184+
text = format_to_array(fmt, <strong>{p.package}</strong>);
185+
}
186+
dfd.notify(text, p.cancel);
187+
})
188+
.then(() => {
189+
dfd.resolve();
190+
})
191+
.catch(error => {
192+
dfd.reject(error);
193+
});
194+
195+
return dfd.promise;
196+
}
197+
198+
update();
199+
check_missing();
200+
return prom;
201+
}

0 commit comments

Comments
 (0)