mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-07-07 19:36:33 +00:00
Generalized the list item renderers.
To support update/downgrade. Signed-off-by: Akos Kitta <kittaakos@typefox.io>
This commit is contained in:
parent
63cd2701b4
commit
c3e2aa4feb
@ -52,8 +52,6 @@ import { ArduinoScmContribution } from './customization/arduino-scm-contribution
|
||||
import { SearchInWorkspaceFrontendContribution } from '@theia/search-in-workspace/lib/browser/search-in-workspace-frontend-contribution';
|
||||
import { ArduinoSearchInWorkspaceContribution } from './customization/arduino-search-in-workspace-contribution';
|
||||
import { LibraryListWidgetFrontendContribution } from './library/library-widget-frontend-contribution';
|
||||
import { LibraryItemRenderer } from './library/library-item-renderer';
|
||||
import { BoardItemRenderer } from './boards/boards-item-renderer';
|
||||
import { MonitorServiceClientImpl } from './monitor/monitor-service-client-impl';
|
||||
import { MonitorServicePath, MonitorService, MonitorServiceClient } from '../common/protocol/monitor-service';
|
||||
import { ConfigService, ConfigServicePath } from '../common/protocol/config-service';
|
||||
@ -72,6 +70,7 @@ import { AboutDialog } from '@theia/core/lib/browser/about-dialog';
|
||||
import { ArduinoAboutDialog } from './customization/arduino-about-dialog';
|
||||
import { ArduinoShellLayoutRestorer } from './shell/arduino-shell-layout-restorer';
|
||||
import { EditorMode } from './editor-mode';
|
||||
import { ListItemRenderer } from './components/component-list/list-item-renderer';
|
||||
const ElementQueries = require('css-element-queries/src/ElementQueries');
|
||||
|
||||
export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Unbind, isBound: interfaces.IsBound, rebind: interfaces.Rebind) => {
|
||||
@ -92,9 +91,11 @@ export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Un
|
||||
bind(LanguageGrammarDefinitionContribution).to(ArduinoLanguageGrammarContribution).inSingletonScope();
|
||||
bind(LanguageClientContribution).to(ArduinoLanguageClientContribution).inSingletonScope();
|
||||
|
||||
// Renderer for both the library and the core widgets.
|
||||
bind(ListItemRenderer).toSelf().inSingletonScope();
|
||||
|
||||
// Library service
|
||||
bind(LibraryService).toDynamicValue(context => WebSocketConnectionProvider.createProxy(context.container, LibraryServicePath)).inSingletonScope();
|
||||
|
||||
// Library list widget
|
||||
bind(LibraryListWidget).toSelf();
|
||||
bindViewContribution(bind, LibraryListWidgetFrontendContribution);
|
||||
@ -103,7 +104,6 @@ export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Un
|
||||
createWidget: () => context.container.get(LibraryListWidget)
|
||||
}));
|
||||
bind(FrontendApplicationContribution).toService(LibraryListWidgetFrontendContribution);
|
||||
bind(LibraryItemRenderer).toSelf().inSingletonScope();
|
||||
|
||||
// Sketch list service
|
||||
bind(SketchesService).toDynamicValue(context => WebSocketConnectionProvider.createProxy(context.container, SketchesServicePath)).inSingletonScope();
|
||||
@ -137,7 +137,6 @@ export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Un
|
||||
createWidget: () => context.container.get(BoardsListWidget)
|
||||
}));
|
||||
bind(FrontendApplicationContribution).toService(BoardsListWidgetFrontendContribution);
|
||||
bind(BoardItemRenderer).toSelf().inSingletonScope();
|
||||
|
||||
// Board select dialog
|
||||
bind(BoardsConfigDialogWidget).toSelf().inSingletonScope();
|
||||
|
@ -1,77 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import { injectable } from 'inversify';
|
||||
import { ListItemRenderer } from '../components/component-list/list-item-renderer';
|
||||
import { BoardPackage } from '../../common/protocol/boards-service';
|
||||
import { Installable } from '../../common/protocol/installable';
|
||||
import { ComponentListItem } from '../components/component-list/component-list-item';
|
||||
|
||||
@injectable()
|
||||
export class BoardItemRenderer extends ListItemRenderer<BoardPackage> {
|
||||
|
||||
renderItem(
|
||||
input: ComponentListItem.State & { item: BoardPackage },
|
||||
install: (item: BoardPackage) => Promise<void>,
|
||||
onVersionChange: (version: Installable.Version) => void): React.ReactNode {
|
||||
|
||||
const { item } = input;
|
||||
const name = <span className='name'>{item.name}</span>;
|
||||
const author = <span className='author'>{item.author}</span>;
|
||||
const installedVersion = !!item.installedVersion && <div className='version-info'>
|
||||
<span className='version'>Version {item.installedVersion}</span>
|
||||
<span className='installed'>INSTALLED</span>
|
||||
</div>;
|
||||
|
||||
const summary = <div className='summary'>{item.summary}</div>;
|
||||
const description = <div className='summary'>{item.description}</div>;
|
||||
|
||||
const moreInfo = !!item.moreInfoLink && <a href={item.moreInfoLink} onClick={this.onMoreInfoClick}>More info</a>;
|
||||
const onClickInstall = () => install(item);
|
||||
const installButton = item.installable &&
|
||||
<button className='install' onClick={onClickInstall}>INSTALL</button>;
|
||||
|
||||
const onSelectChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
const version = event.target.value;
|
||||
if (version) {
|
||||
onVersionChange(version);
|
||||
}
|
||||
}
|
||||
|
||||
const versions = (() => {
|
||||
const { availableVersions } = item;
|
||||
if (availableVersions.length === 0) {
|
||||
return undefined;
|
||||
} else if (availableVersions.length === 1) {
|
||||
return <label>{availableVersions[0]}</label>
|
||||
} else {
|
||||
return <select
|
||||
value={input.selectedVersion}
|
||||
onChange={onSelectChange}>
|
||||
{
|
||||
item.availableVersions
|
||||
.filter(version => version !== item.installedVersion) // Filter the version that is currently installed.
|
||||
.map(version => <option value={version} key={version}>{version}</option>)
|
||||
}
|
||||
</select>;
|
||||
}
|
||||
})();
|
||||
|
||||
return <div className='component-list-item noselect'>
|
||||
<div className='header'>
|
||||
<span>{name} by {author}</span>
|
||||
{installedVersion}
|
||||
</div>
|
||||
<div className='content'>
|
||||
{summary}
|
||||
{description}
|
||||
</div>
|
||||
<div className='info'>
|
||||
{moreInfo}
|
||||
</div>
|
||||
<div className='footer'>
|
||||
{installButton}
|
||||
{versions}
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { BoardPackage, BoardsService } from '../../common/protocol/boards-service';
|
||||
import { ListWidget } from '../components/component-list/list-widget';
|
||||
import { BoardItemRenderer } from './boards-item-renderer';
|
||||
import { ListItemRenderer } from '../components/component-list/list-item-renderer';
|
||||
|
||||
@injectable()
|
||||
export class BoardsListWidget extends ListWidget<BoardPackage> {
|
||||
@ -11,7 +11,7 @@ export class BoardsListWidget extends ListWidget<BoardPackage> {
|
||||
|
||||
constructor(
|
||||
@inject(BoardsService) protected service: BoardsService,
|
||||
@inject(BoardItemRenderer) protected itemRenderer: BoardItemRenderer) {
|
||||
@inject(ListItemRenderer) protected itemRenderer: ListItemRenderer<BoardPackage>) {
|
||||
|
||||
super({
|
||||
id: BoardsListWidget.WIDGET_ID,
|
||||
|
@ -6,7 +6,7 @@ import { ArduinoComponent } from '../../../common/protocol/arduino-component';
|
||||
import { ComponentListItem } from './component-list-item';
|
||||
|
||||
@injectable()
|
||||
export abstract class ListItemRenderer<T extends ArduinoComponent> {
|
||||
export class ListItemRenderer<T extends ArduinoComponent> {
|
||||
|
||||
@inject(WindowService)
|
||||
protected windowService: WindowService;
|
||||
@ -19,10 +19,71 @@ export abstract class ListItemRenderer<T extends ArduinoComponent> {
|
||||
}
|
||||
}
|
||||
|
||||
abstract renderItem(
|
||||
renderItem(
|
||||
input: ComponentListItem.State & { item: T },
|
||||
install: (item: T) => Promise<void>,
|
||||
onVersionChange: (version: Installable.Version) => void
|
||||
): React.ReactNode;
|
||||
): React.ReactNode {
|
||||
|
||||
}
|
||||
const { item } = input;
|
||||
const name = <span className='name'>{item.name}</span>;
|
||||
const author = <span className='author'>{item.author}</span>;
|
||||
const installedVersion = !!item.installedVersion && <div className='version-info'>
|
||||
<span className='version'>Version {item.installedVersion}</span>
|
||||
<span className='installed'>INSTALLED</span>
|
||||
</div>;
|
||||
|
||||
const summary = <div className='summary'>{item.summary}</div>;
|
||||
const description = <div className='summary'>{item.description}</div>;
|
||||
|
||||
const moreInfo = !!item.moreInfoLink && <a href={item.moreInfoLink} onClick={this.onMoreInfoClick}>More info</a>;
|
||||
const onClickInstall = () => install(item);
|
||||
const installButton = item.installable &&
|
||||
<button className='install' onClick={onClickInstall}>INSTALL</button>;
|
||||
|
||||
const onSelectChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
const version = event.target.value;
|
||||
if (version) {
|
||||
onVersionChange(version);
|
||||
}
|
||||
}
|
||||
|
||||
const versions = (() => {
|
||||
const { availableVersions } = item;
|
||||
if (availableVersions.length === 0) {
|
||||
return undefined;
|
||||
} else if (availableVersions.length === 1) {
|
||||
return <label>{availableVersions[0]}</label>
|
||||
} else {
|
||||
return <select
|
||||
value={input.selectedVersion}
|
||||
onChange={onSelectChange}>
|
||||
{
|
||||
item.availableVersions
|
||||
.filter(version => version !== item.installedVersion) // Filter the version that is currently installed.
|
||||
.map(version => <option value={version} key={version}>{version}</option>)
|
||||
}
|
||||
</select>;
|
||||
}
|
||||
})();
|
||||
|
||||
return <div className='component-list-item noselect'>
|
||||
<div className='header'>
|
||||
<span>{name} by {author}</span>
|
||||
{installedVersion}
|
||||
</div>
|
||||
<div className='content'>
|
||||
{summary}
|
||||
{description}
|
||||
</div>
|
||||
<div className='info'>
|
||||
{moreInfo}
|
||||
</div>
|
||||
<div className='footer'>
|
||||
{installButton}
|
||||
{versions}
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,58 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import { injectable } from 'inversify';
|
||||
import { Library } from '../../common/protocol/library-service';
|
||||
import { Installable } from '../../common/protocol/installable';
|
||||
import { ListItemRenderer } from '../components/component-list/list-item-renderer';
|
||||
import { ComponentListItem } from '../components/component-list/component-list-item';
|
||||
|
||||
@injectable()
|
||||
export class LibraryItemRenderer extends ListItemRenderer<Library> {
|
||||
|
||||
renderItem(
|
||||
input: ComponentListItem.State & { item: Library },
|
||||
install: (item: Library, version: Installable.Version) => Promise<void>): React.ReactNode {
|
||||
|
||||
const { item } = input;
|
||||
const name = <span className='name'>{item.name}</span>;
|
||||
const author = <span className='author'>by {item.author}</span>;
|
||||
const installedVersion = !!item.installedVersion && <div className='version-info'>
|
||||
<span className='version'>Version {item.installedVersion}</span>
|
||||
<span className='installed'>INSTALLED</span>
|
||||
</div>;
|
||||
|
||||
const summary = <div className='summary'>{item.summary}</div>;
|
||||
|
||||
const moreInfo = !!item.moreInfoLink && <a href={item.moreInfoLink} onClick={this.onMoreInfoClick}>More info</a>;
|
||||
const installButton = item.installable && !item.installedVersion &&
|
||||
<button className='install' onClick={install.bind(this, item)}>INSTALL</button>;
|
||||
|
||||
const versions = (() => {
|
||||
const { availableVersions } = item;
|
||||
if (!!item.installedVersion || availableVersions.length === 0) {
|
||||
return undefined;
|
||||
} else if (availableVersions.length === 1) {
|
||||
return <label>{availableVersions[0]}</label>
|
||||
} else {
|
||||
return <select>{item.availableVersions.map(version => <option value={version} key={version}>{version}</option>)}</select>;
|
||||
}
|
||||
})();
|
||||
|
||||
return <div className='component-list-item noselect'>
|
||||
<div className='header'>
|
||||
<span>{name} {author}</span>
|
||||
{installedVersion}
|
||||
</div>
|
||||
<div className='content'>
|
||||
{summary}
|
||||
</div>
|
||||
<div className='info'>
|
||||
{moreInfo}
|
||||
</div>
|
||||
<div className='footer'>
|
||||
{installButton}
|
||||
{versions}
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { Library, LibraryService } from '../../common/protocol/library-service';
|
||||
import { ListWidget } from '../components/component-list/list-widget';
|
||||
import { LibraryItemRenderer } from './library-item-renderer';
|
||||
import { ListItemRenderer } from '../components/component-list/list-item-renderer';
|
||||
|
||||
@injectable()
|
||||
export class LibraryListWidget extends ListWidget<Library> {
|
||||
@ -11,7 +11,7 @@ export class LibraryListWidget extends ListWidget<Library> {
|
||||
|
||||
constructor(
|
||||
@inject(LibraryService) protected service: LibraryService,
|
||||
@inject(LibraryItemRenderer) protected itemRenderer: LibraryItemRenderer) {
|
||||
@inject(ListItemRenderer) protected itemRenderer: ListItemRenderer<Library>) {
|
||||
|
||||
super({
|
||||
id: LibraryListWidget.WIDGET_ID,
|
||||
|
@ -59,7 +59,7 @@ export class CoreClientProviderImpl implements CoreClientProvider {
|
||||
resolve(undefined);
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if (!workspaceRootOrResourceUri) {
|
||||
resolve(this.getOrCreateClient(roots[0]));
|
||||
return;
|
||||
|
@ -1,8 +1,10 @@
|
||||
import { injectable, inject } from 'inversify';
|
||||
import { Library, LibraryService } from '../common/protocol/library-service';
|
||||
import { CoreClientProvider } from './core-client-provider';
|
||||
import { LibrarySearchReq, LibrarySearchResp, LibraryListReq, LibraryListResp, LibraryRelease,
|
||||
InstalledLibrary, LibraryInstallReq, LibraryInstallResp } from './cli-protocol/commands/lib_pb';
|
||||
import {
|
||||
LibrarySearchReq, LibrarySearchResp, LibraryListReq, LibraryListResp, LibraryRelease,
|
||||
InstalledLibrary, LibraryInstallReq, LibraryInstallResp
|
||||
} from './cli-protocol/commands/lib_pb';
|
||||
import { ToolOutputServiceServer } from '../common/protocol/tool-output-service';
|
||||
import { Installable } from '../common/protocol/installable';
|
||||
|
||||
@ -44,6 +46,7 @@ export class LibraryServiceImpl implements LibraryService {
|
||||
.filter(item => !!item.getLatest())
|
||||
.slice(0, 50)
|
||||
.map(item => {
|
||||
const availableVersions = item.getReleasesMap().getEntryList().map(([key, _]) => key);
|
||||
let installedVersion: string | undefined;
|
||||
const installed = installedLibsIdx.get(item.getName());
|
||||
if (installed) {
|
||||
@ -52,8 +55,8 @@ export class LibraryServiceImpl implements LibraryService {
|
||||
return toLibrary({
|
||||
name: item.getName(),
|
||||
installable: true,
|
||||
installedVersion
|
||||
}, item.getLatest()!)
|
||||
installedVersion,
|
||||
}, item.getLatest()!, availableVersions)
|
||||
})
|
||||
|
||||
return { items };
|
||||
@ -88,14 +91,14 @@ export class LibraryServiceImpl implements LibraryService {
|
||||
|
||||
}
|
||||
|
||||
function toLibrary(tpl: Partial<Library>, release: LibraryRelease): Library {
|
||||
function toLibrary(tpl: Partial<Library>, release: LibraryRelease, availableVersions: string[]): Library {
|
||||
return {
|
||||
name: "",
|
||||
installable: false,
|
||||
...tpl,
|
||||
|
||||
author: release.getAuthor(),
|
||||
availableVersions: [release.getVersion()],
|
||||
availableVersions,
|
||||
description: release.getSentence(),
|
||||
moreInfoLink: release.getWebsite(),
|
||||
summary: release.getParagraph()
|
||||
|
@ -28,4 +28,4 @@
|
||||
"files": [
|
||||
"../node_modules/@theia/monaco/src/typings/monaco/index.d.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user