Automatically cause reload to trigger devTools

Summary:
To connect to the React Devtools, the app has to be reload (or the dev menu opened).
This diff does force a reload if the React Devtools don't connect timely

Reviewed By: passy

Differential Revision: D19878172

fbshipit-source-id: 0b3e22f70c9d24dae1fdbf0cc351d23367654ae1
This commit is contained in:
Michel Weststrate
2020-02-17 03:36:59 -08:00
committed by Facebook Github Bot
parent 3849807d6b
commit 56297d0cfc
3 changed files with 95 additions and 39 deletions

View File

@@ -65,15 +65,7 @@ const ProgressBarBar = styled.div<{progress: number; color: string}>(
function MetroButton({device}: Props) {
const sendCommand = useCallback(
(command: string) => {
if (device.ws) {
device.ws.send(
JSON.stringify({
version: 2,
type: 'command',
command,
}),
);
}
device.sendCommand(command);
},
[device],
);

View File

@@ -150,7 +150,7 @@ export default class MetroDevice extends BaseDevice {
ws.onmessage = this._handleWSMessage;
}
_handleWSMessage = ({data}: any) => {
private _handleWSMessage = ({data}: any) => {
const message: MetroReportableEvent = JSON.parse(data);
if (message.type === 'client_log') {
const type: LogLevel = metroLogLevelMapping[message.level] || 'unknown';
@@ -182,6 +182,21 @@ export default class MetroDevice extends BaseDevice {
this.metroEventEmitter.emit('event', message);
};
sendCommand(command: string, params?: any) {
if (this.ws) {
this.ws.send(
JSON.stringify({
version: 2,
type: 'command',
command,
params,
}),
);
} else {
console.warn('Cannot send command, no connection', command);
}
}
archive() {
return new ArchivedDevice(
this.serial + v4(),

View File

@@ -7,9 +7,19 @@
* @format
*/
// import {ReactReduxContext} from 'react-redux';
import ReactDevToolsStandalone from 'react-devtools-core/standalone';
import {FlipperPlugin, AndroidDevice, styled, View, Toolbar} from 'flipper';
import React from 'react';
import {
FlipperPlugin,
AndroidDevice,
styled,
View,
Toolbar,
MetroDevice,
ReduxState,
connect,
} from 'flipper';
import React, {useEffect} from 'react';
import getPort from 'get-port';
import address from 'address';
@@ -54,7 +64,30 @@ function detachDevTools(devToolsNode: HTMLElement) {
const CONNECTED = 'DevTools connected';
export default class extends FlipperPlugin<
type GrabMetroDeviceStoreProps = {metroDevice: MetroDevice};
type GrabMetroDeviceOwnProps = {onHasDevice(device: MetroDevice): void};
// Utility component to grab the metroDevice from the store if there is one
const GrabMetroDevice = connect<
GrabMetroDeviceStoreProps,
{},
GrabMetroDeviceOwnProps,
ReduxState
>(({connections: {devices}}) => ({
metroDevice: devices.find(
device => device.os === 'Metro' && !device.isArchived,
) as MetroDevice,
}))(function({
metroDevice,
onHasDevice,
}: GrabMetroDeviceStoreProps & GrabMetroDeviceOwnProps) {
useEffect(() => {
onHasDevice(metroDevice);
}, [metroDevice]);
return null;
});
export default class ReactDevTools extends FlipperPlugin<
{
status: string;
},
@@ -64,6 +97,7 @@ export default class extends FlipperPlugin<
pollHandle?: NodeJS.Timeout;
containerRef: React.RefObject<HTMLDivElement> = React.createRef();
triedToAutoConnect = false;
metroDevice?: MetroDevice;
state = {
status: 'initializing',
@@ -98,7 +132,7 @@ export default class extends FlipperPlugin<
}
setStatus(status: string) {
console.log(`[ReactDevtoolsPlugin] ${status}`);
// console.log(`[ReactDevtoolsPlugin] ${status}`);
if (status.startsWith('The server is listening on')) {
this.setState({status: status + ' Waiting for connection...'});
} else {
@@ -114,9 +148,14 @@ export default class extends FlipperPlugin<
if (!this.triedToAutoConnect) {
this.triedToAutoConnect = true;
this.setStatus(
"The DevTools didn't connect yet. Please open the DevMenu or Reload to connect",
"The DevTools didn't connect yet. Please open the DevMenu in the React Native app, or Reload it to connect",
);
// TODO: send reload command
if (this.metroDevice) {
this.setStatus(
"Sending 'reload' to the Metro to force the DevTools to connect...",
);
this.metroDevice?.sendCommand('reload');
}
}
this.startPollForConnection();
}
@@ -124,32 +163,37 @@ export default class extends FlipperPlugin<
}
async initializeDevTools(devToolsNode: HTMLElement) {
this.setStatus('Waiting for port 8097');
const port = await getPort({port: 8097}); // default port for dev tools
this.setStatus('Starting DevTools server on ' + port);
ReactDevToolsStandalone.setContentDOMNode(devToolsNode)
.setStatusListener(status => {
this.setStatus(status);
})
.startServer(port);
this.setStatus('Waiting for device');
const device = await this.getDevice();
try {
this.setStatus('Waiting for port 8097');
const port = await getPort({port: 8097}); // default port for dev tools
this.setStatus('Starting DevTools server on ' + port);
ReactDevToolsStandalone.setContentDOMNode(devToolsNode)
.setStatusListener(status => {
this.setStatus(status);
})
.startServer(port);
this.setStatus('Waiting for device');
const device = await this.getDevice();
if (device) {
const host =
device.deviceType === 'physical'
? address.ip()
: device instanceof AndroidDevice
? '10.0.2.2' // Host IP for Android emulator host system
: 'localhost';
this.setStatus(`Updating config to ${host}:${port}`);
this.client.call('config', {port, host});
if (device) {
const host =
device.deviceType === 'physical'
? address.ip()
: device instanceof AndroidDevice
? '10.0.2.2' // Host IP for Android emulator host system
: 'localhost';
this.setStatus(`Updating config to ${host}:${port}`);
this.client.call('config', {port, host});
if (['quest', 'go', 'pacific'].includes(device.title.toLowerCase())) {
const device = await this.getDevice();
this.setStatus(`Setting up reverse port mapping: ${port}:${port}`);
(device as AndroidDevice).reverse([port, port]);
if (['quest', 'go', 'pacific'].includes(device.title.toLowerCase())) {
const device = await this.getDevice();
this.setStatus(`Setting up reverse port mapping: ${port}:${port}`);
(device as AndroidDevice).reverse([port, port]);
}
}
} catch (e) {
console.error(e);
this.setStatus('Failed to initialize DevTools: ' + e);
}
}
@@ -160,6 +204,11 @@ export default class extends FlipperPlugin<
<Toolbar>{this.state.status}</Toolbar>
) : null}
<Container ref={this.containerRef} />
<GrabMetroDevice
onHasDevice={device => {
this.metroDevice = device;
}}
/>
</View>
);
}