提交 75ceecf7 编写于 作者: J Joao Moreno

installing extensions state

fixes #2835
上级 0377434a
......@@ -53,7 +53,7 @@ export interface IGalleryService {
export interface IExtensionsService {
serviceId: ServiceIdentifier<any>;
onInstallExtension: Event<IExtensionManifest>;
onDidInstallExtension: Event<IExtension>;
onDidInstallExtension: Event<{ extension: IExtension; error?: Error; }>;
onUninstallExtension: Event<IExtension>;
onDidUninstallExtension: Event<IExtension>;
......
......@@ -261,7 +261,7 @@ class Renderer implements IRenderer<IExtensionEntry> {
updateActions();
data.disposables = disposeAll(data.disposables);
data.disposables.push(this.extensionsService.onDidInstallExtension(e => onExtensionStateChange(e, ExtensionState.Installed)));
data.disposables.push(this.extensionsService.onDidInstallExtension(e => onExtensionStateChange(e.extension, ExtensionState.Installed)));
data.disposables.push(this.extensionsService.onDidUninstallExtension(e => onExtensionStateChange(e, ExtensionState.Uninstalled)));
data.displayName.set(extension.displayName, entry.highlights.displayName);
......
......@@ -6,7 +6,7 @@
import nls = require('vs/nls');
import Severity from 'vs/base/common/severity';
import { emmet as $, append, toggleClass } from 'vs/base/browser/dom';
import lifecycle = require('vs/base/common/lifecycle');
import { IDisposable, combinedDispose } from 'vs/base/common/lifecycle';
import { onUnexpectedPromiseError as _ } from 'vs/base/common/errors';
import { assign } from 'vs/base/common/objects';
import { Action } from 'vs/base/common/actions';
......@@ -15,17 +15,27 @@ import { IExtensionService, IMessage } from 'vs/platform/extensions/common/exten
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IMessageService, CloseAction } from 'vs/platform/message/common/message';
import { UninstallAction } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions';
import { IExtensionsService, commandCategory } from 'vs/workbench/parts/extensions/common/extensions';
import { IExtensionsService, commandCategory, IExtension, IExtensionManifest } from 'vs/workbench/parts/extensions/common/extensions';
import { IQuickOpenService } from 'vs/workbench/services/quickopen/common/quickOpenService';
interface IState {
errors: IMessage[];
installingExtensions: IExtensionManifest[];
}
const InitialState: IState = {
errors: [],
installingExtensions: []
};
function extensionEquals(one: IExtensionManifest, other: IExtensionManifest): boolean {
return one.publisher === other.publisher && one.name === other.name;
}
export class ExtensionsStatusbarItem implements statusbar.IStatusbarItem {
private domNode: HTMLElement;
private state: IState = { errors: [] };
private state: IState = InitialState;
constructor(
@IExtensionService private extensionService: IExtensionService,
......@@ -35,7 +45,7 @@ export class ExtensionsStatusbarItem implements statusbar.IStatusbarItem {
@IQuickOpenService protected quickOpenService: IQuickOpenService
) {}
render(container: HTMLElement): lifecycle.IDisposable {
render(container: HTMLElement): IDisposable {
this.domNode = append(container, $('a.extensions-statusbar'));
this.domNode.onclick = () => this.onClick();
......@@ -49,7 +59,11 @@ export class ExtensionsStatusbarItem implements statusbar.IStatusbarItem {
this.updateState({ errors });
});
return null;
const disposables = [];
this.extensionsService.onInstallExtension(this.onInstallExtension, this, disposables);
this.extensionsService.onDidInstallExtension(this.onDidInstallExtension, this, disposables);
return combinedDispose(...disposables);
}
private updateState(obj: any): void {
......@@ -58,12 +72,18 @@ export class ExtensionsStatusbarItem implements statusbar.IStatusbarItem {
}
private onStateChange(): void {
toggleClass(this.domNode, 'has-errors', this.state.errors.length > 0);
if (this.state.errors.length > 0) {
const issueLabel = this.state.errors.length > 1 ? nls.localize('issues', "issues") : nls.localize('issue', "issue");
const extensionLabel = nls.localize('extension', "extension");
this.domNode.title = `${ this.state.errors.length } ${ extensionLabel } ${ issueLabel }`;
const hasErrors = this.state.errors.length > 0;
const isInstalling = this.state.installingExtensions.length > 0;
toggleClass(this.domNode, 'has-errors', hasErrors);
toggleClass(this.domNode, 'is-installing', !hasErrors && isInstalling);
if (hasErrors) {
const singular = nls.localize('oneIssue', "Extensions (1 issue)");
const plural = nls.localize('multipleIssues', "Extensions ({0} issues)", this.state.errors.length);
this.domNode.title = this.state.errors.length > 1 ? plural : singular;
} else if (isInstalling) {
this.domNode.title = nls.localize('extensionsInstalling', "Extensions ({0} installing...)", this.state.installingExtensions.length);
} else {
this.domNode.title = nls.localize('extensions', "Extensions");
}
......@@ -95,4 +115,15 @@ export class ExtensionsStatusbarItem implements statusbar.IStatusbarItem {
});
});
}
private onInstallExtension(manifest: IExtensionManifest): void {
const installingExtensions = [...this.state.installingExtensions, manifest];
this.updateState({ installingExtensions });
}
private onDidInstallExtension({ extension }: { extension: IExtension; }): void {
const installingExtensions = this.state.installingExtensions
.filter(e => !extensionEquals(extension, e));
this.updateState({ installingExtensions });
}
}
\ No newline at end of file
......@@ -153,3 +153,13 @@
border-radius: 10px;
border: 1px solid white;
}
@keyframes fade {
from { opacity: 0.8; }
50% { opacity: 0.5; }
to { opacity: 0.8; }
}
.monaco-shell .extensions-statusbar.is-installing {
animation: 1.5s ease-in-out infinite fade;
}
\ No newline at end of file
......@@ -20,7 +20,7 @@ import { download, json, IRequestOptions } from 'vs/base/node/request';
import { getProxyAgent } from 'vs/base/node/proxy';
import { IWorkspaceContextService } from 'vs/workbench/services/workspace/common/contextService';
import { Limiter } from 'vs/base/common/async';
import Event, { Emitter } from 'vs/base/common/event';
import { Emitter } from 'vs/base/common/event';
import { UserSettings } from 'vs/workbench/node/userSettings';
import * as semver from 'semver';
import { groupBy, values } from 'vs/base/common/collections';
......@@ -92,16 +92,16 @@ export class ExtensionsService implements IExtensionsService {
private obsoleteFileLimiter: Limiter<void>;
private _onInstallExtension = new Emitter<IExtensionManifest>();
@ServiceEvent onInstallExtension: Event<IExtension> = this._onInstallExtension.event;
@ServiceEvent onInstallExtension = this._onInstallExtension.event;
private _onDidInstallExtension = new Emitter<IExtension>();
@ServiceEvent onDidInstallExtension: Event<IExtension> = this._onDidInstallExtension.event;
private _onDidInstallExtension = new Emitter<{ extension: IExtension; error?: Error; }>();
@ServiceEvent onDidInstallExtension = this._onDidInstallExtension.event;
private _onUninstallExtension = new Emitter<IExtension>();
@ServiceEvent onUninstallExtension: Event<IExtension> = this._onUninstallExtension.event;
@ServiceEvent onUninstallExtension = this._onUninstallExtension.event;
private _onDidUninstallExtension = new Emitter<IExtension>();
@ServiceEvent onDidUninstallExtension: Event<IExtension> = this._onDidUninstallExtension.event;
@ServiceEvent onDidUninstallExtension = this._onDidUninstallExtension.event;
constructor(
@IWorkspaceContextService private contextService: IWorkspaceContextService
......@@ -129,6 +129,8 @@ export class ExtensionsService implements IExtensionsService {
return TPromise.wrapError(new Error(nls.localize('missingGalleryInformation', "Gallery information is missing")));
}
this._onInstallExtension.fire(extension);
return this.getLastValidExtensionVersion(extension, extension.galleryInformation.versions).then(versionInfo => {
const version = versionInfo.version;
const url = versionInfo.downloadUrl;
......@@ -139,11 +141,11 @@ export class ExtensionsService implements IExtensionsService {
return this.request(url)
.then(opts => download(zipPath, opts))
.then(() => validate(zipPath, extension, version))
.then(manifest => { this._onInstallExtension.fire(manifest); return manifest; })
.then(manifest => extract(zipPath, extensionPath, { sourcePath: 'extension', overwrite: true }).then(() => manifest))
.then(manifest => assign({ __metadata: galleryInformation }, manifest))
.then(manifest => pfs.writeFile(manifestPath, JSON.stringify(manifest, null, '\t')))
.then(() => { this._onDidInstallExtension.fire(extension); return extension; });
.then(() => { this._onDidInstallExtension.fire({ extension }); return extension; })
.then(null, error => { this._onDidInstallExtension.fire({ extension, error }); return TPromise.wrapError(error); });
});
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册