import { DialogError, DialogProps } from '@theia/core/lib/browser/dialogs'; import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state'; import { Emitter } from '@theia/core/lib/common/event'; import { nls } from '@theia/core/lib/common/nls'; import { deepClone } from '@theia/core/lib/common/objects'; import type { Message } from '@theia/core/shared/@phosphor/messaging'; import { inject, injectable, postConstruct, } from '@theia/core/shared/inversify'; import React from '@theia/core/shared/react'; import type { ReactNode } from '@theia/core/shared/react/index'; import { EditBoardsConfigActionParams } from '../../common/protocol/board-list'; import { BoardIdentifier, BoardsConfig, BoardWithPackage, DetectedPort, emptyBoardsConfig, PortIdentifier, } from '../../common/protocol/boards-service'; import type { Defined } from '../../common/types'; import { NotificationCenter } from '../notification-center'; import { ReactDialog } from '../theia/dialogs/dialogs'; import { BoardsConfigComponent } from './boards-config-component'; import { BoardsServiceProvider } from './boards-service-provider'; @injectable() export class BoardsConfigDialogProps extends DialogProps {} export type BoardsConfigDialogState = Omit & { selectedBoard: BoardsConfig['selectedBoard'] | BoardWithPackage; }; @injectable() export class BoardsConfigDialog extends ReactDialog { @inject(BoardsServiceProvider) private readonly boardsServiceProvider: BoardsServiceProvider; @inject(NotificationCenter) private readonly notificationCenter: NotificationCenter; @inject(FrontendApplicationStateService) private readonly appStateService: FrontendApplicationStateService; private readonly onFilterTextDidChangeEmitter: Emitter< Defined >; private readonly onBoardSelected = (board: BoardWithPackage): void => { this._boardsConfig.selectedBoard = board; this.update(); }; private readonly onPortSelected = (port: PortIdentifier): void => { this._boardsConfig.selectedPort = port; this.update(); }; private readonly setFocusNode = (element: HTMLElement | undefined): void => { this.focusNode = element; }; private readonly searchBoards = (options: { query?: string; }): Promise => { return this.boardsServiceProvider.searchBoards(options); }; private readonly ports = ( predicate?: (port: DetectedPort) => boolean ): readonly DetectedPort[] => { return this.boardsServiceProvider.boardList.ports(predicate); }; private _boardsConfig: BoardsConfigDialogState; /** * When the dialog's boards result set is limited to a subset of boards when searching, this field is set. */ private _searchSet: BoardIdentifier[] | undefined; private focusNode: HTMLElement | undefined; constructor( @inject(BoardsConfigDialogProps) protected override readonly props: BoardsConfigDialogProps ) { super({ ...props, maxWidth: 500 }); this.node.id = 'select-board-dialog-container'; this.contentNode.classList.add('select-board-dialog'); this.appendCloseButton( nls.localize('vscode/issueMainService/cancel', 'Cancel') ); this.appendAcceptButton(nls.localize('vscode/issueMainService/ok', 'OK')); this._boardsConfig = emptyBoardsConfig(); this.onFilterTextDidChangeEmitter = new Emitter(); } @postConstruct() protected init(): void { this.boardsServiceProvider.onBoardListDidChange(() => { this._boardsConfig = deepClone(this.boardsServiceProvider.boardsConfig); this.update(); }); this._boardsConfig = deepClone(this.boardsServiceProvider.boardsConfig); } override async open( disposeOnResolve = true, params?: EditBoardsConfigActionParams ): Promise { this._searchSet = undefined; this._boardsConfig.selectedBoard = this.boardsServiceProvider.boardsConfig.selectedBoard; this._boardsConfig.selectedPort = this.boardsServiceProvider.boardsConfig.selectedPort; if (params) { if (typeof params.query === 'string') { this.onFilterTextDidChangeEmitter.fire(params.query); } if (params.portToSelect) { this._boardsConfig.selectedPort = params.portToSelect; } if (params.boardToSelect) { this._boardsConfig.selectedBoard = params.boardToSelect; } if (params.searchSet) { this._searchSet = params.searchSet.slice(); } } return super.open(disposeOnResolve); } protected override onAfterAttach(msg: Message): void { super.onAfterAttach(msg); this.update(); } protected override render(): ReactNode { return ( <>
{nls.localize( 'arduino/board/configDialog1', 'Select both a Board and a Port if you want to upload a sketch.' )}
{nls.localize( 'arduino/board/configDialog2', 'If you only select a Board you will be able to compile, but not to upload your sketch.' )}
); } protected override onActivateRequest(msg: Message): void { super.onActivateRequest(msg); if (this.focusNode instanceof HTMLInputElement) { this.focusNode.select(); } (this.focusNode || this.node).focus(); } protected override handleEnter(event: KeyboardEvent): boolean | void { if (event.target instanceof HTMLTextAreaElement) { return false; } } protected override isValid(value: BoardsConfig): DialogError { if (!value.selectedBoard) { if (value.selectedPort) { return nls.localize( 'arduino/board/pleasePickBoard', 'Please pick a board connected to the port you have selected.' ); } return false; } return ''; } get value(): BoardsConfigDialogState { return this._boardsConfig; } }