185 lines
6.9 KiB
JavaScript
185 lines
6.9 KiB
JavaScript
/*
|
|
* This file is part of Cockpit.
|
|
*
|
|
* Copyright (C) 2017 Red Hat, Inc.
|
|
*
|
|
* Cockpit is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation; either version 2.1 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Cockpit is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with Cockpit; If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
import cockpit from "cockpit";
|
|
import React from "react";
|
|
import {
|
|
Alert, AlertActionCloseButton, Button,
|
|
DataList, DataListItem, DataListItemRow, DataListCell,
|
|
DataListAction,
|
|
DataListItemCells,
|
|
Page, PageSection, PageSectionVariants,
|
|
} from "@patternfly/react-core";
|
|
import { RebootingIcon } from "@patternfly/react-icons";
|
|
|
|
import * as PackageKit from "./packagekit.js";
|
|
import { left_click, icon_url, show_error, launch, ProgressBar, CancelButton } from "./utils.jsx";
|
|
|
|
const _ = cockpit.gettext;
|
|
|
|
class ApplicationRow extends React.Component {
|
|
constructor() {
|
|
super();
|
|
this.state = { progress: null };
|
|
}
|
|
|
|
render() {
|
|
var self = this;
|
|
var comp = self.props.comp;
|
|
var state = self.state;
|
|
|
|
function action(func, arg, progress_title) {
|
|
self.setState({ progress_title: progress_title });
|
|
func(arg, (data) => self.setState({ progress: data }))
|
|
.finally(() => self.setState({ progress: null }))
|
|
.catch(show_error);
|
|
}
|
|
|
|
function install() {
|
|
action(PackageKit.install, comp.pkgname, _("Installing"));
|
|
}
|
|
|
|
function remove() {
|
|
action(PackageKit.remove, comp.file, _("Removing"));
|
|
}
|
|
|
|
var name, summary_or_progress, button;
|
|
|
|
if (comp.installed) {
|
|
name = <Button variant="link" isInline id={comp.name} onClick={left_click(() => launch(comp))}>{comp.name}</Button>;
|
|
} else {
|
|
name = <Button variant="link" isInline id={comp.name} onClick={left_click(() => cockpit.location.go(comp.id))}>{comp.name}</Button>;
|
|
}
|
|
|
|
if (state.progress) {
|
|
summary_or_progress = <ProgressBar title={state.progress_title} data={state.progress} />;
|
|
button = <CancelButton data={state.progress} />;
|
|
} else {
|
|
if (state.error) {
|
|
summary_or_progress = (
|
|
<div>
|
|
{comp.summary}
|
|
<Alert isInline variant='danger'
|
|
actionClose={<AlertActionCloseButton onClose={left_click(() => { this.setState({ error: null }) })} />}
|
|
title={state.error} />
|
|
</div>
|
|
);
|
|
} else {
|
|
summary_or_progress = comp.summary;
|
|
}
|
|
|
|
if (comp.installed) {
|
|
button = <Button variant="danger" onClick={left_click(remove)}>{_("Remove")}</Button>;
|
|
} else {
|
|
button = <Button variant="secondary" onClick={left_click(install)}>{_("Install")}</Button>;
|
|
}
|
|
}
|
|
|
|
return (
|
|
<DataListItem className="app-list" aria-labelledby={comp.name}>
|
|
<DataListItemRow>
|
|
<DataListItemCells
|
|
dataListCells={[
|
|
<DataListCell isIcon key="icon">
|
|
<img src={icon_url(comp.icon)} role="presentation" alt="" />
|
|
</DataListCell>,
|
|
<DataListCell width={1} key="app name">
|
|
{name}
|
|
</DataListCell>,
|
|
<DataListCell width={4} key="secondary content">
|
|
{summary_or_progress}
|
|
</DataListCell>,
|
|
]}
|
|
/>
|
|
<DataListAction aria-labelledby={comp.name} aria-label={_("Actions")}>
|
|
{button}
|
|
</DataListAction>
|
|
</DataListItemRow>
|
|
</DataListItem>
|
|
);
|
|
}
|
|
}
|
|
|
|
export class ApplicationList extends React.Component {
|
|
constructor() {
|
|
super();
|
|
this.state = { progress: false };
|
|
}
|
|
|
|
render() {
|
|
var self = this;
|
|
var comps = [];
|
|
for (var id in this.props.metainfo_db.components)
|
|
comps.push(this.props.metainfo_db.components[id]);
|
|
comps.sort((a, b) => a.name.localeCompare(b.name));
|
|
|
|
function refresh() {
|
|
var config = cockpit.manifests.apps.config || { };
|
|
PackageKit.refresh(self.props.metainfo_db.origin_files,
|
|
config.appstream_config_packages || [],
|
|
config.appstream_data_packages || [],
|
|
data => self.setState({ progress: data }))
|
|
.finally(() => self.setState({ progress: false }))
|
|
.catch(show_error);
|
|
}
|
|
|
|
var refresh_progress, refresh_button, empty_caption, tbody;
|
|
if (this.state.progress) {
|
|
refresh_progress = <ProgressBar title={_("Checking for new applications")} data={this.state.progress} />;
|
|
refresh_button = <CancelButton data={this.state.progress} />;
|
|
} else {
|
|
refresh_progress = null;
|
|
refresh_button = (
|
|
<Button variant="secondary" onClick={left_click(refresh)} aria-label={ _("Update package information") }>
|
|
<RebootingIcon />
|
|
</Button>
|
|
);
|
|
}
|
|
|
|
if (comps.length === 0) {
|
|
if (this.props.metainfo_db.ready)
|
|
empty_caption = _("No applications installed or available");
|
|
else
|
|
empty_caption = <div className="spinner spinner-sm" />;
|
|
tbody = <div className="app-list-empty">{empty_caption}</div>;
|
|
} else {
|
|
tbody = comps.map(c => <ApplicationRow comp={c} key={c.id} />);
|
|
}
|
|
|
|
return (
|
|
<Page>
|
|
<PageSection variant={PageSectionVariants.light}>
|
|
<header className='ct-table-header'>
|
|
<h2 className='ct-table-heading'>{_("Applications")}</h2>
|
|
<div className='ct-table-actions'>
|
|
<div className="right-menu">
|
|
{refresh_progress}
|
|
{refresh_button}
|
|
</div>
|
|
</div>
|
|
</header>
|
|
<DataList aria-label={_("Applications list")}>
|
|
{ tbody }
|
|
</DataList>
|
|
</PageSection>
|
|
</Page>
|
|
);
|
|
}
|
|
}
|