Skip to content

Commit b9ad441

Browse files
authored
Merge pull request #42 from co2-git/feature/38
Feature/38
2 parents 0289e5b + cec438e commit b9ad441

29 files changed

+601
-29
lines changed

.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ module.exports = {
88
"max-len": [1, 100, 2, {ignoreComments: true}],
99
"object-curly-spacing": ["error", "never"],
1010
"no-restricted-syntax": 0,
11+
"guard-for-in": 0,
1112

1213
// Flow
1314
"flowtype/boolean-style": [

.flowconfig

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,14 @@
1414

1515
[libs]
1616
./flow/app.js.flow
17+
./app/components/Android/Home.js.flow
18+
./app/components/Android/Run.js.flow
19+
./app/components/Android/RunOptions.js.flow
1720
./app/components/App/App.js.flow
1821
./app/components/App/AppBar.js.flow
22+
./app/components/App/AppBottomBar.js.flow
1923
./app/components/Base/Animated.js.flow
24+
./app/components/Base/IconWithLabel.js.flow
2025
./app/components/FlexBox/Flex.js.flow
2126
./app/components/Layout/AppCard.js.flow
2227
./app/components/Layout/Header.js.flow

app/components/Android/Home.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// @flow
2+
import React, {PureComponent} from 'react';
3+
import PlayIcon from 'material-ui/svg-icons/av/play-arrow';
4+
import LogIcon from 'material-ui/svg-icons/content/content-paste';
5+
import PackageIcon from 'material-ui/svg-icons/content/archive';
6+
import SwipeableViews from 'react-swipeable-views';
7+
8+
import {gutter} from '../../styles/vars/metrics';
9+
import RunAndroid from './Run';
10+
import IconWithLabel from '../Base/IconWithLabel';
11+
import Row from '../FlexBox/Row';
12+
13+
class AndroidHome extends PureComponent<$AndroidHomeProps, $AndroidHomeState> {
14+
state = {
15+
index: 0,
16+
};
17+
render = () => (
18+
<div style={{margin: gutter}}>
19+
<Row between>
20+
<IconWithLabel
21+
icon={<PlayIcon color="#777" />}
22+
label="Run"
23+
onClick={() => this.selectIndex(0)}
24+
/>
25+
<IconWithLabel
26+
icon={<LogIcon color="#777" />}
27+
label="Logs"
28+
onClick={() => this.selectIndex(1)}
29+
/>
30+
<IconWithLabel
31+
icon={<PackageIcon color="#777" />}
32+
label="APKs"
33+
onClick={() => this.selectIndex(2)}
34+
/>
35+
</Row>
36+
<SwipeableViews index={this.state.index}>
37+
<RunAndroid app={this.props.app} />
38+
</SwipeableViews>
39+
</div>
40+
);
41+
selectIndex = (index: number) => this.setState({index});
42+
}
43+
44+
export default AndroidHome;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// @flow
2+
3+
declare type $AndroidHomeOwnProps = {|
4+
+app: $App,
5+
|};
6+
7+
declare type $AndroidHomeProps =
8+
& $AndroidHomeOwnProps;
9+
10+
declare type $AndroidHomeState = {|
11+
index: number,
12+
|};

app/components/Android/Run.js

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// @flow
2+
import {Card, CardHeader, CardActions, CardText} from 'material-ui/Card';
3+
import AndroidIcon from 'material-ui/svg-icons/action/android';
4+
import IconButton from 'material-ui/IconButton';
5+
import open from 'open';
6+
import RaisedButton from 'material-ui/RaisedButton';
7+
import React, {PureComponent} from 'react';
8+
9+
import {adjustWithCard} from '../../styles/vars/metrics';
10+
import {lightInfoMessage, linkStyle} from '../../styles/main';
11+
import AndroidRunOptions from './RunOptions';
12+
import Animated from '../Base/Animated';
13+
import Terminal from '../Terminal/Console';
14+
15+
class RunAndroid extends PureComponent<$RunAndroidProps, $RunAndroidState> {
16+
state = {
17+
options: {},
18+
running: false,
19+
showTerminal: false,
20+
};
21+
terminal: Terminal;
22+
render = () => (
23+
<Animated rubberBand>
24+
<Card>
25+
<CardHeader
26+
actAsExpander
27+
avatar={<IconButton><AndroidIcon /></IconButton>}
28+
showExpandableButton
29+
subtitle="Install your app on Android device (or emulator)"
30+
title="Run Android"
31+
/>
32+
<CardText expandable>
33+
<AndroidRunOptions
34+
onChange={this.onChangeOption}
35+
/>
36+
</CardText>
37+
<CardActions>
38+
<div style={{marginLeft: adjustWithCard}}>
39+
<RaisedButton
40+
primary={!this.state.running}
41+
secondary={this.state.running}
42+
label={this.state.running ? 'Stop' : 'Run'}
43+
onClick={this.onActionClick}
44+
/>
45+
{this.state.running && (
46+
<span>
47+
<span style={lightInfoMessage}>
48+
Might not always terminate sub shells
49+
</span>
50+
<span style={linkStyle} onClick={this.seeBug1}>
51+
See issue
52+
</span>
53+
</span>
54+
)}
55+
</div>
56+
</CardActions>
57+
<CardText>
58+
{this.state.showTerminal && (
59+
<Terminal
60+
command={this.makeCommand()}
61+
cwd={this.props.app.path}
62+
onDone={() => this.setState({running: false})}
63+
onFail={() => this.setState({running: false})}
64+
ref={(terminal) => {
65+
if (!this.terminal && terminal) {
66+
this.terminal = terminal;
67+
}
68+
}}
69+
/>
70+
)}
71+
</CardText>
72+
</Card>
73+
</Animated>
74+
);
75+
onActionClick = () => {
76+
if (this.state.running) {
77+
this.terminal.stop();
78+
setTimeout(() => {
79+
this.setState({running: false});
80+
}, 1500);
81+
} else if (this.state.showTerminal) {
82+
this.setState({showTerminal: false}, () => {
83+
setTimeout(() => {
84+
this.setState({running: true, showTerminal: true});
85+
}, 500);
86+
});
87+
} else {
88+
this.setState({running: true, showTerminal: true});
89+
}
90+
};
91+
onChangeOption = (key: string, option: $CliOptions, value: $AnyPrimitive) => this.setState({
92+
options: {
93+
...this.state.options,
94+
[key]: value,
95+
},
96+
});
97+
makeCommand = () => {
98+
const cmd = 'react-native run-android';
99+
const options = [];
100+
for (const option in this.state.options) {
101+
if (typeof this.state.options[option] === 'boolean') {
102+
options.push(`--${option}`);
103+
} else {
104+
options.push(`--${option} "${this.state.options[option]}"`);
105+
}
106+
}
107+
return `${cmd} ${options.join(' ')}`;
108+
};
109+
seeBug1 = () => open('https://github.com/co2-git/ReactNative/issues/35');
110+
}
111+
112+
export default RunAndroid;

app/components/Android/Run.js.flow

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// @flow
2+
3+
declare type $RunAndroidOwnProps = {|
4+
+app: $App,
5+
|};
6+
7+
declare type $RunAndroidProps =
8+
& $RunAndroidOwnProps;
9+
10+
declare type $RunAndroidState = {|
11+
options: {
12+
[key: string]: any,
13+
},
14+
running: boolean,
15+
showTerminal: boolean,
16+
|};
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// @flow
2+
import React, {PureComponent} from 'react';
3+
import TextField from 'material-ui/TextField';
4+
import Toggle from 'material-ui/Toggle';
5+
import map from 'lodash/map';
6+
import keys from 'lodash/keys';
7+
import startCase from 'lodash/startCase';
8+
9+
import {android} from '../../../config.json';
10+
11+
const defaultState = {};
12+
13+
for (const key in android.runOptions) {
14+
defaultState[key] = android.runOptions[key].default;
15+
}
16+
17+
class AndroidRunOptions extends PureComponent<$AndroidRunOptionsProps, $AndroidRunOptionsState> {
18+
state = {options: {...defaultState}};
19+
render = () => (
20+
<div>
21+
{map(keys(android.runOptions).sort(), (key) => {
22+
const option = android.runOptions[key];
23+
if (option.type === 'boolean') {
24+
return (
25+
<div key={key}>
26+
<Toggle
27+
label={option.label || startCase(key)}
28+
labelStyle={{color: '#777'}}
29+
toggled={this.state.options[key]}
30+
onToggle={(
31+
event: SyntheticUIEvent<HTMLInputElement>,
32+
isInputChecked: boolean,
33+
) => {
34+
this.setState({
35+
options: {
36+
...this.state.options,
37+
[key]: isInputChecked,
38+
},
39+
}, () => {
40+
this.props.onChange(key, option, isInputChecked);
41+
});
42+
}}
43+
/>
44+
</div>
45+
);
46+
}
47+
if (option.type === 'string') {
48+
return (
49+
<div key={key}>
50+
<TextField
51+
hintText={startCase(key)}
52+
floatingLabelText={startCase(key)}
53+
errorText={option.label || startCase(key)}
54+
errorStyle={{color: '#777'}}
55+
floatingLabelStyle={{color: '#777'}}
56+
fullWidth
57+
value={this.state.options[key]}
58+
onChange={(event: SyntheticInputEvent<HTMLInputElement>) => {
59+
const {value} = event.target;
60+
this.setState({
61+
options: {
62+
...this.state.options,
63+
[key]: value,
64+
},
65+
}, () => {
66+
this.props.onChange(key, option, value);
67+
});
68+
}}
69+
/>
70+
</div>
71+
);
72+
}
73+
if (option.type === 'number') {
74+
return (
75+
<div key={key}>
76+
<TextField
77+
hintText={startCase(key)}
78+
floatingLabelText={startCase(key)}
79+
errorText={option.label || startCase(key)}
80+
errorStyle={{color: '#777'}}
81+
floatingLabelStyle={{color: '#777'}}
82+
fullWidth
83+
value={this.state.options[key]}
84+
onChange={(event: SyntheticInputEvent<HTMLInputElement>) => {
85+
const {value} = event.target;
86+
this.setState({
87+
options: {
88+
...this.state.options,
89+
[key]: value,
90+
},
91+
}, () => {
92+
this.props.onChange(key, option, value);
93+
});
94+
}}
95+
/>
96+
</div>
97+
);
98+
}
99+
return null;
100+
})}
101+
</div>
102+
);
103+
}
104+
105+
export default AndroidRunOptions;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// @flow
2+
3+
declare type $AndroidRunOptionsOwnProps = {|
4+
+onChange: (key: string, option: $CliOptions, value: any) => void,
5+
|};
6+
7+
declare type $AndroidRunOptionsProps =
8+
& $AndroidRunOptionsOwnProps;
9+
10+
declare type $AndroidRunOptionsState = {|
11+
options: {
12+
[key: string]: any,
13+
},
14+
|};

app/components/App/App.js

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,51 @@
11
// @flow
2+
import {connect} from 'react-redux';
23
import React from 'react';
34
import SwipeableViews from 'react-swipeable-views';
45

6+
import {appMainStyle, appTabStyle, appTopBarStyle} from '../../styles/main';
7+
import AndroidHome from '../Android/Home';
58
import AppBar from './AppBar';
69
import AppBottomBar from './AppBottomBar';
710
import Info from '../ReactNative/Info';
811
import Page from '../Layout/Page';
912

10-
const App = ({app}: $AppProps) => (
13+
const App = ({app, index}: $AppProps) => (
1114
<Page style={{display: 'flex', flexDirection: 'column'}}>
12-
<div style={{flexShrink: 0}}>
15+
<div style={appTopBarStyle}>
1316
<AppBar app={app} />
1417
</div>
15-
<div style={{flexGrow: 2, overflow: 'auto'}}>
16-
<SwipeableViews>
17-
<div>
18+
<div style={appMainStyle}>
19+
<SwipeableViews index={index}>
20+
<div style={appTabStyle}>
1821
<Info app={app} />
1922
</div>
23+
<div style={appTabStyle}>
24+
Start
25+
</div>
26+
<div style={appTabStyle}>
27+
<AndroidHome app={app} />
28+
</div>
29+
<div style={appTabStyle}>
30+
iOS
31+
</div>
32+
<div style={appTabStyle}>
33+
Upgrade
34+
</div>
35+
<div style={appTabStyle}>
36+
Native
37+
</div>
38+
<div style={appTabStyle}>
39+
Eject
40+
</div>
2041
</SwipeableViews>
2142
</div>
2243
<AppBottomBar app={app} />
2344
</Page>
2445
);
2546

26-
export default App;
47+
const selector = (state: $State, props: $AppOwnProps): $AppConnectProps => ({
48+
index: state.appRouterIndex[props.app.path] || 0,
49+
});
50+
51+
export default connect(selector)(App);

0 commit comments

Comments
 (0)