1
1
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application' ;
2
+ import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state' ;
2
3
import { StorageService } from '@theia/core/lib/browser/storage-service' ;
4
+ import type {
5
+ Command ,
6
+ CommandContribution ,
7
+ CommandRegistry ,
8
+ } from '@theia/core/lib/common/command' ;
3
9
import { DisposableCollection } from '@theia/core/lib/common/disposable' ;
4
10
import { Emitter , Event } from '@theia/core/lib/common/event' ;
5
11
import { ILogger } from '@theia/core/lib/common/logger' ;
6
- import { deepClone } from '@theia/core/lib/common/objects' ;
12
+ import { deepClone , deepFreeze } from '@theia/core/lib/common/objects' ;
7
13
import { inject , injectable , named } from '@theia/core/shared/inversify' ;
8
14
import {
9
15
BoardDetails ,
10
16
BoardsService ,
11
17
ConfigOption ,
12
18
Programmer ,
19
+ isBoardIdentifierChangeEvent ,
13
20
} from '../../common/protocol' ;
14
21
import { notEmpty } from '../../common/utils' ;
22
+ import type {
23
+ StartupTask ,
24
+ StartupTaskProvider ,
25
+ } from '../../electron-common/startup-task' ;
15
26
import { NotificationCenter } from '../notification-center' ;
27
+ import { BoardsServiceProvider } from './boards-service-provider' ;
16
28
17
29
@injectable ( )
18
- export class BoardsDataStore implements FrontendApplicationContribution {
30
+ export class BoardsDataStore
31
+ implements
32
+ FrontendApplicationContribution ,
33
+ StartupTaskProvider ,
34
+ CommandContribution
35
+ {
19
36
@inject ( ILogger )
20
37
@named ( 'store' )
21
38
private readonly logger : ILogger ;
@@ -28,44 +45,110 @@ export class BoardsDataStore implements FrontendApplicationContribution {
28
45
// In other words, store the data (such as the board configs) per sketch, not per IDE2 installation. https://github.com/arduino/arduino-ide/issues/2240
29
46
@inject ( StorageService )
30
47
private readonly storageService : StorageService ;
48
+ @inject ( BoardsServiceProvider )
49
+ private readonly boardsServiceProvider : BoardsServiceProvider ;
50
+ @inject ( FrontendApplicationStateService )
51
+ private readonly appStateService : FrontendApplicationStateService ;
31
52
32
- private readonly onChangedEmitter = new Emitter < string [ ] > ( ) ;
33
- private readonly toDispose = new DisposableCollection ( this . onChangedEmitter ) ;
53
+ private readonly onDidChangeEmitter =
54
+ new Emitter < BoardsDataStoreChangeEvent > ( ) ;
55
+ private readonly toDispose = new DisposableCollection (
56
+ this . onDidChangeEmitter
57
+ ) ;
58
+ private _selectedBoardData : BoardsDataStoreChange | undefined ;
34
59
35
60
onStart ( ) : void {
36
- this . toDispose . push (
61
+ this . toDispose . pushAll ( [
62
+ this . boardsServiceProvider . onBoardsConfigDidChange ( ( event ) => {
63
+ if ( isBoardIdentifierChangeEvent ( event ) ) {
64
+ this . updateSelectedBoardData ( event . selectedBoard ?. fqbn ) ;
65
+ }
66
+ } ) ,
37
67
this . notificationCenter . onPlatformDidInstall ( async ( { item } ) => {
38
- const dataDidChangePerFqbn : string [ ] = [ ] ;
39
- for ( const fqbn of item . boards
68
+ const boardsWithFqbn = item . boards
40
69
. map ( ( { fqbn } ) => fqbn )
41
- . filter ( notEmpty )
42
- . filter ( ( fqbn ) => ! ! fqbn ) ) {
70
+ . filter ( notEmpty ) ;
71
+ const changes : BoardsDataStoreChange [ ] = [ ] ;
72
+ for ( const fqbn of boardsWithFqbn ) {
43
73
const key = this . getStorageKey ( fqbn ) ;
44
- let data = await this . storageService . getData < ConfigOption [ ] > ( key ) ;
45
- if ( ! data || ! data . length ) {
46
- const details = await this . getBoardDetailsSafe ( fqbn ) ;
47
- if ( details ) {
48
- data = details . configOptions ;
49
- if ( data . length ) {
50
- await this . storageService . setData ( key , data ) ;
51
- dataDidChangePerFqbn . push ( fqbn ) ;
52
- }
53
- }
74
+ const storedData =
75
+ await this . storageService . getData < BoardsDataStore . Data > ( key ) ;
76
+ if ( ! storedData ) {
77
+ // if not previously value is available for the board, do not update the cache
78
+ continue ;
79
+ }
80
+ const details = await this . getBoardDetailsSafe ( fqbn ) ;
81
+ if ( details ) {
82
+ const data = createDataStoreEntry ( details ) ;
83
+ await this . storageService . setData ( key , data ) ;
84
+ changes . push ( { fqbn, data } ) ;
54
85
}
55
86
}
56
- if ( dataDidChangePerFqbn . length ) {
57
- this . fireChanged ( ...dataDidChangePerFqbn ) ;
87
+ if ( changes . length ) {
88
+ this . fireChanged ( ...changes ) ;
58
89
}
59
- } )
90
+ } ) ,
91
+ ] ) ;
92
+
93
+ Promise . all ( [
94
+ this . boardsServiceProvider . ready ,
95
+ this . appStateService . reachedState ( 'ready' ) ,
96
+ ] ) . then ( ( ) =>
97
+ this . updateSelectedBoardData (
98
+ this . boardsServiceProvider . boardsConfig . selectedBoard ?. fqbn
99
+ )
60
100
) ;
61
101
}
62
102
103
+ private async getSelectedBoardData (
104
+ fqbn : string | undefined
105
+ ) : Promise < BoardsDataStoreChange | undefined > {
106
+ if ( ! fqbn ) {
107
+ return undefined ;
108
+ } else {
109
+ const data = await this . getData ( fqbn ) ;
110
+ if ( data === BoardsDataStore . Data . EMPTY ) {
111
+ return undefined ;
112
+ }
113
+ return { fqbn, data } ;
114
+ }
115
+ }
116
+
117
+ private async updateSelectedBoardData (
118
+ fqbn : string | undefined
119
+ ) : Promise < void > {
120
+ this . _selectedBoardData = await this . getSelectedBoardData ( fqbn ) ;
121
+ }
122
+
63
123
onStop ( ) : void {
64
124
this . toDispose . dispose ( ) ;
65
125
}
66
126
67
- get onChanged ( ) : Event < string [ ] > {
68
- return this . onChangedEmitter . event ;
127
+ registerCommands ( registry : CommandRegistry ) : void {
128
+ registry . registerCommand ( USE_INHERITED_DATA , {
129
+ execute : async ( arg : unknown ) => {
130
+ if ( isBoardsDataStoreChange ( arg ) ) {
131
+ await this . setData ( arg ) ;
132
+ this . fireChanged ( arg ) ;
133
+ }
134
+ } ,
135
+ } ) ;
136
+ }
137
+
138
+ tasks ( ) : StartupTask [ ] {
139
+ if ( ! this . _selectedBoardData ) {
140
+ return [ ] ;
141
+ }
142
+ return [
143
+ {
144
+ command : USE_INHERITED_DATA . id ,
145
+ args : [ this . _selectedBoardData ] ,
146
+ } ,
147
+ ] ;
148
+ }
149
+
150
+ get onDidChange ( ) : Event < BoardsDataStoreChangeEvent > {
151
+ return this . onDidChangeEmitter . event ;
69
152
}
70
153
71
154
async appendConfigToFqbn (
@@ -84,22 +167,19 @@ export class BoardsDataStore implements FrontendApplicationContribution {
84
167
}
85
168
86
169
const key = this . getStorageKey ( fqbn ) ;
87
- let data = await this . storageService . getData <
170
+ const storedData = await this . storageService . getData <
88
171
BoardsDataStore . Data | undefined
89
172
> ( key , undefined ) ;
90
- if ( BoardsDataStore . Data . is ( data ) ) {
91
- return data ;
173
+ if ( BoardsDataStore . Data . is ( storedData ) ) {
174
+ return storedData ;
92
175
}
93
176
94
177
const boardDetails = await this . getBoardDetailsSafe ( fqbn ) ;
95
178
if ( ! boardDetails ) {
96
179
return BoardsDataStore . Data . EMPTY ;
97
180
}
98
181
99
- data = {
100
- configOptions : boardDetails . configOptions ,
101
- programmers : boardDetails . programmers ,
102
- } ;
182
+ const data = createDataStoreEntry ( boardDetails ) ;
103
183
await this . storageService . setData ( key , data ) ;
104
184
return data ;
105
185
}
@@ -111,17 +191,15 @@ export class BoardsDataStore implements FrontendApplicationContribution {
111
191
fqbn : string ;
112
192
selectedProgrammer : Programmer ;
113
193
} ) : Promise < boolean > {
114
- const data = deepClone ( await this . getData ( fqbn ) ) ;
115
- const { programmers } = data ;
194
+ const storedData = deepClone ( await this . getData ( fqbn ) ) ;
195
+ const { programmers } = storedData ;
116
196
if ( ! programmers . find ( ( p ) => Programmer . equals ( selectedProgrammer , p ) ) ) {
117
197
return false ;
118
198
}
119
199
120
- await this . setData ( {
121
- fqbn,
122
- data : { ...data , selectedProgrammer } ,
123
- } ) ;
124
- this . fireChanged ( fqbn ) ;
200
+ const data = { ...storedData , selectedProgrammer } ;
201
+ await this . setData ( { fqbn, data } ) ;
202
+ this . fireChanged ( { fqbn, data } ) ;
125
203
return true ;
126
204
}
127
205
@@ -153,17 +231,12 @@ export class BoardsDataStore implements FrontendApplicationContribution {
153
231
return false ;
154
232
}
155
233
await this . setData ( { fqbn, data } ) ;
156
- this . fireChanged ( fqbn ) ;
234
+ this . fireChanged ( { fqbn, data } ) ;
157
235
return true ;
158
236
}
159
237
160
- protected async setData ( {
161
- fqbn,
162
- data,
163
- } : {
164
- fqbn : string ;
165
- data : BoardsDataStore . Data ;
166
- } ) : Promise < void > {
238
+ protected async setData ( change : BoardsDataStoreChange ) : Promise < void > {
239
+ const { fqbn, data } = change ;
167
240
const key = this . getStorageKey ( fqbn ) ;
168
241
return this . storageService . setData ( key , data ) ;
169
242
}
@@ -176,7 +249,7 @@ export class BoardsDataStore implements FrontendApplicationContribution {
176
249
fqbn : string
177
250
) : Promise < BoardDetails | undefined > {
178
251
try {
179
- const details = this . boardsService . getBoardDetails ( { fqbn } ) ;
252
+ const details = await this . boardsService . getBoardDetails ( { fqbn } ) ;
180
253
return details ;
181
254
} catch ( err ) {
182
255
if (
@@ -197,8 +270,8 @@ export class BoardsDataStore implements FrontendApplicationContribution {
197
270
}
198
271
}
199
272
200
- protected fireChanged ( ...fqbn : string [ ] ) : void {
201
- this . onChangedEmitter . fire ( fqbn ) ;
273
+ protected fireChanged ( ...changes : BoardsDataStoreChange [ ] ) : void {
274
+ this . onDidChangeEmitter . fire ( { changes } ) ;
202
275
}
203
276
}
204
277
@@ -209,10 +282,10 @@ export namespace BoardsDataStore {
209
282
readonly selectedProgrammer ?: Programmer ;
210
283
}
211
284
export namespace Data {
212
- export const EMPTY : Data = {
285
+ export const EMPTY : Data = deepFreeze ( {
213
286
configOptions : [ ] ,
214
287
programmers : [ ] ,
215
- } ;
288
+ } ) ;
216
289
export function is ( arg : any ) : arg is Data {
217
290
return (
218
291
! ! arg &&
@@ -224,3 +297,32 @@ export namespace BoardsDataStore {
224
297
}
225
298
}
226
299
}
300
+
301
+ function createDataStoreEntry ( details : BoardDetails ) : BoardsDataStore . Data {
302
+ return {
303
+ configOptions : details . configOptions . slice ( ) ,
304
+ programmers : details . programmers . slice ( ) ,
305
+ } ;
306
+ }
307
+
308
+ export interface BoardsDataStoreChange {
309
+ readonly fqbn : string ;
310
+ readonly data : BoardsDataStore . Data ;
311
+ }
312
+
313
+ function isBoardsDataStoreChange ( arg : unknown ) : arg is BoardsDataStoreChange {
314
+ return (
315
+ typeof arg === 'object' &&
316
+ arg !== null &&
317
+ typeof ( < BoardsDataStoreChange > arg ) . fqbn === 'string' &&
318
+ BoardsDataStore . Data . is ( ( < BoardsDataStoreChange > arg ) . data )
319
+ ) ;
320
+ }
321
+
322
+ export interface BoardsDataStoreChangeEvent {
323
+ readonly changes : readonly BoardsDataStoreChange [ ] ;
324
+ }
325
+
326
+ const USE_INHERITED_DATA : Command = {
327
+ id : 'arduino-use-inherited-boards-data' ,
328
+ } ;
0 commit comments