Port VS Code telemetry opt-in dialog (#1130)

This commit is contained in:
Karl Burtram
2018-04-11 15:47:34 -07:00
committed by GitHub
parent ed10f984b6
commit cd0210c88a
22 changed files with 240 additions and 107 deletions

View File

@@ -16,6 +16,8 @@
"darwinBundleIdentifier": "com.sqlopsstudio.oss",
"reportIssueUrl": "https://github.com/Microsoft/sqlopsstudio/issues/new?labels=customer%20reported%20issue",
"requestFeatureUrl": "https://github.com/Microsoft/sqlopsstudio/issues/new?labels=feature-request",
"privacyStatementUrl": "https://privacy.microsoft.com/en-us/privacystatement",
"telemetryOptOutUrl": "https://github.com/Microsoft/sqlopsstudio/wiki/How-to-Disable-Telemetry-Reporting",
"urlProtocol": "sqlops",
"enableTelemetry": true,
"aiConfig": {

View File

@@ -12,7 +12,7 @@ import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { Registry } from 'vs/platform/registry/common/platform';
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { ShowCurrentReleaseNotesAction, ProductContribution } from 'sql/workbench/update/releaseNotes';
import { ShowCurrentReleaseNotesAction } from 'sql/workbench/update/releaseNotes';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
new Actions.BackupAction().registerTask();
@@ -21,8 +21,5 @@ new Actions.NewQueryAction().registerTask();
new Actions.ConfigureDashboardAction().registerTask();
// add product update and release notes contributions
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench)
.registerWorkbenchContribution(ProductContribution, LifecyclePhase.Running);
Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions)
.registerWorkbenchAction(new SyncActionDescriptor(ShowCurrentReleaseNotesAction, ShowCurrentReleaseNotesAction.ID, ShowCurrentReleaseNotesAction.LABEL), 'Show Getting Started');

View File

@@ -49,39 +49,3 @@ export class ShowCurrentReleaseNotesAction extends AbstractShowReleaseNotesActio
super(id, label, pkg.version, editorService, instantiationService);
}
}
export class ProductContribution implements IWorkbenchContribution {
private static KEY = 'releaseNotes/carbonLastVersion';
getId() { return 'carbon.product'; }
constructor(
@IStorageService storageService: IStorageService,
@IInstantiationService instantiationService: IInstantiationService,
@INotificationService notificationService: INotificationService,
@IWorkbenchEditorService editorService: IWorkbenchEditorService
) {
const lastVersion = storageService.get(ProductContribution.KEY, StorageScope.GLOBAL, '');
// was there an update? if so, open release notes
if (product.releaseNotesUrl && pkg.version !== lastVersion) {
instantiationService.invokeFunction(loadReleaseNotes, pkg.version).then(
text => editorService.openEditor(instantiationService.createInstance(ReleaseNotesInput, pkg.version, text), { pinned: true }),
() => {
const actions: INotificationActions = {
primary: [
instantiationService.createInstance(OpenGettingStartedInBrowserAction)
]
};
notificationService.notify({
severity: Severity.Info,
message: nls.localize('read the release notes', "Welcome to {0} April Public Preview! Would you like to view the Getting Started Guide?", product.nameLong, pkg.version),
actions
});
});
}
storageService.store(ProductContribution.KEY, pkg.version, StorageScope.GLOBAL);
}
}

View File

@@ -38,7 +38,7 @@ import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKe
import { OS } from 'vs/base/common/platform';
import { IRange } from 'vs/editor/common/core/range';
import { ITextModel } from 'vs/editor/common/model';
import { INotificationService, INotification, INotificationHandle, NoOpNotification } from 'vs/platform/notification/common/notification';
import { INotificationService, INotification, INotificationHandle, NoOpNotification, IPromptChoice } from 'vs/platform/notification/common/notification';
import { IConfirmation, IConfirmationResult, IConfirmationService } from 'vs/platform/dialogs/common/dialogs';
import { IPosition, Position as Pos } from 'vs/editor/common/core/position';
@@ -292,6 +292,10 @@ export class SimpleNotificationService implements INotificationService {
return SimpleNotificationService.NO_OP;
}
public prompt(severity: Severity, message: string, choices: IPromptChoice[], onCancel?: () => void): INotificationHandle {
return SimpleNotificationService.NO_OP;
}
}
export class StandaloneCommandService implements ICommandService {

View File

@@ -19,7 +19,7 @@ import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKe
import { OS } from 'vs/base/common/platform';
import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding';
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
import { INotificationService, NoOpNotification, INotification } from 'vs/platform/notification/common/notification';
import { INotificationService, NoOpNotification, INotification, IPromptChoice } from 'vs/platform/notification/common/notification';
function createContext(ctx: any) {
return {
@@ -138,6 +138,9 @@ suite('AbstractKeybindingService', () => {
error: (message: any) => {
showMessageCalls.push({ sev: Severity.Error, message });
return new NoOpNotification();
},
prompt(severity: Severity, message: string, choices: IPromptChoice[], onCancel?: () => void): any {
throw new Error('not implemented');
}
};

View File

@@ -62,6 +62,7 @@ export interface IProductConfiguration {
reportIssueUrl: string;
licenseUrl: string;
privacyStatementUrl: string;
telemetryOptOutUrl: string;
npsSurveyUrl: string;
surveys: ISurveyData[];
checksums: { [path: string]: string; };

View File

@@ -89,12 +89,12 @@ export interface INotificationProgress {
done(): void;
}
export interface INotificationHandle extends IDisposable {
export interface INotificationHandle {
/**
* Will be fired once the notification is disposed.
* Will be fired once the notification is closed.
*/
readonly onDidDispose: Event<void>;
readonly onDidClose: Event<void>;
/**
* Allows to indicate progress on the notification even after the
@@ -118,6 +118,36 @@ export interface INotificationHandle extends IDisposable {
* notification is already visible.
*/
updateActions(actions?: INotificationActions): void;
/**
* Hide the notification and remove it from the notification center.
*/
close(): void;
}
export interface IPromptChoice {
/**
* Label to show for the choice to the user.
*/
label: string;
/**
* Primary choices show up as buttons in the notification below the message.
* Secondary choices show up under the gear icon in the header of the notification.
*/
isSecondary?: boolean;
/**
* Wether to keep the notification open after the choice was selected
* by the user. By default, will close the notification upon click.
*/
keepOpen?: boolean;
/**
* Triggered when the user selects the choice.
*/
run: () => void;
}
export interface INotificationService {
@@ -151,23 +181,34 @@ export interface INotificationService {
* method if you need more control over the notification.
*/
error(message: NotificationMessage | NotificationMessage[]): void;
/**
* Shows a prompt in the notification area with the provided choices. The prompt
* is non-modal. If you want to show a modal dialog instead, use `IDialogService`.
*
* @param onCancel will be called if the user closed the notification without picking
* any of the provided choices.
*
* @returns a handle on the notification to e.g. hide it or update message, buttons, etc.
*/
prompt(severity: Severity, message: string, choices: IPromptChoice[], onCancel?: () => void): INotificationHandle;
}
export class NoOpNotification implements INotificationHandle {
readonly progress = new NoOpProgress();
private _onDidDispose: Emitter<void> = new Emitter();
private readonly _onDidClose: Emitter<void> = new Emitter();
public get onDidDispose(): Event<void> {
return this._onDidDispose.event;
public get onDidClose(): Event<void> {
return this._onDidClose.event;
}
updateSeverity(severity: Severity): void { }
updateMessage(message: NotificationMessage): void { }
updateActions(actions?: INotificationActions): void { }
dispose(): void {
this._onDidDispose.dispose();
close(): void {
this._onDidClose.dispose();
}
}

View File

@@ -92,7 +92,7 @@ export class MainThreadMessageService implements MainThreadMessageServiceShape {
// if promise has not been resolved yet, now is the time to ensure a return value
// otherwise if already resolved it means the user clicked one of the buttons
once(messageHandle.onDidDispose)(() => {
once(messageHandle.onDidClose)(() => {
resolve(undefined);
});
});

View File

@@ -282,7 +282,7 @@ export class NotificationsCenter extends Themable {
// Dispose all
while (this.model.notifications.length) {
this.model.notifications[0].dispose();
this.model.notifications[0].close();
}
}
}

View File

@@ -115,7 +115,7 @@ export function registerNotificationCommands(center: INotificationsCenterControl
handler: (accessor, args?: any) => {
const notification = getNotificationFromContext(accessor.get(IListService), args);
if (notification) {
notification.dispose();
notification.close();
}
}
});

View File

@@ -164,7 +164,7 @@ export class NotificationsToasts extends Themable {
}));
// Remove when item gets disposed
once(item.onDidDispose)(() => {
once(item.onDidClose)(() => {
this.removeToast(item);
});

View File

@@ -440,7 +440,7 @@ export class NotificationTemplateRenderer {
this.actionRunner.run(action, notification);
// Hide notification
notification.dispose();
notification.close();
}));
this.inputDisposeables.push(attachButtonStyler(button, this.themeService));

View File

@@ -7,7 +7,7 @@
import { INotification, INotificationHandle, INotificationActions, INotificationProgress, NoOpNotification, Severity, NotificationMessage } from 'vs/platform/notification/common/notification';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import Event, { Emitter, once } from 'vs/base/common/event';
import Event, {Emitter, once } from 'vs/base/common/event';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { isPromiseCanceledError, isErrorWithActions } from 'vs/base/common/errors';
@@ -44,21 +44,21 @@ export interface INotificationChangeEvent {
}
export class NotificationHandle implements INotificationHandle {
private _onDidDispose: Emitter<void> = new Emitter();
private readonly _onDidClose: Emitter<void> = new Emitter();
constructor(private item: INotificationViewItem, private disposeItem: (item: INotificationViewItem) => void) {
constructor(private item: INotificationViewItem, private closeItem: (item: INotificationViewItem) => void) {
this.registerListeners();
}
private registerListeners(): void {
once(this.item.onDidDispose)(() => {
this._onDidDispose.fire();
this._onDidDispose.dispose();
once(this.item.onDidClose)(() => {
this._onDidClose.fire();
this._onDidClose.dispose();
});
}
public get onDidDispose(): Event<void> {
return this._onDidDispose.event;
public get onDidClose(): Event<void> {
return this._onDidClose.event;
}
public get progress(): INotificationProgress {
@@ -77,9 +77,9 @@ export class NotificationHandle implements INotificationHandle {
this.item.updateActions(actions);
}
public dispose(): void {
this.disposeItem(this.item);
this._onDidDispose.dispose();
public close(): void {
this.closeItem(this.item);
this._onDidClose.dispose();
}
}
@@ -89,7 +89,7 @@ export class NotificationsModel implements INotificationsModel {
private _notifications: INotificationViewItem[];
private _onDidNotificationChange: Emitter<INotificationChangeEvent>;
private readonly _onDidNotificationChange: Emitter<INotificationChangeEvent>;
private toDispose: IDisposable[];
constructor() {
@@ -117,7 +117,7 @@ export class NotificationsModel implements INotificationsModel {
// Deduplicate
const duplicate = this.findNotification(item);
if (duplicate) {
duplicate.dispose();
duplicate.close();
}
// Add to list as first entry
@@ -127,15 +127,15 @@ export class NotificationsModel implements INotificationsModel {
this._onDidNotificationChange.fire({ item, index: 0, kind: NotificationChangeType.ADD });
// Wrap into handle
return new NotificationHandle(item, item => this.disposeItem(item));
return new NotificationHandle(item, item => this.closeItem(item));
}
private disposeItem(item: INotificationViewItem): void {
private closeItem(item: INotificationViewItem): void {
const liveItem = this.findNotification(item);
if (liveItem && liveItem !== item) {
liveItem.dispose(); // item could have been replaced with another one, make sure to dispose the live item
liveItem.close(); // item could have been replaced with another one, make sure to close the live item
} else {
item.dispose(); // otherwise just dispose the item that was passed in
item.close(); // otherwise just close the item that was passed in
}
}
@@ -174,7 +174,7 @@ export class NotificationsModel implements INotificationsModel {
}
});
once(item.onDidDispose)(() => {
once(item.onDidClose)(() => {
itemExpansionChangeListener.dispose();
itemLabelChangeListener.dispose();
@@ -204,7 +204,7 @@ export interface INotificationViewItem {
readonly canCollapse: boolean;
readonly onDidExpansionChange: Event<void>;
readonly onDidDispose: Event<void>;
readonly onDidClose: Event<void>;
readonly onDidLabelChange: Event<INotificationViewItemLabelChangeEvent>;
expand(): void;
@@ -217,7 +217,7 @@ export interface INotificationViewItem {
updateMessage(message: NotificationMessage): void;
updateActions(actions?: INotificationActions): void;
dispose(): void;
close(): void;
equals(item: INotificationViewItem);
}
@@ -253,7 +253,7 @@ export interface INotificationViewItemProgress extends INotificationProgress {
export class NotificationViewItemProgress implements INotificationViewItemProgress {
private _state: INotificationViewItemProgressState;
private _onDidChange: Emitter<void>;
private readonly _onDidChange: Emitter<void>;
private toDispose: IDisposable[];
constructor() {
@@ -358,9 +358,9 @@ export class NotificationViewItem implements INotificationViewItem {
private _actions: INotificationActions;
private _progress: NotificationViewItemProgress;
private _onDidExpansionChange: Emitter<void>;
private _onDidDispose: Emitter<void>;
private _onDidLabelChange: Emitter<INotificationViewItemLabelChangeEvent>;
private readonly _onDidExpansionChange: Emitter<void>;
private readonly _onDidClose: Emitter<void>;
private readonly _onDidLabelChange: Emitter<INotificationViewItemLabelChangeEvent>;
public static create(notification: INotification): INotificationViewItem {
if (!notification || !notification.message || isPromiseCanceledError(notification.message)) {
@@ -435,8 +435,8 @@ export class NotificationViewItem implements INotificationViewItem {
this._onDidLabelChange = new Emitter<INotificationViewItemLabelChangeEvent>();
this.toDispose.push(this._onDidLabelChange);
this._onDidDispose = new Emitter<void>();
this.toDispose.push(this._onDidDispose);
this._onDidClose = new Emitter<void>();
this.toDispose.push(this._onDidClose);
}
private setActions(actions: INotificationActions): void {
@@ -454,9 +454,6 @@ export class NotificationViewItem implements INotificationViewItem {
this._actions = actions;
this._expanded = actions.primary.length > 0;
this.toDispose.push(...actions.primary);
this.toDispose.push(...actions.secondary);
}
public get onDidExpansionChange(): Event<void> {
@@ -467,8 +464,8 @@ export class NotificationViewItem implements INotificationViewItem {
return this._onDidLabelChange.event;
}
public get onDidDispose(): Event<void> {
return this._onDidDispose.event;
public get onDidClose(): Event<void> {
return this._onDidClose.event;
}
public get canCollapse(): boolean {
@@ -559,13 +556,17 @@ export class NotificationViewItem implements INotificationViewItem {
}
}
public dispose(): void {
this._onDidDispose.fire();
public close(): void {
this._onDidClose.fire();
this.toDispose = dispose(this.toDispose);
}
public equals(other: INotificationViewItem): boolean {
if (this.hasProgress() || other.hasProgress()) {
return false;
}
if (this._source !== other.source) {
return false;
}
@@ -581,7 +582,7 @@ export class NotificationViewItem implements INotificationViewItem {
}
for (let i = 0; i < primaryActions.length; i++) {
if (primaryActions[i].id !== otherPrimaryActions[i].id) {
if ((primaryActions[i].id + primaryActions[i].label) !== (otherPrimaryActions[i].id + otherPrimaryActions[i].label)) {
return false;
}
}

View File

@@ -101,7 +101,7 @@ export class SaveErrorHandler implements ISaveErrorHandler, IWorkbenchContributi
private onFileSavedOrReverted(resource: URI): void {
const messageHandle = this.messages.get(resource);
if (messageHandle) {
messageHandle.dispose();
messageHandle.close();
this.messages.delete(resource);
}
}
@@ -190,7 +190,7 @@ export class SaveErrorHandler implements ISaveErrorHandler, IWorkbenchContributi
const pendingResolveSaveConflictMessages: INotificationHandle[] = [];
function clearPendingResolveSaveConflictMessages(): void {
while (pendingResolveSaveConflictMessages.length > 0) {
pendingResolveSaveConflictMessages.pop().dispose();
pendingResolveSaveConflictMessages.pop().close();
}
}

View File

@@ -6,9 +6,15 @@
import { Registry } from 'vs/platform/registry/common/platform';
import { GettingStarted } from './gettingStarted';
import { TelemetryOptOut } from './telemetryOptOut';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
// {{SQL CARBON EDIT}}
// Registry
// .as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench)
// .registerWorkbenchContribution(GettingStarted, LifecyclePhase.Running);
Registry
.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench)
.registerWorkbenchContribution(GettingStarted, LifecyclePhase.Running);
.registerWorkbenchContribution(TelemetryOptOut, LifecyclePhase.Eventually);

View File

@@ -0,0 +1,59 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import product from 'vs/platform/node/product';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import URI from 'vs/base/common/uri';
import { localize } from 'vs/nls';
import { onUnexpectedError } from 'vs/base/common/errors';
import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows';
export class TelemetryOptOut implements IWorkbenchContribution {
private static TELEMETRY_OPT_OUT_SHOWN = 'workbench.telemetryOptOutShown';
constructor(
@IStorageService storageService: IStorageService,
@IOpenerService openerService: IOpenerService,
@INotificationService notificationService: INotificationService,
@IWindowService windowService: IWindowService,
@IWindowsService windowsService: IWindowsService,
@ITelemetryService telemetryService: ITelemetryService
) {
if (!product.telemetryOptOutUrl || storageService.get(TelemetryOptOut.TELEMETRY_OPT_OUT_SHOWN)) {
return;
}
Promise.all([
windowService.isFocused(),
windowsService.getWindowCount()
]).then(([focused, count]) => {
if (!focused && count > 1) {
return null;
}
storageService.store(TelemetryOptOut.TELEMETRY_OPT_OUT_SHOWN, true);
const optOutUrl = product.telemetryOptOutUrl;
const privacyUrl = product.privacyStatementUrl || product.telemetryOptOutUrl;
// {{SQL CARBON EDIT}}
const optOutNotice = localize('telemetryOptOut.optOutNotice', "Help improve SQL Operations Studio by allowing Microsoft to collect usage data. Read our [privacy statement]({0}) and how to [opt out]({1}).", privacyUrl, optOutUrl);
const optInNotice = localize('telemetryOptOut.optInNotice', "Help improve SQL Operations Studio by allowing Microsoft to collect usage data. Read our [privacy statement]({0}) and how to [opt in]({1}).", privacyUrl, optOutUrl);
notificationService.prompt(
Severity.Info,
telemetryService.isOptedIn ? optOutNotice : optInNotice,
[{
label: localize('telemetryOptOut.readMore', "Read More"),
run: () => openerService.open(URI.parse(optOutUrl))
}]
);
})
.then(null, onUnexpectedError);
}
}

View File

@@ -134,7 +134,7 @@ export class DialogService implements IChoiceService, IConfirmationService {
c(index);
if (closeNotification) {
handle.dispose();
handle.close();
}
return TPromise.as(void 0);
@@ -171,9 +171,9 @@ export class DialogService implements IChoiceService, IConfirmationService {
handle = this.notificationService.notify({ severity, message, actions });
// Cancel promise when notification gets disposed
once(handle.onDidDispose)(() => promise.cancel());
once(handle.onDidClose)(() => promise.cancel());
}, () => handle.dispose());
}, () => handle.close());
return promise;
}

View File

@@ -3,11 +3,12 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { INotificationService, INotification, INotificationHandle, Severity, NotificationMessage } from 'vs/platform/notification/common/notification';
import { INotificationService, INotification, INotificationHandle, Severity, NotificationMessage, INotificationActions, IPromptChoice } from 'vs/platform/notification/common/notification';
import { INotificationsModel, NotificationsModel } from 'vs/workbench/common/notifications';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { TPromise } from 'vs/base/common/winjs.base';
import { Action } from 'vs/base/common/actions';
import { once } from 'vs/base/common/event';
export class NotificationService implements INotificationService {
@@ -62,6 +63,51 @@ export class NotificationService implements INotificationService {
return this.model.notify(notification);
}
public prompt(severity: Severity, message: string, choices: IPromptChoice[], onCancel?: () => void): INotificationHandle {
let handle: INotificationHandle;
let choiceClicked = false;
// Convert choices into primary/secondary actions
const actions: INotificationActions = { primary: [], secondary: [] };
choices.forEach((choice, index) => {
const action = new Action(`workbench.dialog.choice.${index}`, choice.label, null, true, () => {
choiceClicked = true;
// Pass to runner
choice.run();
// Close notification unless we are told to keep open
if (!choice.keepOpen) {
handle.close();
}
return TPromise.as(void 0);
});
if (!choice.isSecondary) {
actions.primary.push(action);
} else {
actions.secondary.push(action);
}
});
// Show notification with actions
handle = this.notify({ severity, message, actions });
once(handle.onDidClose)(() => {
// Cleanup when notification gets disposed
dispose(...actions.primary, ...actions.secondary);
// Indicate cancellation to the outside if no action was executed
if (!choiceClicked && typeof onCancel === 'function') {
onCancel();
}
});
return handle;
}
public dispose(): void {
this.toDispose = dispose(this.toDispose);
}

View File

@@ -96,11 +96,11 @@ suite('Notifications', () => {
assert.equal(called, 1);
called = 0;
item1.onDidDispose(() => {
item1.onDidClose(() => {
called++;
});
item1.dispose();
item1.close();
assert.equal(called, 1);
// Error with Action
@@ -157,11 +157,11 @@ suite('Notifications', () => {
assert.equal(model.notifications.length, 3);
let called = 0;
item1Handle.onDidDispose(() => {
item1Handle.onDidClose(() => {
called++;
});
item1Handle.dispose();
item1Handle.close();
assert.equal(called, 1);
assert.equal(model.notifications.length, 2);
assert.equal(lastEvent.item.severity, item1.severity);
@@ -176,7 +176,7 @@ suite('Notifications', () => {
assert.equal(lastEvent.index, 0);
assert.equal(lastEvent.kind, NotificationChangeType.ADD);
item2Handle.dispose();
item2Handle.close();
assert.equal(model.notifications.length, 1);
assert.equal(lastEvent.item.severity, item2Duplicate.severity);
assert.equal(lastEvent.item.message.value, item2Duplicate.message);

View File

@@ -9,7 +9,7 @@ import * as assert from 'assert';
import { MainThreadMessageService } from 'vs/workbench/api/electron-browser/mainThreadMessageService';
import { TPromise as Promise, TPromise } from 'vs/base/common/winjs.base';
import { IChoiceService } from 'vs/platform/dialogs/common/dialogs';
import { INotificationService, INotification, NoOpNotification, INotificationHandle } from 'vs/platform/notification/common/notification';
import { INotificationService, INotification, NoOpNotification, INotificationHandle, IPromptChoice, Severity } from 'vs/platform/notification/common/notification';
import { ICommandService } from 'vs/platform/commands/common/commands';
const emptyChoiceService = new class implements IChoiceService {
@@ -41,6 +41,9 @@ const emptyNotificationService = new class implements INotificationService {
error(...args: any[]): never {
throw new Error('not implemented');
}
prompt(severity: Severity, message: string, choices: IPromptChoice[], onCancel?: () => void): INotificationHandle {
throw new Error('not implemented');
}
};
class EmptyNotificationService implements INotificationService {
@@ -64,6 +67,9 @@ class EmptyNotificationService implements INotificationService {
error(message: any): void {
throw new Error('Method not implemented.');
}
prompt(severity: Severity, message: string, choices: IPromptChoice[], onCancel?: () => void): INotificationHandle {
throw new Error('not implemented');
}
}
suite('ExtHostMessageService', function () {

View File

@@ -63,7 +63,7 @@ import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKe
import { ITextBufferFactory, DefaultEndOfLine, EndOfLinePreference } from 'vs/editor/common/model';
import { Range } from 'vs/editor/common/core/range';
import { IChoiceService, IConfirmation, IConfirmationResult, IConfirmationService } from 'vs/platform/dialogs/common/dialogs';
import { INotificationService, INotificationHandle, INotification, NoOpNotification } from 'vs/platform/notification/common/notification';
import { INotificationService, INotificationHandle, INotification, NoOpNotification, IPromptChoice } from 'vs/platform/notification/common/notification';
export function createFileInput(instantiationService: IInstantiationService, resource: URI): FileEditorInput {
return instantiationService.createInstance(FileEditorInput, resource, void 0);
@@ -331,6 +331,10 @@ export class TestNotificationService implements INotificationService {
public notify(notification: INotification): INotificationHandle {
return TestNotificationService.NO_OP;
}
public prompt(severity: Severity, message: string, choices: IPromptChoice[], onCancel?: () => void): INotificationHandle {
return TestNotificationService.NO_OP;
}
}
export class TestConfirmationService implements IConfirmationService {

View File

@@ -112,8 +112,7 @@ import 'vs/workbench/parts/themes/electron-browser/themes.contribution';
import 'vs/workbench/parts/feedback/electron-browser/feedback.contribution';
// {{SQL CARBON EDIT}}
// import 'vs/workbench/parts/welcome/gettingStarted/electron-browser/gettingStarted.contribution';
import 'vs/workbench/parts/welcome/gettingStarted/electron-browser/gettingStarted.contribution';
import 'vs/workbench/parts/update/electron-browser/update.contribution';