import React from '@theia/core/shared/react'; import type { ArduinoComponent } from '../../../common/protocol/arduino-component'; import { Installable } from '../../../common/protocol/installable'; import type { ListItemRenderer } from './list-item-renderer'; import { UserAbortError } from '../../../common/protocol/progressible'; export class ComponentListItem< T extends ArduinoComponent > extends React.Component, ComponentListItem.State> { constructor(props: ComponentListItem.Props) { super(props); this.state = {}; } override render(): React.ReactNode { const { item, itemRenderer } = this.props; const selectedVersion = this.props.edited?.item.name === item.name ? this.props.edited.selectedVersion : this.latestVersion; return ( <> {itemRenderer.renderItem({ item, selectedVersion, inProgress: this.state.inProgress, install: (item) => this.install(item), uninstall: (item) => this.uninstall(item), onVersionChange: (version) => this.onVersionChange(version), })} ); } private async install(item: T): Promise { await this.withState('installing', () => this.props.install( item, this.props.edited?.item.name === item.name ? this.props.edited.selectedVersion : Installable.latest(this.props.item.availableVersions) ) ); } private async uninstall(item: T): Promise { await this.withState('uninstalling', () => this.props.uninstall(item)); } private async withState( inProgress: 'installing' | 'uninstalling', task: () => Promise ): Promise { this.setState({ inProgress }); try { await task(); } catch (err) { if (err instanceof UserAbortError) { // No state update when user cancels the task return; } throw err; } finally { this.setState({ inProgress: undefined }); } } private onVersionChange(version: Installable.Version): void { this.props.onItemEdit(this.props.item, version); } private get latestVersion(): Installable.Version | undefined { return Installable.latest(this.props.item.availableVersions); } } export namespace ComponentListItem { export interface Props { readonly item: T; readonly install: (item: T, version?: Installable.Version) => Promise; readonly uninstall: (item: T) => Promise; readonly edited?: { item: T; selectedVersion: Installable.Version; }; readonly onItemEdit: ( item: T, selectedVersion: Installable.Version ) => void; readonly itemRenderer: ListItemRenderer; } export interface State { inProgress?: 'installing' | 'uninstalling' | undefined; } }