From 1f977f4844e891e21d276a2141dd1659d9203945 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20B=C3=BCchele?= Date: Tue, 31 Jul 2018 07:37:18 -0700 Subject: [PATCH] Store use selected plugin after reconnect Summary: Deselect plugin when app disconnects, but store the information that the users had the app selected. When the app conencts again, restore the user's selection. This also stores the device seleced by the user and reselects the device if it connects. Reviewed By: xiphirx Differential Revision: D8833948 fbshipit-source-id: ad3ef54681550ae674bdd4e695d677aea5c14588 --- src/App.js | 3 +- src/PluginContainer.js | 3 +- src/chrome/DevicesButton.js | 22 +++++---- src/chrome/MainSidebar.js | 5 +-- src/dispatcher/server.js | 1 + src/reducers/connections.js | 89 +++++++++++++++++++++++++++++++++++++ src/reducers/index.js | 7 +-- src/reducers/server.js | 54 ---------------------- 8 files changed, 109 insertions(+), 75 deletions(-) delete mode 100644 src/reducers/server.js diff --git a/src/App.js b/src/App.js index dba3d8999..ac60810f3 100644 --- a/src/App.js +++ b/src/App.js @@ -71,8 +71,7 @@ export class App extends React.Component { export default connect( ({ application: {pluginManagerVisible, bugDialogVisible, leftSidebarVisible}, - connections: {selectedDevice}, - server: {error}, + connections: {selectedDevice, error}, }) => ({ pluginManagerVisible, bugDialogVisible, diff --git a/src/PluginContainer.js b/src/PluginContainer.js index 35e33723e..40e517d11 100644 --- a/src/PluginContainer.js +++ b/src/PluginContainer.js @@ -147,9 +147,8 @@ class PluginContainer extends Component { export default connect( ({ application: {rightSidebarVisible, rightSidebarAvailable}, - connections: {selectedPlugin, selectedDevice, selectedApp}, + connections: {selectedPlugin, selectedDevice, selectedApp, clients}, pluginStates, - server: {clients}, }) => ({ selectedPlugin, selectedDevice, diff --git a/src/chrome/DevicesButton.js b/src/chrome/DevicesButton.js index d341bd27e..722d64e19 100644 --- a/src/chrome/DevicesButton.js +++ b/src/chrome/DevicesButton.js @@ -7,8 +7,8 @@ import {Component, Button} from 'sonar'; import {connect} from 'react-redux'; -import {exec} from 'child_process'; -import {selectDevice} from '../reducers/connections.js'; +import {spawn} from 'child_process'; +import {selectDevice, preferDevice} from '../reducers/connections.js'; import type BaseDevice from '../devices/BaseDevice.js'; type Props = { @@ -16,15 +16,21 @@ type Props = { androidEmulators: Array, devices: Array, selectDevice: (device: BaseDevice) => void, + preferDevice: (device: string) => void, }; class DevicesButton extends Component { launchEmulator = (name: string) => { - exec(`$ANDROID_HOME/tools/emulator @${name}`, error => { - if (error) { - console.error(error); - } - }); + const child = spawn( + `${process.env.ANDROID_HOME || ''}/tools/emulator`, + [`@${name}`], + { + detached: true, + stdio: 'ignore', + }, + ); + child.on('error', console.error); + this.props.preferDevice(name); }; render() { @@ -96,5 +102,5 @@ export default connect( androidEmulators, selectedDevice, }), - {selectDevice}, + {selectDevice, preferDevice}, )(DevicesButton); diff --git a/src/chrome/MainSidebar.js b/src/chrome/MainSidebar.js index 678ca5854..e2b0074d5 100644 --- a/src/chrome/MainSidebar.js +++ b/src/chrome/MainSidebar.js @@ -225,10 +225,7 @@ class MainSidebar extends Component { } export default connect( - ({ - connections: {selectedDevice, selectedPlugin, selectedApp}, - server: {clients}, - }) => ({ + ({connections: {selectedDevice, selectedPlugin, selectedApp, clients}}) => ({ selectedDevice, selectedPlugin, selectedApp, diff --git a/src/dispatcher/server.js b/src/dispatcher/server.js index 72568d4ca..6c9869a0e 100644 --- a/src/dispatcher/server.js +++ b/src/dispatcher/server.js @@ -9,6 +9,7 @@ import Server from '../server.js'; import type {Store} from '../reducers/index.js'; import type Logger from '../fb-stubs/Logger.js'; +import type Client from '../Client.js'; export default (store: Store, logger: Logger) => { const server = new Server(logger); diff --git a/src/reducers/connections.js b/src/reducers/connections.js index 8bc69785f..729d955a9 100644 --- a/src/reducers/connections.js +++ b/src/reducers/connections.js @@ -6,12 +6,19 @@ */ import type BaseDevice from '../devices/BaseDevice'; +import type Client from '../Client'; + export type State = { devices: Array, androidEmulators: Array, selectedDevice: ?BaseDevice, selectedPlugin: ?string, selectedApp: ?string, + userPreferredDevice: ?string, + userPreferredPlugin: ?string, + userPreferredApp: ?string, + error: ?string, + clients: Array, }; export type Action = @@ -37,6 +44,22 @@ export type Action = selectedPlugin: ?string, selectedApp: ?string, }, + } + | { + type: 'SERVER_ERROR', + payload: ?string, + } + | { + type: 'NEW_CLIENT', + payload: Client, + } + | { + type: 'CLIENT_REMOVED', + payload: string, + } + | { + type: 'PREFER_DEVICE', + payload: string, }; const DEFAULT_PLUGIN = 'DeviceLogs'; @@ -47,6 +70,11 @@ const INITAL_STATE: State = { selectedDevice: null, selectedApp: null, selectedPlugin: DEFAULT_PLUGIN, + userPreferredDevice: null, + userPreferredPlugin: null, + userPreferredApp: null, + error: null, + clients: [], }; export default function reducer( @@ -61,6 +89,7 @@ export default function reducer( selectedApp: null, selectedPlugin: DEFAULT_PLUGIN, selectedDevice: payload, + userPreferredDevice: payload.title, }; } case 'REGISTER_ANDROID_EMULATORS': { @@ -75,13 +104,17 @@ export default function reducer( const devices = state.devices.concat(payload); let {selectedDevice} = state; let selection = {}; + if (!selectedDevice) { selectedDevice = payload; selection = { selectedApp: null, selectedPlugin: DEFAULT_PLUGIN, }; + } else if (payload.title === state.userPreferredDevice) { + selectedDevice = payload; } + return { ...state, devices, @@ -127,8 +160,59 @@ export default function reducer( return { ...state, ...payload, + userPreferredApp: payload.selectedApp, + userPreferredPlugin: payload.selectedPlugin, }; } + + case 'NEW_CLIENT': { + const {payload} = action; + const {userPreferredApp, userPreferredPlugin} = state; + let {selectedApp, selectedPlugin} = state; + + if ( + userPreferredApp && + userPreferredPlugin && + payload.id === userPreferredApp && + payload.plugins.includes(userPreferredPlugin) + ) { + // user preferred client did reconnect, so let's select it + selectedApp = userPreferredApp; + selectedPlugin = userPreferredPlugin; + } + + return { + ...state, + clients: state.clients.concat(payload), + selectedApp, + selectedPlugin, + }; + } + case 'CLIENT_REMOVED': { + const {payload} = action; + + let selected = {}; + if (state.selectedApp === payload) { + selected.selectedApp = null; + selected.selectedPlugin = DEFAULT_PLUGIN; + } + + return { + ...state, + ...selected, + clients: state.clients.filter( + (client: Client) => client.id !== payload, + ), + }; + } + case 'PREFER_DEVICE': { + const {payload: userPreferredDevice} = action; + return {...state, userPreferredDevice}; + } + case 'SERVER_ERROR': { + const {payload} = action; + return {...state, error: payload}; + } default: return state; } @@ -139,6 +223,11 @@ export const selectDevice = (payload: BaseDevice): Action => ({ payload, }); +export const preferDevice = (payload: string): Action => ({ + type: 'PREFER_DEVICE', + payload, +}); + export const selectPlugin = (payload: { selectedPlugin: ?string, selectedApp: ?string, diff --git a/src/reducers/index.js b/src/reducers/index.js index f16a3e02a..403b07c55 100644 --- a/src/reducers/index.js +++ b/src/reducers/index.js @@ -8,8 +8,8 @@ import {combineReducers} from 'redux'; import application from './application.js'; import connections from './connections.js'; -import server from './server.js'; import pluginStates from './pluginStates.js'; + import type { State as ApplicationState, Action as ApplicationAction, @@ -22,7 +22,6 @@ import type { State as PluginsState, Action as PluginsAction, } from './pluginStates.js'; -import type {State as ServerState, Action as ServerAction} from './server.js'; import type {Store as ReduxStore} from 'redux'; export type Store = ReduxStore< @@ -30,14 +29,12 @@ export type Store = ReduxStore< application: ApplicationState, connections: DevicesState, pluginStates: PluginsState, - server: ServerState, }, - ApplicationAction | DevicesAction | PluginsAction | ServerAction, + ApplicationAction | DevicesAction | PluginsAction, >; export default combineReducers({ application, connections, pluginStates, - server, }); diff --git a/src/reducers/server.js b/src/reducers/server.js deleted file mode 100644 index 84e342855..000000000 --- a/src/reducers/server.js +++ /dev/null @@ -1,54 +0,0 @@ -/** - * 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 - */ - -export type State = { - error: ?string, - clients: Array, -}; - -export type Action = - | { - type: 'SERVER_ERROR', - payload: ?string, - } - | { - type: 'NEW_CLIENT', - payload: Client, - } - | { - type: 'CLIENT_REMOVED', - payload: string, - }; - -const INITIAL_STATE: State = { - error: null, - clients: [], -}; - -export default function reducer( - state: State = INITIAL_STATE, - action: Action, -): State { - if (action.type === 'NEW_CLIENT') { - const {payload} = action; - return { - ...state, - clients: state.clients.concat(payload), - }; - } else if (action.type === 'CLIENT_REMOVED') { - const {payload} = action; - return { - ...state, - clients: state.clients.filter((client: Client) => client.id !== payload), - }; - } else if (action.type === 'SERVER_ERROR') { - const {payload} = action; - return {...state, error: payload}; - } else { - return state; - } -}