Summary:
Quick notes:
- This looks worse than it is. It adds mandatory parentheses to single argument lambdas. Lots of outrage on Twitter about it, personally I'm {emoji:1f937_200d_2642} about it.
- Space before function, e.g. `a = function ()` is now enforced. I like this because both were fine before.
- I added `eslint-config-prettier` to the config because otherwise a ton of rules conflict with eslint itself.
Close https://github.com/facebook/flipper/pull/915
Reviewed By: jknoxville
Differential Revision: D20594929
fbshipit-source-id: ca1c65376b90e009550dd6d1f4e0831d32cbff03
218 lines
6.0 KiB
TypeScript
218 lines
6.0 KiB
TypeScript
/**
|
|
* 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 {Button, styled} from 'flipper';
|
|
import {connect, ReactReduxContext} from 'react-redux';
|
|
import {spawn} from 'child_process';
|
|
import {dirname} from 'path';
|
|
import {selectDevice, preferDevice} from '../reducers/connections';
|
|
import {
|
|
setActiveSheet,
|
|
ActiveSheet,
|
|
ACTIVE_SHEET_JS_EMULATOR_LAUNCHER,
|
|
} from '../reducers/application';
|
|
import which from 'which';
|
|
import {showOpenDialog} from '../utils/exportData';
|
|
import BaseDevice from '../devices/BaseDevice';
|
|
import React, {Component} from 'react';
|
|
import {State} from '../reducers';
|
|
import GK from '../fb-stubs/GK';
|
|
|
|
type StateFromProps = {
|
|
selectedDevice: BaseDevice | null | undefined;
|
|
androidEmulators: Array<string>;
|
|
devices: Array<BaseDevice>;
|
|
};
|
|
|
|
type DispatchFromProps = {
|
|
selectDevice: (device: BaseDevice) => void;
|
|
preferDevice: (device: string) => void;
|
|
setActiveSheet: (sheet: ActiveSheet) => void;
|
|
};
|
|
|
|
type OwnProps = {};
|
|
|
|
type Props = OwnProps & StateFromProps & DispatchFromProps;
|
|
const DropdownButton = styled(Button)({
|
|
fontSize: 11,
|
|
});
|
|
|
|
class DevicesButton extends Component<Props> {
|
|
launchEmulator = (name: string) => {
|
|
// On Linux, you must run the emulator from the directory it's in because
|
|
// reasons ...
|
|
which('emulator')
|
|
.then((emulatorPath) => {
|
|
if (emulatorPath) {
|
|
const child = spawn(emulatorPath, [`@${name}`], {
|
|
detached: true,
|
|
cwd: dirname(emulatorPath),
|
|
});
|
|
child.stderr.on('data', (data) => {
|
|
console.error(`Android emulator error: ${data}`);
|
|
});
|
|
child.on('error', console.error);
|
|
} else {
|
|
throw new Error('Could not get emulator path');
|
|
}
|
|
})
|
|
.catch(console.error);
|
|
this.props.preferDevice(name);
|
|
};
|
|
|
|
render() {
|
|
const {
|
|
devices,
|
|
androidEmulators,
|
|
selectedDevice,
|
|
selectDevice,
|
|
} = this.props;
|
|
let buttonLabel = 'No device selected';
|
|
let icon = 'minus-circle';
|
|
|
|
if (selectedDevice && selectedDevice.isArchived) {
|
|
buttonLabel = `${selectedDevice.displayTitle() || 'Unknown device'}`;
|
|
icon = 'box';
|
|
} else if (selectedDevice && selectedDevice.deviceType === 'physical') {
|
|
buttonLabel = selectedDevice.displayTitle() || 'Unknown device';
|
|
icon = 'mobile';
|
|
} else if (selectedDevice && selectedDevice.deviceType === 'emulator') {
|
|
buttonLabel = selectedDevice.displayTitle() || 'Unknown emulator';
|
|
icon = 'desktop';
|
|
}
|
|
|
|
const dropdown: any[] = [];
|
|
|
|
// Physical devices
|
|
const connectedDevices = [
|
|
{
|
|
label: 'Connected Devices',
|
|
enabled: false,
|
|
},
|
|
...devices
|
|
.filter((device) => device.deviceType === 'physical')
|
|
.map((device: BaseDevice) => ({
|
|
click: () => selectDevice(device),
|
|
checked: device === selectedDevice,
|
|
label: `📱 ${device.displayTitle()}`,
|
|
type: 'checkbox',
|
|
})),
|
|
];
|
|
if (connectedDevices.length > 1) {
|
|
dropdown.push(...connectedDevices);
|
|
}
|
|
// Emulators
|
|
const runningEmulators = [
|
|
{
|
|
label: 'Running Emulators',
|
|
enabled: false,
|
|
},
|
|
...devices
|
|
.filter((device) => device.deviceType === 'emulator')
|
|
.map((device: BaseDevice) => ({
|
|
click: () => selectDevice(device),
|
|
checked: device === selectedDevice,
|
|
label: device.displayTitle(),
|
|
type: 'checkbox',
|
|
})),
|
|
];
|
|
if (runningEmulators.length > 1) {
|
|
dropdown.push(...runningEmulators);
|
|
}
|
|
// Archived
|
|
const importedFiles = [
|
|
{
|
|
label: 'Disconnected Devices',
|
|
enabled: false,
|
|
},
|
|
...devices
|
|
.filter((device) => device.isArchived)
|
|
.map((device: BaseDevice) => ({
|
|
click: () => selectDevice(device),
|
|
checked: device === selectedDevice,
|
|
label: `📦 ${device.displayTitle()}`,
|
|
type: 'checkbox',
|
|
})),
|
|
];
|
|
if (importedFiles.length > 1) {
|
|
dropdown.push(...importedFiles);
|
|
}
|
|
// Launch JS emulator
|
|
if (GK.get('flipper_js_client_emulator')) {
|
|
dropdown.push(
|
|
{type: 'separator' as 'separator'},
|
|
{
|
|
label: 'Launch JS Web App',
|
|
click: () =>
|
|
this.props.setActiveSheet(ACTIVE_SHEET_JS_EMULATOR_LAUNCHER),
|
|
},
|
|
);
|
|
}
|
|
// Launch Android emulators
|
|
if (androidEmulators.length > 0) {
|
|
const emulators = Array.from(androidEmulators)
|
|
.filter(
|
|
(name: string) =>
|
|
devices.findIndex(
|
|
(device: BaseDevice) =>
|
|
device.title === name && !device.isArchived,
|
|
) === -1,
|
|
)
|
|
.map((name: string) => ({
|
|
label: name,
|
|
click: () => this.launchEmulator(name),
|
|
}));
|
|
|
|
if (emulators.length > 0) {
|
|
dropdown.push(
|
|
{type: 'separator' as 'separator'},
|
|
{
|
|
label: 'Launch Android emulators',
|
|
enabled: false,
|
|
},
|
|
...emulators,
|
|
);
|
|
}
|
|
}
|
|
|
|
if (dropdown.length > 0) {
|
|
dropdown.push({type: 'separator' as 'separator'});
|
|
}
|
|
return (
|
|
<ReactReduxContext.Consumer>
|
|
{({store}) => {
|
|
dropdown.push({
|
|
label: 'Open File...',
|
|
click: () => {
|
|
showOpenDialog(store);
|
|
},
|
|
});
|
|
return (
|
|
<DropdownButton compact={true} icon={icon} dropdown={dropdown}>
|
|
{buttonLabel}
|
|
</DropdownButton>
|
|
);
|
|
}}
|
|
</ReactReduxContext.Consumer>
|
|
);
|
|
}
|
|
}
|
|
export default connect<StateFromProps, DispatchFromProps, OwnProps, State>(
|
|
({connections: {devices, androidEmulators, selectedDevice}}) => ({
|
|
devices,
|
|
androidEmulators,
|
|
selectedDevice,
|
|
}),
|
|
{
|
|
selectDevice,
|
|
preferDevice,
|
|
setActiveSheet,
|
|
},
|
|
)(DevicesButton);
|