Add computeNotifications method to FlipperBasePlugin
Summary: Method `computeNotifications` added to the base plugin class. Plugins should implement this to define a mapping from their state+props to the notifications they are emitting. I've plugged this into componentDidUpdate, because we don't yet have the background plugin infra. When we do, we'll want some other incoming data hook to use so it's not tied to the react component rendering. Example usage added to network plugin in the next commit. Reviewed By: passy Differential Revision: D10127875 fbshipit-source-id: efd4d8cfc0d3d33852a6cf9a290549a5f90d389d
This commit is contained in:
committed by
Facebook Github Bot
parent
1a5b127d58
commit
6df906ed5f
@@ -22,6 +22,8 @@ import {
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {connect} from 'react-redux';
|
import {connect} from 'react-redux';
|
||||||
import {setPluginState} from './reducers/pluginStates.js';
|
import {setPluginState} from './reducers/pluginStates.js';
|
||||||
|
import {setActiveNotifications} from './reducers/notifications.js';
|
||||||
|
import type {NotificationSet} from './plugin.js';
|
||||||
import {devicePlugins} from './device-plugins/index.js';
|
import {devicePlugins} from './device-plugins/index.js';
|
||||||
import plugins from './plugins/index.js';
|
import plugins from './plugins/index.js';
|
||||||
import {activateMenuItems} from './MenuBar.js';
|
import {activateMenuItems} from './MenuBar.js';
|
||||||
@@ -52,6 +54,10 @@ type Props = {
|
|||||||
pluginKey: string,
|
pluginKey: string,
|
||||||
state: Object,
|
state: Object,
|
||||||
}) => void,
|
}) => void,
|
||||||
|
setActiveNotifications: ({
|
||||||
|
pluginId: string,
|
||||||
|
notifications: NotificationSet,
|
||||||
|
}) => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
@@ -124,7 +130,7 @@ class PluginContainer extends Component<Props, State> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {pluginStates, setPluginState} = this.props;
|
const {pluginStates, setPluginState, setActiveNotifications} = this.props;
|
||||||
const {activePlugin, pluginKey, target} = this.state;
|
const {activePlugin, pluginKey, target} = this.state;
|
||||||
|
|
||||||
if (!activePlugin || !target) {
|
if (!activePlugin || !target) {
|
||||||
@@ -136,6 +142,11 @@ class PluginContainer extends Component<Props, State> {
|
|||||||
logger: this.props.logger,
|
logger: this.props.logger,
|
||||||
persistedState: pluginStates[pluginKey] || {},
|
persistedState: pluginStates[pluginKey] || {},
|
||||||
setPersistedState: state => setPluginState({pluginKey, state}),
|
setPersistedState: state => setPluginState({pluginKey, state}),
|
||||||
|
setActiveNotifications: notifications =>
|
||||||
|
setActiveNotifications({
|
||||||
|
pluginId: pluginKey,
|
||||||
|
notifications: notifications,
|
||||||
|
}),
|
||||||
target,
|
target,
|
||||||
ref: this.refChanged,
|
ref: this.refChanged,
|
||||||
};
|
};
|
||||||
@@ -171,5 +182,6 @@ export default connect(
|
|||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
setPluginState,
|
setPluginState,
|
||||||
|
setActiveNotifications,
|
||||||
},
|
},
|
||||||
)(PluginContainer);
|
)(PluginContainer);
|
||||||
|
|||||||
@@ -24,10 +24,22 @@ export type PluginClient = {|
|
|||||||
|
|
||||||
type PluginTarget = BaseDevice | Client;
|
type PluginTarget = BaseDevice | Client;
|
||||||
|
|
||||||
|
export type Notification = {|
|
||||||
|
title: string,
|
||||||
|
message: string,
|
||||||
|
severity: 'warning' | 'error',
|
||||||
|
timestamp?: number,
|
||||||
|
category?: string,
|
||||||
|
action?: string,
|
||||||
|
|};
|
||||||
|
|
||||||
|
export type NotificationSet = {[id: string]: Notification};
|
||||||
|
|
||||||
export type Props<T> = {
|
export type Props<T> = {
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
persistedState: T,
|
persistedState: T,
|
||||||
setPersistedState: (state: $Shape<T>) => void,
|
setPersistedState: (state: $Shape<T>) => void,
|
||||||
|
setActiveNotifications: NotificationSet => void,
|
||||||
target: PluginTarget,
|
target: PluginTarget,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -63,6 +75,9 @@ export class FlipperBasePlugin<
|
|||||||
// methods to be overriden by plugins
|
// methods to be overriden by plugins
|
||||||
init(): void {}
|
init(): void {}
|
||||||
teardown(): void {}
|
teardown(): void {}
|
||||||
|
computeNotifications(props: Props<*>, state: State): NotificationSet {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
// methods to be overridden by subclasses
|
// methods to be overridden by subclasses
|
||||||
_init(): void {}
|
_init(): void {}
|
||||||
_teardown(): void {}
|
_teardown(): void {}
|
||||||
@@ -82,6 +97,10 @@ export class FlipperBasePlugin<
|
|||||||
throw new TypeError(`Reducer ${actionData.type} isn't a function`);
|
throw new TypeError(`Reducer ${actionData.type} isn't a function`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(props: Props<*>, state: State): void {
|
||||||
|
props.setActiveNotifications(this.computeNotifications(props, state));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class FlipperDevicePlugin<S = *, A = *, P = *> extends FlipperBasePlugin<
|
export class FlipperDevicePlugin<S = *, A = *, P = *> extends FlipperBasePlugin<
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {combineReducers} from 'redux';
|
|||||||
import application from './application.js';
|
import application from './application.js';
|
||||||
import connections from './connections.js';
|
import connections from './connections.js';
|
||||||
import pluginStates from './pluginStates.js';
|
import pluginStates from './pluginStates.js';
|
||||||
|
import notifications from './notifications.js';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
State as ApplicationState,
|
State as ApplicationState,
|
||||||
@@ -22,6 +23,10 @@ import type {
|
|||||||
State as PluginsState,
|
State as PluginsState,
|
||||||
Action as PluginsAction,
|
Action as PluginsAction,
|
||||||
} from './pluginStates.js';
|
} from './pluginStates.js';
|
||||||
|
import type {
|
||||||
|
State as NotificationsState,
|
||||||
|
Action as NotificationsAction,
|
||||||
|
} from './notifications.js';
|
||||||
import type {Store as ReduxStore} from 'redux';
|
import type {Store as ReduxStore} from 'redux';
|
||||||
|
|
||||||
export type Store = ReduxStore<
|
export type Store = ReduxStore<
|
||||||
@@ -29,12 +34,18 @@ export type Store = ReduxStore<
|
|||||||
application: ApplicationState,
|
application: ApplicationState,
|
||||||
connections: DevicesState,
|
connections: DevicesState,
|
||||||
pluginStates: PluginsState,
|
pluginStates: PluginsState,
|
||||||
|
notifications: NotificationsState,
|
||||||
},
|
},
|
||||||
ApplicationAction | DevicesAction | PluginsAction | {|type: 'INIT'|},
|
| ApplicationAction
|
||||||
|
| DevicesAction
|
||||||
|
| PluginsAction
|
||||||
|
| NotificationsAction
|
||||||
|
| {|type: 'INIT'|},
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export default combineReducers({
|
export default combineReducers({
|
||||||
application,
|
application,
|
||||||
connections,
|
connections,
|
||||||
pluginStates,
|
pluginStates,
|
||||||
|
notifications,
|
||||||
});
|
});
|
||||||
|
|||||||
104
src/reducers/notifications.js
Normal file
104
src/reducers/notifications.js
Normal file
@@ -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 {Notification, NotificationSet} from '../plugin';
|
||||||
|
|
||||||
|
type PluginNotification = {|
|
||||||
|
id: string,
|
||||||
|
notification: Notification,
|
||||||
|
pluginId: string,
|
||||||
|
|};
|
||||||
|
|
||||||
|
export type State = {
|
||||||
|
activeNotifications: Array<PluginNotification>,
|
||||||
|
invalidatedNotifications: Array<PluginNotification>,
|
||||||
|
};
|
||||||
|
|
||||||
|
type ActiveNotificationsAction = {
|
||||||
|
type: 'SET_ACTIVE_NOTIFICATIONS',
|
||||||
|
payload: NotificationSet,
|
||||||
|
pluginId: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
type ClearAllAction = {
|
||||||
|
type: 'CLEAR_ALL_NOTIFICATIONS',
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Action = ActiveNotificationsAction | ClearAllAction;
|
||||||
|
|
||||||
|
const INITIAL_STATE: State = {
|
||||||
|
activeNotifications: [],
|
||||||
|
invalidatedNotifications: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function reducer(
|
||||||
|
state: State = INITIAL_STATE,
|
||||||
|
action: Action,
|
||||||
|
): State {
|
||||||
|
switch (action.type) {
|
||||||
|
case 'SET_ACTIVE_NOTIFICATIONS': {
|
||||||
|
return activeNotificationsReducer(state, action);
|
||||||
|
}
|
||||||
|
case 'CLEAR_ALL_NOTIFICATIONS':
|
||||||
|
// Q: Should this actually delete them, or just invalidate them?
|
||||||
|
return INITIAL_STATE;
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function activeNotificationsReducer(
|
||||||
|
state: State,
|
||||||
|
action: ActiveNotificationsAction,
|
||||||
|
) {
|
||||||
|
const {payload, pluginId} = action;
|
||||||
|
const newActiveNotifications = [];
|
||||||
|
const newInactivatedNotifications = state.invalidatedNotifications;
|
||||||
|
for (const activeNotification of state.activeNotifications) {
|
||||||
|
if (activeNotification.pluginId !== pluginId) {
|
||||||
|
newActiveNotifications.push(activeNotification);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!payload[activeNotification.id]) {
|
||||||
|
newInactivatedNotifications.push(activeNotification);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const id in payload) {
|
||||||
|
const newNotification = {
|
||||||
|
id,
|
||||||
|
notification: payload[id],
|
||||||
|
pluginId,
|
||||||
|
};
|
||||||
|
newActiveNotifications.push(newNotification);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
activeNotifications: newActiveNotifications,
|
||||||
|
invalidatedNotifications: newInactivatedNotifications,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setActiveNotifications(payload: {
|
||||||
|
notifications: {
|
||||||
|
[id: string]: Notification,
|
||||||
|
},
|
||||||
|
pluginId: string,
|
||||||
|
}): Action {
|
||||||
|
const {notifications, pluginId} = payload;
|
||||||
|
return {
|
||||||
|
type: 'SET_ACTIVE_NOTIFICATIONS',
|
||||||
|
payload: notifications,
|
||||||
|
pluginId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function clearAllNotifications(): Action {
|
||||||
|
return {
|
||||||
|
type: 'CLEAR_ALL_NOTIFICATIONS',
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user