Files
flipper/desktop/flipper-ui-core/src/reducers/notifications.tsx
Andres Suarez 79023ee190 Update copyright headers from Facebook to Meta
Reviewed By: bhamodi

Differential Revision: D33331422

fbshipit-source-id: 016e8dcc0c0c7f1fc353a348b54fda0d5e2ddc01
2021-12-27 14:31:45 -08:00

260 lines
6.2 KiB
TypeScript

/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
import {Notification} from 'flipper-plugin';
import {Actions} from './';
import React from 'react';
import {getStringFromErrorLike} from 'flipper-common';
export const GLOBAL_NOTIFICATION_PLUGIN_ID = 'Flipper';
export type PluginNotification = {
notification: Notification;
pluginId: string;
client: null | string; // id
};
export type PluginNotificationReference = {
notificationId: string;
pluginId: string;
client: null | string; // id
};
export type State = {
activeNotifications: Array<PluginNotification>;
invalidatedNotifications: Array<PluginNotification>;
blocklistedPlugins: Array<string>;
blocklistedCategories: Array<string>;
clearedNotifications: Set<string>;
};
type ActiveNotificationsAction = {
type: 'SET_ACTIVE_NOTIFICATIONS';
payload: {
notifications: Array<Notification>;
client: null | string;
pluginId: string;
};
};
export type Action =
| {
type: 'CLEAR_ALL_NOTIFICATIONS';
}
| {
type: 'SET_ACTIVE_NOTIFICATIONS';
payload: {
notifications: Array<Notification>;
client: null | string;
pluginId: string;
};
}
| {
type: 'UPDATE_PLUGIN_BLOCKLIST';
payload: Array<string>;
}
| {
type: 'UPDATE_CATEGORY_BLOCKLIST';
payload: Array<string>;
}
| {
type: 'ADD_NOTIFICATION';
payload: PluginNotification;
}
| {
type: 'REMOVE_NOTIFICATION';
payload: PluginNotificationReference;
};
const INITIAL_STATE: State = {
activeNotifications: [],
invalidatedNotifications: [],
blocklistedPlugins: [],
blocklistedCategories: [],
clearedNotifications: new Set(),
};
export default function reducer(
state: State = INITIAL_STATE,
action: Actions,
): State {
switch (action.type) {
case 'SET_ACTIVE_NOTIFICATIONS': {
return activeNotificationsReducer(state, action);
}
case 'CLEAR_ALL_NOTIFICATIONS':
const markAsCleared = ({
pluginId,
notification: {id},
}: PluginNotification) =>
state.clearedNotifications.add(`${pluginId}#${id}`);
state.activeNotifications.forEach(markAsCleared);
state.invalidatedNotifications.forEach(markAsCleared);
// Q: Should this actually delete them, or just invalidate them?
return {
...state,
activeNotifications: [],
invalidatedNotifications: [],
};
case 'UPDATE_PLUGIN_BLOCKLIST':
return {
...state,
blocklistedPlugins: action.payload,
};
case 'UPDATE_CATEGORY_BLOCKLIST':
return {
...state,
blocklistedCategories: action.payload,
};
case 'ADD_NOTIFICATION':
return {
...state,
// while adding notifications, remove old duplicates
activeNotifications: [
...state.activeNotifications.filter(
(notif) =>
notif.client !== action.payload.client ||
notif.pluginId !== action.payload.pluginId ||
notif.notification.id !== action.payload.notification.id,
),
action.payload,
],
};
case 'REMOVE_NOTIFICATION':
return {
...state,
activeNotifications: [
...state.activeNotifications.filter(
(notif) =>
notif.client !== action.payload.client ||
notif.pluginId !== action.payload.pluginId ||
notif.notification.id !== action.payload.notificationId,
),
],
};
default:
return state;
}
}
function activeNotificationsReducer(
state: State,
action: ActiveNotificationsAction,
): State {
const {payload} = action;
const newActiveNotifications = [];
const newInactivatedNotifications = state.invalidatedNotifications.slice();
const newIDs = new Set(payload.notifications.map((n: Notification) => n.id));
for (const activeNotification of state.activeNotifications) {
if (activeNotification.pluginId !== payload.pluginId) {
newActiveNotifications.push(activeNotification);
continue;
}
if (!newIDs.has(activeNotification.notification.id)) {
newInactivatedNotifications.push(activeNotification);
}
}
payload.notifications
.filter(
({id}: Notification) =>
!state.clearedNotifications.has(`${payload.pluginId}#${id}`),
)
.forEach((notification: Notification) => {
newActiveNotifications.push({
pluginId: payload.pluginId,
client: payload.client,
notification,
});
});
return {
...state,
activeNotifications: newActiveNotifications,
invalidatedNotifications: newInactivatedNotifications,
};
}
export function addNotification(payload: PluginNotification): Action {
return {
type: 'ADD_NOTIFICATION',
payload,
};
}
export function removeNotification(
payload: PluginNotificationReference,
): Action {
return {
type: 'REMOVE_NOTIFICATION',
payload,
};
}
export function addErrorNotification(
title: string,
message: string | React.ReactNode,
error?: any,
): Action {
// TODO: use this method for https://github.com/facebook/flipper/pull/1478/files as well
console.warn(title, message, error);
return addNotification({
client: null,
pluginId: GLOBAL_NOTIFICATION_PLUGIN_ID,
notification: {
id: title,
title,
message: error ? (
<>
<p>{message}</p>
<p>{getStringFromErrorLike(error)}</p>
</>
) : (
message
),
severity: 'error',
},
});
}
export function setActiveNotifications(payload: {
notifications: Array<Notification>;
client: null | string;
pluginId: string;
}): Action {
return {
type: 'SET_ACTIVE_NOTIFICATIONS',
payload,
};
}
export function clearAllNotifications(): Action {
return {
type: 'CLEAR_ALL_NOTIFICATIONS',
};
}
export function updatePluginBlocklist(payload: Array<string>): Action {
return {
type: 'UPDATE_PLUGIN_BLOCKLIST',
payload,
};
}
export function updateCategoryBlocklist(payload: Array<string>): Action {
return {
type: 'UPDATE_CATEGORY_BLOCKLIST',
payload,
};
}