mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-06-08 05:06:33 +00:00
workaround for non-unique names.
Fine tuned the port unnselection when attached boards change. This should make sure we do not have to `await` for the attached boards from the backend. Signed-off-by: Akos Kitta <kittaakos@typefox.io>
This commit is contained in:
parent
0dc45daf01
commit
66f429c478
@ -1,6 +1,6 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { DisposableCollection } from '@theia/core';
|
import { DisposableCollection } from '@theia/core';
|
||||||
import { BoardsService, Board, AttachedSerialBoard } from '../../common/protocol/boards-service';
|
import { BoardsService, Board, AttachedSerialBoard, AttachedBoardsChangeEvent } from '../../common/protocol/boards-service';
|
||||||
import { BoardsServiceClientImpl } from './boards-service-client-impl';
|
import { BoardsServiceClientImpl } from './boards-service-client-impl';
|
||||||
|
|
||||||
export namespace BoardsConfig {
|
export namespace BoardsConfig {
|
||||||
@ -18,7 +18,7 @@ export namespace BoardsConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface State extends Config {
|
export interface State extends Config {
|
||||||
searchResults: Board[];
|
searchResults: Array<Board & { packageName: string }>;
|
||||||
knownPorts: string[];
|
knownPorts: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,13 +26,15 @@ export namespace BoardsConfig {
|
|||||||
|
|
||||||
export abstract class Item<T> extends React.Component<{
|
export abstract class Item<T> extends React.Component<{
|
||||||
item: T,
|
item: T,
|
||||||
name: string,
|
label: string,
|
||||||
selected: boolean,
|
selected: boolean,
|
||||||
onClick: (item: T) => void,
|
onClick: (item: T) => void,
|
||||||
missing?: boolean }> {
|
missing?: boolean,
|
||||||
|
detail?: string
|
||||||
|
}> {
|
||||||
|
|
||||||
render(): React.ReactNode {
|
render(): React.ReactNode {
|
||||||
const { selected, name, missing } = this.props;
|
const { selected, label, missing, detail } = this.props;
|
||||||
const classNames = ['item'];
|
const classNames = ['item'];
|
||||||
if (selected) {
|
if (selected) {
|
||||||
classNames.push('selected');
|
classNames.push('selected');
|
||||||
@ -40,9 +42,12 @@ export abstract class Item<T> extends React.Component<{
|
|||||||
if (missing === true) {
|
if (missing === true) {
|
||||||
classNames.push('missing')
|
classNames.push('missing')
|
||||||
}
|
}
|
||||||
return <div onClick={this.onClick} className={classNames.join(' ')}>
|
return <div onClick={this.onClick} className={classNames.join(' ')} title={`${label}${!detail ? '' : detail}`}>
|
||||||
{name}
|
<div className='label'>
|
||||||
{selected ? <i className='fa fa-check'></i> : ''}
|
{label}
|
||||||
|
</div>
|
||||||
|
{!detail ? '' : <div className='detail'>{detail}</div>}
|
||||||
|
{!selected ? '' : <div className='selected-icon'><i className='fa fa-check'/></div>}
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,7 +77,7 @@ export class BoardsConfig extends React.Component<BoardsConfig.Props, BoardsConf
|
|||||||
this.props.boardsService.getAttachedBoards().then(({ boards }) => this.updatePorts(boards));
|
this.props.boardsService.getAttachedBoards().then(({ boards }) => this.updatePorts(boards));
|
||||||
const { boardsServiceClient: client } = this.props;
|
const { boardsServiceClient: client } = this.props;
|
||||||
this.toDispose.pushAll([
|
this.toDispose.pushAll([
|
||||||
client.onBoardsChanged(event => this.updatePorts(event.newState.boards)),
|
client.onBoardsChanged(event => this.updatePorts(event.newState.boards, AttachedBoardsChangeEvent.diff(event).detached)),
|
||||||
client.onBoardsConfigChanged(({ selectedBoard, selectedPort }) => {
|
client.onBoardsConfigChanged(({ selectedBoard, selectedPort }) => {
|
||||||
this.setState({ selectedBoard, selectedPort }, () => this.fireConfigChanged());
|
this.setState({ selectedBoard, selectedPort }, () => this.fireConfigChanged());
|
||||||
})
|
})
|
||||||
@ -96,23 +101,24 @@ export class BoardsConfig extends React.Component<BoardsConfig.Props, BoardsConf
|
|||||||
this.queryBoards({ query }).then(({ searchResults }) => this.setState({ searchResults }));
|
this.queryBoards({ query }).then(({ searchResults }) => this.setState({ searchResults }));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected updatePorts = (boards: Board[] = []) => {
|
protected updatePorts = (boards: Board[] = [], detachedBoards: Board[] = []) => {
|
||||||
this.queryPorts(Promise.resolve({ boards })).then(({ knownPorts }) => {
|
this.queryPorts(Promise.resolve({ boards })).then(({ knownPorts }) => {
|
||||||
let { selectedPort } = this.state;
|
let { selectedPort } = this.state;
|
||||||
if (!!selectedPort && knownPorts.indexOf(selectedPort) === -1) {
|
const removedPorts = detachedBoards.filter(AttachedSerialBoard.is).map(({ port }) => port);
|
||||||
|
if (!!selectedPort && removedPorts.indexOf(selectedPort) === -1) {
|
||||||
selectedPort = undefined;
|
selectedPort = undefined;
|
||||||
}
|
}
|
||||||
this.setState({ knownPorts, selectedPort }, () => this.fireConfigChanged());
|
this.setState({ knownPorts, selectedPort }, () => this.fireConfigChanged());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected queryBoards = (options: { query?: string } = {}): Promise<{ searchResults: Board[] }> => {
|
protected queryBoards = (options: { query?: string } = {}): Promise<{ searchResults: Array<Board & { packageName: string }> }> => {
|
||||||
const { boardsService } = this.props;
|
const { boardsService } = this.props;
|
||||||
const query = (options.query || '').toLocaleLowerCase();
|
const query = (options.query || '').toLocaleLowerCase();
|
||||||
return new Promise<{ searchResults: Board[] }>(resolve => {
|
return new Promise<{ searchResults: Array<Board & { packageName: string }> }>(resolve => {
|
||||||
boardsService.search(options)
|
boardsService.search(options)
|
||||||
.then(({ items }) => items
|
.then(({ items }) => items
|
||||||
.map(item => item.boards)
|
.map(item => item.boards.map(board => ({ ...board, packageName: item.name })))
|
||||||
.reduce((acc, curr) => acc.concat(curr), [])
|
.reduce((acc, curr) => acc.concat(curr), [])
|
||||||
.filter(board => board.name.toLocaleLowerCase().indexOf(query) !== -1)
|
.filter(board => board.name.toLocaleLowerCase().indexOf(query) !== -1)
|
||||||
.sort(Board.compare))
|
.sort(Board.compare))
|
||||||
@ -139,7 +145,7 @@ export class BoardsConfig extends React.Component<BoardsConfig.Props, BoardsConf
|
|||||||
this.setState({ selectedPort }, () => this.fireConfigChanged());
|
this.setState({ selectedPort }, () => this.fireConfigChanged());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected selectBoard = (selectedBoard: Board | undefined) => {
|
protected selectBoard = (selectedBoard: Board & { packageName: string } | undefined) => {
|
||||||
this.setState({ selectedBoard }, () => this.fireConfigChanged());
|
this.setState({ selectedBoard }, () => this.fireConfigChanged());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,18 +172,40 @@ export class BoardsConfig extends React.Component<BoardsConfig.Props, BoardsConf
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected renderBoards(): React.ReactNode {
|
protected renderBoards(): React.ReactNode {
|
||||||
const { selectedBoard } = this.state;
|
const { selectedBoard, searchResults } = this.state;
|
||||||
|
// Board names are not unique. We show the corresponding core name as a detail.
|
||||||
|
// https://github.com/arduino/arduino-cli/pull/294#issuecomment-513764948
|
||||||
|
const distinctBoardNames = new Map<string, number>();
|
||||||
|
for (const { name } of searchResults) {
|
||||||
|
const counter = distinctBoardNames.get(name) || 0;
|
||||||
|
distinctBoardNames.set(name, counter + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Due to the non-unique board names, we have to check the package name as well.
|
||||||
|
const selected = (board: Board & { packageName: string }) => {
|
||||||
|
if (!!selectedBoard) {
|
||||||
|
if (Board.equals(board, selectedBoard)) {
|
||||||
|
if ('packageName' in selectedBoard) {
|
||||||
|
return board.packageName === (selectedBoard as any).packageName;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return <React.Fragment>
|
return <React.Fragment>
|
||||||
<div className='search'>
|
<div className='search'>
|
||||||
<input type='search' placeholder='SEARCH BOARD' onChange={this.updateBoards} ref={this.focusNodeSet} />
|
<input type='search' placeholder='SEARCH BOARD' onChange={this.updateBoards} ref={this.focusNodeSet} />
|
||||||
<i className='fa fa-search'></i>
|
<i className='fa fa-search'></i>
|
||||||
</div>
|
</div>
|
||||||
<div className='boards list'>
|
<div className='boards list'>
|
||||||
{this.state.searchResults.map((board, index) => <Item<Board>
|
{this.state.searchResults.map(board => <Item<Board & { packageName: string }>
|
||||||
key={`${board.name}-${index}`}
|
key={`${board.name}-${board.packageName}`}
|
||||||
item={board}
|
item={board}
|
||||||
name={board.name}
|
label={board.name}
|
||||||
selected={!!selectedBoard && Board.equals(board, selectedBoard)}
|
detail={(distinctBoardNames.get(board.name) || 0) > 1 ? ` - ${board.packageName}` : undefined}
|
||||||
|
selected={selected(board)}
|
||||||
onClick={this.selectBoard}
|
onClick={this.selectBoard}
|
||||||
missing={!Board.installed(board)}
|
missing={!Board.installed(board)}
|
||||||
/>)}
|
/>)}
|
||||||
@ -197,7 +225,7 @@ export class BoardsConfig extends React.Component<BoardsConfig.Props, BoardsConf
|
|||||||
{this.state.knownPorts.map(port => <Item<string>
|
{this.state.knownPorts.map(port => <Item<string>
|
||||||
key={port}
|
key={port}
|
||||||
item={port}
|
item={port}
|
||||||
name={port}
|
label={port}
|
||||||
selected={this.state.selectedPort === port}
|
selected={this.state.selectedPort === port}
|
||||||
onClick={this.selectPort}
|
onClick={this.selectPort}
|
||||||
/>)}
|
/>)}
|
||||||
|
@ -83,7 +83,20 @@ div#select-board-dialog .selectBoardContainer .body .list .item.selected i{
|
|||||||
#select-board-dialog .selectBoardContainer .body .list .item {
|
#select-board-dialog .selectBoardContainer .body .list .item {
|
||||||
padding: 10px 5px 10px 10px;
|
padding: 10px 5px 10px 10px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
#select-board-dialog .selectBoardContainer .body .list .item .selected-icon {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#select-board-dialog .selectBoardContainer .body .list .item .detail {
|
||||||
|
font-size: var(--theia-ui-font-size1);
|
||||||
|
color: var(--theia-disabled-color0);
|
||||||
|
width: 155px; /* used heuristics for the calculation */
|
||||||
|
white-space: pre;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
#select-board-dialog .selectBoardContainer .body .list .item.missing {
|
#select-board-dialog .selectBoardContainer .body .list .item.missing {
|
||||||
|
@ -7,6 +7,21 @@ export interface AttachedBoardsChangeEvent {
|
|||||||
readonly oldState: Readonly<{ boards: Board[] }>;
|
readonly oldState: Readonly<{ boards: Board[] }>;
|
||||||
readonly newState: Readonly<{ boards: Board[] }>;
|
readonly newState: Readonly<{ boards: Board[] }>;
|
||||||
}
|
}
|
||||||
|
export namespace AttachedBoardsChangeEvent {
|
||||||
|
|
||||||
|
export function diff(event: AttachedBoardsChangeEvent): Readonly<{ attached: Board[], detached: Board[] }> {
|
||||||
|
const diff = <T>(left: T[], right: T[]) => {
|
||||||
|
return left.filter(item => right.indexOf(item) === -1);
|
||||||
|
}
|
||||||
|
const { boards: newBoards } = event.newState;
|
||||||
|
const { boards: oldBoards } = event.oldState;
|
||||||
|
return {
|
||||||
|
detached: diff(oldBoards, newBoards),
|
||||||
|
attached: diff(newBoards, oldBoards)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
export interface BoardInstalledEvent {
|
export interface BoardInstalledEvent {
|
||||||
readonly pkg: Readonly<BoardPackage>;
|
readonly pkg: Readonly<BoardPackage>;
|
||||||
@ -34,10 +49,6 @@ export interface Board {
|
|||||||
fqbn?: string
|
fqbn?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Port {
|
|
||||||
port?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export namespace Board {
|
export namespace Board {
|
||||||
|
|
||||||
export function is(board: any): board is Board {
|
export function is(board: any): board is Board {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user