Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve extension hover #151886

Merged
merged 1 commit into from Jun 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
49 changes: 12 additions & 37 deletions src/vs/workbench/contrib/extensions/browser/extensionEditor.ts
Expand Up @@ -11,8 +11,8 @@ import { Event, Emitter } from 'vs/base/common/event';
import { Cache, CacheResult } from 'vs/base/common/cache';
import { Action, IAction } from 'vs/base/common/actions';
import { getErrorMessage, isCancellationError, onUnexpectedError } from 'vs/base/common/errors';
import { dispose, toDisposable, Disposable, DisposableStore, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle';
import { append, $, finalHandler, join, addDisposableListener, EventType, setParentFlowTo, reset, Dimension } from 'vs/base/browser/dom';
import { dispose, toDisposable, Disposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle';
import { append, $, join, addDisposableListener, setParentFlowTo, reset, Dimension } from 'vs/base/browser/dom';
import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
Expand All @@ -22,14 +22,14 @@ import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
import { ResolvedKeybinding } from 'vs/base/common/keybindings';
import { ExtensionsInput, IExtensionEditorOptions } from 'vs/workbench/contrib/extensions/common/extensionsInput';
import { IExtensionsWorkbenchService, IExtensionsViewPaneContainer, VIEWLET_ID, IExtension, ExtensionContainers, ExtensionEditorTab, ExtensionState, IExtensionContainer } from 'vs/workbench/contrib/extensions/common/extensions';
import { RatingsWidget, InstallCountWidget, RemoteBadgeWidget, ExtensionWidget, ExtensionStatusWidget, ExtensionRecommendationWidget } from 'vs/workbench/contrib/extensions/browser/extensionsWidgets';
import { RatingsWidget, InstallCountWidget, RemoteBadgeWidget, ExtensionWidget, ExtensionStatusWidget, ExtensionRecommendationWidget, SponsorWidget, onClick } from 'vs/workbench/contrib/extensions/browser/extensionsWidgets';
import { IEditorOpenContext } from 'vs/workbench/common/editor';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import {
UpdateAction, ReloadAction, EnableDropDownAction, DisableDropDownAction, ExtensionStatusLabelAction, SetFileIconThemeAction, SetColorThemeAction,
RemoteInstallAction, ExtensionStatusAction, LocalInstallAction, ToggleSyncExtensionAction, SetProductIconThemeAction,
ActionWithDropDownAction, InstallDropdownAction, InstallingLabelAction, UninstallAction, ExtensionActionWithDropdownActionViewItem, ExtensionDropDownAction,
InstallAnotherVersionAction, ExtensionEditorManageExtensionAction, WebInstallAction, SwitchToPreReleaseVersionAction, SwitchToReleasedVersionAction, MigrateDeprecatedExtensionAction, SponsorExtensionAction, SponsorExtensionActionViewItem
InstallAnotherVersionAction, ExtensionEditorManageExtensionAction, WebInstallAction, SwitchToPreReleaseVersionAction, SwitchToReleasedVersionAction, MigrateDeprecatedExtensionAction
} from 'vs/workbench/contrib/extensions/browser/extensionsActions';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
Expand All @@ -49,7 +49,6 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten
import { getDefaultValue } from 'vs/platform/configuration/common/configurationRegistry';
import { isUndefined } from 'vs/base/common/types';
import { IWebviewService, IWebview, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED } from 'vs/workbench/contrib/webview/browser/webview';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { generateUuid } from 'vs/base/common/uuid';
import { platform } from 'vs/base/common/process';
import { URI } from 'vs/base/common/uri';
Expand Down Expand Up @@ -306,12 +305,15 @@ export class ExtensionEditor extends EditorPane {
rating.setAttribute('role', 'link'); // #132645
const ratingsWidget = this.instantiationService.createInstance(RatingsWidget, rating, false);

const sponsorWidget = this.instantiationService.createInstance(SponsorWidget, append(subtitle, $('.subtitle-entry')));

const widgets: ExtensionWidget[] = [
remoteBadge,
versionWidget,
preReleaseWidget,
installCountWidget,
ratingsWidget,
sponsorWidget,
];

const description = append(details, $('.description'));
Expand Down Expand Up @@ -352,9 +354,6 @@ export class ExtensionEditor extends EditorPane {
if (action instanceof ExtensionDropDownAction) {
return action.createActionViewItem();
}
if (action instanceof SponsorExtensionAction) {
return new SponsorExtensionActionViewItem(undefined, action, { icon: true, label: true });
}
if (action instanceof ActionWithDropDownAction) {
return new ExtensionActionWithDropdownActionViewItem(action, { icon: true, label: true, menuActionsOrProvider: { getActions: () => action.menuActions }, menuActionClassNames: (action.class || '').split(' ') }, this.contextMenuService);
}
Expand Down Expand Up @@ -436,20 +435,6 @@ export class ExtensionEditor extends EditorPane {
};
}

private onClick(element: HTMLElement, callback: () => void): IDisposable {
const disposables: DisposableStore = new DisposableStore();
disposables.add(addDisposableListener(element, EventType.CLICK, finalHandler(callback)));
disposables.add(addDisposableListener(element, EventType.KEY_UP, e => {
const keyboardEvent = new StandardKeyboardEvent(e);
if (keyboardEvent.equals(KeyCode.Space) || keyboardEvent.equals(KeyCode.Enter)) {
e.preventDefault();
e.stopPropagation();
callback();
}
}));
return disposables;
}

override async setInput(input: ExtensionsInput, options: IExtensionEditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise<void> {
await super.setInput(input, options, context, token);
this.updatePreReleaseVersionContext();
Expand Down Expand Up @@ -545,9 +530,9 @@ export class ExtensionEditor extends EditorPane {
template.rating.classList.toggle('clickable', !!extension.url);

if (extension.url) {
this.transientDisposables.add(this.onClick(template.name, () => this.openerService.open(URI.parse(extension.url!))));
this.transientDisposables.add(this.onClick(template.rating, () => this.openerService.open(URI.parse(`${extension.url}&ssr=false#review-details`))));
this.transientDisposables.add(this.onClick(template.publisher, () => {
this.transientDisposables.add(onClick(template.name, () => this.openerService.open(URI.parse(extension.url!))));
this.transientDisposables.add(onClick(template.rating, () => this.openerService.open(URI.parse(`${extension.url}&ssr=false#review-details`))));
this.transientDisposables.add(onClick(template.publisher, () => {
this.paneCompositeService.openPaneComposite(VIEWLET_ID, ViewContainerLocation.Sidebar, true)
.then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer)
.then(viewlet => viewlet.search(`publisher:"${extension.publisherDisplayName}"`));
Expand Down Expand Up @@ -902,7 +887,7 @@ export class ExtensionEditor extends EditorPane {
append(categoriesContainer, $('.additional-details-title', undefined, localize('categories', "Categories")));
const categoriesElement = append(categoriesContainer, $('.categories'));
for (const category of extension.categories) {
this.transientDisposables.add(this.onClick(append(categoriesElement, $('span.category', { tabindex: '0' }, category)), () => {
this.transientDisposables.add(onClick(append(categoriesElement, $('span.category', { tabindex: '0' }, category)), () => {
this.paneCompositeService.openPaneComposite(VIEWLET_ID, ViewContainerLocation.Sidebar, true)
.then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer)
.then(viewlet => viewlet.search(`@category:"${category}"`));
Expand Down Expand Up @@ -930,17 +915,7 @@ export class ExtensionEditor extends EditorPane {
append(extensionResourcesContainer, $('.additional-details-title', undefined, localize('resources', "Extension Resources")));
const resourcesElement = append(extensionResourcesContainer, $('.resources'));
for (const [label, uri] of resources) {
this.transientDisposables.add(this.onClick(append(resourcesElement, $('a.resource', { title: uri.toString(), tabindex: '0' }, label)), () => this.openerService.open(uri)));
}
if (extension.publisherSponsorLink) {
const extensionSponsorContainer = append(resourcesElement, $('.extension-sponsor-container'));
const extensionActionBar = this.transientDisposables.add(new ActionBar(extensionSponsorContainer, {
animated: false,
actionViewItemProvider: (action: IAction) => new SponsorExtensionActionViewItem(undefined, action, { icon: true, label: true }),
}));
const action = this.instantiationService.createInstance(SponsorExtensionAction);
extensionActionBar.push([action], { icon: true, label: true });
action.extension = extension;
this.transientDisposables.add(onClick(append(resourcesElement, $('a.resource', { title: uri.toString(), tabindex: '0' }, label)), () => this.openerService.open(uri)));
}
}
}
Expand Down
58 changes: 0 additions & 58 deletions src/vs/workbench/contrib/extensions/browser/extensionsActions.ts
Expand Up @@ -66,10 +66,6 @@ import { ViewContainerLocation } from 'vs/workbench/common/views';
import { flatten } from 'vs/base/common/arrays';
import { fromNow } from 'vs/base/common/date';
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
import { renderIcon } from 'vs/base/browser/ui/iconLabel/iconLabels';
import { Codicon } from 'vs/base/common/codicons';
import { assertType } from 'vs/base/common/types';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';

export class PromptExtensionInstallFailureAction extends Action {

Expand Down Expand Up @@ -881,57 +877,6 @@ export class MigrateDeprecatedExtensionAction extends ExtensionAction {
}
}

export class SponsorExtensionAction extends ExtensionAction {

private static readonly EnabledClass = `${SponsorExtensionAction.LABEL_ACTION_CLASS} extension-sponsor`;
private static readonly DisabledClass = `${SponsorExtensionAction.EnabledClass} disabled`;

constructor(
@IOpenerService private openerService: IOpenerService,
@ITelemetryService private telemetryService: ITelemetryService,
) {
super('extensionsAction.sponsorExtension', localize('sponsor', "Sponsor"), SponsorExtensionAction.DisabledClass, false);
this.update();
}

update(): void {
this.enabled = false;
this.class = SponsorExtensionAction.DisabledClass;
this.tooltip = '';
if (this.extension?.publisherSponsorLink) {
this.enabled = true;
this.class = SponsorExtensionAction.EnabledClass;
this.tooltip = this.extension.publisherSponsorLink.toString();
}
}

override async run(): Promise<any> {
if (this.extension?.publisherSponsorLink) {
type SponsorExtensionClassification = {
owner: 'sandy081';
comment: 'Reporting when sponosor extension action is executed';
'extensionId': { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Id of the extension to be sponsored' };
};
type SponsorExtensionEvent = {
'extensionId': string;
};
this.telemetryService.publicLog2<SponsorExtensionEvent, SponsorExtensionClassification>('extensionsAction.sponsorExtension', { extensionId: this.extension.identifier.id });
return this.openerService.open(this.extension.publisherSponsorLink);
}
}
}

export class SponsorExtensionActionViewItem extends ActionViewItem {
override render(container: HTMLElement): void {
super.render(container);
assertType(this.label);
const sponsorIcon = renderIcon(Codicon.heart);
const label = document.createElement('span');
label.textContent = this.getAction().label;
DOM.reset(this.label, sponsorIcon, label);
}
}

export class ExtensionActionWithDropdownActionViewItem extends ActionWithDropdownActionViewItem {

constructor(
Expand Down Expand Up @@ -2897,9 +2842,6 @@ export const extensionButtonProminentHoverBackground = registerColor('extensionB
hcLight: null
}, localize('extensionButtonProminentHoverBackground', "Button background hover color for actions extension that stand out (e.g. install button)."));

registerColor('extensionSponsorButton.background', { light: '#B51E78', dark: '#B51E78', hcDark: null, hcLight: '#B51E78' }, localize('extensionSponsorButton.background', "Background color for extension sponsor button."), true);
registerColor('extensionSponsorButton.hoverBackground', { light: '#D61B8C', dark: '#D61B8C', hcDark: null, hcLight: '#D61B8C' }, localize('extensionSponsorButton.hoverBackground', "Background hover color for extension sponsor button."), true);

registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => {
const foregroundColor = theme.getColor(foreground);
if (foregroundColor) {
Expand Down