1
1
import * as React from 'react' ;
2
2
import { DisposableCollection } from '@theia/core' ;
3
- import { BoardsService , Board , AttachedSerialBoard } from '../../common/protocol/boards-service' ;
3
+ import { BoardsService , Board , AttachedSerialBoard , AttachedBoardsChangeEvent } from '../../common/protocol/boards-service' ;
4
4
import { BoardsServiceClientImpl } from './boards-service-client-impl' ;
5
5
6
6
export namespace BoardsConfig {
@@ -18,31 +18,36 @@ export namespace BoardsConfig {
18
18
}
19
19
20
20
export interface State extends Config {
21
- searchResults : Board [ ] ;
21
+ searchResults : Array < Board & { packageName : string } > ;
22
22
knownPorts : string [ ] ;
23
23
}
24
24
25
25
}
26
26
27
27
export abstract class Item < T > extends React . Component < {
28
28
item : T ,
29
- name : string ,
29
+ label : string ,
30
30
selected : boolean ,
31
31
onClick : ( item : T ) => void ,
32
- missing ?: boolean } > {
32
+ missing ?: boolean ,
33
+ detail ?: string
34
+ } > {
33
35
34
36
render ( ) : React . ReactNode {
35
- const { selected, name , missing } = this . props ;
37
+ const { selected, label , missing, detail } = this . props ;
36
38
const classNames = [ 'item' ] ;
37
39
if ( selected ) {
38
40
classNames . push ( 'selected' ) ;
39
41
}
40
42
if ( missing === true ) {
41
43
classNames . push ( 'missing' )
42
44
}
43
- return < div onClick = { this . onClick } className = { classNames . join ( ' ' ) } >
44
- { name }
45
- { selected ? < i className = 'fa fa-check' > </ i > : '' }
45
+ return < div onClick = { this . onClick } className = { classNames . join ( ' ' ) } title = { `${ label } ${ ! detail ? '' : detail } ` } >
46
+ < div className = 'label' >
47
+ { label }
48
+ </ div >
49
+ { ! detail ? '' : < div className = 'detail' > { detail } </ div > }
50
+ { ! selected ? '' : < div className = 'selected-icon' > < i className = 'fa fa-check' /> </ div > }
46
51
</ div > ;
47
52
}
48
53
@@ -72,7 +77,7 @@ export class BoardsConfig extends React.Component<BoardsConfig.Props, BoardsConf
72
77
this . props . boardsService . getAttachedBoards ( ) . then ( ( { boards } ) => this . updatePorts ( boards ) ) ;
73
78
const { boardsServiceClient : client } = this . props ;
74
79
this . toDispose . pushAll ( [
75
- client . onBoardsChanged ( event => this . updatePorts ( event . newState . boards ) ) ,
80
+ client . onBoardsChanged ( event => this . updatePorts ( event . newState . boards , AttachedBoardsChangeEvent . diff ( event ) . detached ) ) ,
76
81
client . onBoardsConfigChanged ( ( { selectedBoard, selectedPort } ) => {
77
82
this . setState ( { selectedBoard, selectedPort } , ( ) => this . fireConfigChanged ( ) ) ;
78
83
} )
@@ -96,23 +101,24 @@ export class BoardsConfig extends React.Component<BoardsConfig.Props, BoardsConf
96
101
this . queryBoards ( { query } ) . then ( ( { searchResults } ) => this . setState ( { searchResults } ) ) ;
97
102
}
98
103
99
- protected updatePorts = ( boards : Board [ ] = [ ] ) => {
104
+ protected updatePorts = ( boards : Board [ ] = [ ] , detachedBoards : Board [ ] = [ ] ) => {
100
105
this . queryPorts ( Promise . resolve ( { boards } ) ) . then ( ( { knownPorts } ) => {
101
106
let { selectedPort } = this . state ;
102
- if ( ! ! selectedPort && knownPorts . indexOf ( selectedPort ) === - 1 ) {
107
+ const removedPorts = detachedBoards . filter ( AttachedSerialBoard . is ) . map ( ( { port } ) => port ) ;
108
+ if ( ! ! selectedPort && removedPorts . indexOf ( selectedPort ) === - 1 ) {
103
109
selectedPort = undefined ;
104
110
}
105
111
this . setState ( { knownPorts, selectedPort } , ( ) => this . fireConfigChanged ( ) ) ;
106
112
} ) ;
107
113
}
108
114
109
- protected queryBoards = ( options : { query ?: string } = { } ) : Promise < { searchResults : Board [ ] } > => {
115
+ protected queryBoards = ( options : { query ?: string } = { } ) : Promise < { searchResults : Array < Board & { packageName : string } > } > => {
110
116
const { boardsService } = this . props ;
111
117
const query = ( options . query || '' ) . toLocaleLowerCase ( ) ;
112
- return new Promise < { searchResults : Board [ ] } > ( resolve => {
118
+ return new Promise < { searchResults : Array < Board & { packageName : string } > } > ( resolve => {
113
119
boardsService . search ( options )
114
120
. then ( ( { items } ) => items
115
- . map ( item => item . boards )
121
+ . map ( item => item . boards . map ( board => ( { ... board , packageName : item . name } ) ) )
116
122
. reduce ( ( acc , curr ) => acc . concat ( curr ) , [ ] )
117
123
. filter ( board => board . name . toLocaleLowerCase ( ) . indexOf ( query ) !== - 1 )
118
124
. sort ( Board . compare ) )
@@ -139,7 +145,7 @@ export class BoardsConfig extends React.Component<BoardsConfig.Props, BoardsConf
139
145
this . setState ( { selectedPort } , ( ) => this . fireConfigChanged ( ) ) ;
140
146
}
141
147
142
- protected selectBoard = ( selectedBoard : Board | undefined ) => {
148
+ protected selectBoard = ( selectedBoard : Board & { packageName : string } | undefined ) => {
143
149
this . setState ( { selectedBoard } , ( ) => this . fireConfigChanged ( ) ) ;
144
150
}
145
151
@@ -166,18 +172,40 @@ export class BoardsConfig extends React.Component<BoardsConfig.Props, BoardsConf
166
172
}
167
173
168
174
protected renderBoards ( ) : React . ReactNode {
169
- const { selectedBoard } = this . state ;
175
+ const { selectedBoard, searchResults } = this . state ;
176
+ // Board names are not unique. We show the corresponding core name as a detail.
177
+ // https://github.com/arduino/arduino-cli/pull/294#issuecomment-513764948
178
+ const distinctBoardNames = new Map < string , number > ( ) ;
179
+ for ( const { name } of searchResults ) {
180
+ const counter = distinctBoardNames . get ( name ) || 0 ;
181
+ distinctBoardNames . set ( name , counter + 1 ) ;
182
+ }
183
+
184
+ // Due to the non-unique board names, we have to check the package name as well.
185
+ const selected = ( board : Board & { packageName : string } ) => {
186
+ if ( ! ! selectedBoard ) {
187
+ if ( Board . equals ( board , selectedBoard ) ) {
188
+ if ( 'packageName' in selectedBoard ) {
189
+ return board . packageName === ( selectedBoard as any ) . packageName ;
190
+ }
191
+ return true ;
192
+ }
193
+ }
194
+ return false ;
195
+ }
196
+
170
197
return < React . Fragment >
171
198
< div className = 'search' >
172
199
< input type = 'search' placeholder = 'SEARCH BOARD' onChange = { this . updateBoards } ref = { this . focusNodeSet } />
173
200
< i className = 'fa fa-search' > </ i >
174
201
</ div >
175
202
< div className = 'boards list' >
176
- { this . state . searchResults . map ( ( board , index ) => < Item < Board >
177
- key = { `${ board . name } -${ index } ` }
203
+ { this . state . searchResults . map ( board => < Item < Board & { packageName : string } >
204
+ key = { `${ board . name } -${ board . packageName } ` }
178
205
item = { board }
179
- name = { board . name }
180
- selected = { ! ! selectedBoard && Board . equals ( board , selectedBoard ) }
206
+ label = { board . name }
207
+ detail = { ( distinctBoardNames . get ( board . name ) || 0 ) > 1 ? ` - ${ board . packageName } ` : undefined }
208
+ selected = { selected ( board ) }
181
209
onClick = { this . selectBoard }
182
210
missing = { ! Board . installed ( board ) }
183
211
/> ) }
@@ -197,7 +225,7 @@ export class BoardsConfig extends React.Component<BoardsConfig.Props, BoardsConf
197
225
{ this . state . knownPorts . map ( port => < Item < string >
198
226
key = { port }
199
227
item = { port }
200
- name = { port }
228
+ label = { port }
201
229
selected = { this . state . selectedPort === port }
202
230
onClick = { this . selectPort }
203
231
/> ) }
0 commit comments