notifications - add service methods to DND per source

This commit is contained in:
Benjamin Pasero
2023-12-18 10:51:34 +01:00
committed by Benjamin Pasero
parent 968af29545
commit 0ff10eb8f3
15 changed files with 191 additions and 61 deletions

View File

@@ -42,7 +42,7 @@ import { IKeybindingItem, KeybindingsRegistry } from 'vs/platform/keybinding/com
import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem';
import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding';
import { ILabelService, ResourceLabelFormatter, IFormatterChangeEvent, Verbosity } from 'vs/platform/label/common/label';
import { INotification, INotificationHandle, INotificationService, IPromptChoice, IPromptOptions, NoOpNotification, IStatusMessageOptions } from 'vs/platform/notification/common/notification';
import { INotification, INotificationHandle, INotificationService, IPromptChoice, IPromptOptions, NoOpNotification, IStatusMessageOptions, INotificationSource } from 'vs/platform/notification/common/notification';
import { IProgressRunner, IEditorProgressService, IProgressService, IProgress, IProgressCompositeOptions, IProgressDialogOptions, IProgressNotificationOptions, IProgressOptions, IProgressStep, IProgressWindowOptions } from 'vs/platform/progress/common/progress';
import { ITelemetryService, TelemetryLevel } from 'vs/platform/telemetry/common/telemetry';
import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier, IWorkspace, IWorkspaceContextService, IWorkspaceFolder, IWorkspaceFoldersChangeEvent, IWorkspaceFoldersWillChangeEvent, WorkbenchState, WorkspaceFolder, STANDALONE_EDITOR_WORKSPACE_ID } from 'vs/platform/workspace/common/workspace';
@@ -306,11 +306,13 @@ export class StandaloneNotificationService implements INotificationService {
readonly onDidRemoveNotification: Event<INotification> = Event.None;
readonly onDidChangeDoNotDisturbMode: Event<void> = Event.None;
readonly onDidChangeGlobalDoNotDisturbMode: Event<void> = Event.None;
readonly onDidChangePerSourceDoNotDisturbMode = Event.None;
public _serviceBrand: undefined;
public isDoNotDisturbMode: boolean = false;
public isGlobalDoNotDisturbMode: boolean = false;
private static readonly NO_OP: INotificationHandle = new NoOpNotification();
@@ -350,9 +352,14 @@ export class StandaloneNotificationService implements INotificationService {
return Disposable.None;
}
public setDoNotDisturbMode(mode: boolean): void {
this.isDoNotDisturbMode = mode;
public setGlobalDoNotDisturbMode(mode: boolean): void {
this.isGlobalDoNotDisturbMode = mode;
}
public isSourceDoNotDisturb(source: INotificationSource): boolean {
return false;
}
public setSourceDoNotDisturb(source: INotificationSource, mode: boolean): void { }
}
export class StandaloneCommandService implements ICommandService {

View File

@@ -17,7 +17,7 @@ import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKe
import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding';
import { createUSLayoutResolvedKeybinding } from 'vs/platform/keybinding/test/common/keybindingsTestUtils';
import { NullLogService } from 'vs/platform/log/common/log';
import { INotification, INotificationService, IPromptChoice, IPromptOptions, IStatusMessageOptions, NoOpNotification } from 'vs/platform/notification/common/notification';
import { INotification, INotificationService, INotificationSource, IPromptChoice, IPromptOptions, IStatusMessageOptions, NoOpNotification } from 'vs/platform/notification/common/notification';
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
function createContext(ctx: any) {
@@ -142,10 +142,11 @@ suite('AbstractKeybindingService', () => {
const notificationService: INotificationService = {
_serviceBrand: undefined,
isDoNotDisturbMode: false,
isGlobalDoNotDisturbMode: false,
onDidAddNotification: undefined!,
onDidRemoveNotification: undefined!,
onDidChangeDoNotDisturbMode: undefined!,
onDidChangeGlobalDoNotDisturbMode: undefined!,
onDidChangePerSourceDoNotDisturbMode: undefined!,
notify: (notification: INotification) => {
showMessageCalls.push({ sev: notification.severity, message: notification.message });
return new NoOpNotification();
@@ -173,8 +174,14 @@ suite('AbstractKeybindingService', () => {
}
};
},
setDoNotDisturbMode(mode: boolean) {
setGlobalDoNotDisturbMode(mode: boolean) {
throw new Error('not implemented');
},
isSourceDoNotDisturb(source: INotificationSource): boolean {
throw new Error('Method not implemented.');
},
setSourceDoNotDisturb(source: INotificationSource, mode: boolean): void {
throw new Error('Method not implemented.');
}
};

View File

@@ -97,6 +97,29 @@ export interface INeverShowAgainOptions {
readonly scope?: NeverShowAgainScope;
}
export interface INotificationSource {
/**
* The id of the source.
*/
readonly id: string;
/**
* The label of the source.
*/
readonly label: string;
}
export function isNotificationSource(thing: unknown): thing is INotificationSource {
if (thing) {
const candidate = thing as INotificationSource;
return typeof candidate.id === 'string' && typeof candidate.label === 'string';
}
return false;
}
export interface INotification extends INotificationProperties {
/**
@@ -120,7 +143,7 @@ export interface INotification extends INotificationProperties {
/**
* The source of the notification appears as additional information.
*/
readonly source?: string | { label: string; id: string };
readonly source?: string | INotificationSource;
/**
* Actions to show as part of the notification. Primary actions show up as
@@ -343,19 +366,34 @@ export interface INotificationService {
readonly onDidRemoveNotification: Event<INotification>;
/**
* Emitted when a do not disturb mode has changed.
* Emitted when the global do not disturb mode has changed.
*/
readonly onDidChangeDoNotDisturbMode: Event<void>;
readonly onDidChangeGlobalDoNotDisturbMode: Event<void>;
/**
* If enabled, only error messages will show as toasts.
*/
readonly isDoNotDisturbMode: boolean;
readonly isGlobalDoNotDisturbMode: boolean;
/**
* Enables or disables the do not disturb mode.
* Enables or disables the global do not disturb mode.
*/
setDoNotDisturbMode(mode: boolean): void;
setGlobalDoNotDisturbMode(mode: boolean): void;
/**
* Emitted when the per-source do not disturb mode has changed.
*/
readonly onDidChangePerSourceDoNotDisturbMode: Event<INotificationSource>;
/**
* Whether the provided source is configured as do not disturb.
*/
isSourceDoNotDisturb(source: INotificationSource): boolean;
/**
* Enables or disables the per-source do not disturb mode.
*/
setSourceDoNotDisturb(source: INotificationSource, mode: boolean): void;
/**
* Show the provided notification to the user. The returned `INotificationHandle`

View File

@@ -5,7 +5,7 @@
import { Event } from 'vs/base/common/event';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { INotification, INotificationHandle, INotificationService, IPromptChoice, IPromptOptions, IStatusMessageOptions, NoOpNotification, Severity } from 'vs/platform/notification/common/notification';
import { INotification, INotificationHandle, INotificationService, INotificationSource, IPromptChoice, IPromptOptions, IStatusMessageOptions, NoOpNotification, Severity } from 'vs/platform/notification/common/notification';
export class TestNotificationService implements INotificationService {
@@ -13,11 +13,13 @@ export class TestNotificationService implements INotificationService {
readonly onDidRemoveNotification: Event<INotification> = Event.None;
readonly onDidChangeDoNotDisturbMode: Event<void> = Event.None;
readonly onDidChangeGlobalDoNotDisturbMode: Event<void> = Event.None;
readonly onDidChangePerSourceDoNotDisturbMode = Event.None;
declare readonly _serviceBrand: undefined;
isDoNotDisturbMode: boolean = false;
isGlobalDoNotDisturbMode: boolean = false;
private static readonly NO_OP: INotificationHandle = new NoOpNotification();
@@ -45,7 +47,13 @@ export class TestNotificationService implements INotificationService {
return Disposable.None;
}
setDoNotDisturbMode(mode: boolean): void {
this.isDoNotDisturbMode = mode;
setGlobalDoNotDisturbMode(mode: boolean): void {
this.isGlobalDoNotDisturbMode = mode;
}
isSourceDoNotDisturb(source: INotificationSource): boolean {
return false;
}
setSourceDoNotDisturb(source: INotificationSource, mode: boolean): void { }
}

View File

@@ -8,7 +8,7 @@ import { DeferredPromise } from 'vs/base/common/async';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { NotificationPriority } from 'vs/platform/notification/common/notification';
import { INotificationSource, NotificationPriority } from 'vs/platform/notification/common/notification';
export const IProgressService = createDecorator<IProgressService>('progressService');
@@ -53,7 +53,7 @@ export const enum ProgressLocation {
export interface IProgressOptions {
readonly location: ProgressLocation | string;
readonly title?: string;
readonly source?: string | { label: string; id: string };
readonly source?: string | INotificationSource;
readonly total?: number;
readonly cancellable?: boolean;
readonly buttons?: string[];

View File

@@ -9,7 +9,7 @@ import { IAction, toAction } from 'vs/base/common/actions';
import { MainThreadMessageServiceShape, MainContext, MainThreadMessageOptions } from '../common/extHost.protocol';
import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers';
import { IDialogService, IPromptButton } from 'vs/platform/dialogs/common/dialogs';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { INotificationService, INotificationSource } from 'vs/platform/notification/common/notification';
import { Event } from 'vs/base/common/event';
import { ICommandService } from 'vs/platform/commands/common/commands';
@@ -51,7 +51,7 @@ export class MainThreadMessageService implements MainThreadMessageServiceShape {
}
}));
let source: string | { label: string; id: string } | undefined;
let source: string | INotificationSource | undefined;
if (options.source) {
source = {
label: nls.localize('extensionSource', "{0} (Extension)", options.source.label),

View File

@@ -6,7 +6,7 @@
import * as assert from 'assert';
import { MainThreadMessageService } from 'vs/workbench/api/browser/mainThreadMessageService';
import { IDialogService, IPrompt, IPromptButton } from 'vs/platform/dialogs/common/dialogs';
import { INotificationService, INotification, NoOpNotification, INotificationHandle, Severity, IPromptChoice, IPromptOptions, IStatusMessageOptions } from 'vs/platform/notification/common/notification';
import { INotificationService, INotification, NoOpNotification, INotificationHandle, Severity, IPromptChoice, IPromptOptions, IStatusMessageOptions, INotificationSource } from 'vs/platform/notification/common/notification';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { mock } from 'vs/base/test/common/mock';
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
@@ -25,10 +25,11 @@ const emptyCommandService: ICommandService = {
const emptyNotificationService = new class implements INotificationService {
declare readonly _serviceBrand: undefined;
isDoNotDisturbMode: boolean = false;
isGlobalDoNotDisturbMode: boolean = false;
onDidAddNotification: Event<INotification> = Event.None;
onDidRemoveNotification: Event<INotification> = Event.None;
onDidChangeDoNotDisturbMode: Event<void> = Event.None;
onDidChangeGlobalDoNotDisturbMode: Event<void> = Event.None;
onDidChangePerSourceDoNotDisturbMode = Event.None;
notify(...args: any[]): never {
throw new Error('not implemented');
}
@@ -47,20 +48,27 @@ const emptyNotificationService = new class implements INotificationService {
status(message: string | Error, options?: IStatusMessageOptions): IDisposable {
return Disposable.None;
}
setDoNotDisturbMode(mode: boolean): void {
setGlobalDoNotDisturbMode(mode: boolean): void {
throw new Error('not implemented');
}
isSourceDoNotDisturb(source: INotificationSource): boolean {
throw new Error('Method not implemented.');
}
setSourceDoNotDisturb(source: INotificationSource, mode: boolean): void {
throw new Error('Method not implemented.');
}
};
class EmptyNotificationService implements INotificationService {
declare readonly _serviceBrand: undefined;
isDoNotDisturbMode: boolean = false;
isGlobalDoNotDisturbMode: boolean = false;
constructor(private withNotify: (notification: INotification) => void) {
}
onDidAddNotification: Event<INotification> = Event.None;
onDidRemoveNotification: Event<INotification> = Event.None;
onDidChangeDoNotDisturbMode: Event<void> = Event.None;
onDidChangeGlobalDoNotDisturbMode: Event<void> = Event.None;
onDidChangePerSourceDoNotDisturbMode = Event.None;
notify(notification: INotification): INotificationHandle {
this.withNotify(notification);
@@ -81,7 +89,13 @@ class EmptyNotificationService implements INotificationService {
status(message: string, options?: IStatusMessageOptions): IDisposable {
return Disposable.None;
}
setDoNotDisturbMode(mode: boolean): void {
setGlobalDoNotDisturbMode(mode: boolean): void {
throw new Error('Method not implemented.');
}
isSourceDoNotDisturb(source: INotificationSource): boolean {
throw new Error('Method not implemented.');
}
setSourceDoNotDisturb(source: INotificationSource, mode: boolean): void {
throw new Error('Method not implemented.');
}
}

View File

@@ -1338,7 +1338,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
if (!restoring) {
zenModeExitInfo.transitionedToFullScreen = toggleFullScreen;
zenModeExitInfo.transitionedToCenteredEditorLayout = !this.isMainEditorLayoutCentered() && config.centerLayout;
zenModeExitInfo.handleNotificationsDoNotDisturbMode = !this.notificationService.isDoNotDisturbMode;
zenModeExitInfo.handleNotificationsDoNotDisturbMode = !this.notificationService.isGlobalDoNotDisturbMode;
zenModeExitInfo.wasVisible.sideBar = this.isVisible(Parts.SIDEBAR_PART);
zenModeExitInfo.wasVisible.panel = this.isVisible(Parts.PANEL_PART);
zenModeExitInfo.wasVisible.auxiliaryBar = this.isVisible(Parts.AUXILIARYBAR_PART);
@@ -1367,7 +1367,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
}
if (config.silentNotifications && zenModeExitInfo.handleNotificationsDoNotDisturbMode) {
this.notificationService.setDoNotDisturbMode(true);
this.notificationService.setGlobalDoNotDisturbMode(true);
}
if (config.centerLayout) {
@@ -1404,7 +1404,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
if (e.affectsConfiguration(ZenModeSettings.SILENT_NOTIFICATIONS)) {
const zenModeSilentNotifications = !!this.configurationService.getValue(ZenModeSettings.SILENT_NOTIFICATIONS);
if (zenModeExitInfo.handleNotificationsDoNotDisturbMode) {
this.notificationService.setDoNotDisturbMode(zenModeSilentNotifications);
this.notificationService.setGlobalDoNotDisturbMode(zenModeSilentNotifications);
}
}
@@ -1444,7 +1444,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
}
if (zenModeExitInfo.handleNotificationsDoNotDisturbMode) {
this.notificationService.setDoNotDisturbMode(false);
this.notificationService.setGlobalDoNotDisturbMode(false);
}
setLineNumbers();

View File

@@ -67,10 +67,10 @@ export class NotificationsCenter extends Themable implements INotificationsCente
private registerListeners(): void {
this._register(this.model.onDidChangeNotification(e => this.onDidChangeNotification(e)));
this._register(this.layoutService.onDidLayoutMainContainer(dimension => this.layout(Dimension.lift(dimension))));
this._register(this.notificationService.onDidChangeDoNotDisturbMode(() => this.onDidChangeDoNotDisturbMode()));
this._register(this.notificationService.onDidChangeGlobalDoNotDisturbMode(() => this.onDidChangeGlobalDoNotDisturbMode()));
}
private onDidChangeDoNotDisturbMode(): void {
private onDidChangeGlobalDoNotDisturbMode(): void {
this.hide(); // hide the notification center when do not disturb is toggled
}

View File

@@ -287,7 +287,7 @@ export function registerNotificationCommands(center: INotificationsCenterControl
CommandsRegistry.registerCommand(TOGGLE_DO_NOT_DISTURB_MODE, accessor => {
const notificationService = accessor.get(INotificationService);
notificationService.setDoNotDisturbMode(!notificationService.isDoNotDisturbMode);
notificationService.setGlobalDoNotDisturbMode(!notificationService.isGlobalDoNotDisturbMode);
});
// Commands for Command Palette

View File

@@ -39,7 +39,7 @@ export class NotificationsStatus extends Disposable {
private registerListeners(): void {
this._register(this.model.onDidChangeNotification(e => this.onDidChangeNotification(e)));
this._register(this.model.onDidChangeStatusMessage(e => this.onDidChangeStatusMessage(e)));
this._register(this.notificationService.onDidChangeDoNotDisturbMode(() => this.updateNotificationsCenterStatusItem()));
this._register(this.notificationService.onDidChangeGlobalDoNotDisturbMode(() => this.updateNotificationsCenterStatusItem()));
}
private onDidChangeNotification(e: INotificationChangeEvent): void {
@@ -83,7 +83,7 @@ export class NotificationsStatus extends Disposable {
showBeak: this.isNotificationsCenterVisible
};
if (this.notificationService.isDoNotDisturbMode) {
if (this.notificationService.isGlobalDoNotDisturbMode) {
statusProperties = {
...statusProperties,
text: `${notificationsInProgress > 0 || this.newNotificationsCount > 0 ? '$(bell-slash-dot)' : '$(bell-slash)'}`,

View File

@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { INotification, INotificationHandle, INotificationActions, INotificationProgress, NoOpNotification, Severity, NotificationMessage, IPromptChoice, IStatusMessageOptions, NotificationsFilter, INotificationProgressProperties, IPromptChoiceWithMenu, NotificationPriority } from 'vs/platform/notification/common/notification';
import { INotification, INotificationHandle, INotificationActions, INotificationProgress, NoOpNotification, Severity, NotificationMessage, IPromptChoice, IStatusMessageOptions, NotificationsFilter, INotificationProgressProperties, IPromptChoiceWithMenu, NotificationPriority, INotificationSource, isNotificationSource } from 'vs/platform/notification/common/notification';
import { toErrorMessage, isErrorWithActions } from 'vs/base/common/errorMessage';
import { Event, Emitter } from 'vs/base/common/event';
import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
@@ -495,7 +495,7 @@ export class NotificationViewItem extends Disposable implements INotificationVie
if (priority === NotificationPriority.DEFAULT && severity !== Severity.Error) {
if (filter.global === NotificationsFilter.ERROR) {
priority = NotificationPriority.SILENT; // filtered globally
} else if (notification.source && typeof notification.source !== 'string' && filter.sources.has(notification.source.id)) {
} else if (isNotificationSource(notification.source) && filter.sources.has(notification.source.id)) {
priority = NotificationPriority.SILENT; // filtered by source
}
}
@@ -537,7 +537,7 @@ export class NotificationViewItem extends Disposable implements INotificationVie
private _sticky: boolean | undefined,
private _priority: NotificationPriority,
private _message: INotificationMessage,
private _source: string | { label: string; id: string } | undefined,
private _source: string | INotificationSource | undefined,
progress: INotificationProgressProperties | undefined,
actions?: INotificationActions
) {

View File

@@ -193,7 +193,7 @@ suite('ConfigurationEditing', () => {
test('do not notify error', async () => {
instantiationService.stub(ITextFileService, 'isDirty', true);
const target = sinon.stub();
instantiationService.stub(INotificationService, <INotificationService>{ prompt: target, _serviceBrand: undefined, isDoNotDisturbMode: false, onDidAddNotification: undefined!, onDidRemoveNotification: undefined!, onDidChangeDoNotDisturbMode: undefined!, notify: null!, error: null!, info: null!, warn: null!, status: null!, setDoNotDisturbMode: null! });
instantiationService.stub(INotificationService, <INotificationService>{ prompt: target, _serviceBrand: undefined, isGlobalDoNotDisturbMode: false, onDidAddNotification: undefined!, onDidRemoveNotification: undefined!, onDidChangeGlobalDoNotDisturbMode: undefined!, onDidChangePerSourceDoNotDisturbMode: null!, notify: null!, error: null!, info: null!, warn: null!, status: null!, setGlobalDoNotDisturbMode: null!, isSourceDoNotDisturb: null!, setSourceDoNotDisturb: null! });
try {
await testObject.writeConfiguration(EditableConfigurationTarget.USER_LOCAL, { key: 'configurationEditing.service.testSetting', value: 'value' }, { donotNotifyError: true });
} catch (error) {

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { localize } from 'vs/nls';
import { INotificationService, INotification, INotificationHandle, Severity, NotificationMessage, INotificationActions, IPromptChoice, IPromptOptions, IStatusMessageOptions, NoOpNotification, NeverShowAgainScope, NotificationsFilter, INeverShowAgainOptions } from 'vs/platform/notification/common/notification';
import { INotificationService, INotification, INotificationHandle, Severity, NotificationMessage, INotificationActions, IPromptChoice, IPromptOptions, IStatusMessageOptions, NoOpNotification, NeverShowAgainScope, NotificationsFilter, INeverShowAgainOptions, INotificationSource } from 'vs/platform/notification/common/notification';
import { NotificationsModel, ChoiceAction, NotificationChangeType } from 'vs/workbench/common/notifications';
import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
import { Emitter, Event } from 'vs/base/common/event';
@@ -12,6 +12,10 @@ import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/
import { IAction, Action } from 'vs/base/common/actions';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
interface INotificationSourceDoNotDisturb extends INotificationSource {
readonly filter: NotificationsFilter;
}
export class NotificationService extends Disposable implements INotificationService {
declare readonly _serviceBrand: undefined;
@@ -59,33 +63,77 @@ export class NotificationService extends Disposable implements INotificationServ
}));
}
//#region Do not disturb mode
//#region Do not disturb mode (global)
static readonly DND_SETTINGS_KEY = 'notifications.doNotDisturbMode';
private static readonly GLOBAL_DND_SETTINGS_KEY = 'notifications.doNotDisturbMode';
private readonly _onDidChangeDoNotDisturbMode = this._register(new Emitter<void>());
readonly onDidChangeDoNotDisturbMode = this._onDidChangeDoNotDisturbMode.event;
private readonly _onDidChangeGlobalDoNotDisturbMode = this._register(new Emitter<void>());
readonly onDidChangeGlobalDoNotDisturbMode = this._onDidChangeGlobalDoNotDisturbMode.event;
private _isDoNotDisturbMode = this.storageService.getBoolean(NotificationService.DND_SETTINGS_KEY, StorageScope.APPLICATION, false);
get isDoNotDisturbMode() { return this._isDoNotDisturbMode; }
private _isGlobalDoNotDisturbMode = this.storageService.getBoolean(NotificationService.GLOBAL_DND_SETTINGS_KEY, StorageScope.APPLICATION, false);
get isGlobalDoNotDisturbMode() { return this._isGlobalDoNotDisturbMode; }
setDoNotDisturbMode(enabled: boolean): void {
if (this._isDoNotDisturbMode === enabled) {
setGlobalDoNotDisturbMode(enabled: boolean): void {
if (this._isGlobalDoNotDisturbMode === enabled) {
return; // no change
}
this.storageService.store(NotificationService.DND_SETTINGS_KEY, enabled, StorageScope.APPLICATION, StorageTarget.MACHINE);
this._isDoNotDisturbMode = enabled;
// Store into model and persist
this._isGlobalDoNotDisturbMode = enabled;
this.storageService.store(NotificationService.GLOBAL_DND_SETTINGS_KEY, enabled, StorageScope.APPLICATION, StorageTarget.MACHINE);
// Toggle via filter
// Update model
this.updateDoNotDisturbFilters();
// Events
this._onDidChangeDoNotDisturbMode.fire();
this._onDidChangeGlobalDoNotDisturbMode.fire();
}
//#endregion
//#region Do not disturb mode (per-source)
private static readonly PER_SOURCE_DND_SETTINGS_KEY = 'notifications.perSourceDoNotDisturbMode';
private readonly _onDidChangePerSourceDoNotDisturbMode = this._register(new Emitter<INotificationSource>());
readonly onDidChangePerSourceDoNotDisturbMode = this._onDidChangePerSourceDoNotDisturbMode.event;
private readonly mapSourceIdToDoNotDisturb: Map<string /** source id */, INotificationSourceDoNotDisturb> = (() => {
const map = new Map<string, INotificationSourceDoNotDisturb>();
for (const sourceFilter of this.storageService.getObject<INotificationSourceDoNotDisturb[]>(NotificationService.PER_SOURCE_DND_SETTINGS_KEY, StorageScope.APPLICATION, Object.create(null))) {
map.set(sourceFilter.id, sourceFilter);
}
return map;
})();
isSourceDoNotDisturb(source: INotificationSource): boolean {
return this.mapSourceIdToDoNotDisturb.get(source.id)?.filter === NotificationsFilter.ERROR;
}
setSourceDoNotDisturb(source: INotificationSource, mode: boolean): void {
const existing = this.mapSourceIdToDoNotDisturb.get(source.id);
if (existing?.filter === (mode ? NotificationsFilter.ERROR : NotificationsFilter.OFF)) {
return; // no change
}
// Store into model and persist
this.mapSourceIdToDoNotDisturb.set(source.id, { id: source.id, label: source.label, filter: mode ? NotificationsFilter.ERROR : NotificationsFilter.OFF });
this.storageService.store(NotificationService.PER_SOURCE_DND_SETTINGS_KEY, JSON.stringify([...this.mapSourceIdToDoNotDisturb.values()]), StorageScope.APPLICATION, StorageTarget.MACHINE);
// Update model
this.updateDoNotDisturbFilters();
// Events
this._onDidChangePerSourceDoNotDisturbMode.fire(source);
}
private updateDoNotDisturbFilters(): void {
this.model.setFilter({ global: this._isDoNotDisturbMode ? NotificationsFilter.ERROR : NotificationsFilter.OFF });
this.model.setFilter({
global: this._isGlobalDoNotDisturbMode ? NotificationsFilter.ERROR : NotificationsFilter.OFF,
sources: new Map([...this.mapSourceIdToDoNotDisturb.values()].map(source => [source.id, source.filter]))
});
}
//#endregion

View File

@@ -11,7 +11,7 @@ import { IProgressService, IProgressOptions, IProgressStep, ProgressLocation, IP
import { StatusbarAlignment, IStatusbarService, IStatusbarEntryAccessor, IStatusbarEntry } from 'vs/workbench/services/statusbar/browser/statusbar';
import { DeferredPromise, RunOnceScheduler, timeout } from 'vs/base/common/async';
import { ProgressBadge, IActivityService } from 'vs/workbench/services/activity/common/activity';
import { INotificationService, Severity, INotificationHandle, NotificationPriority } from 'vs/platform/notification/common/notification';
import { INotificationService, Severity, INotificationHandle, NotificationPriority, isNotificationSource } from 'vs/platform/notification/common/notification';
import { Action } from 'vs/base/common/actions';
import { Event, Emitter } from 'vs/base/common/event';
import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions';
@@ -68,8 +68,16 @@ export class ProgressService extends Disposable implements IProgressService {
}
switch (location) {
case ProgressLocation.Notification:
return this.withNotificationProgress({ ...options, location, priority: this.notificationService.isDoNotDisturbMode ? NotificationPriority.SILENT : undefined }, task, onDidCancel);
case ProgressLocation.Notification: {
let priority: NotificationPriority | undefined = undefined;
if (this.notificationService.isGlobalDoNotDisturbMode) {
priority = NotificationPriority.SILENT;
} else if (isNotificationSource(options.source) && this.notificationService.isSourceDoNotDisturb(options.source)) {
priority = NotificationPriority.SILENT;
}
return this.withNotificationProgress({ ...options, location, priority }, task, onDidCancel);
}
case ProgressLocation.Window: {
const type = (options as IProgressWindowOptions).type;
if ((options as IProgressWindowOptions).command) {