JS apps support 1/n

Summary:
### Connecting Flipper with JS apps by using electron's BrowserWindow and IPC

1. UI: there is a menu item in Devices tab which opens JS Emulator Launcher Sheet. Here we can configure URL to open and initial size of the window.
2. BrowserWindow, preloaded js: there is SupportJSClientPreload.js which initialize communication between flipper and app via electron's ipc
3. On flipper's side there is src/utils/js-client/serverUtils.tsx which contains most of JS emulator related code
4. Extracting of FlipperClientConnection: since we don't use RScocket to communicate with JS app I extracted needed methods to FlipperClientConnection (located in Client) and partly implemented them in JSClientFlipperConnection (requestResponse is just send a message now, doesn't return actual result)

Reviewed By: jknoxville

Differential Revision: D18572882

fbshipit-source-id: 56d1ca1a60ed2e51329b917021a09382cbb1ceec
This commit is contained in:
Timur Valiev
2019-11-22 03:09:41 -08:00
committed by Facebook Github Bot
parent e7ad713df8
commit c685493db0
10 changed files with 496 additions and 11 deletions

View File

@@ -12,11 +12,17 @@ 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 {default as 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;
@@ -27,6 +33,7 @@ type StateFromProps = {
type DispatchFromProps = {
selectDevice: (device: BaseDevice) => void;
preferDevice: (device: string) => void;
setActiveSheet: (sheet: ActiveSheet) => void;
};
type OwnProps = {};
@@ -154,17 +161,30 @@ class DevicesButton extends Component<Props> {
label: name,
click: () => this.launchEmulator(name),
}));
if (emulators.length > 0) {
// Launch JS emulator
if (GK.get('flipper_js_client_emulator')) {
if (emulators.length > 0) {
dropdown.push(
{type: 'separator' as 'separator'},
{
label: 'Launch Android emulators',
enabled: false,
},
...emulators,
);
}
dropdown.push(
{type: 'separator' as 'separator'},
{
label: 'Launch Android emulators',
enabled: false,
label: 'Launch JS Web App',
click: () =>
this.props.setActiveSheet(ACTIVE_SHEET_JS_EMULATOR_LAUNCHER),
},
...emulators,
);
}
}
if (dropdown.length > 0) {
dropdown.push({type: 'separator' as 'separator'});
}
@@ -196,5 +216,6 @@ export default connect<StateFromProps, DispatchFromProps, OwnProps, State>(
{
selectDevice,
preferDevice,
setActiveSheet,
},
)(DevicesButton);

View File

@@ -0,0 +1,122 @@
/**
* 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 {
FlexColumn,
Button,
styled,
Text,
FlexRow,
Spacer,
Input,
Label,
} from 'flipper';
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {State as Store} from '../reducers';
import {launchJsEmulator} from '../utils/js-client/serverUtils';
const Container = styled(FlexColumn)({
padding: 20,
width: 800,
});
const Title = styled(Text)({
marginBottom: 18,
marginRight: 10,
fontWeight: 100,
fontSize: '40px',
});
const textareaStyle = {
margin: 0,
marginBottom: 10,
};
const TitleInput = styled(Input)({
...textareaStyle,
height: 30,
});
type OwnProps = {
onHide: () => void;
};
type StateFromProps = {};
type DispatchFromProps = {};
type State = {
url: string;
width: number;
height: number;
};
type Props = OwnProps & StateFromProps & DispatchFromProps;
class JSEmulatorLauncherSheet extends Component<Props, State> {
state: State = {
url: 'http://localhost:8888',
width: 800,
height: 600,
};
onUrlChange = (e: React.ChangeEvent<HTMLInputElement>) => {
this.setState({url: e.target.value});
};
onHeightChange = (e: React.ChangeEvent<HTMLInputElement>) => {
this.setState({height: Number(e.target.value)});
};
onWidthChange = (e: React.ChangeEvent<HTMLInputElement>) => {
this.setState({width: Number(e.target.value)});
};
render() {
const {url, height, width} = this.state;
return (
<Container>
<Title>Launch Web App</Title>
<Label>Url</Label>
<TitleInput value={url} onChange={this.onUrlChange} />
<Label>Height</Label>
<TitleInput value={height} onChange={this.onHeightChange} />
<Label>Width</Label>
<TitleInput value={width} onChange={this.onWidthChange} />
<br />
<FlexRow>
<Spacer />
<Button compact padded onClick={this.props.onHide}>
Cancel
</Button>
<Button
type="primary"
compact
padded
onClick={() => {
launchJsEmulator(
this.state.url,
this.state.height,
this.state.width,
);
this.props.onHide();
}}>
Launch
</Button>
</FlexRow>
</Container>
);
}
}
export default connect<StateFromProps, DispatchFromProps, OwnProps, Store>(
() => ({}),
{},
)(JSEmulatorLauncherSheet);