mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-10-11 20:38:32 +00:00
220 lines
5.7 KiB
TypeScript
220 lines
5.7 KiB
TypeScript
import { injectable, inject, named } from '@theia/core/shared/inversify';
|
|
import { ILogger } from '@theia/core/lib/common/logger';
|
|
import { deepClone } from '@theia/core/lib/common/objects';
|
|
import { Event, Emitter } from '@theia/core/lib/common/event';
|
|
import {
|
|
FrontendApplicationContribution,
|
|
LocalStorageService,
|
|
} from '@theia/core/lib/browser';
|
|
import { notEmpty } from '../../common/utils';
|
|
import {
|
|
BoardsService,
|
|
ConfigOption,
|
|
BoardDetails,
|
|
Programmer,
|
|
} from '../../common/protocol';
|
|
import { NotificationCenter } from '../notification-center';
|
|
|
|
@injectable()
|
|
export class BoardsDataStore implements FrontendApplicationContribution {
|
|
@inject(ILogger)
|
|
@named('store')
|
|
protected readonly logger: ILogger;
|
|
|
|
@inject(BoardsService)
|
|
protected readonly boardsService: BoardsService;
|
|
|
|
@inject(NotificationCenter)
|
|
protected readonly notificationCenter: NotificationCenter;
|
|
|
|
@inject(LocalStorageService)
|
|
protected readonly storageService: LocalStorageService;
|
|
|
|
protected readonly onChangedEmitter = new Emitter<void>();
|
|
|
|
onStart(): void {
|
|
this.notificationCenter.onPlatformDidInstall(async ({ item }) => {
|
|
let shouldFireChanged = false;
|
|
for (const fqbn of item.boards
|
|
.map(({ fqbn }) => fqbn)
|
|
.filter(notEmpty)
|
|
.filter((fqbn) => !!fqbn)) {
|
|
const key = this.getStorageKey(fqbn);
|
|
let data = await this.storageService.getData<
|
|
ConfigOption[] | undefined
|
|
>(key);
|
|
if (!data || !data.length) {
|
|
const details = await this.getBoardDetailsSafe(fqbn);
|
|
if (details) {
|
|
data = details.configOptions;
|
|
if (data.length) {
|
|
await this.storageService.setData(key, data);
|
|
shouldFireChanged = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (shouldFireChanged) {
|
|
this.fireChanged();
|
|
}
|
|
});
|
|
}
|
|
|
|
get onChanged(): Event<void> {
|
|
return this.onChangedEmitter.event;
|
|
}
|
|
|
|
async appendConfigToFqbn(
|
|
fqbn: string | undefined,
|
|
): Promise<string | undefined> {
|
|
if (!fqbn) {
|
|
return undefined;
|
|
}
|
|
const { configOptions } = await this.getData(fqbn);
|
|
return ConfigOption.decorate(fqbn, configOptions);
|
|
}
|
|
|
|
async getData(fqbn: string | undefined): Promise<BoardsDataStore.Data> {
|
|
if (!fqbn) {
|
|
return BoardsDataStore.Data.EMPTY;
|
|
}
|
|
|
|
const key = this.getStorageKey(fqbn);
|
|
let data = await this.storageService.getData<
|
|
BoardsDataStore.Data | undefined
|
|
>(key, undefined);
|
|
if (BoardsDataStore.Data.is(data)) {
|
|
return data;
|
|
}
|
|
|
|
const boardDetails = await this.getBoardDetailsSafe(fqbn);
|
|
if (!boardDetails) {
|
|
return BoardsDataStore.Data.EMPTY;
|
|
}
|
|
|
|
data = {
|
|
configOptions: boardDetails.configOptions,
|
|
programmers: boardDetails.programmers,
|
|
};
|
|
await this.storageService.setData(key, data);
|
|
return data;
|
|
}
|
|
|
|
async selectProgrammer(
|
|
{
|
|
fqbn,
|
|
selectedProgrammer,
|
|
}: { fqbn: string; selectedProgrammer: Programmer },
|
|
): Promise<boolean> {
|
|
const data = deepClone(await this.getData(fqbn));
|
|
const { programmers } = data;
|
|
if (!programmers.find((p) => Programmer.equals(selectedProgrammer, p))) {
|
|
return false;
|
|
}
|
|
|
|
await this.setData({
|
|
fqbn,
|
|
data: { ...data, selectedProgrammer },
|
|
});
|
|
this.fireChanged();
|
|
return true;
|
|
}
|
|
|
|
async selectConfigOption(
|
|
{
|
|
fqbn,
|
|
option,
|
|
selectedValue,
|
|
}: { fqbn: string; option: string; selectedValue: string }
|
|
): Promise<boolean> {
|
|
const data = deepClone(await this.getData(fqbn));
|
|
const { configOptions } = data;
|
|
const configOption = configOptions.find((c) => c.option === option);
|
|
if (!configOption) {
|
|
return false;
|
|
}
|
|
let updated = false;
|
|
for (const value of configOption.values) {
|
|
if (value.value === selectedValue) {
|
|
(value as any).selected = true;
|
|
updated = true;
|
|
} else {
|
|
(value as any).selected = false;
|
|
}
|
|
}
|
|
if (!updated) {
|
|
return false;
|
|
}
|
|
await this.setData({ fqbn, data });
|
|
this.fireChanged();
|
|
return true;
|
|
}
|
|
|
|
protected async setData({
|
|
fqbn,
|
|
data,
|
|
}: {
|
|
fqbn: string;
|
|
data: BoardsDataStore.Data;
|
|
}): Promise<void> {
|
|
const key = this.getStorageKey(fqbn);
|
|
return this.storageService.setData(key, data);
|
|
}
|
|
|
|
protected getStorageKey(fqbn: string): string {
|
|
return `.arduinoIDE-configOptions-${fqbn}`;
|
|
}
|
|
|
|
protected async getBoardDetailsSafe(
|
|
fqbn: string
|
|
): Promise<BoardDetails | undefined> {
|
|
try {
|
|
const details = this.boardsService.getBoardDetails({ fqbn });
|
|
return details;
|
|
} catch (err) {
|
|
if (
|
|
err instanceof Error &&
|
|
err.message.includes('loading board data') &&
|
|
err.message.includes('is not installed')
|
|
) {
|
|
this.logger.warn(
|
|
`The boards package is not installed for board with FQBN: ${fqbn}`
|
|
);
|
|
} else {
|
|
this.logger.error(
|
|
`An unexpected error occurred while retrieving the board details for ${fqbn}.`,
|
|
err
|
|
);
|
|
}
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
protected fireChanged(): void {
|
|
this.onChangedEmitter.fire();
|
|
}
|
|
}
|
|
|
|
export namespace BoardsDataStore {
|
|
export interface Data {
|
|
readonly configOptions: ConfigOption[];
|
|
readonly programmers: Programmer[];
|
|
readonly selectedProgrammer?: Programmer;
|
|
}
|
|
export namespace Data {
|
|
export const EMPTY: Data = {
|
|
configOptions: [],
|
|
programmers: [],
|
|
};
|
|
export function is(arg: any): arg is Data {
|
|
return (
|
|
!!arg &&
|
|
'configOptions' in arg &&
|
|
Array.isArray(arg['configOptions']) &&
|
|
'programmers' in arg &&
|
|
Array.isArray(arg['programmers'])
|
|
);
|
|
}
|
|
}
|
|
}
|