Removal of feedback control (fix #188960) (#189027)

This commit is contained in:
Benjamin Pasero
2023-07-28 10:12:52 +02:00
committed by GitHub
parent 55b75c5504
commit cbdfe4e0b4
15 changed files with 18 additions and 878 deletions

View File

@@ -281,7 +281,6 @@
"workbench-editor-resolver": {"assign": ["lramos15"]},
"workbench-editors": {"assign": ["bpasero"]},
"workbench-electron": {"assign": ["deepak1556"]},
"workbench-feedback": {"assign": ["bpasero"]},
"workbench-fonts": {"assign": []},
"workbench-history": {"assign": ["bpasero"]},
"workbench-hot-exit": {"assign": ["bpasero"]},

File diff suppressed because one or more lines are too long

View File

@@ -86,10 +86,6 @@
"name": "vs/workbench/contrib/externalTerminal",
"project": "vscode-workbench"
},
{
"name": "vs/workbench/contrib/feedback",
"project": "vscode-workbench"
},
{
"name": "vs/workbench/contrib/files",
"project": "vscode-workbench"

View File

@@ -130,11 +130,6 @@ export interface IProductConfiguration {
readonly ariaKey: string;
};
readonly sendASmile?: {
readonly reportIssueUrl: string;
readonly requestFeatureUrl: string;
};
readonly documentationUrl?: string;
readonly serverDocumentationUrl?: string;
readonly releaseNotesUrl?: string;
@@ -144,7 +139,7 @@ export interface IProductConfiguration {
readonly introductoryVideosUrl?: string;
readonly tipsAndTricksUrl?: string;
readonly newsletterSignupUrl?: string;
readonly twitterUrl?: string;
readonly youTubeUrl?: string;
readonly requestFeatureUrl?: string;
readonly reportIssueUrl?: string;
readonly reportMarketplaceIssueUrl?: string;

View File

@@ -125,9 +125,9 @@ export class Menubar {
this.fallbackMenuHandlers['workbench.action.clearRecentFiles'] = () => this.workspacesHistoryMainService.clearRecentlyOpened();
// Help Menu Items
const twitterUrl = this.productService.twitterUrl;
if (twitterUrl) {
this.fallbackMenuHandlers['workbench.action.openTwitterUrl'] = () => this.openUrl(twitterUrl, 'openTwitterUrl');
const youTubeUrl = this.productService.youTubeUrl;
if (youTubeUrl) {
this.fallbackMenuHandlers['workbench.action.openYouTubeUrl'] = () => this.openUrl(youTubeUrl, 'openYouTubeUrl');
}
const requestFeatureUrl = this.productService.requestFeatureUrl;

View File

@@ -177,18 +177,18 @@ class OpenNewsletterSignupUrlAction extends Action2 {
}
}
class OpenTwitterUrlAction extends Action2 {
class OpenYouTubeUrlAction extends Action2 {
static readonly ID = 'workbench.action.openTwitterUrl';
static readonly AVAILABLE = !!product.twitterUrl;
static readonly ID = 'workbench.action.openYouTubeUrl';
static readonly AVAILABLE = !!product.youTubeUrl;
constructor() {
super({
id: OpenTwitterUrlAction.ID,
id: OpenYouTubeUrlAction.ID,
title: {
value: localize('openTwitterUrl', "Join Us on Twitter"),
mnemonicTitle: localize({ key: 'miTwitter', comment: ['&& denotes a mnemonic'] }, "&&Join Us on Twitter"),
original: 'Join Us on Twitter'
value: localize('openYouTubeUrl', "Join Us on YouTube"),
mnemonicTitle: localize({ key: 'miYouTube', comment: ['&& denotes a mnemonic'] }, "&&Join Us on YouTube"),
original: 'Join Us on YouTube'
},
category: Categories.Help,
f1: true,
@@ -204,8 +204,8 @@ class OpenTwitterUrlAction extends Action2 {
const productService = accessor.get(IProductService);
const openerService = accessor.get(IOpenerService);
if (productService.twitterUrl) {
openerService.open(URI.parse(productService.twitterUrl));
if (productService.youTubeUrl) {
openerService.open(URI.parse(productService.youTubeUrl));
}
}
}
@@ -337,8 +337,8 @@ if (OpenNewsletterSignupUrlAction.AVAILABLE) {
registerAction2(OpenNewsletterSignupUrlAction);
}
if (OpenTwitterUrlAction.AVAILABLE) {
registerAction2(OpenTwitterUrlAction);
if (OpenYouTubeUrlAction.AVAILABLE) {
registerAction2(OpenYouTubeUrlAction);
}
if (OpenRequestFeatureUrlAction.AVAILABLE) {

View File

@@ -1,11 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Registry } from 'vs/platform/registry/common/platform';
import { FeedbackStatusbarConribution } from 'vs/workbench/contrib/feedback/browser/feedbackStatusbarItem';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(FeedbackStatusbarConribution, LifecyclePhase.Starting);

View File

@@ -1,455 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/feedback';
import { localize } from 'vs/nls';
import { IDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IIntegrityService } from 'vs/workbench/services/integrity/common/integrity';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { editorWidgetBackground, editorWidgetForeground, widgetShadow, inputBorder, inputForeground, inputBackground, editorBackground, asCssVariable, asCssVariableWithDefault, widgetBorder, contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { append, $, addDisposableListener, EventType, EventHelper, prepend } from 'vs/base/browser/dom';
import { IAnchor } from 'vs/base/browser/ui/contextview/contextview';
import { Button } from 'vs/base/browser/ui/button/button';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions';
import { IStatusbarService } from 'vs/workbench/services/statusbar/browser/statusbar';
import { IProductService } from 'vs/platform/product/common/productService';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
import { Codicon } from 'vs/base/common/codicons';
import { ThemeIcon } from 'vs/base/common/themables';
import { Emitter } from 'vs/base/common/event';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { defaultButtonStyles } from 'vs/platform/theme/browser/defaultStyles';
export interface IFeedback {
feedback: string;
sentiment: number;
}
export interface IFeedbackDelegate {
submitFeedback(feedback: IFeedback, openerService: IOpenerService): void;
getCharacterLimit(sentiment: number): number;
}
export interface IFeedbackWidgetOptions {
feedbackService: IFeedbackDelegate;
}
export class FeedbackWidget extends Disposable {
private visible: boolean | undefined;
private _onDidChangeVisibility = new Emitter<boolean>();
readonly onDidChangeVisibility = this._onDidChangeVisibility.event;
private maxFeedbackCharacters: number;
private feedback: string = '';
private sentiment: number = 1;
private readonly feedbackDelegate: IFeedbackDelegate;
private feedbackForm: HTMLFormElement | undefined = undefined;
private feedbackDescriptionInput: HTMLTextAreaElement | undefined = undefined;
private smileyInput: HTMLElement | undefined = undefined;
private frownyInput: HTMLElement | undefined = undefined;
private sendButton: Button | undefined = undefined;
private hideButton: HTMLInputElement | undefined = undefined;
private remainingCharacterCount: HTMLElement | undefined = undefined;
private requestFeatureLink: string | undefined;
private isPure: boolean = true;
constructor(
options: IFeedbackWidgetOptions,
@IContextViewService private readonly contextViewService: IContextViewService,
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
@ICommandService private readonly commandService: ICommandService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IIntegrityService private readonly integrityService: IIntegrityService,
@IThemeService private readonly themeService: IThemeService,
@IStatusbarService private readonly statusbarService: IStatusbarService,
@IProductService productService: IProductService,
@IOpenerService private readonly openerService: IOpenerService
) {
super();
this.feedbackDelegate = options.feedbackService;
this.maxFeedbackCharacters = this.feedbackDelegate.getCharacterLimit(this.sentiment);
if (productService.sendASmile) {
this.requestFeatureLink = productService.sendASmile.requestFeatureUrl;
}
this.integrityService.isPure().then(result => {
if (!result.isPure) {
this.isPure = false;
}
});
// Hide feedback widget whenever notifications appear
this._register(this.layoutService.onDidChangeNotificationsVisibility(visible => {
if (visible) {
this.hide();
}
}));
}
private getAnchor(): IAnchor {
const dimension = this.layoutService.dimension;
return {
x: dimension.width - 8,
y: dimension.height - 31
};
}
private renderContents(container: HTMLElement): IDisposable {
const disposables = new DisposableStore();
container.classList.add('monaco-menu-container');
// Form
this.feedbackForm = append<HTMLFormElement>(container, $('form.feedback-form'));
this.feedbackForm.setAttribute('action', 'javascript:void(0);');
// Title
append(this.feedbackForm, $('h2.title')).textContent = localize("label.sendASmile", "Tweet us your feedback.");
// Close Button (top right)
const closeBtn = append(this.feedbackForm, $(`div.cancel${ThemeIcon.asCSSSelector(Codicon.close)}`));
closeBtn.tabIndex = 0;
closeBtn.setAttribute('role', 'button');
closeBtn.title = localize('close', "Close");
disposables.add(addDisposableListener(container, EventType.KEY_DOWN, keyboardEvent => {
const standardKeyboardEvent = new StandardKeyboardEvent(keyboardEvent);
if (standardKeyboardEvent.keyCode === KeyCode.Escape) {
this.hide();
}
}));
disposables.add(addDisposableListener(closeBtn, EventType.MOUSE_OVER, () => {
const theme = this.themeService.getColorTheme();
let darkenFactor: number | undefined;
switch (theme.type) {
case 'light':
darkenFactor = 0.1;
break;
case 'dark':
darkenFactor = 0.2;
break;
}
if (darkenFactor) {
const backgroundBaseColor = theme.getColor(editorWidgetBackground);
if (backgroundBaseColor) {
const backgroundColor = backgroundBaseColor.darken(darkenFactor);
if (backgroundColor) {
closeBtn.style.backgroundColor = backgroundColor.toString();
}
}
}
}));
disposables.add(addDisposableListener(closeBtn, EventType.MOUSE_OUT, () => {
closeBtn.style.backgroundColor = '';
}));
this.invoke(closeBtn, disposables, () => this.hide());
// Content
const content = append(this.feedbackForm, $('div.content'));
// Sentiment Buttons
const sentimentContainer = append(content, $('div'));
if (!this.isPure) {
append(sentimentContainer, $('span')).textContent = localize("patchedVersion1", "Your installation is corrupt.");
sentimentContainer.appendChild(document.createElement('br'));
append(sentimentContainer, $('span')).textContent = localize("patchedVersion2", "Please specify this if you submit a bug.");
sentimentContainer.appendChild(document.createElement('br'));
}
append(sentimentContainer, $('span')).textContent = localize("sentiment", "How was your experience?");
const feedbackSentiment = append(sentimentContainer, $('div.feedback-sentiment'));
// Sentiment: Smiley
this.smileyInput = append(feedbackSentiment, $('div.sentiment'));
this.smileyInput.classList.add('smile');
this.smileyInput.setAttribute('aria-checked', 'false');
this.smileyInput.setAttribute('aria-label', localize('smileCaption', "Happy Feedback Sentiment"));
this.smileyInput.setAttribute('role', 'checkbox');
this.smileyInput.title = localize('smileCaption', "Happy Feedback Sentiment");
this.smileyInput.tabIndex = 0;
this.invoke(this.smileyInput, disposables, () => this.setSentiment(true));
// Sentiment: Frowny
this.frownyInput = append(feedbackSentiment, $('div.sentiment'));
this.frownyInput.classList.add('frown');
this.frownyInput.setAttribute('aria-checked', 'false');
this.frownyInput.setAttribute('aria-label', localize('frownCaption', "Sad Feedback Sentiment"));
this.frownyInput.setAttribute('role', 'checkbox');
this.frownyInput.title = localize('frownCaption', "Sad Feedback Sentiment");
this.frownyInput.tabIndex = 0;
this.invoke(this.frownyInput, disposables, () => this.setSentiment(false));
if (this.sentiment === 1) {
this.smileyInput.classList.add('checked');
this.smileyInput.setAttribute('aria-checked', 'true');
} else {
this.frownyInput.classList.add('checked');
this.frownyInput.setAttribute('aria-checked', 'true');
}
// Contact Us Box
const contactUsContainer = append(content, $('div.contactus'));
append(contactUsContainer, $('span')).textContent = localize("other ways to contact us", "Other ways to contact us");
const channelsContainer = append(contactUsContainer, $('div.channels'));
// Contact: Submit a Bug
const submitBugLinkContainer = append(channelsContainer, $('div'));
const submitBugLink = append(submitBugLinkContainer, $('a'));
submitBugLink.setAttribute('target', '_blank');
submitBugLink.setAttribute('href', '#');
submitBugLink.textContent = localize("submit a bug", "Submit a bug");
submitBugLink.tabIndex = 0;
disposables.add(addDisposableListener(submitBugLink, 'click', e => {
EventHelper.stop(e);
const actionId = 'workbench.action.openIssueReporter';
this.commandService.executeCommand(actionId);
this.hide();
this.telemetryService.publicLog2<WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification>('workbenchActionExecuted', { id: actionId, from: 'feedback' });
}));
// Contact: Request a Feature
if (!!this.requestFeatureLink) {
const requestFeatureLinkContainer = append(channelsContainer, $('div'));
const requestFeatureLink = append(requestFeatureLinkContainer, $('a'));
requestFeatureLink.setAttribute('target', '_blank');
requestFeatureLink.setAttribute('href', this.requestFeatureLink);
requestFeatureLink.textContent = localize("request a missing feature", "Request a missing feature");
requestFeatureLink.tabIndex = 0;
disposables.add(addDisposableListener(requestFeatureLink, 'click', e => this.hide()));
}
// Remaining Characters
const remainingCharacterCountContainer = append(this.feedbackForm, $('h3'));
remainingCharacterCountContainer.textContent = localize("tell us why", "Tell us why?");
this.remainingCharacterCount = append(remainingCharacterCountContainer, $('span.char-counter'));
this.remainingCharacterCount.textContent = this.getCharCountText(0);
// Feedback Input Form
this.feedbackDescriptionInput = append<HTMLTextAreaElement>(this.feedbackForm, $('textarea.feedback-description'));
this.feedbackDescriptionInput.rows = 3;
this.feedbackDescriptionInput.maxLength = this.maxFeedbackCharacters;
this.feedbackDescriptionInput.textContent = this.feedback;
this.feedbackDescriptionInput.required = true;
this.feedbackDescriptionInput.setAttribute('aria-label', localize("feedbackTextInput", "Tell us your feedback"));
this.feedbackDescriptionInput.focus();
disposables.add(addDisposableListener(this.feedbackDescriptionInput, 'keyup', () => this.updateCharCountText()));
// Feedback Input Form Buttons Container
const buttonsContainer = append(this.feedbackForm, $('div.form-buttons'));
// Checkbox: Hide Feedback Smiley
const hideButtonContainer = append(buttonsContainer, $('div.hide-button-container'));
this.hideButton = append(hideButtonContainer, $('input.hide-button')) as HTMLInputElement;
this.hideButton.type = 'checkbox';
this.hideButton.checked = true;
this.hideButton.id = 'hide-button';
const hideButtonLabel = append(hideButtonContainer, $('label'));
hideButtonLabel.setAttribute('for', 'hide-button');
hideButtonLabel.textContent = localize('showFeedback', "Show Feedback Icon in Status Bar");
// Button: Send Feedback
this.sendButton = new Button(buttonsContainer, defaultButtonStyles);
this.sendButton.enabled = false;
this.sendButton.label = localize('tweet', "Tweet");
prepend(this.sendButton.element, $(`span${ThemeIcon.asCSSSelector(Codicon.twitter)}`));
this.sendButton.element.classList.add('send');
this.sendButton.element.title = localize('tweetFeedback', "Tweet Feedback");
this.sendButton.onDidClick(() => this.onSubmit());
if (this.feedbackForm) {
this.feedbackForm.style.backgroundColor = asCssVariable(editorWidgetBackground);
this.feedbackForm.style.color = asCssVariable(editorWidgetForeground);
this.feedbackForm.style.boxShadow = `0 0 8px 2px ${asCssVariable(widgetShadow)}`;
this.feedbackForm.style.border = `1px solid ${asCssVariable(widgetBorder)}`;
}
if (this.feedbackDescriptionInput) {
this.feedbackDescriptionInput.style.backgroundColor = asCssVariable(inputBackground);
this.feedbackDescriptionInput.style.color = asCssVariable(inputForeground);
this.feedbackDescriptionInput.style.border = `1px solid ${asCssVariableWithDefault(inputBorder, 'transparent')}`;
}
contactUsContainer.style.backgroundColor = asCssVariable(editorBackground);
contactUsContainer.style.border = `1px solid ${asCssVariableWithDefault(contrastBorder, 'transparent')}`;
return {
dispose: () => {
this.feedbackForm = undefined;
this.feedbackDescriptionInput = undefined;
this.smileyInput = undefined;
this.frownyInput = undefined;
disposables.dispose();
}
};
}
private updateFeedbackDescription() {
if (this.feedbackDescriptionInput && this.feedbackDescriptionInput.textLength > this.maxFeedbackCharacters) {
this.feedbackDescriptionInput.value = this.feedbackDescriptionInput.value.substring(0, this.maxFeedbackCharacters);
}
}
private getCharCountText(charCount: number): string {
const remaining = this.maxFeedbackCharacters - charCount;
const text = (remaining === 1)
? localize("character left", "character left")
: localize("characters left", "characters left");
return `(${remaining} ${text})`;
}
private updateCharCountText(): void {
if (this.feedbackDescriptionInput && this.remainingCharacterCount && this.sendButton) {
this.remainingCharacterCount.innerText = this.getCharCountText(this.feedbackDescriptionInput.value.length);
this.sendButton.enabled = this.feedbackDescriptionInput.value.length > 0;
}
}
private setSentiment(smile: boolean): void {
if (smile) {
if (this.smileyInput) {
this.smileyInput.classList.add('checked');
this.smileyInput.setAttribute('aria-checked', 'true');
}
if (this.frownyInput) {
this.frownyInput.classList.remove('checked');
this.frownyInput.setAttribute('aria-checked', 'false');
}
} else {
if (this.frownyInput) {
this.frownyInput.classList.add('checked');
this.frownyInput.setAttribute('aria-checked', 'true');
}
if (this.smileyInput) {
this.smileyInput.classList.remove('checked');
this.smileyInput.setAttribute('aria-checked', 'false');
}
}
this.sentiment = smile ? 1 : 0;
this.maxFeedbackCharacters = this.feedbackDelegate.getCharacterLimit(this.sentiment);
this.updateFeedbackDescription();
this.updateCharCountText();
if (this.feedbackDescriptionInput) {
this.feedbackDescriptionInput.maxLength = this.maxFeedbackCharacters;
}
}
private invoke(element: HTMLElement, disposables: DisposableStore, callback: () => void): HTMLElement {
disposables.add(addDisposableListener(element, 'click', callback));
disposables.add(addDisposableListener(element, 'keypress', e => {
if (e instanceof KeyboardEvent) {
const keyboardEvent = <KeyboardEvent>e;
if (keyboardEvent.keyCode === 13 || keyboardEvent.keyCode === 32) { // Enter or Spacebar
callback();
}
}
}));
return element;
}
show(): void {
if (this.visible) {
return;
}
this.visible = true;
this.contextViewService.showContextView({
getAnchor: () => this.getAnchor(),
render: (container) => {
return this.renderContents(container);
},
onDOMEvent: (e, activeElement) => {
this.onEvent(e, activeElement);
},
onHide: () => this._onDidChangeVisibility.fire(false)
});
this._onDidChangeVisibility.fire(true);
this.updateCharCountText();
}
hide(): void {
if (!this.visible) {
return;
}
if (this.feedbackDescriptionInput) {
this.feedback = this.feedbackDescriptionInput.value;
}
if (this.hideButton && !this.hideButton.checked) {
this.statusbarService.updateEntryVisibility('status.feedback', false);
}
this.visible = false;
this.contextViewService.hideContextView();
}
isVisible(): boolean {
return !!this.visible;
}
private onEvent(e: Event, activeElement: HTMLElement): void {
if (e instanceof KeyboardEvent) {
const keyboardEvent = <KeyboardEvent>e;
if (keyboardEvent.keyCode === 27) { // Escape
this.hide();
}
}
}
private onSubmit(): void {
if (!this.feedbackForm || !this.feedbackDescriptionInput || (this.feedbackForm.checkValidity && !this.feedbackForm.checkValidity())) {
return;
}
this.feedbackDelegate.submitFeedback({
feedback: this.feedbackDescriptionInput.value,
sentiment: this.sentiment
}, this.openerService);
this.hide();
}
}

View File

@@ -1,118 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Disposable } from 'vs/base/common/lifecycle';
import { FeedbackWidget, IFeedback, IFeedbackDelegate } from 'vs/workbench/contrib/feedback/browser/feedback';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IProductService } from 'vs/platform/product/common/productService';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IStatusbarService, StatusbarAlignment, IStatusbarEntry, IStatusbarEntryAccessor } from 'vs/workbench/services/statusbar/browser/statusbar';
import { localize } from 'vs/nls';
import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { URI } from 'vs/base/common/uri';
import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
import { Categories } from 'vs/platform/action/common/actionCommonCategories';
import { HIDE_NOTIFICATIONS_CENTER, HIDE_NOTIFICATION_TOAST } from 'vs/workbench/browser/parts/notifications/notificationsCommands';
import { isIOS } from 'vs/base/common/platform';
class TwitterFeedbackService implements IFeedbackDelegate {
private static TWITTER_URL: string = 'https://twitter.com/intent/tweet';
private static VIA_NAME: string = 'code';
private static HASHTAGS: string[] = ['HappyCoding'];
private combineHashTagsAsString(): string {
return TwitterFeedbackService.HASHTAGS.join(',');
}
submitFeedback(feedback: IFeedback, openerService: IOpenerService): void {
const queryString = `?${feedback.sentiment === 1 ? `hashtags=${this.combineHashTagsAsString()}&` : ''}ref_src=twsrc%5Etfw&related=twitterapi%2Ctwitter&text=${encodeURIComponent(feedback.feedback)}&tw_p=tweetbutton&via=${TwitterFeedbackService.VIA_NAME}`;
const url = TwitterFeedbackService.TWITTER_URL + queryString;
openerService.open(URI.parse(url));
}
getCharacterLimit(sentiment: number): number {
let length: number = 0;
if (sentiment === 1) {
TwitterFeedbackService.HASHTAGS.forEach(element => {
length += element.length + 2;
});
}
if (TwitterFeedbackService.VIA_NAME) {
length += ` via @${TwitterFeedbackService.VIA_NAME}`.length;
}
return 280 - length;
}
}
export class FeedbackStatusbarConribution extends Disposable implements IWorkbenchContribution {
private static readonly TOGGLE_FEEDBACK_COMMAND = 'help.tweetFeedback';
private widget: FeedbackWidget | undefined;
private entry: IStatusbarEntryAccessor | undefined;
constructor(
@IStatusbarService private readonly statusbarService: IStatusbarService,
@IProductService productService: IProductService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@ICommandService private readonly commandService: ICommandService) {
super();
if (productService.sendASmile && !isIOS) {
this.createFeedbackStatusEntry();
}
}
private createFeedbackStatusEntry(): void {
// Status entry
this.entry = this._register(this.statusbarService.addEntry(this.getStatusEntry(), 'status.feedback', StatusbarAlignment.RIGHT, -100 /* towards the end of the right hand side */));
// Command to toggle
CommandsRegistry.registerCommand(FeedbackStatusbarConribution.TOGGLE_FEEDBACK_COMMAND, () => this.toggleFeedback());
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
command: {
id: FeedbackStatusbarConribution.TOGGLE_FEEDBACK_COMMAND,
category: Categories.Help,
title: localize('status.feedback', "Tweet Feedback")
}
});
}
private toggleFeedback(): void {
if (!this.widget) {
this.widget = this._register(this.instantiationService.createInstance(FeedbackWidget, {
feedbackService: this.instantiationService.createInstance(TwitterFeedbackService)
}));
this._register(this.widget.onDidChangeVisibility(visible => this.entry!.update(this.getStatusEntry(visible))));
}
if (this.widget) {
if (!this.widget.isVisible()) {
this.commandService.executeCommand(HIDE_NOTIFICATION_TOAST);
this.commandService.executeCommand(HIDE_NOTIFICATIONS_CENTER);
this.widget.show();
} else {
this.widget.hide();
}
}
}
private getStatusEntry(showBeak?: boolean): IStatusbarEntry {
return {
name: localize('status.feedback.name', "Feedback"),
text: '$(feedback)',
ariaLabel: localize('status.feedback', "Tweet Feedback"),
tooltip: localize('status.feedback', "Tweet Feedback"),
command: FeedbackStatusbarConribution.TOGGLE_FEEDBACK_COMMAND,
showBeak
};
}
}

View File

@@ -1,245 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-workbench .feedback-form {
width: 420px;
top: 30px;
right: 6px;
padding: 1em;
cursor: default;
border-radius: 4px;
}
.monaco-workbench .feedback-form h2 {
margin: 0;
padding: 0;
font-weight: normal;
font-size: 1.8em;
}
.monaco-workbench .feedback-form h3 {
margin: 1em 0 0;
padding: 0;
font-weight: normal;
font-size: 1.2em;
}
.monaco-workbench .feedback-form .content {
font-size: 1.2em;
}
.monaco-workbench .feedback-form .content > div {
display: inline-block;
vertical-align: top;
margin-top: 20px;
}
.monaco-workbench .feedback-form .content .contactus {
padding: 10px;
float: right;
}
.monaco-workbench .feedback-form .content .channels {
margin-top: 5px;
font-size: 0.9em;
}
.monaco-workbench .char-counter {
padding-left: 3px;
}
.monaco-workbench .feedback-form .content .channels a {
padding: 2px 0;
color: var(--vscode-textLink-foreground, var(--vscode-contrastBorder));
}
.monaco-workbench .feedback-form .content .channels a:hover {
text-decoration: underline;
}
.monaco-workbench .feedback-form .feedback-alias, .monaco-workbench .feedback-form .feedback-description {
resize: none;
font-size: 1.1em;
margin: 6px 0 0;
width: 100%;
padding: 3px 2px;
box-sizing: border-box;
}
.monaco-workbench .feedback-form .feedback-empty {
margin-top: .2em;
}
.monaco-workbench .feedback-form .feedback-alias-checkbox {
display: inline-block;
vertical-align: text-top;
margin-left: 0;
}
.monaco-workbench .feedback-form .feedback-alias {
height: 26px;
}
.monaco-workbench .feedback-form .feedback-alias:disabled {
opacity: 0.6;
}
.monaco-workbench .feedback-form .cancel {
position: absolute;
top: 0;
right: 0;
padding: .5em;
width: 22px;
height: 22px;
line-height: 22px;
border: none;
cursor: pointer;
}
.monaco-workbench .feedback-form .form-buttons {
margin-top: 18px;
text-align: right;
}
.monaco-workbench .feedback-form .sentiment {
height: 32px;
width: 32px;
display: inline-block;
margin: .5em 0;
cursor: pointer;
box-sizing: border-box;
}
.monaco-workbench .feedback-form .sentiment:hover {
background-color: #eaeaea;
}
.monaco-workbench .feedback-form .sentiment.checked {
border: 1px solid var(--vscode-inputOption-activeBorder);
}
/* Theming */
.monaco-workbench.vs .feedback-form .feedback-alias,
.monaco-workbench.vs .feedback-form .feedback-description {
font-family: inherit;
border: 1px solid transparent;
}
.monaco-workbench .feedback-form .form-buttons {
display: flex;
}
.monaco-workbench .feedback-form .form-buttons .hide-button-container {
display: flex;
}
.monaco-workbench .feedback-form .form-buttons .hide-button-container input,
.monaco-workbench .feedback-form .form-buttons .hide-button-container label {
align-self: center;
}
.monaco-workbench .feedback-form .form-buttons .send {
width: auto;
padding: 8px 12px;
margin-left: auto;
display: flex;
align-items: center;
}
.monaco-workbench .feedback-form .form-buttons .send .codicon {
color: currentColor;
font-size: 20px;
padding-right: 8px;
}
.monaco-workbench .feedback-form .form-buttons .send.in-progress,
.monaco-workbench .feedback-form .form-buttons .send:hover {
background-color: #006BB3;
}
.monaco-workbench .feedback-form .form-buttons .send:disabled {
pointer-events: none;
cursor: not-allowed;
opacity: .65;
}
.monaco-workbench .feedback-form .form-buttons .send.success {
background-color: #2d883e;
}
.monaco-workbench .feedback-form .form-buttons .send.error {
background-color: #E51400;
}
.monaco-workbench.vs-dark .feedback-form h3 {
font-weight: normal;
font-size: 1.2em;
}
.monaco-workbench.vs-dark .feedback-form .sentiment:hover {
background-color: rgba(30,30,30,0.8);
}
.monaco-workbench.vs-dark .feedback-form .feedback-alias,
.monaco-workbench.vs-dark .feedback-form .feedback-description {
font-family: inherit;
}
.monaco-workbench .feedback-form .sentiment.smile {
background-image: url('happy.svg');
background-position: center;
background-repeat: no-repeat;
}
.monaco-workbench .feedback-form .sentiment.frown {
background-image: url('sad.svg');
background-position: center;
background-repeat: no-repeat;
}
/* High Contrast Theming */
.monaco-workbench.hc-black .feedback-form {
outline: 2px solid #6fc3df;
outline-offset: -2px;
}
.monaco-workbench.hc-light .feedback-form {
outline: 2px solid #0F4A85;
outline-offset: -2px;
}
.monaco-workbench.hc-black .feedback-form .feedback-alias,
.monaco-workbench.hc-black .feedback-form .feedback-description,
.monaco-workbench.hc-light .feedback-form .feedback-alias,
.monaco-workbench.hc-light .feedback-form .feedback-description {
font-family: inherit;
}
.monaco-workbench.hc-black .feedback-form .content .contactus,
.monaco-workbench.hc-light .feedback-form .content .contactus {
padding: 10px;
float: right;
}
.monaco-workbench.hc-black .feedback-form .form-buttons .send,
.monaco-workbench.hc-black .feedback-form .form-buttons .send.in-progress,
.monaco-workbench.hc-black .feedback-form .form-buttons .send.success {
background-color: #0C141F;
color: #D4D4D4;
border: 1px solid #6FC3DF;
}
.monaco-workbench.hc-light .feedback-form .form-buttons .send,
.monaco-workbench.hc-light .feedback-form .form-buttons .send.in-progress,
.monaco-workbench.hc-light .feedback-form .form-buttons .send.success {
background-color: #FFFFFF;
color: #292929;
border: 1px solid #0F4A85;
}
.monaco-workbench.hc-black .feedback-form .form-buttons .send:hover {
background-color: #0C141F;
}

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 16 16" enable-background="new 0 0 16 16"><g><path fill="#fc0" d="M8 1c-3.866 0-7 3.134-7 7s3.134 7 7 7 7-3.134 7-7-3.134-7-7-7z"/><path fill="#424242" d="M11 6.5c0 .5-.447.5-1 .5s-1 0-1-.5c0-1.104 1-1.5 1-1.5s1 .396 1 1.5z"/><path fill="#424242" d="M10.943 9.025c-.457 1.177-1.644 2.058-2.943 2.058s-2.455-.881-2.899-2.058h-.952c.455 1.709 1.999 2.975 3.851 2.975 1.853 0 3.396-1.266 3.851-2.975h-.908z"/><path fill="#424242" d="M7 6.5s-.447.5-1 .5-1 0-1-.5c0-1.104 1-1.5 1-1.5s1 .396 1 1.5z"/></g></svg>

Before

Width:  |  Height:  |  Size: 580 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 16 16" enable-background="new 0 0 16 16"><g><circle fill="#fc0" cx="8" cy="8" r="7"/><path fill="#424242" d="M10.973 6.5c0 .5-.447.5-1 .5s-1 0-1-.5c0-1.104 1-1.5 1-1.5s1 .396 1 1.5z"/><path fill="#424242" d="M6.973 6.5c0 .5-.447.5-1 .5s-1 0-1-.5c0-1.104 1-1.5 1-1.5s1 .396 1 1.5z"/><path fill="#424242" d="M5.104 12c.459-1.146 1.593-2 2.869-2 1.277 0 2.439.854 2.912 2h.916c-.475-1.678-1.998-2.917-3.828-2.917s-3.355 1.239-3.83 2.917h.961z"/></g></svg>

Before

Width:  |  Height:  |  Size: 528 B

View File

@@ -326,9 +326,6 @@ import 'vs/workbench/contrib/languageDetection/browser/languageDetection.contrib
// Language Status
import 'vs/workbench/contrib/languageStatus/browser/languageStatus.contribution';
// Send a Smile
import 'vs/workbench/contrib/feedback/browser/feedback.contribution';
// User Data Sync
import 'vs/workbench/contrib/userDataSync/browser/userDataSync.contribution';

View File

@@ -13,8 +13,7 @@ export const enum StatusBarElement {
INDENTATION_STATUS = 4,
ENCODING_STATUS = 5,
EOL_STATUS = 6,
LANGUAGE_STATUS = 7,
FEEDBACK_ICON = 8
LANGUAGE_STATUS = 7
}
export class StatusBar {
@@ -57,8 +56,6 @@ export class StatusBar {
return `.statusbar-item[id="status.editor.eol"]`;
case StatusBarElement.LANGUAGE_STATUS:
return `.statusbar-item[id="status.editor.mode"]`;
case StatusBarElement.FEEDBACK_ICON:
return `.statusbar-item[id="status.feedback"]`;
default:
throw new Error(element);
}

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { join } from 'path';
import { Application, Quality, StatusBarElement, Logger } from '../../../../automation';
import { Application, StatusBarElement, Logger } from '../../../../automation';
import { installAllHandlers } from '../../utils';
export function setup(logger: Logger) {
@@ -16,9 +16,6 @@ export function setup(logger: Logger) {
it('verifies presence of all default status bar elements', async function () {
const app = this.app as Application;
await app.workbench.statusbar.waitForStatusbarElement(StatusBarElement.BRANCH_STATUS);
if (app.quality !== Quality.Dev && app.quality !== Quality.OSS) {
await app.workbench.statusbar.waitForStatusbarElement(StatusBarElement.FEEDBACK_ICON);
}
await app.workbench.statusbar.waitForStatusbarElement(StatusBarElement.SYNC_STATUS);
await app.workbench.statusbar.waitForStatusbarElement(StatusBarElement.PROBLEMS_STATUS);
@@ -66,15 +63,5 @@ export function setup(logger: Logger) {
await app.workbench.statusbar.waitForEOL('CRLF');
});
it(`verifies that 'Tweet us feedback' pop-up appears when clicking on 'Feedback' icon`, async function () {
const app = this.app as Application;
if (app.quality === Quality.Dev || app.quality === Quality.OSS) {
return this.skip();
}
await app.workbench.statusbar.clickOn(StatusBarElement.FEEDBACK_ICON);
await app.code.waitForElement('.feedback-form');
});
});
}