diff --git a/headless/index.js b/headless/index.js index 291427bc9..ffcfaadd7 100644 --- a/headless/index.js +++ b/headless/index.js @@ -21,7 +21,7 @@ import {listDevices} from '../src/utils/listDevices'; // $FlowFixMe this file exist, trust me, flow! import setup from '../static/setup.js'; import type {Store} from '../src/reducers'; -import {getActivePluginNames} from '../src/utils/pluginUtils.js'; +import {getPersistentPlugins} from '../src/utils/pluginUtils.js'; import {serialize} from '../src/utils/serialization'; import type BaseDevice from '../src/devices/BaseDevice'; @@ -360,7 +360,7 @@ async function startFlipper(userArguments: UserArguments) { return Promise.resolve({ exit: true, result: await serialize( - getActivePluginNames(store.getState().plugins), + getPersistentPlugins(store.getState().plugins), ), }); } diff --git a/src/App.js b/src/App.js index d78b8ae21..3a3d194e7 100644 --- a/src/App.js +++ b/src/App.js @@ -16,6 +16,7 @@ import BugReporterDialog from './chrome/BugReporterDialog.js'; import ErrorBar from './chrome/ErrorBar.js'; import ShareSheet from './chrome/ShareSheet.js'; import SignInSheet from './chrome/SignInSheet.js'; +import ExportDataPluginSheet from './chrome/ExportDataPluginSheet.js'; import ShareSheetExportFile from './chrome/ShareSheetExportFile.js'; import PluginContainer from './PluginContainer.js'; import Sheet from './chrome/Sheet.js'; @@ -27,14 +28,14 @@ import { ACTIVE_SHEET_SHARE_DATA, ACTIVE_SHEET_SIGN_IN, ACTIVE_SHEET_SHARE_DATA_IN_FILE, + ACTIVE_SHEET_SELECT_PLUGINS_TO_EXPORT, ACTIVE_SHEET_PLUGIN_SHEET, } from './reducers/application.js'; - +import type {ShareType} from './reducers/application.js'; import type {Logger} from './fb-interfaces/Logger.js'; import type BugReporter from './fb-stubs/BugReporter.js'; import type BaseDevice from './devices/BaseDevice.js'; import type {ActiveSheet} from './reducers/application.js'; - const version = remote.app.getVersion(); type OwnProps = {| @@ -48,7 +49,7 @@ type Props = {| selectedDevice: ?BaseDevice, error: ?string, activeSheet: ActiveSheet, - exportFile: ?string, + share: ?ShareType, |}; export class App extends React.Component { @@ -68,7 +69,8 @@ export class App extends React.Component { } getSheet = (onHide: () => mixed) => { - switch (this.props.activeSheet) { + const {activeSheet} = this.props; + switch (activeSheet) { case ACTIVE_SHEET_BUG_REPORTER: return ( { return ; case ACTIVE_SHEET_SIGN_IN: return ; + case ACTIVE_SHEET_SELECT_PLUGINS_TO_EXPORT: + return ; case ACTIVE_SHEET_SHARE_DATA_IN_FILE: return ( ); @@ -119,13 +127,13 @@ export class App extends React.Component { export default connect( ({ - application: {leftSidebarVisible, activeSheet, exportFile}, + application: {leftSidebarVisible, activeSheet, share}, connections: {selectedDevice, error}, }) => ({ leftSidebarVisible, selectedDevice, activeSheet, - exportFile, + share: share, error, }), )(App); diff --git a/src/MenuBar.js b/src/MenuBar.js index 3f25e6997..9d7fed06a 100644 --- a/src/MenuBar.js +++ b/src/MenuBar.js @@ -10,6 +10,7 @@ import {showOpenDialog} from './utils/exportData.js'; import { setExportDataToFileActiveSheet, setActiveSheet, + setSelectPluginsToExportActiveSheet, ACTIVE_SHEET_SHARE_DATA, } from './reducers/application'; import type {Store} from './reducers/'; @@ -201,7 +202,9 @@ function getTemplate( if (!file) { return; } - store.dispatch(setExportDataToFileActiveSheet(file)); + store.dispatch( + setSelectPluginsToExportActiveSheet({type: 'file', file: file}), + ); }, ); }, @@ -212,7 +215,7 @@ function getTemplate( label: 'Sharable Link', accelerator: 'CommandOrControl+Shift+E', click: async function(item: Object, focusedWindow: Object) { - store.dispatch(setActiveSheet(ACTIVE_SHEET_SHARE_DATA)); + store.dispatch(setSelectPluginsToExportActiveSheet({type: 'link'})); }, }); } diff --git a/src/__tests__/App.electron.js b/src/__tests__/App.electron.js index 163fd428b..1b6a48470 100644 --- a/src/__tests__/App.electron.js +++ b/src/__tests__/App.electron.js @@ -29,7 +29,7 @@ test('Empty app state matches snapshot', () => { selectedDevice={null} error={null} activeSheet={null} - exportFile={null} + share={null} /> , ); diff --git a/src/chrome/ExportDataPluginSheet.js b/src/chrome/ExportDataPluginSheet.js new file mode 100644 index 000000000..d86e80b53 --- /dev/null +++ b/src/chrome/ExportDataPluginSheet.js @@ -0,0 +1,101 @@ +/** + * 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 {Component, connect} from 'flipper'; +import type {ShareType} from '../reducers/application.js'; +import type {State as PluginState} from '../reducers/plugins.js'; +import type {State as PluginStatesState} from '../reducers/pluginStates.js'; +import type {ActiveSheet} from '../reducers/application.js'; +import {selectedPlugins as actionForSelectedPlugins} from '../reducers/plugins.js'; +import {getActivePersistentPlugins} from '../utils/pluginUtils'; +import { + ACTIVE_SHEET_SHARE_DATA, + setActiveSheet as getActiveSheetAction, + setExportDataToFileActiveSheet as getExportDataToFileActiveSheetAction, +} from '../reducers/application.js'; +import SelectPluginSheet from './SelectPluginSheet'; + +type OwnProps = {| + onHide: () => mixed, +|}; + +type Props = {| + ...OwnProps, + share: ShareType, + plugins: PluginState, + pluginStates: PluginStatesState, + selectedPlugins: (payload: Array) => void, + setActiveSheet: (payload: ActiveSheet) => void, + setExportDataToFileActiveSheet: (payload: string) => void, +|}; + +class ExportDataPluginSheet extends Component { + render() { + const {plugins, pluginStates, onHide} = this.props; + return ( + { + this.props.selectedPlugins(selectedArray); + const {share} = this.props; + if (!share) { + console.error( + 'applications.share is undefined, whereas it was expected to be defined', + ); + } else { + switch (share.type) { + case 'link': + this.props.setActiveSheet(ACTIVE_SHEET_SHARE_DATA); + break; + case 'file': { + const file = share.file; + if (file) { + this.props.setExportDataToFileActiveSheet(file); + } else { + console.error('share.file is undefined'); + } + } + } + } + }} + plugins={getActivePersistentPlugins(pluginStates, plugins).reduce( + (acc, plugin) => { + acc.set( + plugin, + plugins.selectedPlugins.length <= 0 + ? true + : plugins.selectedPlugins.includes(plugin), + ); + return acc; + }, + new Map(), + )} + onHide={onHide} + /> + ); + } +} + +export default connect( + ({application: {share}, plugins, pluginStates}) => ({ + share: share, + plugins, + pluginStates, + }), + dispatch => { + return { + selectedPlugins: (plugins: Array) => { + dispatch(actionForSelectedPlugins(plugins)); + }, + setActiveSheet: (payload: ActiveSheet) => { + dispatch(getActiveSheetAction(payload)); + }, + setExportDataToFileActiveSheet: (payload: string) => { + dispatch(getExportDataToFileActiveSheetAction(payload)); + }, + }; + }, +)(ExportDataPluginSheet); diff --git a/src/chrome/SelectPluginSheet.js b/src/chrome/SelectPluginSheet.js new file mode 100644 index 000000000..b8396b69d --- /dev/null +++ b/src/chrome/SelectPluginSheet.js @@ -0,0 +1,167 @@ +/** + * 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 { + Component, + Text, + FlexColumn, + styled, + FlexRow, + Button, + Spacer, + Checkbox, + colors, + View, +} from 'flipper'; + +export type PluginSelection = Map; + +type Props = {| + onSelect: (plugins: Array) => void, + onHide: () => mixed, + plugins: PluginSelection, +|}; + +const Title = styled(Text)({ + margin: 6, +}); + +type State = { + plugins: PluginSelection, +}; + +const Container = styled(FlexColumn)({ + padding: 8, + width: 700, + maxHeight: 700, +}); + +const Line = styled(View)({ + backgroundColor: colors.greyTint2, + height: 1, + width: 'auto', + flexShrink: 0, +}); + +const PluginRowComponentContainer = styled(FlexColumn)({ + overflow: 'scroll', + height: 'auto', + backgroundColor: colors.white, + maxHeight: 500, +}); + +const Padder = styled('div')( + ({paddingLeft, paddingRight, paddingBottom, paddingTop}) => ({ + paddingLeft: paddingLeft || 0, + paddingRight: paddingRight || 0, + paddingBottom: paddingBottom || 0, + paddingTop: paddingTop || 0, + }), +); + +type PluginRowComponentProps = { + name: string, + selected: boolean, + onChange: (name: string, selected: boolean) => void, +}; + +class PluginRowComponent extends Component { + render() { + const {name, selected, onChange} = this.props; + return ( + + + + {name} + + { + onChange(name, selected); + }} + /> + + + + + ); + } +} + +export default class SelectPluginSheet extends Component { + state = {plugins: new Map()}; + static getDerivedStateFromProps(props: Props, state: State) { + if (state.plugins.size > 0) { + return null; + } + return {plugins: props.plugins}; + } + + onSubmit(plugins: PluginSelection) { + const selectedArray = Array.from(plugins.entries()).reduce( + (acc, [plugin, selected]) => { + if (selected) { + acc.push(plugin); + } + return acc; + }, + [], + ); + this.props.onSelect(selectedArray); + } + render() { + const {onHide} = this.props; + const {plugins} = this.state; + + return ( + + + + Select the plugins for which you want to export the data + + + {Array.from(plugins.entries()).map( + ([pluginID: string, selected: boolean]) => { + return ( + { + plugins.set(id, selected); + this.setState({plugins}); + }} + /> + ); + }, + )} + + + + + + + + + + + ); + } +} diff --git a/src/reducers/application.js b/src/reducers/application.js index 1a5ed9c4b..662e4e293 100644 --- a/src/reducers/application.js +++ b/src/reducers/application.js @@ -12,6 +12,8 @@ export const ACTIVE_SHEET_PLUGIN_SHEET: 'PLUGIN_SHEET' = 'PLUGIN_SHEET'; export const ACTIVE_SHEET_BUG_REPORTER: 'BUG_REPORTER' = 'BUG_REPORTER'; export const ACTIVE_SHEET_PLUGIN_DEBUGGER: 'PLUGIN_DEBUGGER' = 'PLUGIN_DEBUGGER'; +export const ACTIVE_SHEET_SELECT_PLUGINS_TO_EXPORT: 'SELECT_PLUGINS_TO_EXPORT' = + 'SELECT_PLUGINS_TO_EXPORT'; export const ACTIVE_SHEET_SHARE_DATA: 'SHARE_DATA' = 'SHARE_DATA'; export const ACTIVE_SHEET_SIGN_IN: 'SIGN_IN' = 'SIGN_IN'; export const ACTIVE_SHEET_SHARE_DATA_IN_FILE: 'SHARE_DATA_IN_FILE' = @@ -24,6 +26,7 @@ export type ActiveSheet = | typeof ACTIVE_SHEET_SHARE_DATA | typeof ACTIVE_SHEET_SIGN_IN | typeof ACTIVE_SHEET_SHARE_DATA_IN_FILE + | typeof ACTIVE_SHEET_SELECT_PLUGINS_TO_EXPORT | null; export type LauncherMsg = { @@ -35,13 +38,20 @@ export type ServerPorts = { secure: number, }; +export type ShareType = + | { + type: 'file', + file: string, + } + | {type: 'link'}; + export type State = { leftSidebarVisible: boolean, rightSidebarVisible: boolean, rightSidebarAvailable: boolean, windowIsFocused: boolean, activeSheet: ActiveSheet, - exportFile: ?string, + share: ?ShareType, sessionId: ?string, serverPorts: ServerPorts, downloadingImportData: boolean, @@ -69,6 +79,10 @@ export type Action = type: typeof ACTIVE_SHEET_SHARE_DATA_IN_FILE, payload: {file: string}, } + | { + type: typeof ACTIVE_SHEET_SELECT_PLUGINS_TO_EXPORT, + payload: ShareType, + } | { type: 'SET_SERVER_PORTS', payload: { @@ -96,7 +110,7 @@ const initialState: () => State = () => ({ rightSidebarAvailable: false, windowIsFocused: remote.getCurrentWindow().isFocused(), activeSheet: null, - exportFile: null, + share: null, sessionId: uuidv1(), serverPorts: { insecure: 8089, @@ -142,7 +156,13 @@ export default function reducer(state: State, action: Action): State { return { ...state, activeSheet: ACTIVE_SHEET_SHARE_DATA_IN_FILE, - exportFile: action.payload.file, + share: {type: 'file', file: action.payload.file}, + }; + } else if (action.type === ACTIVE_SHEET_SELECT_PLUGINS_TO_EXPORT) { + return { + ...state, + activeSheet: ACTIVE_SHEET_SELECT_PLUGINS_TO_EXPORT, + share: action.payload, }; } else if (action.type === 'SET_SERVER_PORTS') { return { @@ -172,6 +192,13 @@ export const toggleAction = ( payload, }); +export const setSelectPluginsToExportActiveSheet = ( + payload: ShareType, +): Action => ({ + type: ACTIVE_SHEET_SELECT_PLUGINS_TO_EXPORT, + payload, +}); + export const setExportDataToFileActiveSheet = (file: string): Action => ({ type: ACTIVE_SHEET_SHARE_DATA_IN_FILE, payload: {file}, diff --git a/src/reducers/plugins.js b/src/reducers/plugins.js index 2a161497c..f1b2295f5 100644 --- a/src/reducers/plugins.js +++ b/src/reducers/plugins.js @@ -94,16 +94,20 @@ export default function reducer( failedPlugins: state.failedPlugins.concat(action.payload), }; } else if (action.type === 'SELECTED_PLUGINS') { - const {selectedPlugins} = state; return { ...state, - selectedPlugins: selectedPlugins.concat(action.payload), + selectedPlugins: action.payload, }; } else { return state; } } +export const selectedPlugins = (payload: Array): Action => ({ + type: 'SELECTED_PLUGINS', + payload, +}); + export const registerPlugins = (payload: Array

): Action => ({ type: 'REGISTER_PLUGINS', payload, diff --git a/src/utils/__tests__/pluginUtils.node.js b/src/utils/__tests__/pluginUtils.node.js index 27625c932..ced5925cb 100644 --- a/src/utils/__tests__/pluginUtils.node.js +++ b/src/utils/__tests__/pluginUtils.node.js @@ -5,9 +5,39 @@ * @format */ -import {getActivePluginNames} from '../pluginUtils'; +import {getPersistentPlugins, getActivePersistentPlugins} from '../pluginUtils'; import type {State as PluginsState} from '../../reducers/plugins.js'; +import type {State as PluginStatesState} from '../../reducers/pluginStates.js'; import type {PluginDefinition} from '../../dispatcher/plugins'; +import {FlipperBasePlugin} from '../../..'; +import type {MiddlewareAPI} from '../../reducers/index.js'; +class MockFlipperPluginWithDefaultPersistedState extends FlipperBasePlugin< + *, + *, + {msg: string}, +> { + static defaultPersistedState = {msg: 'MockFlipperPluginWithPersistedState'}; +} + +class MockFlipperPluginWithExportPersistedState extends FlipperBasePlugin< + *, + *, + {msg: string}, +> { + static exportPersistedState = ( + callClient: (string, ?Object) => Promise, + persistedState: ?{msg: string}, + store: ?MiddlewareAPI, + ): Promise => { + return Promise.resolve({msg: 'MockFlipperPluginWithExportPersistedState'}); + }; +} + +class MockFlipperPluginWithNoPersistedState extends FlipperBasePlugin< + *, + *, + *, +> {} function mockPluginState( gatekeepedPlugins: Array, @@ -16,16 +46,16 @@ function mockPluginState( ): PluginsState { return { devicePlugins: new Map([ - //$FlowFixMe: Class instance won't be used in the test - ['DevicePlugin1', undefined], - //$FlowFixMe: Class instance won't be used in the test - ['DevicePlugin2', undefined], + //$FlowFixMe: Just for testing + ['DevicePlugin1', MockFlipperPluginWithDefaultPersistedState], + //$FlowFixMe: Just for testing + ['DevicePlugin2', MockFlipperPluginWithDefaultPersistedState], ]), clientPlugins: new Map([ - //$FlowFixMe: Class instance won't be used in the test - ['ClientPlugin1', undefined], - //$FlowFixMe: Class instance won't be used in the test - ['ClientPlugin2', undefined], + //$FlowFixMe: Just for testing + ['ClientPlugin1', MockFlipperPluginWithDefaultPersistedState], + //$FlowFixMe: Just for testing + ['ClientPlugin2', MockFlipperPluginWithDefaultPersistedState], ]), gatekeepedPlugins, disabledPlugins, @@ -41,19 +71,19 @@ function mockPluginDefinition(name: string): PluginDefinition { }; } -test('getActivePluginNames with the plugins getting excluded', () => { +test('getPersistentPlugins with the plugins getting excluded', () => { const state = mockPluginState( [mockPluginDefinition('DevicePlugin1')], [mockPluginDefinition('ClientPlugin1')], [[mockPluginDefinition('DevicePlugin2'), 'DevicePlugin2']], ); - const list = getActivePluginNames(state); + const list = getPersistentPlugins(state); expect(list).toEqual(['ClientPlugin2']); }); -test('getActivePluginNames with the no plugins getting excluded', () => { +test('getPersistentPlugins with no plugins getting excluded', () => { const state = mockPluginState([], [], []); - const list = getActivePluginNames(state); + const list = getPersistentPlugins(state); expect(list).toEqual([ 'ClientPlugin1', 'ClientPlugin2', @@ -61,3 +91,110 @@ test('getActivePluginNames with the no plugins getting excluded', () => { 'DevicePlugin2', ]); }); + +test('getPersistentPlugins, where the plugins with exportPersistedState not getting excluded', () => { + const state: PluginsState = { + devicePlugins: new Map([ + //$FlowFixMe: Just for testing + ['DevicePlugin1', MockFlipperPluginWithExportPersistedState], + //$FlowFixMe: Just for testing + ['DevicePlugin2', MockFlipperPluginWithExportPersistedState], + ]), + clientPlugins: new Map([ + //$FlowFixMe: Just for testing + ['ClientPlugin1', MockFlipperPluginWithExportPersistedState], + //$FlowFixMe: Just for testing + ['ClientPlugin2', MockFlipperPluginWithExportPersistedState], + ]), + gatekeepedPlugins: [], + disabledPlugins: [], + failedPlugins: [], + selectedPlugins: [], + }; + const list = getPersistentPlugins(state); + expect(list).toEqual([ + 'ClientPlugin1', + 'ClientPlugin2', + 'DevicePlugin1', + 'DevicePlugin2', + ]); +}); + +test('getPersistentPlugins, where the non persistent plugins getting excluded', () => { + const state: PluginsState = { + devicePlugins: new Map([ + //$FlowFixMe: Just for testing + ['DevicePlugin1', MockFlipperPluginWithNoPersistedState], + //$FlowFixMe: Just for testing + ['DevicePlugin2', MockFlipperPluginWithDefaultPersistedState], + ]), + clientPlugins: new Map([ + //$FlowFixMe: Just for testing + ['ClientPlugin1', MockFlipperPluginWithDefaultPersistedState], + //$FlowFixMe: Just for testing + ['ClientPlugin2', MockFlipperPluginWithNoPersistedState], + ]), + gatekeepedPlugins: [], + disabledPlugins: [], + failedPlugins: [], + selectedPlugins: [], + }; + const list = getPersistentPlugins(state); + expect(list).toEqual(['ClientPlugin1', 'DevicePlugin2']); +}); + +test('getActivePersistentPlugins, where the non persistent plugins getting excluded', () => { + const state: PluginsState = { + devicePlugins: new Map([ + //$FlowFixMe: Just for testing + ['DevicePlugin1', MockFlipperPluginWithNoPersistedState], + //$FlowFixMe: Just for testing + ['DevicePlugin2', MockFlipperPluginWithDefaultPersistedState], + ]), + clientPlugins: new Map([ + //$FlowFixMe: Just for testing + ['ClientPlugin1', MockFlipperPluginWithDefaultPersistedState], + //$FlowFixMe: Just for testing + ['ClientPlugin2', MockFlipperPluginWithNoPersistedState], + ]), + gatekeepedPlugins: [], + disabledPlugins: [], + failedPlugins: [], + selectedPlugins: [], + }; + const plugins: PluginStatesState = { + 'serial#app#DevicePlugin1': {msg: 'DevicePlugin1'}, + 'serial#app#DevicePlugin2': {msg: 'DevicePlugin2'}, + 'serial#app#ClientPlugin1': {msg: 'ClientPlugin1'}, + 'serial#app#ClientPlugin2': {msg: 'ClientPlugin2'}, + }; + const list = getActivePersistentPlugins(plugins, state); + expect(list).toEqual(['ClientPlugin1', 'DevicePlugin2']); +}); + +test('getActivePersistentPlugins, where the plugins not in pluginState gets excluded', () => { + const state: PluginsState = { + devicePlugins: new Map([ + //$FlowFixMe: Just for testing + ['DevicePlugin1', MockFlipperPluginWithDefaultPersistedState], + //$FlowFixMe: Just for testing + ['DevicePlugin2', MockFlipperPluginWithDefaultPersistedState], + ]), + clientPlugins: new Map([ + //$FlowFixMe: Just for testing + ['ClientPlugin1', MockFlipperPluginWithDefaultPersistedState], + //$FlowFixMe: Just for testing + ['ClientPlugin2', MockFlipperPluginWithDefaultPersistedState], + ]), + gatekeepedPlugins: [], + disabledPlugins: [], + failedPlugins: [], + selectedPlugins: [], + }; + const plugins: PluginStatesState = { + 'serial#app#DevicePlugin1': {msg: 'DevicePlugin1'}, + 'serial#app#ClientPlugin2': {msg: 'ClientPlugin2'}, + }; + const list = getActivePersistentPlugins(plugins, state); + expect(list).toEqual(['ClientPlugin2', 'DevicePlugin1']); +}); diff --git a/src/utils/exportData.js b/src/utils/exportData.js index 7a51a71cb..09fe330ac 100644 --- a/src/utils/exportData.js +++ b/src/utils/exportData.js @@ -62,6 +62,7 @@ type AddSaltToDeviceSerialOptions = { clients: Array, pluginStates: PluginStatesState, pluginNotification: Array, + selectedPlugins: Array, statusUpdate?: (msg: string) => void, }; @@ -164,6 +165,7 @@ const addSaltToDeviceSerial = async ( pluginStates, pluginNotification, statusUpdate, + selectedPlugins, } = options; const {serial} = device; const newSerial = salt + '-' + serial; @@ -172,7 +174,7 @@ const addSaltToDeviceSerial = async ( device.deviceType, device.title, device.os, - device.getLogs(), + selectedPlugins.includes('DeviceLogs') ? device.getLogs() : [], ); statusUpdate && statusUpdate('Adding salt to the selected device id in the client data...'); @@ -278,6 +280,7 @@ export const processStore = async ( pluginStates: processedPluginStates, pluginNotification: processedActiveNotifications, statusUpdate, + selectedPlugins, }); return exportFlipperData; } diff --git a/src/utils/pluginUtils.js b/src/utils/pluginUtils.js index dad59168d..774356d22 100644 --- a/src/utils/pluginUtils.js +++ b/src/utils/pluginUtils.js @@ -45,7 +45,26 @@ export function getPersistedState( return persistedState; } -export function getActivePluginNames(plugins: PluginsState): Array { +export function getActivePersistentPlugins( + pluginsState: PluginStatesState, + plugins: PluginsState, +): Array { + const pluginsMap: Map< + string, + Class | FlipperPlugin<>>, + > = pluginsClassMap(plugins); + return getPersistentPlugins(plugins).filter(plugin => { + const pluginClass = pluginsMap.get(plugin); + const keys = Object.keys(pluginsState).map(key => key.split('#').pop()); + return ( + (pluginClass && pluginClass.exportPersistedState != undefined) || + plugin == 'DeviceLogs' || + keys.includes(plugin) + ); + }); +} + +export function getPersistentPlugins(plugins: PluginsState): Array { const pluginsMap: Map< string, Class | FlipperPlugin<>>, @@ -65,5 +84,16 @@ export function getActivePluginNames(plugins: PluginsState): Array { pluginsMap.delete(plugin[0].name); } }); - return [...pluginsMap.keys()]; + + const activePlugins = [...pluginsMap.keys()]; + + return activePlugins.filter(plugin => { + const pluginClass = pluginsMap.get(plugin); + return ( + plugin == 'DeviceLogs' || + (pluginClass && + (pluginClass.defaultPersistedState != undefined || + pluginClass.exportPersistedState != undefined)) + ); + }); }