Create popup edit comment like facebook’s editing comment modal

Create popup edit comment like facebook’s editing comment modal

Today, I’m going to share how to create a modal similar to facebook edit, delete comment modal.

As you can see in the video, user use their finger to touch the comment for 1-2 seconds, the popup will show up right at the position user touch in the screen. So the idea is: we will catch the event onLongPress, this even provide us the position where our finger touch in the screen. We pass this position to our modal then show the popup at that position.

  <TouchableOpacity onLongPress={evt => this.handleOnLongPressComment(evt, comment)}
    delayLongPress={500}
  >
   {this.renderCommentItem(comment)}
  </TouchableOpacity>

In callback handleOnLongPressComment, we get the position (evt.nativeEvent.pageX, evt.nativeEvent.pageY) then pass it to our modal

  handleOnLongPressComment(evt, comment) {
    dismissKeyboard();

    const currentFeed = this.props.currentFeedComment;
    const currentUser = this.props.currentUser;

    if (
      comment.user.id === currentUser.user.id ||
      (comment.user.id !== currentUser.user.id && currentFeed.user.id === currentUser.user.id)
    ) {
      this.setState({
        commentActionModalVisible: true,
        commentPosition: {
          x: evt.nativeEvent.pageX,
          y: evt.nativeEvent.pageY,
        },
      });
      this.props.setActiveComment(comment);
    }
  }
  renderCommentActionModal() {
    return (
      <CommentActionModal actionModalVisible={this.state.commentActionModalVisible} commentPosition={this.state.commentPosition} onCloseActionModal={this.handleOnCloseCommentActionModal} onPressEditComment={this.handleOnPressEditComment} />
    );
  }

Now, we look at the CommentActionModal
As usual, we use react native Modal component, animationType will be set to none, we don’t want to show any animation here.

  render() {
    return (
      <Modal animationType="none" transparent visible={this.props.actionModalVisible} onRequestClose={() => {
          this.props.onCloseActionModal();
        }}
      >
        <View style={{ flex: 1 }}>
          <View style={styles.background} />
          <TouchableWithoutFeedback onPress={() => this.props.onCloseActionModal()}>
            <View style={styles.content}>{this.renderBody()}</View>
          </TouchableWithoutFeedback>
        </View>
      </Modal>
    );
  }

Apart from two required props actionModalVisible and onCloseActionModal, this component also accept the prop name commentPosition.

  commentPosition: PropTypes.shape({
    x: PropTypes.number,
    y: PropTypes.number,
  }).isRequired,

In event componentWillReceiveProps, we use this commentPosition to calculate the position (top,left) of the popup which we want to show.

  componentWillReceiveProps({ actionModalVisible, commentPosition }) {
    if (actionModalVisible && commentPosition) {
      let py = commentPosition.y;
      if (py + 130 > height) {
        py -= 100;
      }
      this.setState({
        customStyle: {
          top: py,
          left: width / 2 - 50,
        },
      });
    }
  }

Hmm, it’s pretty easy, right? Here is the full code if you guys interested in fullcode
Thanks.

Full code of facebook’s comment editing popup

Popup edit comment like facebook’s modal

Full code of facebook’s comment editing popup. code explanation here

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import PropTypes from 'prop-types';
import {
  Modal,
  View,
  Text,
  StyleSheet,
  TouchableOpacity,
  InteractionManager,
  TouchableWithoutFeedback,
  Dimensions,
  Alert,
} from 'react-native';
import I18n from '../app/i18n';
import { removeComment, focusCommentBox } from './feedActions';
import { RALEWAY_REGULAR } from '../commons/fonts';

const { width, height } = Dimensions.get('window');

class CommentActionModal extends Component {
  constructor(props) {
    super(props);
    this.state = {
      isLoadingScreen: true,
      customStyle: {
        top: 0,
        left: 0,
      },
    };
  }

  componentDidMount() {
    // this.showModalAnimation();
    InteractionManager.runAfterInteractions(() => {
      this.setState({
        isLoadingScreen: false,
      });
    });
  }

  componentWillReceiveProps({ actionModalVisible, commentPosition }) {
    if (actionModalVisible && commentPosition) {
      let py = commentPosition.y;
      if (py + 130 > height) {
        py -= 100;
      }
      this.setState({
        customStyle: {
          top: py,
          left: width / 2 - 50,
        },
      });
    }
  }

  onPressAction(action) {
    switch (action.id) {
      case 1: // edit
        this.props.onPressEditComment();
        this.props.onCloseActionModal();
        break;
      case 2: // delete
        Alert.alert(
            '',
            I18n.t('delete_comment_confirm_message'),
            [
              {
                text: I18n.t('cancel').toUpperCase(),
                onPress: () => this.props.onCloseActionModal(),
              },
              {
                text: I18n.t('delete').toUpperCase(),
                onPress: () => {
                  this.props.onCloseActionModal();
                  this.props.removeComment(this.props.activeComment.id);
                  this.props.focusCommentBox(false);
                },
              },
            ],
            { cancelable: true }
          );
        break;
      case 3: // cancel
        this.props.onCloseActionModal();
        break;
      default:
        break;
    }
  }

  renderBody() {
    let feedActions = [
        {
          id: 1,
          name: I18n.t('edit'),
        },
        {
          id: 2,
          name: I18n.t('delete'),
        },
        {
          id: 3,
          name: I18n.t('cancel'),
        },
    ];

    const currentUser = this.props.currentUser;
    const comment = this.props.activeComment;
    if (comment) {
      if (comment.user.id !== currentUser.user.id) {
        feedActions.splice(0, 1);
      }
    }

    return (
      <TouchableWithoutFeedback>
        <View style={[styles.body, this.state.customStyle]}>
          {feedActions.map(action => (
            <TouchableOpacity key={action.id} onPress={() => this.onPressAction(action)}>
              <Text style={[styles.actionItemText]}>{action.name}</Text>
            </TouchableOpacity>
          ))}
        </View>
      </TouchableWithoutFeedback>
    );
  }

  render() {
    return (
      <Modal
        animationType="none"
        transparent
        visible={this.props.actionModalVisible}
        onRequestClose={() => {
          this.props.onCloseActionModal();
        }}
      >
        <View style={{ flex: 1 }}>
          <View style={styles.background} />
          <TouchableWithoutFeedback onPress={() => this.props.onCloseActionModal()}>
            <View style={styles.content}>{this.renderBody()}</View>
          </TouchableWithoutFeedback>
        </View>
      </Modal>
    );
  }
}

const styles = StyleSheet.create({
  background: {
    position: 'absolute',
    top: 0,
    right: 0,
    bottom: 0,
    left: 0,
    backgroundColor: '#000',
    opacity: 0.3,
  },
  content: {
    backgroundColor: 'transparent',
    position: 'absolute',
    top: 0,
    right: 0,
    bottom: 0,
    left: 0,
    flex: 1,
  },
  body: {
    backgroundColor: '#fff',
    position: 'absolute',
    borderRadius: 3,
    borderWidth: 1,
    borderColor: '#fff',
  },
  actionItemText: {
    padding: 13,
    paddingLeft: 20,
    paddingRight: 20,
    color: '#000',
    fontFamily: RALEWAY_REGULAR,
  },
  cancel: {
    color: 'red',
  },
});

CommentActionModal.propTypes = {
  onPressEditComment: PropTypes.func.isRequired,
  onCloseActionModal: PropTypes.func.isRequired,
  activeComment: PropTypes.shape({
    id: PropTypes.number,
  }),
  currentUser: PropTypes.shape({
    user: PropTypes.shape({
      id: PropTypes.number,
    }),
  }),
  actionModalVisible: PropTypes.bool,
  removeComment: PropTypes.func,
  focusCommentBox: PropTypes.func,
  commentPosition: PropTypes.shape({
    x: PropTypes.number,
    y: PropTypes.number,
  }).isRequired,
};

CommentActionModal.defaultProps = {
  activeComment: null,
  currentUser: null,
  actionModalVisible: false,
  removeComment: null,
  focusCommentBox: null,
};

function mapDispatchToProps(dispatch) {
  return bindActionCreators({ removeComment, focusCommentBox }, dispatch);
}

function mapStateToProps(state) {
  return {
    appTheme: state.appTheme,
    activeComment: state.activeComment,
    currentUser: state.currentUser,
  };
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(CommentActionModal);

How to solve issue “native base toast is not showing in react native modal”

How to solve issue “native base toast is not showing in react native modal”

If you are using native base in your project, you might encounter this issue when you want to use component native base Toast inside react native Modal component. Native base Toast

It’s very easy to fix this issue, we just need to create another class extend from native base Toast

import { Toast } from 'native-base';

class MyToast extends Toast {}

export default MyToast;

Then in the react native modal, you import it and use it just like you are using native base Toast

import MyToast from '../commons/myToast';

...

MyToast.show({
  text: I18n.t('please_select_segment'),
  buttonText: I18n.t('okay'),
  duration: 3000,
  position: 'bottom',
});

Thanks.

Full code of swipable Facebook’s comment modal

Full code of swipable Facebook’s comment modal, code explanation http://reactnative.vn/create-swipable-modal-like-facebooks-comment-modal/

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import {
  Modal,
  View,
  StyleSheet,
  Animated,
  Dimensions,
  PanResponder,
  StatusBar,
  TouchableWithoutFeedback,
  Platform,
  Easing,
} from 'react-native';

import CommentList from './commentList';
import CommentBox from './commentBox';

const dismissKeyboard = require('dismissKeyboard');

const { height: screenHeight } = Dimensions.get('window');
const TIMING_CONFIG = { duration: 300, easing: Easing.inOut(Easing.ease) };
class CommentModal extends Component {
  constructor(props) {
    super(props);
    this.state = {
      text: '',
      height: 50,
      isSaving: false,
      canSendComment: false,
      pan: new Animated.ValueXY({ x: 0, y: screenHeight }),
      isAnimating: false,
    };

    this.handleOnClose = this.handleOnClose.bind(this);
  }

  componentWillMount() {
    if (!this) {
      return;
    }
    this.animatedValueX = 0;
    this.animatedValueY = 0;
    this.state.pan.x.addListener(value => (this.animatedValueX = value.value));
    this.state.pan.y.addListener(value => (this.animatedValueY = value.value));

    this.panResponder = PanResponder.create({
      // Ask to be the responder:
      onStartShouldSetPanResponder: () => false,
      onStartShouldSetPanResponderCapture: () => false,
      onMoveShouldSetPanResponder: (evt, gestureState) => {
        if (this.state.isAnimating) {
          return false;
        }
        if (gestureState.dy > 30 || gestureState.dy < -30) {
          return true;
        }
        return false;
      },
      onPanResponderGrant: () => {
        this.state.pan.setOffset({
          x: this.animatedValueX,
          y: this.animatedValueY,
        });
        this.state.pan.setValue({ x: 0, y: 0 }); // Initial value
      },
      onPanResponderMove: (evt, gestureState) => {
        this.setState({
          backgroundColor: 'transparent',
        });
        this.state.pan.setValue({ x: 0, y: gestureState.dy });
      },
      onPanResponderRelease: (evt, gestureState) => {
        // The user has released all touches while this view is the
        // responder. This typically means a gesture has succeeded
        // this.state.pan.flattenOffset(); // Flatten the offset so it resets the default positioning
        if (gestureState.vy <= -0.7 || gestureState.dy <= -300) {
          Animated.timing(this.state.pan, {
            toValue: { x: 0, y: -screenHeight },
            ...TIMING_CONFIG,
          }).start(() => {
            this.props.onClose();
          });
        } else if (gestureState.vy >= 0.5 || gestureState.dy >= 300) {
          Animated.timing(this.state.pan, {
            toValue: { x: 0, y: screenHeight },
            ...TIMING_CONFIG,
          }).start(() => {
            this.props.onClose();
          });
        } else {
          Animated.spring(this.state.pan, {
            toValue: 0,
          }).start();
          this.setState({
            backgroundColor: '#000',
          });
        }
      },
    });
  }

  componentWillReceiveProps(newProp) {
    if (newProp.modalVisible) {
      this.showCommentAnimation();
    }
  }

  showCommentAnimation() {
    setTimeout(() => {
      if (Platform.OS === 'android') {
        StatusBar.setBackgroundColor('#000');
      }
    }, 100);
    this.setState({
      isAnimating: true,
    });
    Animated.timing(this.state.pan, {
      ...TIMING_CONFIG,
      toValue: { x: 0, y: 0 },
    }).start(() => {
      this.setState({
        isAnimating: false,
      });
    });
  }

  handleOnClose() {
    dismissKeyboard();
    if (Platform.OS === 'android') {
      StatusBar.setBackgroundColor(this.props.appTheme.mainColor);
    }

    Animated.timing(this.state.pan, {
      ...TIMING_CONFIG,
      toValue: { x: 0, y: screenHeight },
    }).start(() => {
      this.props.onClose();
    });
  }

  handleOnChange(event) {
    this.setState({
      text: event.nativeEvent.text,
      height: event.nativeEvent.contentSize.height + 10,
    });

    if (event.nativeEvent.text) {
      this.setState({
        canSendComment: true,
      });
    } else {
      this.setState({
        canSendComment: false,
      });
    }
  }

  handleGetStyle(opacity) {
    return [
      styles.container,
      {
        transform: [...this.state.pan.getTranslateTransform()],
        opacity: opacity,
      },
    ];
  }

  render() {
    const body = <CommentList />;
    const interpolateBackgroundOpacity = this.state.pan.y.interpolate({
      inputRange: [-screenHeight, 0, screenHeight],
      outputRange: [0, 1, 0],
    });
    const { appTheme } = this.props;
    return (
      <Modal
        animationType="none"
        transparent
        visible={this.props.modalVisible}
        onRequestClose={() => {
          this.handleOnClose();
        }}
      >
        <Animated.View style={[styles.background, { opacity: interpolateBackgroundOpacity }]} />
        <Animated.View
          style={this.handleGetStyle(interpolateBackgroundOpacity)}
          {...this.panResponder.panHandlers}
        >
          <TouchableWithoutFeedback>
            <View style={{ flex: 1 }}>
              <View style={styles.body}>{body}</View>
              <View style={[styles.seperator, { backgroundColor: appTheme.mainColor }]} />
              <CommentBox isCommentModal isRoundCorner onClose={this.handleOnClose} />
            </View>
          </TouchableWithoutFeedback>
        </Animated.View>
      </Modal>
    );
  }
}

const styles = StyleSheet.create({
  loadingScreen: {
    flex: 1,
    marginLeft: 10,
    marginRight: 10,
  },
  background: {
    opacity: 0,
    flex: 1,
    backgroundColor: '#000',
  },
  container: {
    flex: 1,
    backgroundColor: '#fff',
    borderColor: '#000',
    borderRadius: 10,
    position: 'absolute',
    top: 0,
    right: 0,
    bottom: 0,
    left: 0,
  },
  body: {
    flex: 1,
    backgroundColor: '#fff',
    borderTopLeftRadius: 10,
    borderTopRightRadius: 10,
  },
  seperator: {
    backgroundColor: '#ddd',
    height: 0.5,
    marginLeft: 10,
    marginRight: 10,
  },
});

CommentModal.propTypes = {
  onClose: PropTypes.func.isRequired,
  appTheme: PropTypes.shape({
    mainColor: PropTypes.string,
  }).isRequired,
  modalVisible: PropTypes.bool.isRequired,
};

function mapStateToProps(state) {
  return {
    appTheme: state.appTheme,
  };
}

export default connect(mapStateToProps)(CommentModal);

[Performance Issue] React native app crashes after several seconds

App crashed after several seconds.

Sometime, app is running smoothly and suddenly crashed after several seconds or minutes, we don’t know what happened. With my experience, I found some reasons which could be your problems. So, lets check it out and see if one of them are your issues.

  1. The first reason could be somewhere in your code, you don’t check null or undefined before using that object. It’s easy to figure out this problem. Just need to enable debugging mode and use app for a while then you are able to reproduce problem.
  2. The second reason: it’s really tough to figure out this issue even in debugging mode. That’s heap memory size. You could encounter this issue if you are using many large PNG images in your app. This is a known issue which is mentioned in react-native official page (https://facebook.github.io/react-native/releases/0.23/docs/known-issues.html) . To fix this issue, there are some ways to do like this:
    1. If you know native code, you can write a module to reduce image size ( I don’t choose this way because I don’t know native code :D)
    2. Try to use images with size as small as possible. To do this, you can handle images from server side, whenever an image is uploaded to server, save this image to different versions such as: thumbnail, medium and actual size, save them to cdn. From client, fetch the properly images with specified size.

If you guys know others reason, please let me know then I can add them to the list, it would be useful for others in future.

Thanks

Create redux middleware to refresh token in react native app

Redux middleware to refresh token automatically

In your react native app, if you are using token to authenticate with server, you could encounter the token expire issue. The issue is that for example, your token timeout is 1 hour, you open app and leave it for a long time, the token will be expired, if at that moment, you do something which interact with server, those request will be failed because the token is timeout. What you have to do now is that send a refresh token to server to refresh the token, and use that token for new requests to server.

To do it in redux, we create a middle-ware to detect every requests to server. The idea is:

  1. In token object responded from server, it should contain a timeout
  2. Every request from client should go through this middle-ware before sending to server
  3. In this middle-ware, with every requests to server, check the token timeout, if token is expired, hold those request, send request to server to refresh token, wait until received new token, use this new token to send those request to server. If the token is still valid, let it be.

Now, let see the code

import * as types from './authenticationTypes';
import Api from '../libs/api';
import Utils from '../commons/utils';

export default function authMiddleware({ dispatch, getState }) {
  return (next) => (action) => {
    if (typeof action === 'function') {
      let state = getState();
      if(!state) {
        if(state.token && isExpired(state.token)) {
          // make sure we are not already refreshing the token
          if(!state.refreshTokenPromise) {
            return refreshToken(dispatch, state).then(() => next(action));
          } else {
            return state.refreshTokenPromise.then(() => next(action));
          }
        }
      }

    }
    return next(action);
  }
}

function isExpired(token) {
  let currentTime = new Date();
  let expires_date = new Date(token.expires_date);
  return currentTime > expires_date;
}

function refreshToken(dispatch, state) {
  let refreshTokenPromise = Api.post('/token', {
    grant_type: 'refresh_token',
    username: state.token.username,
    refresh_token: state.token.refresh_token
  }, null, true).then(resp => {
    dispatch({
      type: types.DONE_REFRESHING_TOKEN
    });
    dispatch({
      type: types.LOGIN_SUCCESS,
      data: resp
    });
    dispatch({
      type: types.SET_HEADER,
      header: {
        Authorization: resp.token_type + ' ' + resp.access_token,
        Instance: state.currentInstance.id
      }
    });
    return resp ? Promise.resolve(resp) : Promise.reject({
        message: 'could not refresh token'
    });
  }).catch(ex => {
    console.log('exception refresh_token', ex);
    dispatch({
      type: types.DONE_REFRESHING_TOKEN
    });
    dispatch({
      type: types.LOGIN_FAILED,
      exception: ex
    });
  });

  dispatch({
    type: types.REFRESHING_TOKEN,
    // we want to keep track of token promise in the state so that we don't     try to refresh the token again while refreshing is in process
    refreshTokenPromise
  });

  return refreshTokenPromise;
}

Lets go through the code.

  1. Create authMiddleware: every request will go to this middle-ware before sending to server.
refreshTokenPromise: just for make sure that if there are many requests to server at the same time, but we only send one refresh token request to server.
export default function authMiddleware({ dispatch, getState }) {
  return (next) => (action) => {
    // check if token is valid => return to current action
    // otherwise refresh token before returning to current action
    if (typeof action === 'function') {
      let state = getState();
      if(!state) {
        if(state.token && isExpired(state.token)) {
          // make sure we are not already refreshing the token
          if(!state.refreshTokenPromise) {
            return refreshToken(dispatch, state).then(() => next(action));
          } else {
            return state.refreshTokenPromise.then(() => next(action));
          }
        }
      }
    }
    return next(action);
  }
}

2. Create action to refresh token (see the code and comment above)

Note:

  • API is a class which I create to send request to server using Fetch library
  • If you don’t understand the code above, please take a look http://redux.js.org/

Redux middleware to refresh token automatically

Thanks

React native remote push notification Integrate remote push notification GCM, azure hubs and react native in android

React native remote push notification GCM, Azure hubs in android

Hi every one, I have finished integrating react native remote push notification with GCM and azure.
I realized that there is not many articles talk about this, it took me some days to figured out how to do.
Today is weekend, I decide to write it here, hope it can help others in some cases.

Before starting, I would like to say “sorry for my poor English”, if you guys don’t understand any part of this article, just let me know, I can fix it.
Everything can be fixed, right 😀

OK, Lets begin.

Firstly, we are going to use this third party library (https://github.com/zo0r/react-native-push-notification) to help doing some stuff behind the scene.

Just follow the instruction on that page and install the lib to your project.

I just have a notice here, you could get a building error after installing the lib even you followed exactly the instruction :D.

No worries, I’m here to help you.

The reason is the path to your project is still fine until you install this push notification lib, there could be two reasons here:

  1. Your project path is long, when combine with this lib, there are something inside this lib which has the path is also long -> error
  2. Your project path has space between -> error

How to fix : you just need to change your project directory to a new place which has a path as sort as possible.

If it’s still not working after changing the to the new sort path, then try to choose another path without space between.

We have finished the first step to install push notification lib.

Lets move to next step: register a GCM server.

To do it, navigate to this page (https://console.developers.google.com). Click to Google Cloud Messaging url

Previously, it will navigate you to google cloud messaging page, but starting from Sept. 2016 new server key can only be created in the Firebase Console using the Cloud Messaging tab of the Settings panel.

This is google policy, so we just need to follow them.

On this page, click on Go to console. It will navigate us to firebase management screen.

Click “Create new project”. Fill up the fields and click Create to create a new project.

When creating process is finished, it will navigate you to management screen.

Click to Project settings to get into the setting page.

Click on tab cloud messaging

Ah, from this page we can see the server key and sender id. These two values are very important, we will use them right now in the next step.

We have finished creating FCM (Firebase cloud messaging) server.

This server in charge of listening event from our azure hubs and send notification to client (android or ios phone).

Now, we continue setup our azure server to connect to FCM.

To to that, you can follow the instruction from Microsoft, link here (https://docs.microsoft.com/en-us/azure/notification-hubs/notification-hubs-android-push-notification-google-gcm-get-started).

It that document, they also guide us how to create GCM server, but that doc is obsoleted, that why I had to create a new instruction above.

Follow that document will help you connect to FCM server, only one thing I should mention here is that the server key which is created in above step now is using in azure in this step.

I won’t mention how to create service to send notifications from your system to azure, the reason is that I’m not back-end guy so I’m not good at this part.

But you can follow the instruction from the URL which I added above to setup it by yourself ( it’s a good document by the way :D).

Okay. We have finished connecting FCM to azure.

Now move to next step, configure our react native app connecting to FCM server (should be interesting and easiest part).

Lets open the code.

Now you can see, we are using sender ID which is created in above step (FCM settings page).

In this step, you don’t need to care about onRegister event, I will explain you guys later. So, to make the phone to be able to connect to FCM, very simple, just follow these steps:

  1. Create a component
  2. In componentDidMount event, write code to config push notification which is mentioned clearly in the push notification document
  3. Add the sender ID(collect it from FCM server, settings page)

That’s it. You should be able to connect and receive push notification from now.

One notice here: remote push notification is not working in genymotion (not sure why, the reason could be because of I’m using free version of it).

Anyway, what I mean is that lets use real devices to test it.

Now your devices can receive notification, but the thing is we are receiving all notifications, it’s not really good in some cases.

For example: in Facebook, if we post a new feed, we want only users who commented or liked that post will receive notification, not all.

If you did not comment or like that feed, you should not receive any notification related to that post, it’s very annoying if you haven’t touched that post but keep receiving notification from that post.

To restrict them, we will move to next step.

In this step, we will restrict notifications which are send to corresponding users only. The idea is:

  1. From client (android or ios phone), connect to FCM and register a token ID
  2. Send token ID and user information (such as user ID, user role, …) to our server (back-end) for notifying that I have registered to FCM and received this token, please handle and send correct messages to me, don’t send all messages  or I’ll kill you.
  3. Server received those information, base on those information (user id, user role …), handle the messages and send to FCM with token id.
  4. Base on the token id which received from our server, FCM server send the corresponding messages to client.

As I mentioned above, I will not guide you how to do from back-end side, the attached link will help set it up.

But from front-end side, I’ll give you guys a hand :D.

Now, it’s time to use the event onRegister, this event will be fired when app open and return a token id, use this token send to server.

In case of you are using azure, then the endpoint should contain two parameters: RegistrationID and Platform (gcm, apns, wns).

  1. RegistrationID is token id,
  2. Platform: gcm (google cloud messaging). apns (push notification server from apple), wns( window notification server).

Now, let me see if I miss anything.

hmm …. I think it’s pretty enough for you guys to start integrating with GCM now.

If you have any inquiries, just leave a comment or send me message by mail baotoan1905@gmail.com, skype: baotoan1905, facebook: baotoan1905@gmail.com

Chào thân ái và quyết thắng.

Hướng dẫn viết ứng dụng mobile với react native cơ bản (P1)

Chào mọi người, như mọi người đã biết react-native ra đời cũng đã được gần hai năm rồi. Không biết đã ai thử viết app bằng react-native chưa? Mình cũng mò mò tập tành viết sơ sơ (hiện tại cũng đang viết một app, ý tưởng thì hôm nào khác mình chia sẻ nhé). Hôm nay rảnh rỗi, ngồi soạn bài này, hy vọng sẽ giúp cho những người mới bắt đầu tìm hiểu đỡ vất vả hơn một chút, mình cũng muốn đóng góp chút cho cộng đồng react native việt, mong muốn cộng đồng ở việt nam sẽ lớn mạnh để có gì cùng trao đổi cho xôm tụ.

Ok. Đầu tiên để bắt đầu thì mình phải tiến hành setup môi trường làm việc đã. Cho nên việc thứ nhất sẽ là “Cài đặt react-native”. Mọi người cứ theo hướng dẫn tại trang Hướng dẫn cài đặt react-native này nhé. Cứ làm theo hướng dẫn để cài đặt tùy theo môi trường window hay mac nhé. Mình liệt kê sẵn một vài hướng dẫn khác phòng trường hợp cần (cho window) (hồi đó mình cũng mò thấy mẹ mới ra)

  1. Trong window, thư mục android sdk thường nằm trong thư mục sau C:\Users\TOY1HC\AppData\Local\Android\android-sdk
  2. Thư mục JDK thường nằm trong thư mục C:\Program Files\Java\jdk1.7.0_79 (x64)
  3. Có một vài trường hợp sẽ báo lỗi proxy khi bạn update android, do đó bạn phải cấu hình proxy trước khi tiến hành update.

Menu tools/options

android-proxy-setup

 

4. Setup biến môi trường JAVA_HOME, ANDROID_HOME:

– Mở My computer bằng window explorer, right click chọn properties.-> Advanced system settings -> click button Environment Variables…

– Trong phần system variables, chọn new

set-java_home-variable

– Tương tự cho các biến môi trường khác (nếu cần)

5. Cài đặt Genymotion (mình khuyên dùng cái này vì đơn giản, dễ cài đặt), tuy nhiên các bạn có thể sử dụng cái khác tùy vào sở thích cá nhân nhé 🙂

Rồi, nhiêu đây chắc đủ để bắt tay vô cài đặt môi trường rồi nhỉ. Bài tiếp theo mình sẽ hướng dẫn các bạn tạo một project với react native. Hẹn gặp lại các bạn ở bài tiếp theo.

Navigator transitions

A normal Navigator with transitions is push from right.

 <Navigator
     style={styles.container}
     initialRoute={initialRoute}
     configureScene={() => Navigator.SceneConfigs.PushFromRight}
     renderScene={RouteMapper}
 />

We also can change the transitions type by replacing PushFromRight by one of the following types:

PushFromRight
FloatFromRight
FloatFromLeft
FloatFromBottom
FloatFromBottomAndroid
FadeAndroid
HorizontalSwipeJump
HorizontalSwipeJumpFromRight
VerticalUpSwipeJump
VerticalDownSwipeJump