mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-07-12 22:06:34 +00:00
PROEDITOR-46: Added a core auto-installer.
Signed-off-by: Akos Kitta <kittaakos@typefox.io>
This commit is contained in:
parent
fb6785c5d3
commit
de1caf1451
@ -67,6 +67,7 @@ import { TabBarDecoratorService } from '@theia/core/lib/browser/shell/tab-bar-de
|
||||
import { ArduinoTabBarDecoratorService } from './shell/arduino-tab-bar-decorator';
|
||||
import { ProblemManager } from '@theia/markers/lib/browser';
|
||||
import { ArduinoProblemManager } from './markers/arduino-problem-manager';
|
||||
import { BoardsAutoInstaller } from './boards/boards-auto-installer';
|
||||
const ElementQueries = require('css-element-queries/src/ElementQueries');
|
||||
|
||||
export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Unbind, isBound: interfaces.IsBound, rebind: interfaces.Rebind) => {
|
||||
@ -120,6 +121,10 @@ export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Un
|
||||
return client;
|
||||
}).inSingletonScope();
|
||||
|
||||
// boards auto-installer
|
||||
bind(BoardsAutoInstaller).toSelf().inSingletonScope();
|
||||
bind(FrontendApplicationContribution).toService(BoardsAutoInstaller);
|
||||
|
||||
// Boards list widget
|
||||
bind(BoardsListWidget).toSelf();
|
||||
bindViewContribution(bind, BoardsListWidgetFrontendContribution);
|
||||
|
@ -0,0 +1,63 @@
|
||||
import { injectable, inject } from 'inversify';
|
||||
import { MessageService } from '@theia/core/lib/common/message-service';
|
||||
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
|
||||
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 { BoardsConfig } from './boards-config';
|
||||
|
||||
|
||||
/**
|
||||
* Listens on `BoardsConfig.Config` changes, if a board is selected which does not
|
||||
* have the corresponding core installed, it proposes the user to install the core.
|
||||
*/
|
||||
@injectable()
|
||||
export class BoardsAutoInstaller implements FrontendApplicationContribution {
|
||||
|
||||
@inject(MessageService)
|
||||
protected readonly messageService: MessageService;
|
||||
|
||||
@inject(BoardsService)
|
||||
protected readonly boardsService: BoardsService;
|
||||
|
||||
@inject(BoardsServiceClientImpl)
|
||||
protected readonly boardsServiceClient: BoardsServiceClientImpl;
|
||||
|
||||
@inject(BoardsListWidgetFrontendContribution)
|
||||
protected readonly boardsManagerFrontendContribution: BoardsListWidgetFrontendContribution;
|
||||
|
||||
onStart(): void {
|
||||
this.boardsServiceClient.onBoardsConfigChanged(this.ensureCoreExists.bind(this));
|
||||
this.ensureCoreExists(this.boardsServiceClient.boardsConfig);
|
||||
}
|
||||
|
||||
protected ensureCoreExists(config: BoardsConfig.Config): void {
|
||||
const { selectedBoard } = config;
|
||||
if (selectedBoard) {
|
||||
this.boardsService.search({}).then(({ items }) => {
|
||||
const candidates = items
|
||||
.filter(item => item.boards.some(board => Board.sameAs(board, selectedBoard)))
|
||||
.filter(({ installable, installedVersion }) => installable && !installedVersion);
|
||||
for (const candidate of candidates) {
|
||||
// tslint:disable-next-line:max-line-length
|
||||
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?`, 'Yes', 'Install Manually').then(async answer => {
|
||||
if (answer === 'Yes') {
|
||||
const dialog = new InstallationProgressDialog(candidate.name);
|
||||
dialog.open();
|
||||
try {
|
||||
await this.boardsService.install(candidate);
|
||||
} finally {
|
||||
dialog.close();
|
||||
}
|
||||
}
|
||||
if (answer) {
|
||||
this.boardsManagerFrontendContribution.openView({ reveal: true }).then(widget => widget.refresh(candidate.name.toLocaleLowerCase()));
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import debounce = require('lodash.debounce');
|
||||
import { Event } from '@theia/core/lib/common/event';
|
||||
import { Searchable } from '../../../common/protocol/searchable';
|
||||
import { Installable } from '../../../common/protocol/installable';
|
||||
import { InstallationProgressDialog } from '../installation-progress-dialog';
|
||||
@ -20,6 +21,7 @@ export class FilterableListContainer<T> extends React.Component<FilterableListCo
|
||||
componentWillMount(): void {
|
||||
this.search = debounce(this.search, 500);
|
||||
this.handleFilterTextChange('');
|
||||
this.props.filterTextChangeEvent(this.handleFilterTextChange.bind(this));
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
@ -57,8 +59,8 @@ export class FilterableListContainer<T> extends React.Component<FilterableListCo
|
||||
this.setState({ filterText });
|
||||
this.search(filterText);
|
||||
}
|
||||
|
||||
protected search (query: string): void {
|
||||
|
||||
protected search(query: string): void {
|
||||
const { searchable } = this.props;
|
||||
searchable.search({ query: query.trim() }).then(result => {
|
||||
const { items } = result;
|
||||
@ -97,6 +99,7 @@ export namespace FilterableListContainer {
|
||||
readonly itemRenderer: ListItemRenderer<T>;
|
||||
readonly resolveContainer: (element: HTMLElement) => void;
|
||||
readonly resolveFocus: (element: HTMLElement | undefined) => void;
|
||||
readonly filterTextChangeEvent: Event<string>;
|
||||
}
|
||||
|
||||
export interface State<T> {
|
||||
|
@ -2,6 +2,7 @@ import * as React from 'react';
|
||||
import { injectable, postConstruct } from 'inversify';
|
||||
import { Message } from '@phosphor/messaging';
|
||||
import { Deferred } from '@theia/core/lib/common/promise-util';
|
||||
import { Emitter } from '@theia/core/lib/common/event';
|
||||
import { MaybePromise } from '@theia/core/lib/common/types';
|
||||
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
|
||||
import { Installable } from '../../../common/protocol/installable';
|
||||
@ -17,6 +18,7 @@ export abstract class ListWidget<T> extends ReactWidget {
|
||||
*/
|
||||
protected focusNode: HTMLElement | undefined;
|
||||
protected readonly deferredContainer = new Deferred<HTMLElement>();
|
||||
protected readonly filterTextChangeEmitter = new Emitter<string>();
|
||||
|
||||
constructor(protected options: ListWidget.Options<T>) {
|
||||
super();
|
||||
@ -31,6 +33,7 @@ export abstract class ListWidget<T> extends ReactWidget {
|
||||
this.scrollOptions = {
|
||||
suppressScrollX: true
|
||||
}
|
||||
this.toDispose.push(this.filterTextChangeEmitter);
|
||||
}
|
||||
|
||||
@postConstruct()
|
||||
@ -63,7 +66,12 @@ export abstract class ListWidget<T> extends ReactWidget {
|
||||
searchable={this.options.searchable}
|
||||
installable={this.options.installable}
|
||||
itemLabel={this.options.itemLabel}
|
||||
itemRenderer={this.options.itemRenderer} />;
|
||||
itemRenderer={this.options.itemRenderer}
|
||||
filterTextChangeEvent={this.filterTextChangeEmitter.event}/>;
|
||||
}
|
||||
|
||||
refresh(filterText: string): void {
|
||||
this.deferredContainer.promise.then(() => this.filterTextChangeEmitter.fire(filterText));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -59,6 +59,17 @@ export namespace Board {
|
||||
return left.name === right.name && left.fqbn === right.fqbn;
|
||||
}
|
||||
|
||||
export function sameAs(left: Board, right: string | Board): boolean {
|
||||
// How to associate a selected board with one of the available cores: https://typefox.slack.com/archives/CJJHJCJSJ/p1571142327059200
|
||||
// 1. How to use the FQBN if any and infer the package ID from it: https://typefox.slack.com/archives/CJJHJCJSJ/p1571147549069100
|
||||
// 2. How to trim the `/Genuino` from the name: https://arduino.slack.com/archives/CJJHJCJSJ/p1571146951066800?thread_ts=1571142327.059200&cid=CJJHJCJSJ
|
||||
const other = typeof right === 'string' ? { name: right } : right;
|
||||
if (left.fqbn && other.fqbn) {
|
||||
return left.fqbn === other.fqbn;
|
||||
}
|
||||
return left.name.replace('/Genuino', '') === other.name.replace('/Genuino', '');
|
||||
}
|
||||
|
||||
export function compare(left: Board, right: Board): number {
|
||||
let result = left.name.localeCompare(right.name);
|
||||
if (result === 0) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user