Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ import { NotificationFocusedContext, NotificationsCenterVisibleContext, Notifica
import { INotificationService, INotificationSourceFilter, NotificationsFilter } from '../../../../platform/notification/common/notification.js';
import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js';
import { ActionRunner, IAction, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from '../../../../base/common/actions.js';
import { IQuickInputService, IQuickPickItem } from '../../../../platform/quickinput/common/quickInput.js';
import { IQuickInputButton, IQuickInputService, IQuickPickItem } from '../../../../platform/quickinput/common/quickInput.js';
import { DisposableStore } from '../../../../base/common/lifecycle.js';
import { AccessibilitySignal, IAccessibilitySignalService } from '../../../../platform/accessibilitySignal/browser/accessibilitySignalService.js';
import { Codicon } from '../../../../base/common/codicons.js';
import { ThemeIcon } from '../../../../base/common/themables.js';
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';

// Center
Expand Down Expand Up @@ -285,14 +286,21 @@ export function registerNotificationCommands(center: INotificationsCenterControl

const sortedFilters = notificationService.getFilters().sort((a, b) => a.label.localeCompare(b.label));

const removeSourceButton: IQuickInputButton = {
iconClass: ThemeIcon.asClassName(Codicon.removeClose),
tooltip: localize('removeSource', "Remove Source"),
alwaysVisible: true
};

const disposables = new DisposableStore();
const picker = disposables.add(quickInputService.createQuickPick<IQuickPickItem & INotificationSourceFilter>());

picker.items = sortedFilters.map(source => ({
id: source.id,
label: source.label,
tooltip: `${source.label} (${source.id})`,
filter: source.filter
filter: source.filter,
buttons: [removeSourceButton]
}));

picker.canSelectMany = true;
Expand All @@ -301,6 +309,21 @@ export function registerNotificationCommands(center: INotificationsCenterControl

picker.show();

disposables.add(picker.onDidTriggerItemButton(event => {
if (event.button !== removeSourceButton || !event.item.id) {
return;
}

notificationService.removeFilter(event.item.id);

// Rebuild items so the removed entry disappears immediately
// while preserving the user's current selection for the rest.
const previousSelectedIds = new Set(picker.selectedItems.map(item => item.id));
const remaining = picker.items.filter(item => item.id !== event.item.id);
picker.items = remaining;
picker.selectedItems = remaining.filter(item => previousSelectedIds.has(item.id));
}));

disposables.add(picker.onDidAccept(async () => {
for (const item of picker.items) {
notificationService.setFilter({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import assert from 'assert';
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';
import { NotificationsFilter } from '../../../../../platform/notification/common/notification.js';
import { TestStorageService } from '../../../../test/common/workbenchTestServices.js';
import { NotificationService } from '../../common/notificationService.js';

suite('NotificationService - filters', () => {

const disposables = ensureNoDisposablesAreLeakedInTestSuite();

function createService(): NotificationService {
const storageService = disposables.add(new TestStorageService());
return disposables.add(new NotificationService(storageService));
}

test('getFilters returns sources registered via setFilter', () => {
const service = createService();

service.setFilter({ id: 'ext.a', label: 'Extension A', filter: NotificationsFilter.OFF });
service.setFilter({ id: 'ext.b', label: 'Extension B', filter: NotificationsFilter.ERROR });

const filters = service.getFilters().sort((a, b) => a.id.localeCompare(b.id));
assert.strictEqual(filters.length, 2);
assert.strictEqual(filters[0].id, 'ext.a');
assert.strictEqual(filters[0].filter, NotificationsFilter.OFF);
assert.strictEqual(filters[1].id, 'ext.b');
assert.strictEqual(filters[1].filter, NotificationsFilter.ERROR);
});

test('removeFilter drops a previously tracked source', () => {
const service = createService();

service.setFilter({ id: 'ext.a', label: 'Extension A', filter: NotificationsFilter.OFF });
service.setFilter({ id: 'ext.b', label: 'Extension B', filter: NotificationsFilter.ERROR });
assert.strictEqual(service.getFilters().length, 2);

service.removeFilter('ext.a');

const filters = service.getFilters();
assert.strictEqual(filters.length, 1);
assert.strictEqual(filters[0].id, 'ext.b');

// After removal, the per-source filter falls back to the default (OFF).
assert.strictEqual(service.getFilter({ id: 'ext.a', label: 'Extension A' }), NotificationsFilter.OFF);
});

test('removeFilter is a no-op for unknown source ids', () => {
const service = createService();

service.setFilter({ id: 'ext.a', label: 'Extension A', filter: NotificationsFilter.ERROR });

service.removeFilter('ext.unknown');

const filters = service.getFilters();
assert.strictEqual(filters.length, 1);
assert.strictEqual(filters[0].id, 'ext.a');
assert.strictEqual(filters[0].filter, NotificationsFilter.ERROR);
});

test('removeFilter persists across service instances using the same storage', () => {
const storageService = disposables.add(new TestStorageService());

const first = disposables.add(new NotificationService(storageService));
first.setFilter({ id: 'ext.a', label: 'Extension A', filter: NotificationsFilter.ERROR });
first.setFilter({ id: 'ext.b', label: 'Extension B', filter: NotificationsFilter.OFF });
first.removeFilter('ext.a');

// A fresh service backed by the same storage should not see the removed source.
const second = disposables.add(new NotificationService(storageService));
const filters = second.getFilters();
assert.strictEqual(filters.length, 1);
assert.strictEqual(filters[0].id, 'ext.b');
});

test('removing the last filter leaves the picker source list empty', () => {
const service = createService();

service.setFilter({ id: 'ext.only', label: 'Only Extension', filter: NotificationsFilter.OFF });
assert.strictEqual(service.getFilters().length, 1);

service.removeFilter('ext.only');

assert.strictEqual(service.getFilters().length, 0);
});
});
Loading