Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add "Do Not Disturb" Mode #149645

Merged
merged 35 commits into from Jun 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
e576297
Add mute icon to notification center toolbar
daviddossett May 14, 2022
c231ae5
Add placeholder mute method
daviddossett May 14, 2022
4f318f4
Add config service support
daviddossett May 14, 2022
38eb9f8
Refactor mute toggle behavior
daviddossett May 15, 2022
d925a97
More refactoring
daviddossett May 15, 2022
a7d3461
Add hack for switching actions on toggle
daviddossett May 15, 2022
c198416
Use do-not-disturb icons
daviddossett May 15, 2022
6024c5e
Update status bar icon
daviddossett May 16, 2022
1da9ecb
Add comment for hack todo
daviddossett May 16, 2022
65d61b0
Add experimental tag
daviddossett May 16, 2022
3cb6e9f
Merge branch 'main' into ddossett/rear-halibut
daviddossett May 16, 2022
2752290
Update setting name references
daviddossett May 16, 2022
2da987d
Fix typo
daviddossett May 18, 2022
93bd36d
Update status bar icon and filter all errors
daviddossett May 25, 2022
8a8a54a
Merge branch 'main' into ddossett/rear-halibut
daviddossett Jun 17, 2022
fa4dfc1
Update icons
daviddossett Jun 17, 2022
0175950
Updates icon and toggle behavior
daviddossett Jun 17, 2022
7c64574
Merge branch 'main' into ddossett/rear-halibut
daviddossett Jun 17, 2022
407efd3
Update codicons ttf
daviddossett Jun 17, 2022
8f3a491
Merge branch 'main' into ddossett/rear-halibut
bpasero Jun 19, 2022
e01a02d
cleanups
bpasero Jun 19, 2022
6ae0169
Use UI state instead of setting
daviddossett Jun 23, 2022
8cc01e8
Show window progress instead of notification
daviddossett Jun 24, 2022
bd70e26
Merge branch 'main' into ddossett/rear-halibut
daviddossett Jun 24, 2022
adf795b
Update Storage scopes
daviddossett Jun 24, 2022
42949a6
Update Storage scopes
daviddossett Jun 24, 2022
e1bb708
Refactor to use NotificationService
daviddossett Jun 24, 2022
595547a
Minor fixes
daviddossett Jun 24, 2022
222194d
Update tests
daviddossett Jun 24, 2022
da813af
Address PR feedback
daviddossett Jun 27, 2022
264bebb
Update tests
daviddossett Jun 27, 2022
adb15f4
:lipstick:
bpasero Jun 27, 2022
72f6836
:lipstick:
bpasero Jun 27, 2022
1bca7ce
zen - use dnd mode for filtering
bpasero Jun 27, 2022
359ee4c
set filters right on startup
bpasero Jun 27, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 5 additions & 3 deletions src/vs/editor/standalone/browser/standaloneServices.ts
Expand Up @@ -40,7 +40,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 } from 'vs/platform/label/common/label';
import { INotification, INotificationHandle, INotificationService, IPromptChoice, IPromptOptions, NoOpNotification, IStatusMessageOptions, NotificationsFilter } from 'vs/platform/notification/common/notification';
import { INotification, INotificationHandle, INotificationService, IPromptChoice, IPromptOptions, NoOpNotification, IStatusMessageOptions } from 'vs/platform/notification/common/notification';
import { IProgressRunner, IEditorProgressService } from 'vs/platform/progress/common/progress';
import { ITelemetryInfo, ITelemetryService, TelemetryLevel } from 'vs/platform/telemetry/common/telemetry';
import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier, IWorkspace, IWorkspaceContextService, IWorkspaceFolder, IWorkspaceFoldersChangeEvent, IWorkspaceFoldersWillChangeEvent, WorkbenchState, WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
Expand Down Expand Up @@ -232,8 +232,12 @@ export class StandaloneNotificationService implements INotificationService {

readonly onDidRemoveNotification: Event<INotification> = Event.None;

readonly onDidChangeDoNotDisturbMode: Event<void> = Event.None;

public _serviceBrand: undefined;

public doNotDisturbMode: boolean = false;

private static readonly NO_OP: INotificationHandle = new NoOpNotification();

public info(message: string): INotificationHandle {
Expand Down Expand Up @@ -271,8 +275,6 @@ export class StandaloneNotificationService implements INotificationService {
public status(message: string | Error, options?: IStatusMessageOptions): IDisposable {
return Disposable.None;
}

public setFilter(filter: NotificationsFilter): void { }
}

export class StandaloneCommandService implements ICommandService {
Expand Down
Expand Up @@ -141,8 +141,10 @@ suite('AbstractKeybindingService', () => {

const notificationService: INotificationService = {
_serviceBrand: undefined,
doNotDisturbMode: false,
onDidAddNotification: undefined!,
onDidRemoveNotification: undefined!,
onDidChangeDoNotDisturbMode: undefined!,
notify: (notification: INotification) => {
showMessageCalls.push({ sev: notification.severity, message: notification.message });
return new NoOpNotification();
Expand All @@ -169,8 +171,7 @@ suite('AbstractKeybindingService', () => {
statusMessageCallsDisposed!.push(message);
}
};
},
setFilter() { }
}
};

const resolver = new KeybindingResolver(items, [], () => { });
Expand Down
19 changes: 12 additions & 7 deletions src/vs/platform/notification/common/notification.ts
Expand Up @@ -319,6 +319,13 @@ export interface INotificationService {

readonly _serviceBrand: undefined;

/**
* The DND mode can be enabled or disabled
* and will result in all info and warning
* notifications to be silent.
*/
doNotDisturbMode: boolean;

/**
* Emitted when a new notification is added.
*/
Expand All @@ -329,6 +336,11 @@ export interface INotificationService {
*/
readonly onDidRemoveNotification: Event<INotification>;

/**
* Emitted when a do not disturb mode has changed.
*/
readonly onDidChangeDoNotDisturbMode: Event<void>;

/**
* Show the provided notification to the user. The returned `INotificationHandle`
* can be used to control the notification afterwards.
Expand Down Expand Up @@ -381,13 +393,6 @@ export interface INotificationService {
* @returns a disposable to hide the status message
*/
status(message: NotificationMessage, options?: IStatusMessageOptions): IDisposable;

/**
* Allows to configure a filter for notifications.
*
* @param filter the filter to use
*/
setFilter(filter: NotificationsFilter): void;
}

export class NoOpNotification implements INotificationHandle {
Expand Down
Expand Up @@ -5,16 +5,20 @@

import { Event } from 'vs/base/common/event';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { INotification, INotificationHandle, INotificationService, IPromptChoice, IPromptOptions, IStatusMessageOptions, NoOpNotification, NotificationsFilter, Severity } from 'vs/platform/notification/common/notification';
import { INotification, INotificationHandle, INotificationService, IPromptChoice, IPromptOptions, IStatusMessageOptions, NoOpNotification, Severity } from 'vs/platform/notification/common/notification';

export class TestNotificationService implements INotificationService {

readonly onDidAddNotification: Event<INotification> = Event.None;

readonly onDidRemoveNotification: Event<INotification> = Event.None;

readonly onDidChangeDoNotDisturbMode: Event<void> = Event.None;

declare readonly _serviceBrand: undefined;

doNotDisturbMode: boolean = false;

private static readonly NO_OP: INotificationHandle = new NoOpNotification();

info(message: string): INotificationHandle {
Expand All @@ -40,6 +44,4 @@ export class TestNotificationService implements INotificationService {
status(message: string | Error, options?: IStatusMessageOptions): IDisposable {
return Disposable.None;
}

setFilter(filter: NotificationsFilter): void { }
}
13 changes: 5 additions & 8 deletions src/vs/workbench/api/test/browser/extHostMessagerService.test.ts
Expand Up @@ -6,7 +6,7 @@
import * as assert from 'assert';
import { MainThreadMessageService } from 'vs/workbench/api/browser/mainThreadMessageService';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { INotificationService, INotification, NoOpNotification, INotificationHandle, Severity, IPromptChoice, IPromptOptions, IStatusMessageOptions, NotificationsFilter } from 'vs/platform/notification/common/notification';
import { INotificationService, INotification, NoOpNotification, INotificationHandle, Severity, IPromptChoice, IPromptOptions, IStatusMessageOptions } 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';
Expand All @@ -24,8 +24,10 @@ const emptyCommandService: ICommandService = {

const emptyNotificationService = new class implements INotificationService {
declare readonly _serviceBrand: undefined;
doNotDisturbMode: boolean = false;
onDidAddNotification: Event<INotification> = Event.None;
onDidRemoveNotification: Event<INotification> = Event.None;
onDidChangeDoNotDisturbMode: Event<void> = Event.None;
notify(...args: any[]): never {
throw new Error('not implemented');
}
Expand All @@ -44,19 +46,17 @@ const emptyNotificationService = new class implements INotificationService {
status(message: string | Error, options?: IStatusMessageOptions): IDisposable {
return Disposable.None;
}
setFilter(filter: NotificationsFilter): void {
throw new Error('not implemented.');
}
};

class EmptyNotificationService implements INotificationService {
declare readonly _serviceBrand: undefined;

doNotDisturbMode: boolean = false;
constructor(private withNotify: (notification: INotification) => void) {
}

onDidAddNotification: Event<INotification> = Event.None;
onDidRemoveNotification: Event<INotification> = Event.None;
onDidChangeDoNotDisturbMode: Event<void> = Event.None;
notify(notification: INotification): INotificationHandle {
this.withNotify(notification);

Expand All @@ -77,9 +77,6 @@ class EmptyNotificationService implements INotificationService {
status(message: string, options?: IStatusMessageOptions): IDisposable {
return Disposable.None;
}
setFilter(filter: NotificationsFilter): void {
throw new Error('Method not implemented.');
}
}

suite('ExtHostMessageService', function () {
Expand Down
20 changes: 12 additions & 8 deletions src/vs/workbench/browser/layout.ts
Expand Up @@ -33,7 +33,7 @@ import { IFileService } from 'vs/platform/files/common/files';
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
import { coalesce } from 'vs/base/common/arrays';
import { assertIsDefined, isNumber } from 'vs/base/common/types';
import { INotificationService, NotificationsFilter } from 'vs/platform/notification/common/notification';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { WINDOW_ACTIVE_BORDER, WINDOW_INACTIVE_BORDER } from 'vs/workbench/common/theme';
import { LineNumbersType } from 'vs/editor/common/config/editorOptions';
Expand Down Expand Up @@ -1087,6 +1087,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
if (!restoring) {
zenModeExitInfo.transitionedToFullScreen = toggleFullScreen;
zenModeExitInfo.transitionedToCenteredEditorLayout = !this.isEditorLayoutCentered() && config.centerLayout;
zenModeExitInfo.handleNotificationsDoNotDisturbMode = !this.notificationService.doNotDisturbMode;
zenModeExitInfo.wasVisible.sideBar = this.isVisible(Parts.SIDEBAR_PART);
zenModeExitInfo.wasVisible.panel = this.isVisible(Parts.PANEL_PART);
zenModeExitInfo.wasVisible.auxiliaryBar = this.isVisible(Parts.AUXILIARYBAR_PART);
Expand Down Expand Up @@ -1114,13 +1115,15 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
this.windowState.runtime.zenMode.transitionDisposables.add(this.editorGroupService.enforcePartOptions({ showTabs: false }));
}

if (config.silentNotifications) {
this.notificationService.setFilter(NotificationsFilter.ERROR);
if (config.silentNotifications && zenModeExitInfo.handleNotificationsDoNotDisturbMode) {
this.notificationService.doNotDisturbMode = true;
}
this.windowState.runtime.zenMode.transitionDisposables.add(this.configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration(WorkbenchLayoutSettings.ZEN_MODE_SILENT_NOTIFICATIONS)) {
const filter = this.configurationService.getValue(WorkbenchLayoutSettings.ZEN_MODE_SILENT_NOTIFICATIONS) ? NotificationsFilter.ERROR : NotificationsFilter.OFF;
this.notificationService.setFilter(filter);
const zenModeSilentNotifications = !!this.configurationService.getValue(WorkbenchLayoutSettings.ZEN_MODE_SILENT_NOTIFICATIONS);
if (zenModeExitInfo.handleNotificationsDoNotDisturbMode) {
this.notificationService.doNotDisturbMode = zenModeSilentNotifications;
}
}
}));

Expand Down Expand Up @@ -1155,13 +1158,14 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
this.centerEditorLayout(false, true);
}

if (zenModeExitInfo.handleNotificationsDoNotDisturbMode) {
this.notificationService.doNotDisturbMode = false;
}

setLineNumbers();

this.focus();

// Clear notifications filter
this.notificationService.setFilter(NotificationsFilter.OFF);

toggleFullScreen = zenModeExitInfo.transitionedToFullScreen && this.windowState.runtime.fullscreen;
}

Expand Down
1 change: 1 addition & 0 deletions src/vs/workbench/browser/layoutState.ts
Expand Up @@ -46,6 +46,7 @@ export const LayoutStateKeys = {
ZEN_MODE_EXIT_INFO: new RuntimeStateKey('zenMode.exitInfo', StorageScope.WORKSPACE, StorageTarget.USER, {
transitionedToCenteredEditorLayout: false,
transitionedToFullScreen: false,
handleNotificationsDoNotDisturbMode: false,
wasVisible: {
auxiliaryBar: false,
panel: false,
Expand Down
Expand Up @@ -9,7 +9,7 @@ import { localize } from 'vs/nls';
import { Action, IAction, ActionRunner, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { CLEAR_NOTIFICATION, EXPAND_NOTIFICATION, COLLAPSE_NOTIFICATION, CLEAR_ALL_NOTIFICATIONS, HIDE_NOTIFICATIONS_CENTER } from 'vs/workbench/browser/parts/notifications/notificationsCommands';
import { CLEAR_NOTIFICATION, EXPAND_NOTIFICATION, COLLAPSE_NOTIFICATION, CLEAR_ALL_NOTIFICATIONS, HIDE_NOTIFICATIONS_CENTER, TOGGLE_DO_NOT_DISTURB_MODE } from 'vs/workbench/browser/parts/notifications/notificationsCommands';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { Codicon } from 'vs/base/common/codicons';
Expand All @@ -23,6 +23,7 @@ const hideIcon = registerIcon('notifications-hide', Codicon.chevronDown, localiz
const expandIcon = registerIcon('notifications-expand', Codicon.chevronUp, localize('expandIcon', 'Icon for the expand action in notifications.'));
const collapseIcon = registerIcon('notifications-collapse', Codicon.chevronDown, localize('collapseIcon', 'Icon for the collapse action in notifications.'));
const configureIcon = registerIcon('notifications-configure', Codicon.gear, localize('configureIcon', 'Icon for the configure action in notifications.'));
const doNotDisturbIcon = registerIcon('notifications-do-not-disturb', Codicon.bellSlash, localize('doNotDisturbIcon', 'Icon for the mute all action in notifications.'));

export class ClearNotificationAction extends Action {

Expand Down Expand Up @@ -60,6 +61,24 @@ export class ClearAllNotificationsAction extends Action {
}
}

export class ToggleDoNotDisturbAction extends Action {

static readonly ID = TOGGLE_DO_NOT_DISTURB_MODE;
static readonly LABEL = localize('toggleDoNotDisturbMode', "Toggle Do Not Disturb Mode");

constructor(
id: string,
label: string,
@ICommandService private readonly commandService: ICommandService
) {
super(id, label, ThemeIcon.asClassName(doNotDisturbIcon));
}

override async run(): Promise<void> {
this.commandService.executeCommand(TOGGLE_DO_NOT_DISTURB_MODE);
}
}

export class HideNotificationsCenterAction extends Action {

static readonly ID = HIDE_NOTIFICATIONS_CENTER;
Expand Down
Expand Up @@ -19,11 +19,12 @@ import { widgetShadow } from 'vs/platform/theme/common/colorRegistry';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { localize } from 'vs/nls';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { ClearAllNotificationsAction, HideNotificationsCenterAction, NotificationActionRunner } from 'vs/workbench/browser/parts/notifications/notificationsActions';
import { ClearAllNotificationsAction, HideNotificationsCenterAction, NotificationActionRunner, ToggleDoNotDisturbAction } from 'vs/workbench/browser/parts/notifications/notificationsActions';
import { IAction } from 'vs/base/common/actions';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { assertAllDefined, assertIsDefined } from 'vs/base/common/types';
import { NotificationsCenterVisibleContext } from 'vs/workbench/common/contextkeys';
import { INotificationService } from 'vs/platform/notification/common/notification';

export class NotificationsCenter extends Themable implements INotificationsCenterController {

Expand All @@ -40,6 +41,7 @@ export class NotificationsCenter extends Themable implements INotificationsCente
private workbenchDimensions: Dimension | undefined;
private readonly notificationsCenterVisibleContextKey = NotificationsCenterVisibleContext.bindTo(this.contextKeyService);
private clearAllAction: ClearAllNotificationsAction | undefined;
private toggleDoNotDisturbAction: ToggleDoNotDisturbAction | undefined;

constructor(
private readonly container: HTMLElement,
Expand All @@ -49,7 +51,8 @@ export class NotificationsCenter extends Themable implements INotificationsCente
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IEditorGroupsService private readonly editorGroupService: IEditorGroupsService,
@IKeybindingService private readonly keybindingService: IKeybindingService
@IKeybindingService private readonly keybindingService: IKeybindingService,
@INotificationService private readonly notificationService: INotificationService,
) {
super(themeService);

Expand All @@ -61,6 +64,13 @@ export class NotificationsCenter extends Themable implements INotificationsCente
private registerListeners(): void {
this._register(this.model.onDidChangeNotification(e => this.onDidChangeNotification(e)));
this._register(this.layoutService.onDidLayout(dimension => this.layout(Dimension.lift(dimension))));
this._register(this.notificationService.onDidChangeDoNotDisturbMode(() => this.onDidChangeDoNotDisturbMode()));
}

private onDidChangeDoNotDisturbMode(): void {
if (this.notificationService.doNotDisturbMode) {
this.hide(); // hide the notification center when do not disturb is enabled
}
}

get isVisible(): boolean {
Expand Down Expand Up @@ -154,6 +164,9 @@ export class NotificationsCenter extends Themable implements INotificationsCente
this.clearAllAction = this._register(this.instantiationService.createInstance(ClearAllNotificationsAction, ClearAllNotificationsAction.ID, ClearAllNotificationsAction.LABEL));
notificationsToolBar.push(this.clearAllAction, { icon: true, label: false, keybinding: this.getKeybindingLabel(this.clearAllAction) });

this.toggleDoNotDisturbAction = this._register(this.instantiationService.createInstance(ToggleDoNotDisturbAction, ToggleDoNotDisturbAction.ID, ToggleDoNotDisturbAction.LABEL));
notificationsToolBar.push(this.toggleDoNotDisturbAction, { icon: true, label: false, keybinding: this.getKeybindingLabel(this.toggleDoNotDisturbAction) });

const hideAllAction = this._register(this.instantiationService.createInstance(HideNotificationsCenterAction, HideNotificationsCenterAction.ID, HideNotificationsCenterAction.LABEL));
notificationsToolBar.push(hideAllAction, { icon: true, label: false, keybinding: this.getKeybindingLabel(hideAllAction) });

Expand Down Expand Up @@ -316,6 +329,7 @@ export class NotificationsCenter extends Themable implements INotificationsCente
}
}


registerThemingParticipant((theme, collector) => {
const notificationBorderColor = theme.getColor(NOTIFICATIONS_BORDER);
if (notificationBorderColor) {
Expand Down