Permalink
Browse files

CLI: Add basic Navigation template (Chat)

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 #12153

Differential Revision: D4494776

Pulled By: mkonicek

fbshipit-source-id: b43eafd7a1424477f9493a3eb4083ba4dd3d3846
  • Loading branch information...
mkonicek authored and facebook-github-bot committed Feb 2, 2017
1 parent 81b2d69 commit 3ee3d2b4b26e0febb4a7be4258c7706feb040516
@@ -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;
@@ -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;
@@ -0,0 +1,5 @@
+import { AppRegistry } from 'react-native';
+
+import MainNavigator from './views/MainNavigator';
+
+AppRegistry.registerComponent('ChatExample', () => MainNavigator);
@@ -0,0 +1,5 @@
+import { AppRegistry } from 'react-native';
+
+import MainNavigator from './views/MainNavigator';
+
+AppRegistry.registerComponent('ChatExample', () => MainNavigator);
@@ -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;
@@ -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;
@@ -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,
+ },
+});
Oops, something went wrong.

0 comments on commit 3ee3d2b

Please sign in to comment.