diff --git a/src/dispatcher/notifications.js b/src/dispatcher/notifications.js index 93a865dee..476ed4db8 100644 --- a/src/dispatcher/notifications.js +++ b/src/dispatcher/notifications.js @@ -15,6 +15,7 @@ import {selectPlugin} from '../reducers/connections'; import { setActiveNotifications, updatePluginBlacklist, + updateCategoryBlacklist, } from '../reducers/notifications'; import {textContent} from '../utils/index'; import {clientPlugins} from '../plugins/index.js'; @@ -54,6 +55,14 @@ export default (store: Store, logger: Logger) => { 'notification-hide-category', pluginNotification, ); + + const {category} = pluginNotification.notification; + const {blacklistedCategories} = store.getState().notifications; + if (category && blacklistedCategories.indexOf(category) === -1) { + store.dispatch( + updateCategoryBlacklist([...blacklistedCategories, category]), + ); + } } else if (arg === 2) { // Hide plugin logger.track('usage', 'notification-hide-plugin', pluginNotification); @@ -102,13 +111,19 @@ export default (store: Store, logger: Logger) => { } }); - const {activeNotifications, blacklistedPlugins} = notifications; + const { + activeNotifications, + blacklistedPlugins, + blacklistedCategories, + } = notifications; activeNotifications.forEach((n: PluginNotification) => { if ( store.getState().connections.selectedPlugin !== 'notifications' && !knownNotifications.has(n.notification.id) && - blacklistedPlugins.indexOf(n.pluginId) === -1 + blacklistedPlugins.indexOf(n.pluginId) === -1 && + (!n.notification.category || + blacklistedCategories.indexOf(n.notification.category) === -1) ) { ipcRenderer.send('sendNotification', { payload: { diff --git a/src/reducers/__tests__/notifications.node.js b/src/reducers/__tests__/notifications.node.js new file mode 100644 index 000000000..59ce52d11 --- /dev/null +++ b/src/reducers/__tests__/notifications.node.js @@ -0,0 +1,104 @@ +/** + * Copyright 2018-present Facebook. + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * @format + */ + +import type {State} from '../notifications'; + +import { + default as reducer, + setActiveNotifications, + clearAllNotifications, + updatePluginBlacklist, + updateCategoryBlacklist, +} from '../notifications'; + +const notification = { + id: 'id', + title: 'title', + message: 'message', + severity: 'warning', +}; + +function getInitialState(): State { + return { + activeNotifications: [], + invalidatedNotifications: [], + blacklistedPlugins: [], + blacklistedCategories: [], + clearedNotifications: new Set(), + }; +} + +test('reduce updateCategoryBlacklist', () => { + const blacklistedCategories = ['blacklistedCategory']; + const res = reducer( + getInitialState(), + updateCategoryBlacklist(blacklistedCategories), + ); + expect(res).toEqual({ + ...getInitialState(), + blacklistedCategories, + }); +}); + +test('reduce updatePluginBlacklist', () => { + const blacklistedPlugins = ['blacklistedPlugin']; + const res = reducer( + getInitialState(), + updatePluginBlacklist(blacklistedPlugins), + ); + expect(res).toEqual({ + ...getInitialState(), + blacklistedPlugins, + }); +}); + +test('reduce clearAllNotifications', () => { + const pluginId = 'pluginId'; + const client = 'client'; + + const res = reducer( + { + ...getInitialState(), + activeNotifications: [ + { + client, + pluginId, + notification, + }, + ], + }, + clearAllNotifications(), + ); + expect(res).toEqual({ + ...getInitialState(), + clearedNotifications: new Set([`${pluginId}#${notification.id}`]), + }); +}); + +test('reduce setActiveNotifications', () => { + const pluginId = 'pluginId'; + const client = 'client'; + + const res = reducer( + getInitialState(), + setActiveNotifications({ + notifications: [notification], + client, + pluginId, + }), + ); + expect(res).toEqual({ + ...getInitialState(), + activeNotifications: [ + { + client, + pluginId, + notification, + }, + ], + }); +}); diff --git a/src/reducers/index.js b/src/reducers/index.js index 6f3ff4062..a77dff7a1 100644 --- a/src/reducers/index.js +++ b/src/reducers/index.js @@ -64,7 +64,7 @@ export default combineReducers({ { key: 'notifications', storage, - whitelist: ['blacklistedPlugins'], + whitelist: ['blacklistedPlugins', 'blacklistedCategories'], }, notifications, ), diff --git a/src/reducers/notifications.js b/src/reducers/notifications.js index 8abdd0ad2..63ec7006b 100644 --- a/src/reducers/notifications.js +++ b/src/reducers/notifications.js @@ -16,6 +16,7 @@ export type State = { activeNotifications: Array, invalidatedNotifications: Array, blacklistedPlugins: Array, + blacklistedCategories: Array, clearedNotifications: Set, }; @@ -28,24 +29,32 @@ type ActiveNotificationsAction = { }, }; -type ClearAllAction = { - type: 'CLEAR_ALL_NOTIFICATIONS', -}; - -type UpdateBlacklistAction = { - type: 'UPDATE_PLUGIN_BLACKLIST', - payload: Array, -}; - export type Action = - | ActiveNotificationsAction - | ClearAllAction - | UpdateBlacklistAction; + | { + type: 'CLEAR_ALL_NOTIFICATIONS', + } + | { + type: 'SET_ACTIVE_NOTIFICATIONS', + payload: { + notifications: Array, + client: ?string, + pluginId: string, + }, + } + | { + type: 'UPDATE_PLUGIN_BLACKLIST', + payload: Array, + } + | { + type: 'UPDATE_CATEGORY_BLACKLIST', + payload: Array, + }; const INITIAL_STATE: State = { activeNotifications: [], invalidatedNotifications: [], blacklistedPlugins: [], + blacklistedCategories: [], clearedNotifications: new Set(), }; @@ -77,6 +86,11 @@ export default function reducer( ...state, blacklistedPlugins: action.payload, }; + case 'UPDATE_CATEGORY_BLACKLIST': + return { + ...state, + blacklistedCategories: action.payload, + }; default: return state; } @@ -140,11 +154,16 @@ export function clearAllNotifications(): Action { }; } -export function updatePluginBlacklist( - payload: Array, -): UpdateBlacklistAction { +export function updatePluginBlacklist(payload: Array) { return { type: 'UPDATE_PLUGIN_BLACKLIST', payload, }; } + +export function updateCategoryBlacklist(payload: Array) { + return { + type: 'UPDATE_CATEGORY_BLACKLIST', + payload, + }; +}