11import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application' ;
2+ import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state' ;
23import { StorageService } from '@theia/core/lib/browser/storage-service' ;
4+ import type {
5+ Command ,
6+ CommandContribution ,
7+ CommandRegistry ,
8+ } from '@theia/core/lib/common/command' ;
39import { DisposableCollection } from '@theia/core/lib/common/disposable' ;
410import { Emitter , Event } from '@theia/core/lib/common/event' ;
511import { ILogger } from '@theia/core/lib/common/logger' ;
@@ -10,12 +16,23 @@ import {
1016 BoardsService ,
1117 ConfigOption ,
1218 Programmer ,
19+ isBoardIdentifierChangeEvent ,
1320} from '../../common/protocol' ;
1421import { notEmpty } from '../../common/utils' ;
22+ import type {
23+ StartupTask ,
24+ StartupTaskProvider ,
25+ } from '../../electron-common/startup-task' ;
1526import { NotificationCenter } from '../notification-center' ;
27+ import { BoardsServiceProvider } from './boards-service-provider' ;
1628
1729@injectable ( )
18- export class BoardsDataStore implements FrontendApplicationContribution {
30+ export class BoardsDataStore
31+ implements
32+ FrontendApplicationContribution ,
33+ StartupTaskProvider ,
34+ CommandContribution
35+ {
1936 @inject ( ILogger )
2037 @named ( 'store' )
2138 private readonly logger : ILogger ;
@@ -28,44 +45,101 @@ export class BoardsDataStore implements FrontendApplicationContribution {
2845 // 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
2946 @inject ( StorageService )
3047 private readonly storageService : StorageService ;
48+ @inject ( BoardsServiceProvider )
49+ private readonly boardsServiceProvider : BoardsServiceProvider ;
50+ @inject ( FrontendApplicationStateService )
51+ private readonly appStateService : FrontendApplicationStateService ;
3152
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 ;
3459
3560 onStart ( ) : void {
36- this . toDispose . push (
61+ this . toDispose . pushAll ( [
62+ this . boardsServiceProvider . onBoardsConfigDidChange ( async ( event ) => {
63+ if ( isBoardIdentifierChangeEvent ( event ) ) {
64+ this . _selectedBoardData = await this . getSelectedBoardData (
65+ event . selectedBoard ?. fqbn
66+ ) ;
67+ }
68+ } ) ,
3769 this . notificationCenter . onPlatformDidInstall ( async ( { item } ) => {
38- const dataDidChangePerFqbn : string [ ] = [ ] ;
70+ const changes : BoardsDataStoreChange [ ] = [ ] ;
3971 for ( const fqbn of item . boards
4072 . map ( ( { fqbn } ) => fqbn )
41- . filter ( notEmpty )
42- . filter ( ( fqbn ) => ! ! fqbn ) ) {
73+ . filter ( notEmpty ) ) {
4374 const key = this . getStorageKey ( fqbn ) ;
44- let data = await this . storageService . getData < ConfigOption [ ] > ( key ) ;
45- if ( ! data || ! data . length ) {
75+ const storedData =
76+ await this . storageService . getData < BoardsDataStore . Data > ( key ) ;
77+ if ( ! storedData ) {
4678 const details = await this . getBoardDetailsSafe ( fqbn ) ;
4779 if ( details ) {
48- data = details . configOptions ;
49- if ( data . length ) {
50- await this . storageService . setData ( key , data ) ;
51- dataDidChangePerFqbn . push ( fqbn ) ;
52- }
80+ const data = createDataStoreEntry ( details ) ;
81+ await this . storageService . setData ( key , data ) ;
82+ changes . push ( { fqbn, data } ) ;
5383 }
5484 }
5585 }
56- if ( dataDidChangePerFqbn . length ) {
57- this . fireChanged ( ...dataDidChangePerFqbn ) ;
86+ if ( changes . length ) {
87+ this . fireChanged ( ...changes ) ;
5888 }
59- } )
60- ) ;
89+ } ) ,
90+ ] ) ;
91+ Promise . all ( [
92+ this . boardsServiceProvider . ready ,
93+ this . appStateService . reachedState ( 'ready' ) ,
94+ ] ) . then ( async ( ) => {
95+ const fqbn = this . boardsServiceProvider . boardsConfig . selectedBoard ?. fqbn ;
96+ this . _selectedBoardData = await this . getSelectedBoardData ( fqbn ) ;
97+ } ) ;
98+ }
99+
100+ private async getSelectedBoardData (
101+ fqbn : string | undefined
102+ ) : Promise < BoardsDataStoreChange | undefined > {
103+ if ( ! fqbn ) {
104+ return undefined ;
105+ } else {
106+ const data = await this . getData ( fqbn ) ;
107+ if ( data === BoardsDataStore . Data . EMPTY ) {
108+ return undefined ;
109+ }
110+ return { fqbn, data } ;
111+ }
61112 }
62113
63114 onStop ( ) : void {
64115 this . toDispose . dispose ( ) ;
65116 }
66117
67- get onChanged ( ) : Event < string [ ] > {
68- return this . onChangedEmitter . event ;
118+ registerCommands ( registry : CommandRegistry ) : void {
119+ registry . registerCommand ( USE_INHERITED_DATA , {
120+ execute : async ( arg : unknown ) => {
121+ if ( isBoardsDataStoreChange ( arg ) ) {
122+ await this . setData ( arg ) ;
123+ this . fireChanged ( arg ) ;
124+ }
125+ } ,
126+ } ) ;
127+ }
128+
129+ tasks ( ) : StartupTask [ ] {
130+ if ( ! this . _selectedBoardData ) {
131+ return [ ] ;
132+ }
133+ return [
134+ {
135+ command : USE_INHERITED_DATA . id ,
136+ args : [ this . _selectedBoardData ] ,
137+ } ,
138+ ] ;
139+ }
140+
141+ get onDidChange ( ) : Event < BoardsDataStoreChangeEvent > {
142+ return this . onDidChangeEmitter . event ;
69143 }
70144
71145 async appendConfigToFqbn (
@@ -84,22 +158,19 @@ export class BoardsDataStore implements FrontendApplicationContribution {
84158 }
85159
86160 const key = this . getStorageKey ( fqbn ) ;
87- let data = await this . storageService . getData <
161+ const storedData = await this . storageService . getData <
88162 BoardsDataStore . Data | undefined
89163 > ( key , undefined ) ;
90- if ( BoardsDataStore . Data . is ( data ) ) {
91- return data ;
164+ if ( BoardsDataStore . Data . is ( storedData ) ) {
165+ return storedData ;
92166 }
93167
94168 const boardDetails = await this . getBoardDetailsSafe ( fqbn ) ;
95169 if ( ! boardDetails ) {
96170 return BoardsDataStore . Data . EMPTY ;
97171 }
98172
99- data = {
100- configOptions : boardDetails . configOptions ,
101- programmers : boardDetails . programmers ,
102- } ;
173+ const data = createDataStoreEntry ( boardDetails ) ;
103174 await this . storageService . setData ( key , data ) ;
104175 return data ;
105176 }
@@ -111,17 +182,15 @@ export class BoardsDataStore implements FrontendApplicationContribution {
111182 fqbn : string ;
112183 selectedProgrammer : Programmer ;
113184 } ) : Promise < boolean > {
114- const data = deepClone ( await this . getData ( fqbn ) ) ;
115- const { programmers } = data ;
185+ const storedData = deepClone ( await this . getData ( fqbn ) ) ;
186+ const { programmers } = storedData ;
116187 if ( ! programmers . find ( ( p ) => Programmer . equals ( selectedProgrammer , p ) ) ) {
117188 return false ;
118189 }
119190
120- await this . setData ( {
121- fqbn,
122- data : { ...data , selectedProgrammer } ,
123- } ) ;
124- this . fireChanged ( fqbn ) ;
191+ const data = { ...storedData , selectedProgrammer } ;
192+ await this . setData ( { fqbn, data } ) ;
193+ this . fireChanged ( { fqbn, data } ) ;
125194 return true ;
126195 }
127196
@@ -153,17 +222,12 @@ export class BoardsDataStore implements FrontendApplicationContribution {
153222 return false ;
154223 }
155224 await this . setData ( { fqbn, data } ) ;
156- this . fireChanged ( fqbn ) ;
225+ this . fireChanged ( { fqbn, data } ) ;
157226 return true ;
158227 }
159228
160- protected async setData ( {
161- fqbn,
162- data,
163- } : {
164- fqbn : string ;
165- data : BoardsDataStore . Data ;
166- } ) : Promise < void > {
229+ protected async setData ( change : BoardsDataStoreChange ) : Promise < void > {
230+ const { fqbn, data } = change ;
167231 const key = this . getStorageKey ( fqbn ) ;
168232 return this . storageService . setData ( key , data ) ;
169233 }
@@ -176,7 +240,7 @@ export class BoardsDataStore implements FrontendApplicationContribution {
176240 fqbn : string
177241 ) : Promise < BoardDetails | undefined > {
178242 try {
179- const details = this . boardsService . getBoardDetails ( { fqbn } ) ;
243+ const details = await this . boardsService . getBoardDetails ( { fqbn } ) ;
180244 return details ;
181245 } catch ( err ) {
182246 if (
@@ -197,8 +261,8 @@ export class BoardsDataStore implements FrontendApplicationContribution {
197261 }
198262 }
199263
200- protected fireChanged ( ...fqbn : string [ ] ) : void {
201- this . onChangedEmitter . fire ( fqbn ) ;
264+ protected fireChanged ( ...changes : BoardsDataStoreChange [ ] ) : void {
265+ this . onDidChangeEmitter . fire ( { changes } ) ;
202266 }
203267}
204268
@@ -224,3 +288,32 @@ export namespace BoardsDataStore {
224288 }
225289 }
226290}
291+
292+ function createDataStoreEntry ( details : BoardDetails ) : BoardsDataStore . Data {
293+ return {
294+ configOptions : details . configOptions . slice ( ) ,
295+ programmers : details . programmers . slice ( ) ,
296+ } ;
297+ }
298+
299+ export interface BoardsDataStoreChange {
300+ readonly fqbn : string ;
301+ readonly data : BoardsDataStore . Data ;
302+ }
303+
304+ function isBoardsDataStoreChange ( arg : unknown ) : arg is BoardsDataStoreChange {
305+ return (
306+ typeof arg === 'object' &&
307+ arg !== null &&
308+ typeof ( < BoardsDataStoreChange > arg ) . fqbn === 'string' &&
309+ typeof ( < BoardsDataStoreChange > arg ) . data === 'object' // TODO: better type guard!
310+ ) ;
311+ }
312+
313+ export interface BoardsDataStoreChangeEvent {
314+ readonly changes : readonly BoardsDataStoreChange [ ] ;
315+ }
316+
317+ const USE_INHERITED_DATA : Command = {
318+ id : 'arduino-use-inherited-boards-data' ,
319+ } ;
0 commit comments