Implemented uninstall.

Signed-off-by: Akos Kitta <kittaakos@typefox.io>
This commit is contained in:
Akos Kitta 2019-11-22 15:12:30 +01:00
parent b4848f62fa
commit b1388be5f9
12 changed files with 143 additions and 10 deletions

View File

@ -4,7 +4,7 @@ import { FrontendApplicationContribution } from '@theia/core/lib/browser/fronten
import { BoardsService, Board } from '../../common/protocol/boards-service';
import { BoardsServiceClientImpl } from './boards-service-client-impl';
import { BoardsListWidgetFrontendContribution } from './boards-widget-frontend-contribution';
import { InstallationProgressDialog } from '../components/installation-progress-dialog';
import { InstallationProgressDialog } from '../components/progress-dialog';
import { BoardsConfig } from './boards-config';
@ -60,4 +60,4 @@ export class BoardsAutoInstaller implements FrontendApplicationContribution {
}
}
}
}

View File

@ -3,7 +3,7 @@ import { Emitter } from '@theia/core/lib/common/event';
import { ILogger } from '@theia/core/lib/common/logger';
import { LocalStorageService } from '@theia/core/lib/browser/storage-service';
import { RecursiveRequired } from '../../common/types';
import { BoardsServiceClient, AttachedBoardsChangeEvent, BoardInstalledEvent, AttachedSerialBoard, Board, Port } from '../../common/protocol/boards-service';
import { BoardsServiceClient, AttachedBoardsChangeEvent, BoardInstalledEvent, AttachedSerialBoard, Board, Port, BoardUninstalledEvent } from '../../common/protocol/boards-service';
import { BoardsConfig } from './boards-config';
@injectable()
@ -16,6 +16,7 @@ export class BoardsServiceClientImpl implements BoardsServiceClient {
protected storageService: LocalStorageService;
protected readonly onBoardInstalledEmitter = new Emitter<BoardInstalledEvent>();
protected readonly onBoardUninstalledEmitter = new Emitter<BoardUninstalledEvent>();
protected readonly onAttachedBoardsChangedEmitter = new Emitter<AttachedBoardsChangeEvent>();
protected readonly onSelectedBoardsConfigChangedEmitter = new Emitter<BoardsConfig.Config>();
@ -31,6 +32,7 @@ export class BoardsServiceClientImpl implements BoardsServiceClient {
readonly onBoardsChanged = this.onAttachedBoardsChangedEmitter.event;
readonly onBoardInstalled = this.onBoardInstalledEmitter.event;
readonly onBoardUninstalled = this.onBoardUninstalledEmitter.event;
readonly onBoardsConfigChanged = this.onSelectedBoardsConfigChangedEmitter.event;
@postConstruct()
@ -87,6 +89,11 @@ export class BoardsServiceClientImpl implements BoardsServiceClient {
this.onBoardInstalledEmitter.fire(event);
}
notifyBoardUninstalled(event: BoardUninstalledEvent): void {
this.logger.info('Board uninstalled: ', JSON.stringify(event));
this.onBoardUninstalledEmitter.fire(event);
}
set boardsConfig(config: BoardsConfig.Config) {
this.logger.info('Board config changed: ', JSON.stringify(config));
this._boardsConfig = config;

View File

@ -19,13 +19,22 @@ export class ComponentListItem<T extends ArduinoComponent> extends React.Compone
await this.props.install(item, this.state.selectedVersion);
}
protected async uninstall(item: T): Promise<void> {
await this.props.uninstall(item);
}
protected onVersionChange(version: Installable.Version) {
this.setState({ selectedVersion: version });
}
render(): React.ReactNode {
const { item, itemRenderer } = this.props;
return itemRenderer.renderItem(Object.assign(this.state, { item }), this.install.bind(this), this.onVersionChange.bind(this));
return itemRenderer.renderItem(
Object.assign(this.state, { item }),
this.install.bind(this),
this.uninstall.bind(this),
this.onVersionChange.bind(this)
);
}
}
@ -35,6 +44,7 @@ export namespace ComponentListItem {
export interface Props<T extends ArduinoComponent> {
readonly item: T;
readonly install: (item: T, version?: Installable.Version) => Promise<void>;
readonly uninstall: (item: T) => Promise<void>;
readonly itemRenderer: ListItemRenderer<T>;
}

View File

@ -31,7 +31,8 @@ export class ComponentList<T extends ArduinoComponent> extends React.Component<C
key={this.props.itemLabel(item)}
item={item}
itemRenderer={this.props.itemRenderer}
install={this.props.install} />
install={this.props.install}
uninstall={this.props.uninstall} />
}
}
@ -43,6 +44,7 @@ export namespace ComponentList {
readonly itemLabel: (item: T) => string;
readonly itemRenderer: ListItemRenderer<T>;
readonly install: (item: T, version?: Installable.Version) => Promise<void>;
readonly uninstall: (item: T) => Promise<void>;
readonly resolveContainer: (element: HTMLElement) => void;
}

View File

@ -1,10 +1,11 @@
import * as React from 'react';
import debounce = require('lodash.debounce');
import { Event } from '@theia/core/lib/common/event';
import { ConfirmDialog } from '@theia/core/lib/browser/dialogs';
import { Searchable } from '../../../common/protocol/searchable';
import { Installable } from '../../../common/protocol/installable';
import { ArduinoComponent } from '../../../common/protocol/arduino-component';
import { InstallationProgressDialog } from '../installation-progress-dialog';
import { InstallationProgressDialog, UninstallationProgressDialog } from '../progress-dialog';
import { SearchBar } from './search-bar';
import { ListWidget } from './list-widget';
import { ComponentList } from './component-list';
@ -59,6 +60,7 @@ export class FilterableListContainer<T extends ArduinoComponent> extends React.C
itemLabel={itemLabel}
itemRenderer={itemRenderer}
install={this.install.bind(this)}
uninstall={this.uninstall.bind(this)}
resolveContainer={resolveContainer}
/>
}
@ -96,6 +98,28 @@ export class FilterableListContainer<T extends ArduinoComponent> extends React.C
}
}
protected async uninstall(item: T): Promise<void> {
const uninstall = await new ConfirmDialog({
title: 'Uninstall',
msg: `Do you want to uninstall ${item.name}?`,
ok: 'Yes',
cancel: 'No'
}).open();
if (!uninstall) {
return;
}
const { installable, searchable, itemLabel } = this.props;
const dialog = new UninstallationProgressDialog(itemLabel(item));
dialog.open();
try {
await installable.uninstall({ item });
const { items } = await searchable.search({ query: this.state.filterText });
this.setState({ items: this.sort(items) });
} finally {
dialog.close();
}
}
}
export namespace FilterableListContainer {

View File

@ -22,15 +22,17 @@ export class ListItemRenderer<T extends ArduinoComponent> {
renderItem(
input: ComponentListItem.State & { item: T },
install: (item: T) => Promise<void>,
uninstall: (item: T) => Promise<void>,
onVersionChange: (version: Installable.Version) => void
): React.ReactNode {
const { item } = input;
const name = <span className='name'>{item.name}</span>;
const author = <span className='author'>{item.author}</span>;
const onClickUninstall = () => uninstall(item);
const installedVersion = !!item.installedVersion && <div className='version-info'>
<span className='version'>Version {item.installedVersion}</span>
<span className='installed'>INSTALLED</span>
<span className='installed' onClick={onClickUninstall} />
</div>;
const summary = <div className='summary'>{item.summary}</div>;

View File

@ -10,3 +10,14 @@ export class InstallationProgressDialog extends AbstractDialog<undefined> {
}
}
export class UninstallationProgressDialog extends AbstractDialog<undefined> {
readonly value = undefined;
constructor(componentName: string) {
super({ title: 'Uninstallation in progress' });
this.contentNode.textContent = `Uninstalling ${componentName}. Please wait...`;
}
}

View File

@ -116,8 +116,9 @@ https://github.com/arduino/arduino-pro-ide/issues/82 */
color: var(--theia-ui-font-color2);
}
.component-list-item .header .installed {
.component-list-item .header .installed:before {
margin-left: 4px;
display: inline-block;
justify-self: end;
background-color: var(--theia-accent-color1);
padding: 2px 4px 2px 4px;
@ -125,6 +126,13 @@ https://github.com/arduino/arduino-pro-ide/issues/82 */
font-weight: bold;
max-height: calc(1em + 4px);
color: var(--theia-inverse-ui-font-color0);
content: 'INSTALLED';
}
.component-list-item .header .installed:hover:before {
background-color: var(--theia-inverse-ui-font-color0);
color: var(--theia-accent-color1);
content: 'UNINSTALL';
}
.component-list-item[min-width~="170px"] .footer {

View File

@ -46,10 +46,15 @@ export interface BoardInstalledEvent {
readonly pkg: Readonly<BoardPackage>;
}
export interface BoardUninstalledEvent {
readonly pkg: Readonly<BoardPackage>;
}
export const BoardsServiceClient = Symbol('BoardsServiceClient');
export interface BoardsServiceClient {
notifyAttachedBoardsChanged(event: AttachedBoardsChangeEvent): void;
notifyBoardInstalled(event: BoardInstalledEvent): void
notifyBoardUninstalled(event: BoardUninstalledEvent): void
}
export const BoardsServicePath = '/services/boards-service';

View File

@ -6,6 +6,11 @@ export interface Installable<T extends ArduinoComponent> {
* If `options.version` is specified, that will be installed. Otherwise, `item.availableVersions[0]`.
*/
install(options: { item: T, version?: Installable.Version }): Promise<void>;
/**
* Uninstalls the given component. It is a NOOP if not installed.
*/
uninstall(options: { item: T }): Promise<void>;
}
export namespace Installable {
export type Version = string;

View File

@ -2,7 +2,7 @@ import * as PQueue from 'p-queue';
import { injectable, inject, postConstruct, named } from 'inversify';
import { ILogger } from '@theia/core/lib/common/logger';
import { BoardsService, AttachedSerialBoard, BoardPackage, Board, AttachedNetworkBoard, BoardsServiceClient, Port } from '../common/protocol/boards-service';
import { PlatformSearchReq, PlatformSearchResp, PlatformInstallReq, PlatformInstallResp, PlatformListReq, PlatformListResp, Platform } from './cli-protocol/commands/core_pb';
import { PlatformSearchReq, PlatformSearchResp, PlatformInstallReq, PlatformInstallResp, PlatformListReq, PlatformListResp, Platform, PlatformUninstallReq } from './cli-protocol/commands/core_pb';
import { CoreClientProvider } from './core-client-provider';
import { BoardListReq, BoardListResp } from './cli-protocol/commands/board_pb';
import { ToolOutputServiceServer } from '../common/protocol/tool-output-service';
@ -288,4 +288,37 @@ export class BoardsServiceImpl implements BoardsService {
console.info("Board installation done", pkg);
}
async uninstall(options: { item: BoardPackage }): Promise<void> {
const pkg = options.item;
const coreClient = await this.coreClientProvider.getClient();
if (!coreClient) {
return;
}
const { client, instance } = coreClient;
const [platform, boardName] = pkg.id.split(":");
const req = new PlatformUninstallReq();
req.setInstance(instance);
req.setArchitecture(boardName);
req.setPlatformPackage(platform);
console.info("Starting board uninstallation", pkg);
const resp = client.platformUninstall(req);
resp.on('data', (r: PlatformInstallResp) => {
const prog = r.getProgress();
if (prog && prog.getFile()) {
this.toolOutputService.publishNewOutput("board uninstall", `uninstalling ${prog.getFile()}\n`)
}
});
await new Promise<void>((resolve, reject) => {
resp.on('end', resolve);
resp.on('error', reject);
});
if (this.client) {
this.client.notifyBoardUninstalled({ pkg });
}
console.info("Board uninstallation done", pkg);
}
}

View File

@ -3,7 +3,7 @@ import { Library, LibraryService } from '../common/protocol/library-service';
import { CoreClientProvider } from './core-client-provider';
import {
LibrarySearchReq, LibrarySearchResp, LibraryListReq, LibraryListResp, LibraryRelease,
InstalledLibrary, LibraryInstallReq, LibraryInstallResp
InstalledLibrary, LibraryInstallReq, LibraryInstallResp, LibraryUninstallReq
} from './cli-protocol/commands/lib_pb';
import { ToolOutputServiceServer } from '../common/protocol/tool-output-service';
import { Installable } from '../common/protocol/installable';
@ -90,6 +90,32 @@ export class LibraryServiceImpl implements LibraryService {
});
}
async uninstall(options: { item: Library }): Promise<void> {
const library = options.item;
const coreClient = await this.coreClientProvider.getClient();
if (!coreClient) {
return;
}
const { client, instance } = coreClient;
const req = new LibraryUninstallReq();
req.setInstance(instance);
req.setName(library.name);
req.setVersion(library.installedVersion!);
const resp = client.libraryInstall(req);
resp.on('data', (r: LibraryInstallResp) => {
const prog = r.getProgress();
if (prog) {
this.toolOutputService.publishNewOutput("library uninstall", `uninstalling ${prog.getFile()}: ${prog.getCompleted()}%\n`)
}
});
await new Promise<void>((resolve, reject) => {
resp.on('end', resolve);
resp.on('error', reject);
});
}
}
function toLibrary(tpl: Partial<Library>, release: LibraryRelease, availableVersions: string[]): Library {