From 477311f84a3b8cadb8b039710df0b14e79f01be1 Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Mon, 18 Mar 2019 15:52:38 -0300 Subject: [PATCH] [FIX] Deep linking between multiple logged servers (#730) --- app/index.js | 74 ++++++++++++++++------ app/push/index.js | 14 ++-- app/push/push.android.js | 7 +- app/push/push.ios.js | 1 + app/sagas/deepLinking.js | 38 +++++------ app/sagas/selectServer.js | 1 + app/views/AuthLoadingView.js | 31 ++------- ios/RocketChatRN.xcodeproj/project.pbxproj | 4 +- 8 files changed, 86 insertions(+), 84 deletions(-) diff --git a/app/index.js b/app/index.js index eea4ea1b4..2ce6b541b 100644 --- a/app/index.js +++ b/app/index.js @@ -6,6 +6,7 @@ import { Provider } from 'react-redux'; import { useScreens } from 'react-native-screens'; // eslint-disable-line import/no-unresolved import { Linking } from 'react-native'; +import { appInit } from './actions'; import { deepLinkingOpen } from './actions/deepLinking'; import OnboardingView from './views/OnboardingView'; import NewServerView from './views/NewServerView'; @@ -39,30 +40,25 @@ import OAuthView from './views/OAuthView'; import SetUsernameView from './views/SetUsernameView'; import { HEADER_BACKGROUND, HEADER_TITLE, HEADER_BACK } from './constants/colors'; import parseQuery from './lib/methods/helpers/parseQuery'; -import { initializePushNotifications } from './push'; +import { initializePushNotifications, onNotification } from './push'; import store from './lib/createStore'; useScreens(); -initializePushNotifications(); -const handleOpenURL = ({ url }) => { +const parseDeepLinking = (url) => { if (url) { url = url.replace(/rocketchat:\/\/|https:\/\/go.rocket.chat\//, ''); const regex = /^(room|auth)\?/; if (url.match(regex)) { - url = url.replace(regex, ''); - const params = parseQuery(url); - store.dispatch(deepLinkingOpen(params)); + url = url.replace(regex, '').trim(); + if (url) { + return parseQuery(url); + } } } + return null; }; -Linking - .getInitialURL() - .then(url => handleOpenURL({ url })) - .catch(e => console.warn(e)); -Linking.addEventListener('url', handleOpenURL); - const defaultHeader = { headerStyle: { backgroundColor: HEADER_BACKGROUND @@ -184,12 +180,48 @@ const App = createAppContainer(createSwitchNavigator( } )); -export default () => ( - - { - Navigation.setTopLevelNavigator(navigatorRef); - }} - /> - -); +export default class Root extends React.Component { + constructor(props) { + super(props); + this.init(); + } + + componentDidMount() { + this.listenerTimeout = setTimeout(() => { + Linking.addEventListener('url', ({ url }) => { + const parsedDeepLinkingURL = parseDeepLinking(url); + if (parsedDeepLinkingURL) { + store.dispatch(deepLinkingOpen(parsedDeepLinkingURL)); + } + }); + }, 5000); + } + + componentWillUnmount() { + clearTimeout(this.listenerTimeout); + } + + init = async() => { + const [notification, deepLinking] = await Promise.all([initializePushNotifications(), Linking.getInitialURL()]); + const parsedDeepLinkingURL = parseDeepLinking(deepLinking); + if (notification) { + onNotification(notification); + } else if (parsedDeepLinkingURL) { + store.dispatch(deepLinkingOpen(parsedDeepLinkingURL)); + } else { + store.dispatch(appInit()); + } + } + + render() { + return ( + + { + Navigation.setTopLevelNavigator(navigatorRef); + }} + /> + + ); + } +} diff --git a/app/push/index.js b/app/push/index.js index c67f3d2de..d78af4c7b 100644 --- a/app/push/index.js +++ b/app/push/index.js @@ -4,7 +4,7 @@ import PushNotification from './push'; import store from '../lib/createStore'; import { deepLinkingOpen } from '../actions/deepLinking'; -const onNotification = (notification) => { +export const onNotification = (notification) => { if (notification) { const data = notification.getData(); if (data) { @@ -31,13 +31,11 @@ const onNotification = (notification) => { } }; -const getDeviceToken = () => PushNotification.getDeviceToken(); -const setBadgeCount = count => PushNotification.setBadgeCount(count); -const initializePushNotifications = () => { - PushNotification.configure({ +export const getDeviceToken = () => PushNotification.getDeviceToken(); +export const setBadgeCount = count => PushNotification.setBadgeCount(count); +export const initializePushNotifications = () => { + setBadgeCount(); + return PushNotification.configure({ onNotification }); - setBadgeCount(); }; - -export { initializePushNotifications, getDeviceToken, setBadgeCount }; diff --git a/app/push/push.android.js b/app/push/push.android.js index d3351ac67..d83184594 100644 --- a/app/push/push.android.js +++ b/app/push/push.android.js @@ -25,12 +25,7 @@ class PushNotification { this.onRegister = params.onRegister; this.onNotification = params.onNotification; NotificationsAndroid.refreshToken(); - - PendingNotifications.getInitialNotification() - .then((notification) => { - this.onNotification(notification); - }) - .catch(e => console.warn(e)); + return PendingNotifications.getInitialNotification(); } } diff --git a/app/push/push.ios.js b/app/push/push.ios.js index 0c1415715..56cfa3e9f 100644 --- a/app/push/push.ios.js +++ b/app/push/push.ios.js @@ -30,6 +30,7 @@ class PushNotification { this.onNotification = params.onNotification; NotificationsIOS.consumeBackgroundQueue(); + return Promise.resolve(); } } export default new PushNotification(); diff --git a/app/sagas/deepLinking.js b/app/sagas/deepLinking.js index 837e0e342..0e0e17bed 100644 --- a/app/sagas/deepLinking.js +++ b/app/sagas/deepLinking.js @@ -1,7 +1,7 @@ import { AsyncStorage } from 'react-native'; import { delay } from 'redux-saga'; import { - takeLatest, take, select, put, all, race + takeLatest, take, select, put, all } from 'redux-saga/effects'; import Navigation from '../lib/Navigation'; @@ -10,28 +10,25 @@ import { selectServerRequest } from '../actions/server'; import database from '../lib/realm'; import RocketChat from '../lib/rocketchat'; import EventEmitter from '../utils/events'; +import { appStart } from '../actions'; const roomTypes = { channel: 'c', direct: 'd', group: 'p' }; const navigate = function* navigate({ params }) { + yield put(appStart('inside')); if (params.rid) { const canOpenRoom = yield RocketChat.canOpenRoom(params); if (canOpenRoom) { const [type, name] = params.path.split('/'); + yield Navigation.navigate('RoomsListView'); Navigation.navigate('RoomView', { rid: params.rid, name, t: roomTypes[type] }); } } }; const handleOpen = function* handleOpen({ params }) { - const isReady = yield select(state => state.app.ready); - - if (!isReady) { - yield take(types.APP.READY); - } - if (!params.host) { return; } @@ -54,29 +51,28 @@ const handleOpen = function* handleOpen({ params }) { // if deep link is from same server if (server === host) { if (user) { - yield race({ - typing: take(types.SERVER.SELECT_SUCCESS), - timeout: delay(3000) - }); + const connected = yield select(state => state.server.connected); + if (!connected) { + yield put(selectServerRequest(host)); + yield take(types.SERVER.SELECT_SUCCESS); + } yield navigate({ params }); + } else { + yield put(appStart('outside')); } } else { - // if deep link is from a different server - const result = yield RocketChat.testServer(server); - if (!result.success) { - return; - } - // search if deep link's server already exists const servers = yield database.databases.serversDB.objects('servers').filtered('id = $0', host); // TODO: need better test if (servers.length && user) { yield put(selectServerRequest(host)); - yield race({ - typing: take(types.SERVER.SELECT_SUCCESS), - timeout: delay(3000) - }); + yield take(types.SERVER.SELECT_SUCCESS); yield navigate({ params }); } else { + // if deep link is from a different server + const result = yield RocketChat.testServer(server); + if (!result.success) { + return; + } Navigation.navigate('OnboardingView', { previousServer: server }); yield delay(1000); EventEmitter.emit('NewServer', { server: host }); diff --git a/app/sagas/selectServer.js b/app/sagas/selectServer.js index c4b1b59dd..389490ef3 100644 --- a/app/sagas/selectServer.js +++ b/app/sagas/selectServer.js @@ -24,6 +24,7 @@ const handleSelectServer = function* handleSelectServer({ server }) { yield put(actions.appStart('inside')); } else { RocketChat.connect({ server }); + yield put(actions.appStart('outside')); } const settings = database.objects('settings'); diff --git a/app/views/AuthLoadingView.js b/app/views/AuthLoadingView.js index 01e997f9b..a2ef3e509 100644 --- a/app/views/AuthLoadingView.js +++ b/app/views/AuthLoadingView.js @@ -1,11 +1,8 @@ import React from 'react'; import { StyleSheet, Image } from 'react-native'; -import { connect } from 'react-redux'; -import PropTypes from 'prop-types'; import StatusBar from '../containers/StatusBar'; import { isAndroid } from '../utils/deviceInfo'; -import { appInit as appInitAction } from '../actions'; const styles = StyleSheet.create({ image: { @@ -14,25 +11,9 @@ const styles = StyleSheet.create({ } }); -@connect(null, dispatch => ({ - appInit: () => dispatch(appInitAction()) -})) -export default class Loading extends React.PureComponent { - static propTypes = { - appInit: PropTypes.func - } - - constructor(props) { - super(props); - props.appInit(); - } - - render() { - return ( - - - {isAndroid ? : null} - - ); - } -} +export default React.memo(() => ( + + + {isAndroid ? : null} + +)); diff --git a/ios/RocketChatRN.xcodeproj/project.pbxproj b/ios/RocketChatRN.xcodeproj/project.pbxproj index 1db0c5198..6d365baea 100644 --- a/ios/RocketChatRN.xcodeproj/project.pbxproj +++ b/ios/RocketChatRN.xcodeproj/project.pbxproj @@ -1606,7 +1606,6 @@ ); PRODUCT_BUNDLE_IDENTIFIER = chat.rocket.reactnative; PRODUCT_NAME = RocketChatRN; - PROVISIONING_PROFILE = "573947ae-c3f2-425e-aa82-0181297becf9"; PROVISIONING_PROFILE_SPECIFIER = "match Development chat.rocket.reactnative"; VERSIONING_SYSTEM = "apple-generic"; }; @@ -1653,8 +1652,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = chat.rocket.reactnative; PRODUCT_NAME = RocketChatRN; - PROVISIONING_PROFILE = "c630cec9-82b4-44ed-a9c1-922232f9dd1f"; - PROVISIONING_PROFILE_SPECIFIER = "match AppStore chat.rocket.reactnative"; + PROVISIONING_PROFILE_SPECIFIER = "match AppStore chat.rocket.reactnative 1552925104"; VERSIONING_SYSTEM = "apple-generic"; }; name = Release;