/** * 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 { FlipperPlugin, FlipperDevicePlugin, Props as PluginProps, } from './plugin'; import {Logger} from './fb-interfaces/Logger'; import BaseDevice from './devices/BaseDevice'; import {pluginKey as getPluginKey} from './reducers/pluginStates'; import Client from './Client'; import { ErrorBoundary, FlexColumn, FlexRow, colors, styled, ArchivedDevice, } from 'flipper'; import {StaticView, setStaticView} from './reducers/connections'; import React, {PureComponent} from 'react'; import {connect} from 'react-redux'; import {setPluginState} from './reducers/pluginStates'; import {selectPlugin} from './reducers/connections'; import {State as Store} from './reducers/index'; import NotificationsHub from './NotificationsHub'; import {activateMenuItems} from './MenuBar'; const Container = styled(FlexColumn)({ width: 0, flexGrow: 1, flexShrink: 1, backgroundColor: colors.white, }); const SidebarContainer = styled(FlexRow)({ backgroundColor: colors.light02, height: '100%', overflow: 'scroll', }); type OwnProps = { logger: Logger; }; type StateFromProps = { pluginState: Object; activePlugin: typeof FlipperPlugin | typeof FlipperDevicePlugin | null; target: Client | BaseDevice | null; pluginKey: string | null; deepLinkPayload: string | null; selectedApp: string | null; isArchivedDevice: boolean; }; type DispatchFromProps = { selectPlugin: (payload: { selectedPlugin: string | null; selectedApp?: string | null; deepLinkPayload: string | null; }) => any; setPluginState: (payload: {pluginKey: string; state: any}) => void; setStaticView: (payload: StaticView) => void; }; type Props = StateFromProps & DispatchFromProps & OwnProps; class PluginContainer extends PureComponent { plugin: | FlipperPlugin | FlipperDevicePlugin | null | undefined; refChanged = ( ref: | FlipperPlugin | FlipperDevicePlugin | null | undefined, ) => { if (this.plugin) { this.plugin._teardown(); this.plugin = null; } if (ref && this.props.target) { activateMenuItems(ref); ref._init(); this.props.logger.trackTimeSince(`activePlugin-${ref.constructor.id}`); this.plugin = ref; } }; componentWillUnmount() { if (this.plugin) { this.plugin._teardown(); this.plugin = null; } } render() { const { pluginState, setPluginState, activePlugin, pluginKey, target, isArchivedDevice, selectedApp, } = this.props; if (!activePlugin || !target || !pluginKey) { console.warn(`No selected plugin. Rendering empty!`); return null; } const props: PluginProps & { key: string; ref: ( ref: | FlipperPlugin | FlipperDevicePlugin | null | undefined, ) => void; } = { key: pluginKey, logger: this.props.logger, selectedApp, persistedState: activePlugin.defaultPersistedState ? { ...activePlugin.defaultPersistedState, ...pluginState, } : pluginState, setStaticView: (payload: StaticView) => this.props.setStaticView(payload), setPersistedState: state => setPluginState({pluginKey, state}), target, deepLinkPayload: this.props.deepLinkPayload, selectPlugin: (pluginID: string, deepLinkPayload: string | null) => { const {target} = this.props; // check if plugin will be available if ( target instanceof Client && target.plugins.some(p => p === pluginID) ) { this.props.selectPlugin({selectedPlugin: pluginID, deepLinkPayload}); return true; } else if (target instanceof BaseDevice) { this.props.selectPlugin({selectedPlugin: pluginID, deepLinkPayload}); return true; } else { return false; } }, ref: this.refChanged, isArchivedDevice, }; return ( {React.createElement(activePlugin, props)} ); } } export default connect( ({ connections: { selectedPlugin, selectedDevice, selectedApp, clients, deepLinkPayload, }, pluginStates, plugins: {devicePlugins, clientPlugins}, }) => { let pluginKey = null; let target = null; let activePlugin: | typeof FlipperDevicePlugin | typeof FlipperPlugin | null = null; if (selectedPlugin) { if (selectedPlugin === NotificationsHub.id) { activePlugin = NotificationsHub; } else if (selectedPlugin) { activePlugin = devicePlugins.get(selectedPlugin) || null; } target = selectedDevice; if (selectedDevice && activePlugin) { pluginKey = getPluginKey(selectedDevice.serial, activePlugin.id); } else { target = clients.find((client: Client) => client.id === selectedApp) || null; activePlugin = clientPlugins.get(selectedPlugin) || null; if (activePlugin && target) { pluginKey = getPluginKey(target.id, activePlugin.id); } } } const isArchivedDevice = !selectedDevice ? false : selectedDevice instanceof ArchivedDevice; const s: StateFromProps = { pluginState: pluginStates[pluginKey as string], activePlugin: activePlugin, target, deepLinkPayload, pluginKey, isArchivedDevice, selectedApp: selectedApp || null, }; return s; }, { setPluginState, selectPlugin, setStaticView, }, )(PluginContainer);