Skip to content

Commit 6a73507

Browse files
author
Akos Kitta
committed
Added progress for install/uninstall.
Signed-off-by: Akos Kitta <[email protected]>
1 parent 3fc1ffc commit 6a73507

23 files changed

+378
-211
lines changed

arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ import { WorkspaceService } from './theia/workspace/workspace-service';
4444
import { ArduinoToolbar } from './toolbar/arduino-toolbar';
4545
import { HostedPluginSupport } from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin';
4646
import { FileService } from '@theia/filesystem/lib/browser/file-service';
47-
import { OutputService } from '../common/protocol/output-service';
47+
import { ResponseService } from '../common/protocol/response-service';
4848
import { ArduinoPreferences } from './arduino-preferences';
4949
import { SketchesServiceClientImpl } from '../common/protocol/sketches-service-client-impl';
5050
import { SaveAsSketch } from './contributions/save-as-sketch';
@@ -151,8 +151,8 @@ export class ArduinoFrontendContribution implements FrontendApplicationContribut
151151
@inject(ExecutableService)
152152
protected executableService: ExecutableService;
153153

154-
@inject(OutputService)
155-
protected readonly outputService: OutputService;
154+
@inject(ResponseService)
155+
protected readonly responseService: ResponseService;
156156

157157
@inject(ArduinoPreferences)
158158
protected readonly arduinoPreferences: ArduinoPreferences;

arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts

+11-6
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,8 @@ import { OutputChannelRegistryMainImpl as TheiaOutputChannelRegistryMainImpl, Ou
120120
import { ExecutableService, ExecutableServicePath } from '../common/protocol';
121121
import { MonacoTextModelService as TheiaMonacoTextModelService } from '@theia/monaco/lib/browser/monaco-text-model-service';
122122
import { MonacoTextModelService } from './theia/monaco/monaco-text-model-service';
123-
import { OutputServiceImpl } from './output-service-impl';
124-
import { OutputServicePath, OutputService } from '../common/protocol/output-service';
123+
import { ResponseServiceImpl } from './response-service-impl';
124+
import { ResponseServicePath, ResponseService } from '../common/protocol/response-service';
125125
import { NotificationCenter } from './notification-center';
126126
import { NotificationServicePath, NotificationServiceServer } from '../common/protocol';
127127
import { About } from './contributions/about';
@@ -159,6 +159,8 @@ import { MonacoEditorProvider as TheiaMonacoEditorProvider } from '@theia/monaco
159159
import { DebugEditorModel } from './theia/debug/debug-editor-model';
160160
import { DebugEditorModelFactory } from '@theia/debug/lib/browser/editor/debug-editor-model';
161161
import { StorageWrapper } from './storage-wrapper';
162+
import { NotificationManager } from './theia/messages/notifications-manager';
163+
import { NotificationManager as TheiaNotificationManager } from '@theia/messages/lib/browser/notifications-manager';
162164

163165
const ElementQueries = require('css-element-queries/src/ElementQueries');
164166

@@ -383,11 +385,11 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
383385
Contribution.configure(bind, ArchiveSketch);
384386
Contribution.configure(bind, AddZipLibrary);
385387

386-
bind(OutputServiceImpl).toSelf().inSingletonScope().onActivation(({ container }, outputService) => {
387-
WebSocketConnectionProvider.createProxy(container, OutputServicePath, outputService);
388-
return outputService;
388+
bind(ResponseServiceImpl).toSelf().inSingletonScope().onActivation(({ container }, responseService) => {
389+
WebSocketConnectionProvider.createProxy(container, ResponseServicePath, responseService);
390+
return responseService;
389391
});
390-
bind(OutputService).toService(OutputServiceImpl);
392+
bind(ResponseService).toService(ResponseServiceImpl);
391393

392394
bind(NotificationCenter).toSelf().inSingletonScope();
393395
bind(FrontendApplicationContribution).toService(NotificationCenter);
@@ -439,4 +441,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
439441

440442
bind(StorageWrapper).toSelf().inSingletonScope();
441443
bind(CommandContribution).toService(StorageWrapper);
444+
445+
bind(NotificationManager).toSelf().inSingletonScope();
446+
rebind(TheiaNotificationManager).toService(NotificationManager);
442447
});

arduino-ide-extension/src/browser/boards/boards-auto-installer.ts

+12-8
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ import { FrontendApplicationContribution } from '@theia/core/lib/browser/fronten
44
import { BoardsService, BoardsPackage } from '../../common/protocol/boards-service';
55
import { BoardsServiceProvider } from './boards-service-provider';
66
import { BoardsListWidgetFrontendContribution } from './boards-widget-frontend-contribution';
7-
import { InstallationProgressDialog } from '../widgets/progress-dialog';
87
import { BoardsConfig } from './boards-config';
8+
import { Installable } from '../../common/protocol';
9+
import { ResponseServiceImpl } from '../response-service-impl';
910

1011
/**
1112
* Listens on `BoardsConfig.Config` changes, if a board is selected which does not
@@ -23,6 +24,9 @@ export class BoardsAutoInstaller implements FrontendApplicationContribution {
2324
@inject(BoardsServiceProvider)
2425
protected readonly boardsServiceClient: BoardsServiceProvider;
2526

27+
@inject(ResponseServiceImpl)
28+
protected readonly responseService: ResponseServiceImpl;
29+
2630
@inject(BoardsListWidgetFrontendContribution)
2731
protected readonly boardsManagerFrontendContribution: BoardsListWidgetFrontendContribution;
2832

@@ -42,13 +46,13 @@ export class BoardsAutoInstaller implements FrontendApplicationContribution {
4246
// tslint:disable-next-line:max-line-length
4347
this.messageService.info(`The \`"${candidate.name}"\` core has to be installed for the currently selected \`"${selectedBoard.name}"\` board. Do you want to install it now?`, 'Install Manually', 'Yes').then(async answer => {
4448
if (answer === 'Yes') {
45-
const dialog = new InstallationProgressDialog(candidate.name, candidate.availableVersions[0]);
46-
dialog.open();
47-
try {
48-
await this.boardsService.install({ item: candidate });
49-
} finally {
50-
dialog.close();
51-
}
49+
await Installable.installWithProgress({
50+
installable: this.boardsService,
51+
item: candidate,
52+
messageService: this.messageService,
53+
responseService: this.responseService,
54+
version: candidate.availableVersions[0]
55+
});
5256
}
5357
if (answer) {
5458
this.boardsManagerFrontendContribution.openView({ reveal: true }).then(widget => widget.refresh(candidate.name.toLocaleLowerCase()));

arduino-ide-extension/src/browser/boards/boards-list-widget.ts

+8-3
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,14 @@ export class BoardsListWidget extends ListWidget<BoardsPackage> {
3333
]);
3434
}
3535

36-
async install({ item, version }: { item: BoardsPackage; version: string; }): Promise<void> {
37-
await super.install({ item, version });
38-
this.messageService.info(`Successfully installed platform ${item.name}:${version}.`, { timeout: 3000 });
36+
protected async install({ item, progressId, version }: { item: BoardsPackage, progressId: string, version: string; }): Promise<void> {
37+
await super.install({ item, progressId, version });
38+
this.messageService.info(`Successfully installed platform ${item.name}:${version}`, { timeout: 3000 });
39+
}
40+
41+
protected async uninstall({ item, progressId }: { item: BoardsPackage, progressId: string }): Promise<void> {
42+
await super.uninstall({ item, progressId });
43+
this.messageService.info(`Successfully uninstalled platform ${item.name}:${item.installedVersion}`, { timeout: 3000 });
3944
}
4045

4146
}

arduino-ide-extension/src/browser/contributions/add-zip-library.ts

+15-11
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
import { inject, injectable } from 'inversify';
22
import { remote } from 'electron';
3+
import URI from '@theia/core/lib/common/uri';
4+
import { ConfirmDialog } from '@theia/core/lib/browser/dialogs';
5+
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
36
import { ArduinoMenus } from '../menu/arduino-menus';
7+
import { ResponseServiceImpl } from '../response-service-impl';
8+
import { Installable, LibraryService } from '../../common/protocol';
49
import { SketchContribution, Command, CommandRegistry, MenuModelRegistry } from './contribution';
5-
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
6-
import URI from '@theia/core/lib/common/uri';
7-
import { InstallationProgressDialog } from '../widgets/progress-dialog';
8-
import { LibraryService } from '../../common/protocol';
9-
import { ConfirmDialog } from '@theia/core/lib/browser';
1010

1111
@injectable()
1212
export class AddZipLibrary extends SketchContribution {
1313

1414
@inject(EnvVariablesServer)
1515
protected readonly envVariableServer: EnvVariablesServer;
1616

17+
@inject(ResponseServiceImpl)
18+
protected readonly responseService: ResponseServiceImpl;
19+
1720
@inject(LibraryService)
1821
protected readonly libraryService: LibraryService;
1922

@@ -69,11 +72,14 @@ export class AddZipLibrary extends SketchContribution {
6972
}
7073

7174
private async doInstall(zipUri: string, overwrite?: boolean): Promise<void> {
72-
const dialog = new InstallationProgressDialog('Installing library', 'zip');
7375
try {
74-
this.outputChannelManager.getChannel('Arduino').clear();
75-
dialog.open();
76-
await this.libraryService.installZip({ zipUri, overwrite });
76+
await Installable.doWithProgress({
77+
messageService: this.messageService,
78+
progressText: `Processing ${new URI(zipUri).path.base}`,
79+
responseService: this.responseService,
80+
run: () => this.libraryService.installZip({ zipUri, overwrite })
81+
});
82+
this.messageService.info(`Successfully installed library from ${new URI(zipUri).path.base} archive`, { timeout: 3000 });
7783
} catch (error) {
7884
if (error instanceof Error) {
7985
const match = error.message.match(/library (.*?) already installed/);
@@ -88,8 +94,6 @@ export class AddZipLibrary extends SketchContribution {
8894
}
8995
this.messageService.error(error.toString());
9096
throw error;
91-
} finally {
92-
dialog.close();
9397
}
9498
}
9599

arduino-ide-extension/src/browser/library/library-list-widget.ts

+8-3
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export class LibraryListWidget extends ListWidget<LibraryPackage> {
3737
]);
3838
}
3939

40-
protected async install({ item, version }: { item: LibraryPackage, version: Installable.Version }): Promise<void> {
40+
protected async install({ item, progressId, version }: { item: LibraryPackage, progressId: string, version: Installable.Version }): Promise<void> {
4141
const dependencies = await this.service.listDependencies({ item, version, filterSelf: true });
4242
let installDependencies: boolean | undefined = undefined;
4343
if (dependencies.length) {
@@ -84,11 +84,16 @@ export class LibraryListWidget extends ListWidget<LibraryPackage> {
8484
}
8585

8686
if (typeof installDependencies === 'boolean') {
87-
await this.service.install({ item, version, installDependencies });
88-
this.messageService.info(`Successfully installed library ${item.name}:${version}.`, { timeout: 3000 });
87+
await this.service.install({ item, version, progressId, installDependencies });
88+
this.messageService.info(`Successfully installed library ${item.name}:${version}`, { timeout: 3000 });
8989
}
9090
}
9191

92+
protected async uninstall({ item, progressId }: { item: LibraryPackage, progressId: string }): Promise<void> {
93+
await super.uninstall({ item, progressId });
94+
this.messageService.info(`Successfully uninstalled library ${item.name}:${item.installedVersion}`, { timeout: 3000 });
95+
}
96+
9297
}
9398

9499
class MessageBoxDialog extends AbstractDialog<MessageBoxDialog.Result> {

arduino-ide-extension/src/browser/output-service-impl.ts

-22
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { inject, injectable } from 'inversify';
2+
import { Emitter } from '@theia/core/lib/common/event';
3+
import { OutputContribution } from '@theia/output/lib/browser/output-contribution';
4+
import { OutputChannelManager } from '@theia/output/lib/common/output-channel';
5+
import { ResponseService, OutputMessage, ProgressMessage } from '../common/protocol/response-service';
6+
7+
@injectable()
8+
export class ResponseServiceImpl implements ResponseService {
9+
10+
@inject(OutputContribution)
11+
protected outputContribution: OutputContribution;
12+
13+
@inject(OutputChannelManager)
14+
protected outputChannelManager: OutputChannelManager;
15+
16+
protected readonly progressDidChangeEmitter = new Emitter<ProgressMessage>();
17+
readonly onProgressDidChange = this.progressDidChangeEmitter.event;
18+
19+
appendToOutput(message: OutputMessage): void {
20+
const { chunk } = message;
21+
const channel = this.outputChannelManager.getChannel('Arduino');
22+
channel.show({ preserveFocus: true });
23+
channel.append(chunk);
24+
}
25+
26+
clearArduinoChannel(): void {
27+
this.outputChannelManager.getChannel('Arduino').clear();
28+
}
29+
30+
reportProgress(progress: ProgressMessage): void {
31+
this.progressDidChangeEmitter.fire(progress);
32+
}
33+
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { injectable } from 'inversify';
2+
import { CancellationToken } from '@theia/core/lib/common/cancellation';
3+
import { ProgressMessage, ProgressUpdate } from '@theia/core/lib/common/message-service-protocol';
4+
import { NotificationManager as TheiaNotificationManager } from '@theia/messages/lib/browser/notifications-manager';
5+
6+
@injectable()
7+
export class NotificationManager extends TheiaNotificationManager {
8+
9+
async reportProgress(messageId: string, update: ProgressUpdate, originalMessage: ProgressMessage, cancellationToken: CancellationToken): Promise<void> {
10+
const notification = this.find(messageId);
11+
if (!notification) {
12+
return;
13+
}
14+
if (cancellationToken.isCancellationRequested) {
15+
this.clear(messageId);
16+
} else {
17+
notification.message = originalMessage.text && update.message ? `${originalMessage.text}: ${update.message}` :
18+
originalMessage.text || update?.message || notification.message;
19+
20+
// Unlike in Theia, we allow resetting the progress monitor to zero.
21+
const candidate = this.toPlainProgress(update);
22+
notification.progress = typeof candidate === 'number' ? candidate : notification.progress;
23+
}
24+
this.fireUpdatedEvent();
25+
}
26+
27+
}

arduino-ide-extension/src/browser/widgets/component-list/filterable-list-container.tsx

+20-33
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,15 @@ import debounce = require('lodash.debounce');
33
import { Event } from '@theia/core/lib/common/event';
44
import { CommandService } from '@theia/core/lib/common/command';
55
import { MessageService } from '@theia/core/lib/common/message-service';
6-
import { OutputCommands } from '@theia/output/lib/browser/output-commands';
76
import { ConfirmDialog } from '@theia/core/lib/browser/dialogs';
87
import { Searchable } from '../../../common/protocol/searchable';
98
import { Installable } from '../../../common/protocol/installable';
109
import { ArduinoComponent } from '../../../common/protocol/arduino-component';
11-
import { InstallationProgressDialog, UninstallationProgressDialog } from '../progress-dialog';
1210
import { SearchBar } from './search-bar';
1311
import { ListWidget } from './list-widget';
1412
import { ComponentList } from './component-list';
1513
import { ListItemRenderer } from './list-item-renderer';
14+
import { ResponseServiceImpl } from '../../response-service-impl';
1615

1716
export class FilterableListContainer<T extends ArduinoComponent> extends React.Component<FilterableListContainer.Props<T>, FilterableListContainer.State<T>> {
1817

@@ -84,20 +83,14 @@ export class FilterableListContainer<T extends ArduinoComponent> extends React.C
8483
}
8584

8685
protected async install(item: T, version: Installable.Version): Promise<void> {
87-
const { install, searchable, itemLabel } = this.props;
88-
const dialog = new InstallationProgressDialog(itemLabel(item), version);
89-
try {
90-
dialog.open();
91-
await this.clearArduinoChannel();
92-
await install({ item, version });
93-
const items = await searchable.search({ query: this.state.filterText });
94-
this.setState({ items: this.sort(items) });
95-
} catch (error) {
96-
this.props.messageService.error(error instanceof Error ? error.message : String(error));
97-
throw error;
98-
} finally {
99-
dialog.close();
100-
}
86+
const { install, searchable } = this.props;
87+
await Installable.doWithProgress({
88+
...this.props,
89+
progressText: `Processing ${item.name}:${version}`,
90+
run: ({ progressId }) => install({ item, progressId, version })
91+
});
92+
const items = await searchable.search({ query: this.state.filterText });
93+
this.setState({ items: this.sort(items) });
10194
}
10295

10396
protected async uninstall(item: T): Promise<void> {
@@ -110,21 +103,14 @@ export class FilterableListContainer<T extends ArduinoComponent> extends React.C
110103
if (!ok) {
111104
return;
112105
}
113-
const { uninstall, searchable, itemLabel } = this.props;
114-
const dialog = new UninstallationProgressDialog(itemLabel(item));
115-
try {
116-
await this.clearArduinoChannel();
117-
dialog.open();
118-
await uninstall({ item });
119-
const items = await searchable.search({ query: this.state.filterText });
120-
this.setState({ items: this.sort(items) });
121-
} finally {
122-
dialog.close();
123-
}
124-
}
125-
126-
private async clearArduinoChannel(): Promise<void> {
127-
return this.props.commandService.executeCommand(OutputCommands.CLEAR.id, { name: 'Arduino' });
106+
const { uninstall, searchable } = this.props;
107+
await Installable.doWithProgress({
108+
...this.props,
109+
progressText: `Processing ${item.name}${item.installedVersion ? `:${item.installedVersion}` : ''}`,
110+
run: ({ progressId }) => uninstall({ item, progressId })
111+
});
112+
const items = await searchable.search({ query: this.state.filterText });
113+
this.setState({ items: this.sort(items) });
128114
}
129115

130116
}
@@ -139,9 +125,10 @@ export namespace FilterableListContainer {
139125
readonly resolveContainer: (element: HTMLElement) => void;
140126
readonly resolveFocus: (element: HTMLElement | undefined) => void;
141127
readonly filterTextChangeEvent: Event<string | undefined>;
142-
readonly install: ({ item, version }: { item: T, version: Installable.Version }) => Promise<void>;
143-
readonly uninstall: ({ item }: { item: T }) => Promise<void>;
144128
readonly messageService: MessageService;
129+
readonly responseService: ResponseServiceImpl;
130+
readonly install: ({ item, progressId, version }: { item: T, progressId: string, version: Installable.Version }) => Promise<void>;
131+
readonly uninstall: ({ item, progressId }: { item: T, progressId: string }) => Promise<void>;
145132
readonly commandService: CommandService;
146133
}
147134

0 commit comments

Comments
 (0)