diff --git a/packages/vscode-extensions/src/browser/style/index.css b/packages/vscode-extensions/src/browser/style/index.css index 654fb7aa0d3af..9c7d0fd388e2e 100644 --- a/packages/vscode-extensions/src/browser/style/index.css +++ b/packages/vscode-extensions/src/browser/style/index.css @@ -62,7 +62,8 @@ } .extensionButton.working { - background: none; + background: var(--theia-disabled-color1); + color: var(--theia-ui-font-color0); } .extensionButton.working:hover { diff --git a/packages/vscode-extensions/src/browser/view/detail/vscode-extension-detail-widget-factory.ts b/packages/vscode-extensions/src/browser/view/detail/vscode-extension-detail-widget-factory.ts index ffc8c6d8d0926..346d6c435ea92 100644 --- a/packages/vscode-extensions/src/browser/view/detail/vscode-extension-detail-widget-factory.ts +++ b/packages/vscode-extensions/src/browser/view/detail/vscode-extension-detail-widget-factory.ts @@ -21,6 +21,8 @@ import { VSCodeExtensionDetailWidget } from './vscode-extensions-detail-widget'; import { VSCodeExtensionPartResolved } from '../../vscode-extensions-types'; import { VSCodeExtensionsService } from '../../vscode-extensions-service'; import { VSCodeExtensionsModel } from '../../vscode-extensions-model'; +import { ProgressLocationService } from '@theia/core/lib/browser/progress-location-service'; +import { ProgressService } from '@theia/core/lib/common'; export interface VSCodeExtensionDetailWidgetOptions { readonly extension: VSCodeExtensionPartResolved; @@ -34,9 +36,12 @@ export class VSCodeExtensionDetailWidgetFactory implements WidgetFactory { @inject(VSCodeExtensionsService) protected readonly service: VSCodeExtensionsService; @inject(VSCodeExtensionsModel) protected readonly model: VSCodeExtensionsModel; + @inject(ProgressLocationService) protected readonly progressLocationService: ProgressLocationService; + @inject(ProgressService) protected readonly progressService: ProgressService; async createWidget(options: VSCodeExtensionDetailWidgetOptions): Promise { - const widget = new VSCodeExtensionDetailWidget(options, this.service, this.model); + const widget = new VSCodeExtensionDetailWidget( + options, this.service, this.model, this.progressService, this.progressLocationService); widget.id = 'vscode-extension:' + options.extension.name; widget.title.closable = true; widget.title.label = options.extension.name; diff --git a/packages/vscode-extensions/src/browser/view/detail/vscode-extension-open-handler.ts b/packages/vscode-extensions/src/browser/view/detail/vscode-extension-open-handler.ts index 5ac6d17552374..4659fc4761a87 100644 --- a/packages/vscode-extensions/src/browser/view/detail/vscode-extension-open-handler.ts +++ b/packages/vscode-extensions/src/browser/view/detail/vscode-extension-open-handler.ts @@ -21,13 +21,13 @@ import { VSCodeExtensionDetailWidget } from './vscode-extensions-detail-widget'; import { VSCodeExtensionDetailWidgetOptions } from './vscode-extension-detail-widget-factory'; export namespace VSCodeExtensionUri { - export const scheme = 'vscx'; + export const scheme = 'vscode'; export function toUri(extensionName: string): URI { - return new URI('').withScheme(scheme).withFragment(extensionName); + return new URI('').withScheme(scheme).withPath('extension/' + extensionName); } export function toExtensionName(uri: URI): string { - if (uri.scheme === scheme) { - return uri.fragment; + if (uri.scheme === scheme && uri.path.dir.toString() === 'extension') { + return uri.path.base; } throw new Error('The given uri is not an vscode extension URI, uri: ' + uri); } diff --git a/packages/vscode-extensions/src/browser/view/detail/vscode-extensions-detail-widget.tsx b/packages/vscode-extensions/src/browser/view/detail/vscode-extensions-detail-widget.tsx index 679ca0167439f..e127172248e44 100644 --- a/packages/vscode-extensions/src/browser/view/detail/vscode-extensions-detail-widget.tsx +++ b/packages/vscode-extensions/src/browser/view/detail/vscode-extensions-detail-widget.tsx @@ -20,35 +20,43 @@ import { VSCodeExtensionDetailWidgetOptions } from './vscode-extension-detail-wi import { VSCXDetailHeader } from './vscx-detail-header-component'; import { VSCodeExtensionsService } from '../../vscode-extensions-service'; import { VSCodeExtensionsModel } from '../../vscode-extensions-model'; +import { ProgressService } from '@theia/core/lib/common'; +import { ProgressLocationService } from '@theia/core/lib/browser/progress-location-service'; export class VSCodeExtensionDetailWidget extends ReactWidget { constructor( protected readonly options: VSCodeExtensionDetailWidgetOptions, protected readonly service: VSCodeExtensionsService, - protected readonly model: VSCodeExtensionsModel + protected readonly model: VSCodeExtensionsModel, + protected readonly progressService: ProgressService, + protected readonly progressLocationService: ProgressLocationService ) { super(); this.addClass('vscode-extension-detail'); + + this.id = options.extension.publisher + '-' + options.extension.name + '-' + 'detail'; + service.onDidUpdateInstalled(() => { - this.init(); + this.update(); }); - this.init(); - } - - protected init(): void { - this.options.extension.busy = false; this.update(); } protected render(): React.ReactNode { return - -
-
- + +
+
+ +
-
; } diff --git a/packages/vscode-extensions/src/browser/view/detail/vscx-detail-header-component.tsx b/packages/vscode-extensions/src/browser/view/detail/vscx-detail-header-component.tsx index 020627bdd76f2..e2ffe289cf0d7 100644 --- a/packages/vscode-extensions/src/browser/view/detail/vscx-detail-header-component.tsx +++ b/packages/vscode-extensions/src/browser/view/detail/vscx-detail-header-component.tsx @@ -19,64 +19,82 @@ import { VSCodeExtensionPartResolved, VSCodeExtensionFullResolved } from '../../ import { VSCXStars } from './vscx-stars-component'; import { VSCXInstallButton } from '../vscx-install-button-component'; import { VSCodeExtensionsService } from '../../vscode-extensions-service'; +import { ProgressService, DisposableCollection } from '@theia/core/lib/common'; +import { ProgressLocationService } from '@theia/core/lib/browser/progress-location-service'; +import { ProgressBar } from '@theia/core/lib/browser/progress-bar'; export class VSCXDetailHeader extends React.Component { + protected detailHeaderRef: (ref: HTMLElement | null) => void; + protected progressLocation: string; + constructor(props: VSCXDetailHeader.Props) { super(props); + + this.progressLocation = this.props.id; + + this.detailHeaderRef = ref => { + if (ref) { + const onProgress = this.props.progressLocationService.onProgress(this.progressLocation); + this.props.toDispose.push(new ProgressBar({ container: ref, insertMode: 'prepend' }, onProgress)); + } + }; } render(): JSX.Element { const extension = this.props.extension as VSCodeExtensionFullResolved; return -
- { - extension.iconUrl ? -
-
- +
+
+ { + extension.iconUrl ? +
+
+ +
+
: '' + } +
+
+

{extension.name}

+
+
{extension.publisher}
+ +
{extension.version}
+ { + extension.averageRating ? + + +
+ +
+
+ : '' + } + { + extension.repository ? + + + Repository + + : '' + } + { + extension.license ? + + + {extension.license} + + : '' + }
-
: '' - } -
-
-

{extension.name}

-
-
{extension.publisher}
- -
{extension.version}
- { - extension.averageRating ? - - -
- -
-
- : '' - } - { - extension.repository ? - - - Repository - - : '' - } - { - extension.license ? - - - {extension.license} - - : '' - }
+
{extension.description}
+
-
{extension.description}
-
; @@ -85,8 +103,12 @@ export class VSCXDetailHeader extends React.Component - + ; } diff --git a/packages/vscode-extensions/src/browser/view/list/vscode-extensions-widget.ts b/packages/vscode-extensions/src/browser/view/list/vscode-extensions-widget.ts index 8be69286bddba..4814c0c0e36c2 100644 --- a/packages/vscode-extensions/src/browser/view/list/vscode-extensions-widget.ts +++ b/packages/vscode-extensions/src/browser/view/list/vscode-extensions-widget.ts @@ -20,6 +20,7 @@ import { VSCodeExtensionsSearchbarWidget } from './vscode-extensions-searchbar-w import { VSCodeExtensionsListWidget } from './vscode-extensions-list-widget'; import { VSCodeExtensionsService } from '../../vscode-extensions-service'; import { VSCodeExtensionsCommands } from '../../vscode-extensions-contribution'; +import { ProgressBar } from '@theia/core/lib/browser/progress-bar'; export const VSCXInstalledList = Symbol('VSCXInstalledList'); export const VSCXRegistryList = Symbol('VSCXList'); @@ -30,6 +31,8 @@ export class VSCodeExtensionsWidget extends ViewContainer { static ID = 'vscode-extensions'; static LABEL = 'Extensions'; + protected progressLocation: string; + @inject(VSCodeExtensionsSearchbarWidget) protected readonly vscxSearchbar: VSCodeExtensionsSearchbarWidget; @inject(VSCodeExtensionsService) protected readonly service: VSCodeExtensionsService; @inject(VSCXInstalledList) protected readonly vscxInstalledList: VSCodeExtensionsListWidget; @@ -77,6 +80,10 @@ export class VSCodeExtensionsWidget extends ViewContainer { priority: 1, onDidChange }); + + this.progressLocation = 'vscode-extensions-list'; + const onProgress = this.progressLocationService.onProgress(this.progressLocation); + this.toDispose.push(new ProgressBar({ container: this.node, insertMode: 'prepend' }, onProgress)); } getSearchTerm(): string { diff --git a/packages/vscode-extensions/src/browser/view/list/vscx-list-component.tsx b/packages/vscode-extensions/src/browser/view/list/vscx-list-component.tsx index f37f21f2b2d95..c2817e6da91e7 100644 --- a/packages/vscode-extensions/src/browser/view/list/vscx-list-component.tsx +++ b/packages/vscode-extensions/src/browser/view/list/vscx-list-component.tsx @@ -19,6 +19,7 @@ import { VSCodeExtensionPart } from '../../vscode-extensions-types'; import { VSCXListItem } from './vscx-list-item-component'; import { VSCodeExtensionsService } from '../../vscode-extensions-service'; import { VSCodeExtensionsModel } from '../../vscode-extensions-model'; +import { ProgressService } from '@theia/core/lib/common'; export class VSCXList extends React.Component { @@ -27,7 +28,14 @@ export class VSCXList extends React.Component { { this.props.extensions && this.props.extensions.length > 0 ? this.props.extensions.map(extension => - ) + ) :
No Extensions Found @@ -42,5 +50,7 @@ export namespace VSCXList { extensions?: VSCodeExtensionPart[]; service: VSCodeExtensionsService; model: VSCodeExtensionsModel; + progressService: ProgressService, + progressLocation: string } } diff --git a/packages/vscode-extensions/src/browser/view/list/vscx-list-item-component.tsx b/packages/vscode-extensions/src/browser/view/list/vscx-list-item-component.tsx index 311ce9ff8bacd..4e26b5048263b 100644 --- a/packages/vscode-extensions/src/browser/view/list/vscx-list-item-component.tsx +++ b/packages/vscode-extensions/src/browser/view/list/vscx-list-item-component.tsx @@ -14,50 +14,59 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ - import * as React from 'react'; - import { VSCodeExtensionPartResolved, VSCodeExtensionPart } from '../../vscode-extensions-types'; +import * as React from 'react'; +import { VSCodeExtensionPartResolved, VSCodeExtensionPart } from '../../vscode-extensions-types'; import { VSCXInstallButton } from '../vscx-install-button-component'; import { VSCodeExtensionsService } from '../../vscode-extensions-service'; import { VSCodeExtensionsModel } from '../../vscode-extensions-model'; +import { ProgressService } from '@theia/core/lib/common'; - export class VSCXListItem extends React.Component { - +export class VSCXListItem extends React.Component { + protected headerContainer: HTMLElement | null; protected readonly extensionClick = () => this.props.service.openExtensionDetail(this.props.extension); + constructor(props: VSCXListItem.Props) { + super(props); + } + render(): JSX.Element { - const { extension, service, model } = this.props; + const { extension, service, model, progressService, progressLocation } = this.props; const extensionResolved = new VSCodeExtensionPartResolved(extension, model); const tooltip = extension.description; const icon = extension.iconUrl; return
- { - icon ? -
-
- -
-
: '' - } -
-
-
-
{extension.displayName || extension.name}
-
{extension.version}
-
-
-
{extension.description}
-
-
-
{extension.publisher}
- + { + icon ? +
+
+
+
: '' + } +
+
+
+
{extension.displayName || extension.name}
+
{extension.version}
+
+
+
{extension.description}
+
+
+
{extension.publisher}
+
- ; +
+ ; } } @@ -66,6 +75,8 @@ export namespace VSCXListItem { service: VSCodeExtensionsService, model: VSCodeExtensionsModel, extension: VSCodeExtensionPart, + progressService: ProgressService, + progressLocation: string } export interface State { diff --git a/packages/vscode-extensions/src/browser/view/vscx-install-button-component.tsx b/packages/vscode-extensions/src/browser/view/vscx-install-button-component.tsx index 513ac4b4c7088..e9a8ca2ca0f16 100644 --- a/packages/vscode-extensions/src/browser/view/vscx-install-button-component.tsx +++ b/packages/vscode-extensions/src/browser/view/vscx-install-button-component.tsx @@ -17,8 +17,18 @@ import * as React from 'react'; import { VSCodeExtensionPartResolved } from '../vscode-extensions-types'; import { VSCodeExtensionsService } from '../vscode-extensions-service'; +import { ProgressService } from '@theia/core/lib/common'; export class VSCXInstallButton extends React.Component { + + constructor(props: VSCXInstallButton.Props) { + super(props); + + this.state = { + busy: false + }; + } + render(): JSX.Element { return
@@ -27,13 +37,17 @@ export class VSCXInstallButton extends React.Component; } - protected readonly onInstallButtonClicked = async () => { - this.props.extension.busy = true; - this.props.service.install(this.props.extension); - } - protected readonly onUninstallButtonClicked = async () => { - this.props.extension.busy = true; - this.props.service.uninstall(this.props.extension); + protected readonly onInstallButtonClicked = async (kind: 'install' | 'uninstall') => { + this.setState({ busy: true }); + const progress = await this.props.progressService.showProgress({ + text: `${kind}ing ${this.props.extension.displayName || this.props.extension.name}`, + options: { + location: this.props.progressLocation + } + }); + await this.props.service[kind](this.props.extension); + progress.cancel(); + this.setState({ busy: false }); } protected createButtons(extension: VSCodeExtensionPartResolved): React.ReactNode[] { @@ -42,36 +56,25 @@ export class VSCXInstallButton extends React.Component; - const content = extension.busy ? faEl : btnLabel; buttonArr.push(
{ - if (!extension.busy) { - if (extension.installed) { - this.onUninstallButtonClicked(); - } else { - this.onInstallButtonClicked(); - } + if (!this.state.busy) { + this.onInstallButtonClicked(extension.installed ? 'uninstall' : 'install'); event.stopPropagation(); } }} - >{content}
); - - // if (extension.outdated) { - // buttonArr.push(
{ - // if (!extension.busy) { - // extension.update(); - // } - // }}>{extension.busy ? faEl : 'Update'}
); - // } + >{btnLabel}
); return buttonArr; } } @@ -79,10 +82,12 @@ export class VSCXInstallButton extends React.Component { let res: () => void; const p = new Promise(r => res = r); - setTimeout(() => res(), 2000); + setTimeout(() => res(), 3000); return p; } diff --git a/packages/vscode-extensions/src/browser/vscode-extensions-types.ts b/packages/vscode-extensions/src/browser/vscode-extensions-types.ts index 0abab4dfd5097..d80508baf11ca 100644 --- a/packages/vscode-extensions/src/browser/vscode-extensions-types.ts +++ b/packages/vscode-extensions/src/browser/vscode-extensions-types.ts @@ -53,7 +53,6 @@ export class VSCodeExtensionFull extends VSCodeExtensionPart { } export class VSCodeExtensionPartResolved extends VSCodeExtensionPart { - busy?: boolean; outdated?: boolean; constructor(extension: VSCodeExtensionPart, protected model: VSCodeExtensionsModel) {