mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-14 03:58:33 -05:00
Port VS Code telemetry opt-in dialog (#1130)
This commit is contained in:
@@ -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": {
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@ export interface IProductConfiguration {
|
||||
reportIssueUrl: string;
|
||||
licenseUrl: string;
|
||||
privacyStatementUrl: string;
|
||||
telemetryOptOutUrl: string;
|
||||
npsSurveyUrl: string;
|
||||
surveys: ISurveyData[];
|
||||
checksums: { [path: string]: string; };
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -164,7 +164,7 @@ export class NotificationsToasts extends Themable {
|
||||
}));
|
||||
|
||||
// Remove when item gets disposed
|
||||
once(item.onDidDispose)(() => {
|
||||
once(item.onDidClose)(() => {
|
||||
this.removeToast(item);
|
||||
});
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user