Move app/src (mostly) to flipper-ui-core/src
Summary: This diff moves all UI code from app/src to app/flipper-ui-core. That is now slightly too much (e.g. node deps are not removed yet), but from here it should be easier to move things out again, as I don't want this diff to be open for too long to avoid too much merge conflicts. * But at least flipper-ui-core is Electron free :) * Killed all cross module imports as well, as they where now even more in the way * Some unit test needed some changes, most not too big (but emotion hashes got renumbered in the snapshots, feel free to ignore that) * Found some files that were actually meaningless (tsconfig in plugins, WatchTools files, that start generating compile errors, removed those Follow up work: * make flipper-ui-core configurable, and wire up flipper-server-core in Electron instead of here * remove node deps (aigoncharov) * figure out correct place to load GKs, plugins, make intern requests etc., and move to the correct module * clean up deps Reviewed By: aigoncharov Differential Revision: D32427722 fbshipit-source-id: 14fe92e1ceb15b9dcf7bece367c8ab92df927a70
This commit is contained in:
committed by
Facebook GitHub Bot
parent
54b7ce9308
commit
7e50c0466a
162
desktop/flipper-ui-core/src/dispatcher/notifications.tsx
Normal file
162
desktop/flipper-ui-core/src/dispatcher/notifications.tsx
Normal file
@@ -0,0 +1,162 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its 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 {Store} from '../reducers/index';
|
||||
import {Logger} from 'flipper-common';
|
||||
import {PluginNotification} from '../reducers/notifications';
|
||||
import reactElementToJSXString from 'react-element-to-jsx-string';
|
||||
import {
|
||||
updatePluginBlocklist,
|
||||
updateCategoryBlocklist,
|
||||
} from '../reducers/notifications';
|
||||
import {textContent} from 'flipper-plugin';
|
||||
import {getPluginTitle} from '../utils/pluginUtils';
|
||||
import {sideEffect} from '../utils/sideEffect';
|
||||
import {openNotification} from '../sandy-chrome/notification/Notification';
|
||||
import {getRenderHostInstance} from '../RenderHost';
|
||||
|
||||
export type NotificationEvents =
|
||||
| 'show'
|
||||
| 'click'
|
||||
| 'close'
|
||||
| 'reply'
|
||||
| 'action';
|
||||
|
||||
const NOTIFICATION_THROTTLE = 5 * 1000; // in milliseconds
|
||||
|
||||
export default (store: Store, logger: Logger) => {
|
||||
const knownNotifications: Set<string> = new Set();
|
||||
const lastNotificationTime: Map<string, number> = new Map();
|
||||
|
||||
getRenderHostInstance().onIpcEvent(
|
||||
'notificationEvent',
|
||||
(
|
||||
eventName: NotificationEvents,
|
||||
pluginNotification: PluginNotification,
|
||||
arg: null | string | number,
|
||||
) => {
|
||||
if (eventName === 'click' || (eventName === 'action' && arg === 0)) {
|
||||
openNotification(store, pluginNotification);
|
||||
} else if (eventName === 'action') {
|
||||
if (arg === 1 && pluginNotification.notification.category) {
|
||||
// Hide similar (category)
|
||||
logger.track(
|
||||
'usage',
|
||||
'notification-hide-category',
|
||||
pluginNotification,
|
||||
);
|
||||
|
||||
const {category} = pluginNotification.notification;
|
||||
const {blocklistedCategories} = store.getState().notifications;
|
||||
if (category && blocklistedCategories.indexOf(category) === -1) {
|
||||
store.dispatch(
|
||||
updateCategoryBlocklist([...blocklistedCategories, category]),
|
||||
);
|
||||
}
|
||||
} else if (arg === 2) {
|
||||
// Hide plugin
|
||||
logger.track('usage', 'notification-hide-plugin', pluginNotification);
|
||||
|
||||
const {blocklistedPlugins} = store.getState().notifications;
|
||||
if (blocklistedPlugins.indexOf(pluginNotification.pluginId) === -1) {
|
||||
store.dispatch(
|
||||
updatePluginBlocklist([
|
||||
...blocklistedPlugins,
|
||||
pluginNotification.pluginId,
|
||||
]),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
sideEffect(
|
||||
store,
|
||||
{name: 'notifications', throttleMs: 500},
|
||||
({notifications, plugins}) => ({
|
||||
notifications,
|
||||
devicePlugins: plugins.devicePlugins,
|
||||
clientPlugins: plugins.clientPlugins,
|
||||
}),
|
||||
({notifications, devicePlugins, clientPlugins}, store) => {
|
||||
function getPlugin(name: string) {
|
||||
return devicePlugins.get(name) ?? clientPlugins.get(name);
|
||||
}
|
||||
|
||||
const {activeNotifications, blocklistedPlugins, blocklistedCategories} =
|
||||
notifications;
|
||||
|
||||
activeNotifications
|
||||
.map((n) => ({
|
||||
...n,
|
||||
notification: {
|
||||
...n.notification,
|
||||
message: textContent(n.notification.message),
|
||||
},
|
||||
}))
|
||||
.forEach((n: PluginNotification) => {
|
||||
if (
|
||||
store.getState().connections.selectedPlugin !== 'notifications' &&
|
||||
!knownNotifications.has(n.notification.id) &&
|
||||
blocklistedPlugins.indexOf(n.pluginId) === -1 &&
|
||||
(!n.notification.category ||
|
||||
blocklistedCategories.indexOf(n.notification.category) === -1)
|
||||
) {
|
||||
const prevNotificationTime: number =
|
||||
lastNotificationTime.get(n.pluginId) || 0;
|
||||
lastNotificationTime.set(n.pluginId, new Date().getTime());
|
||||
knownNotifications.add(n.notification.id);
|
||||
|
||||
if (
|
||||
new Date().getTime() - prevNotificationTime <
|
||||
NOTIFICATION_THROTTLE
|
||||
) {
|
||||
// Don't send a notification if the plugin has sent a notification
|
||||
// within the NOTIFICATION_THROTTLE.
|
||||
return;
|
||||
}
|
||||
const plugin = getPlugin(n.pluginId);
|
||||
getRenderHostInstance().sendIpcEvent('sendNotification', {
|
||||
payload: {
|
||||
title: n.notification.title,
|
||||
body: reactElementToJSXString(n.notification.message),
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
text: 'Show',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
text: 'Hide similar',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
text: `Hide all ${
|
||||
plugin != null ? getPluginTitle(plugin) : ''
|
||||
}`,
|
||||
},
|
||||
],
|
||||
closeButtonText: 'Hide',
|
||||
},
|
||||
closeAfter: 10000,
|
||||
pluginNotification: n,
|
||||
});
|
||||
logger.track('usage', 'native-notification', {
|
||||
...n.notification,
|
||||
message:
|
||||
typeof n.notification.message === 'string'
|
||||
? n.notification.message
|
||||
: '<ReactNode>',
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user