Skip to content

Commit

Permalink
CLI: Add basic Navigation template (Chat)
Browse files Browse the repository at this point in the history
Summary:
Basic template using 'react-navigation' to make it easy to get started.

Not duplicating all the files in `android` and `ios` folders. These will be taken from the `HelloWorld` template. Let's not duplicate all of these files (it's a lot and they are large, especially the Xcode projects).

**Test plan (required)**

The app works locally. This PR is just a preparation for a next PR that will add support for 'react-native init --template Navigation'. Will have a proper test plan there.
Closes facebook#12153

Differential Revision: D4494776

Pulled By: mkonicek

fbshipit-source-id: b43eafd7a1424477f9493a3eb4083ba4dd3d3846
  • Loading branch information
Martin Konicek authored and nicktate committed Feb 9, 2017
1 parent fd7a431 commit acd9926
Show file tree
Hide file tree
Showing 12 changed files with 523 additions and 0 deletions.
115 changes: 115 additions & 0 deletions local-cli/templates/HelloNavigation/components/KeyboardSpacer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/* @flow */

import React, { PropTypes, Component } from 'react';
import {
Platform,
View,
Keyboard,
LayoutAnimation,
UIManager,
} from 'react-native';

type Props = {
offset?: number;
}

type State = {
keyboardHeight: number
}

// Consider contributing this to the popular library:
// https://github.com/Andr3wHur5t/react-native-keyboard-spacer

/**
* On iOS, the software keyboard covers the screen by default.
* This is not desirable if there are TextInputs near the bottom of the screen -
* they would be covered by the keyboard and the user cannot see what they
* are typing.
* To get around this problem, place a `<KeyboardSpacer />` at the bottom
* of the screen, after your TextInputs. The keyboard spacer has size 0 and
* when the keyboard is shown it will grow to the same size as the keyboard,
* shifting all views above it and therefore making them visible.
*
* On Android, this component is not needed because resizing the UI when
* the keyboard is shown is supported by the OS.
* Simply set the `android:windowSoftInputMode="adjustResize"` attribute
* on the <activity> element in your AndroidManifest.xml.
*
* How is this different from KeyboardAvoidingView?
* The KeyboardAvoidingView doesn't work when used together with
* a ScrollView/ListView.
*/
const KeyboardSpacer = () => (
Platform.OS === 'ios' ? <KeyboardSpacerIOS /> : null
)

class KeyboardSpacerIOS extends Component<Props, Props, State> {
static propTypes = {
offset: PropTypes.number,
};

static defaultProps = {
offset: 0,
};

state: State = {
keyboardHeight: 0,
};

componentWillMount() {
this._registerEvents();
}

componentWillUnmount() {
this._unRegisterEvents();
}

_keyboardWillShowSubscription: { remove: Function };
_keyboardWillHideSubscription: { remove: Function };

_registerEvents = () => {
this._keyboardWillShowSubscription = Keyboard.addListener(
'keyboardWillShow',
this._keyboardWillShow
);
this._keyboardWillHideSubscription = Keyboard.addListener(
'keyboardWillHide',
this._keyboardWillHide
);
};

_unRegisterEvents = () => {
this._keyboardWillShowSubscription.remove();
this._keyboardWillHideSubscription.remove();
};

_configureLayoutAnimation = () => {
// Any duration is OK here. The `type: 'keyboard defines the animation.
LayoutAnimation.configureNext({
duration: 100,
update: {
type: 'keyboard',
}
});
}

_keyboardWillShow = (e: any) => {
this._configureLayoutAnimation();
this.setState({
keyboardHeight: e.endCoordinates.height - (this.props.offset || 0),
});
};

_keyboardWillHide = () => {
this._configureLayoutAnimation();
this.setState({
keyboardHeight: 0,
});
};

render() {
return <View style={{ height: this.state.keyboardHeight }} />;
}
}

export default KeyboardSpacer;
52 changes: 52 additions & 0 deletions local-cli/templates/HelloNavigation/components/ListItem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React, { Component } from 'react';
import {
Platform,
StyleSheet,
Text,
TouchableHighlight,
TouchableNativeFeedback,
View,
} from 'react-native';

/**
* Renders the right type of Touchable for the list item, based on platform.
*/
const Touchable = ({onPress, children}) => {
const child = React.Children.only(children);
if (Platform.OS === 'android') {
return (
<TouchableNativeFeedback onPress={onPress}>
{child}
</TouchableNativeFeedback>
);
} else {
return (
<TouchableHighlight onPress={onPress} underlayColor='#ddd'>
{child}
</TouchableHighlight>
);
}
}

const ListItem = ({label, onPress}) => (
<Touchable onPress={onPress}>
<View style={styles.item}>
<Text style={styles.label}>{label}</Text>
</View>
</Touchable>
);

const styles = StyleSheet.create({
item: {
height: 48,
justifyContent: 'center',
paddingLeft: 12,
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: '#ddd',
},
label: {
fontSize: 16,
}
});

export default ListItem;
5 changes: 5 additions & 0 deletions local-cli/templates/HelloNavigation/index.android.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { AppRegistry } from 'react-native';

import MainNavigator from './views/MainNavigator';

AppRegistry.registerComponent('ChatExample', () => MainNavigator);
5 changes: 5 additions & 0 deletions local-cli/templates/HelloNavigation/index.ios.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { AppRegistry } from 'react-native';

import MainNavigator from './views/MainNavigator';

AppRegistry.registerComponent('ChatExample', () => MainNavigator);
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React, { Component } from 'react';
import {
ListView,
Platform,
Text,
} from 'react-native';
import { TabNavigator } from 'react-navigation';

import ChatListScreen from './chat/ChatListScreen';
import FriendListScreen from './friends/FriendListScreen';

/**
* Screen with tabs shown on app startup.
*/
const HomeScreenTabNavigator = TabNavigator({
Chats: {
screen: ChatListScreen,
},
Friends: {
screen: FriendListScreen,
},
});

export default HomeScreenTabNavigator;
25 changes: 25 additions & 0 deletions local-cli/templates/HelloNavigation/views/MainNavigator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* This is an example React Native app demonstrates ListViews, text input and
* navigation between a few screens.
* https://github.com/facebook/react-native
*/

import React, { Component } from 'react';
import { StackNavigator } from 'react-navigation';

import HomeScreenTabNavigator from './HomeScreenTabNavigator';
import ChatScreen from './chat/ChatScreen';

/**
* Top-level navigator. Renders the application UI.
*/
const MainNavigator = StackNavigator({
Home: {
screen: HomeScreenTabNavigator,
},
Chat: {
screen: ChatScreen,
},
});

export default MainNavigator;
68 changes: 68 additions & 0 deletions local-cli/templates/HelloNavigation/views/chat/ChatListScreen.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React, { Component } from 'react';
import {
Image,
ListView,
Platform,
StyleSheet,
} from 'react-native';
import ListItem from '../../components/ListItem';

export default class ChatListScreen extends Component {

static navigationOptions = {
title: 'Chats',
header: {
visible: Platform.OS === 'ios',
},
tabBar: {
icon: ({ tintColor }) => (
<Image
// Using react-native-vector-icons works here too
source={require('./chat-icon.png')}
style={[styles.icon, {tintColor: tintColor}]}
/>
),
},
}

constructor(props) {
super(props);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: ds.cloneWithRows([
'Claire', 'John'
])
};
}

// Binding the function so it can be passed to ListView below
// and 'this' works properly inside _renderRow
_renderRow = (name) => {
return (
<ListItem
label={name}
onPress={() => this.props.navigation.navigate('Chat', {name: name})}
/>
)
}

render() {
return (
<ListView
dataSource={this.state.dataSource}
renderRow={this._renderRow}
style={styles.listView}
/>
);
}
}

const styles = StyleSheet.create({
listView: {
backgroundColor: 'white',
},
icon: {
width: 30,
height: 26,
},
});
Loading

0 comments on commit acd9926

Please sign in to comment.