[FIX] Deep linking between multiple logged servers (#730)
This commit is contained in:
parent
b8eb75748b
commit
477311f84a
74
app/index.js
74
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 { useScreens } from 'react-native-screens'; // eslint-disable-line import/no-unresolved
|
||||||
import { Linking } from 'react-native';
|
import { Linking } from 'react-native';
|
||||||
|
|
||||||
|
import { appInit } from './actions';
|
||||||
import { deepLinkingOpen } from './actions/deepLinking';
|
import { deepLinkingOpen } from './actions/deepLinking';
|
||||||
import OnboardingView from './views/OnboardingView';
|
import OnboardingView from './views/OnboardingView';
|
||||||
import NewServerView from './views/NewServerView';
|
import NewServerView from './views/NewServerView';
|
||||||
|
@ -39,30 +40,25 @@ import OAuthView from './views/OAuthView';
|
||||||
import SetUsernameView from './views/SetUsernameView';
|
import SetUsernameView from './views/SetUsernameView';
|
||||||
import { HEADER_BACKGROUND, HEADER_TITLE, HEADER_BACK } from './constants/colors';
|
import { HEADER_BACKGROUND, HEADER_TITLE, HEADER_BACK } from './constants/colors';
|
||||||
import parseQuery from './lib/methods/helpers/parseQuery';
|
import parseQuery from './lib/methods/helpers/parseQuery';
|
||||||
import { initializePushNotifications } from './push';
|
import { initializePushNotifications, onNotification } from './push';
|
||||||
import store from './lib/createStore';
|
import store from './lib/createStore';
|
||||||
|
|
||||||
useScreens();
|
useScreens();
|
||||||
initializePushNotifications();
|
|
||||||
|
|
||||||
const handleOpenURL = ({ url }) => {
|
const parseDeepLinking = (url) => {
|
||||||
if (url) {
|
if (url) {
|
||||||
url = url.replace(/rocketchat:\/\/|https:\/\/go.rocket.chat\//, '');
|
url = url.replace(/rocketchat:\/\/|https:\/\/go.rocket.chat\//, '');
|
||||||
const regex = /^(room|auth)\?/;
|
const regex = /^(room|auth)\?/;
|
||||||
if (url.match(regex)) {
|
if (url.match(regex)) {
|
||||||
url = url.replace(regex, '');
|
url = url.replace(regex, '').trim();
|
||||||
const params = parseQuery(url);
|
if (url) {
|
||||||
store.dispatch(deepLinkingOpen(params));
|
return parseQuery(url);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
Linking
|
|
||||||
.getInitialURL()
|
|
||||||
.then(url => handleOpenURL({ url }))
|
|
||||||
.catch(e => console.warn(e));
|
|
||||||
Linking.addEventListener('url', handleOpenURL);
|
|
||||||
|
|
||||||
const defaultHeader = {
|
const defaultHeader = {
|
||||||
headerStyle: {
|
headerStyle: {
|
||||||
backgroundColor: HEADER_BACKGROUND
|
backgroundColor: HEADER_BACKGROUND
|
||||||
|
@ -184,12 +180,48 @@ const App = createAppContainer(createSwitchNavigator(
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
|
|
||||||
export default () => (
|
export default class Root extends React.Component {
|
||||||
<Provider store={store}>
|
constructor(props) {
|
||||||
<App
|
super(props);
|
||||||
ref={(navigatorRef) => {
|
this.init();
|
||||||
Navigation.setTopLevelNavigator(navigatorRef);
|
}
|
||||||
}}
|
|
||||||
/>
|
componentDidMount() {
|
||||||
</Provider>
|
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 (
|
||||||
|
<Provider store={store}>
|
||||||
|
<App
|
||||||
|
ref={(navigatorRef) => {
|
||||||
|
Navigation.setTopLevelNavigator(navigatorRef);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import PushNotification from './push';
|
||||||
import store from '../lib/createStore';
|
import store from '../lib/createStore';
|
||||||
import { deepLinkingOpen } from '../actions/deepLinking';
|
import { deepLinkingOpen } from '../actions/deepLinking';
|
||||||
|
|
||||||
const onNotification = (notification) => {
|
export const onNotification = (notification) => {
|
||||||
if (notification) {
|
if (notification) {
|
||||||
const data = notification.getData();
|
const data = notification.getData();
|
||||||
if (data) {
|
if (data) {
|
||||||
|
@ -31,13 +31,11 @@ const onNotification = (notification) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getDeviceToken = () => PushNotification.getDeviceToken();
|
export const getDeviceToken = () => PushNotification.getDeviceToken();
|
||||||
const setBadgeCount = count => PushNotification.setBadgeCount(count);
|
export const setBadgeCount = count => PushNotification.setBadgeCount(count);
|
||||||
const initializePushNotifications = () => {
|
export const initializePushNotifications = () => {
|
||||||
PushNotification.configure({
|
setBadgeCount();
|
||||||
|
return PushNotification.configure({
|
||||||
onNotification
|
onNotification
|
||||||
});
|
});
|
||||||
setBadgeCount();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export { initializePushNotifications, getDeviceToken, setBadgeCount };
|
|
||||||
|
|
|
@ -25,12 +25,7 @@ class PushNotification {
|
||||||
this.onRegister = params.onRegister;
|
this.onRegister = params.onRegister;
|
||||||
this.onNotification = params.onNotification;
|
this.onNotification = params.onNotification;
|
||||||
NotificationsAndroid.refreshToken();
|
NotificationsAndroid.refreshToken();
|
||||||
|
return PendingNotifications.getInitialNotification();
|
||||||
PendingNotifications.getInitialNotification()
|
|
||||||
.then((notification) => {
|
|
||||||
this.onNotification(notification);
|
|
||||||
})
|
|
||||||
.catch(e => console.warn(e));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ class PushNotification {
|
||||||
this.onNotification = params.onNotification;
|
this.onNotification = params.onNotification;
|
||||||
|
|
||||||
NotificationsIOS.consumeBackgroundQueue();
|
NotificationsIOS.consumeBackgroundQueue();
|
||||||
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export default new PushNotification();
|
export default new PushNotification();
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { AsyncStorage } from 'react-native';
|
import { AsyncStorage } from 'react-native';
|
||||||
import { delay } from 'redux-saga';
|
import { delay } from 'redux-saga';
|
||||||
import {
|
import {
|
||||||
takeLatest, take, select, put, all, race
|
takeLatest, take, select, put, all
|
||||||
} from 'redux-saga/effects';
|
} from 'redux-saga/effects';
|
||||||
|
|
||||||
import Navigation from '../lib/Navigation';
|
import Navigation from '../lib/Navigation';
|
||||||
|
@ -10,28 +10,25 @@ import { selectServerRequest } from '../actions/server';
|
||||||
import database from '../lib/realm';
|
import database from '../lib/realm';
|
||||||
import RocketChat from '../lib/rocketchat';
|
import RocketChat from '../lib/rocketchat';
|
||||||
import EventEmitter from '../utils/events';
|
import EventEmitter from '../utils/events';
|
||||||
|
import { appStart } from '../actions';
|
||||||
|
|
||||||
const roomTypes = {
|
const roomTypes = {
|
||||||
channel: 'c', direct: 'd', group: 'p'
|
channel: 'c', direct: 'd', group: 'p'
|
||||||
};
|
};
|
||||||
|
|
||||||
const navigate = function* navigate({ params }) {
|
const navigate = function* navigate({ params }) {
|
||||||
|
yield put(appStart('inside'));
|
||||||
if (params.rid) {
|
if (params.rid) {
|
||||||
const canOpenRoom = yield RocketChat.canOpenRoom(params);
|
const canOpenRoom = yield RocketChat.canOpenRoom(params);
|
||||||
if (canOpenRoom) {
|
if (canOpenRoom) {
|
||||||
const [type, name] = params.path.split('/');
|
const [type, name] = params.path.split('/');
|
||||||
|
yield Navigation.navigate('RoomsListView');
|
||||||
Navigation.navigate('RoomView', { rid: params.rid, name, t: roomTypes[type] });
|
Navigation.navigate('RoomView', { rid: params.rid, name, t: roomTypes[type] });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOpen = function* handleOpen({ params }) {
|
const handleOpen = function* handleOpen({ params }) {
|
||||||
const isReady = yield select(state => state.app.ready);
|
|
||||||
|
|
||||||
if (!isReady) {
|
|
||||||
yield take(types.APP.READY);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!params.host) {
|
if (!params.host) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -54,29 +51,28 @@ const handleOpen = function* handleOpen({ params }) {
|
||||||
// if deep link is from same server
|
// if deep link is from same server
|
||||||
if (server === host) {
|
if (server === host) {
|
||||||
if (user) {
|
if (user) {
|
||||||
yield race({
|
const connected = yield select(state => state.server.connected);
|
||||||
typing: take(types.SERVER.SELECT_SUCCESS),
|
if (!connected) {
|
||||||
timeout: delay(3000)
|
yield put(selectServerRequest(host));
|
||||||
});
|
yield take(types.SERVER.SELECT_SUCCESS);
|
||||||
|
}
|
||||||
yield navigate({ params });
|
yield navigate({ params });
|
||||||
|
} else {
|
||||||
|
yield put(appStart('outside'));
|
||||||
}
|
}
|
||||||
} else {
|
} 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
|
// search if deep link's server already exists
|
||||||
const servers = yield database.databases.serversDB.objects('servers').filtered('id = $0', host); // TODO: need better test
|
const servers = yield database.databases.serversDB.objects('servers').filtered('id = $0', host); // TODO: need better test
|
||||||
if (servers.length && user) {
|
if (servers.length && user) {
|
||||||
yield put(selectServerRequest(host));
|
yield put(selectServerRequest(host));
|
||||||
yield race({
|
yield take(types.SERVER.SELECT_SUCCESS);
|
||||||
typing: take(types.SERVER.SELECT_SUCCESS),
|
|
||||||
timeout: delay(3000)
|
|
||||||
});
|
|
||||||
yield navigate({ params });
|
yield navigate({ params });
|
||||||
} else {
|
} else {
|
||||||
|
// if deep link is from a different server
|
||||||
|
const result = yield RocketChat.testServer(server);
|
||||||
|
if (!result.success) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
Navigation.navigate('OnboardingView', { previousServer: server });
|
Navigation.navigate('OnboardingView', { previousServer: server });
|
||||||
yield delay(1000);
|
yield delay(1000);
|
||||||
EventEmitter.emit('NewServer', { server: host });
|
EventEmitter.emit('NewServer', { server: host });
|
||||||
|
|
|
@ -24,6 +24,7 @@ const handleSelectServer = function* handleSelectServer({ server }) {
|
||||||
yield put(actions.appStart('inside'));
|
yield put(actions.appStart('inside'));
|
||||||
} else {
|
} else {
|
||||||
RocketChat.connect({ server });
|
RocketChat.connect({ server });
|
||||||
|
yield put(actions.appStart('outside'));
|
||||||
}
|
}
|
||||||
|
|
||||||
const settings = database.objects('settings');
|
const settings = database.objects('settings');
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { StyleSheet, Image } from 'react-native';
|
import { StyleSheet, Image } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import StatusBar from '../containers/StatusBar';
|
import StatusBar from '../containers/StatusBar';
|
||||||
import { isAndroid } from '../utils/deviceInfo';
|
import { isAndroid } from '../utils/deviceInfo';
|
||||||
import { appInit as appInitAction } from '../actions';
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
image: {
|
image: {
|
||||||
|
@ -14,25 +11,9 @@ const styles = StyleSheet.create({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@connect(null, dispatch => ({
|
export default React.memo(() => (
|
||||||
appInit: () => dispatch(appInitAction())
|
<React.Fragment>
|
||||||
}))
|
<StatusBar />
|
||||||
export default class Loading extends React.PureComponent {
|
{isAndroid ? <Image source={{ uri: 'launch_screen' }} style={styles.image} /> : null}
|
||||||
static propTypes = {
|
</React.Fragment>
|
||||||
appInit: PropTypes.func
|
));
|
||||||
}
|
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
props.appInit();
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
<StatusBar />
|
|
||||||
{isAndroid ? <Image source={{ uri: 'launch_screen' }} style={styles.image} /> : null}
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1606,7 +1606,6 @@
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = chat.rocket.reactnative;
|
PRODUCT_BUNDLE_IDENTIFIER = chat.rocket.reactnative;
|
||||||
PRODUCT_NAME = RocketChatRN;
|
PRODUCT_NAME = RocketChatRN;
|
||||||
PROVISIONING_PROFILE = "573947ae-c3f2-425e-aa82-0181297becf9";
|
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "match Development chat.rocket.reactnative";
|
PROVISIONING_PROFILE_SPECIFIER = "match Development chat.rocket.reactnative";
|
||||||
VERSIONING_SYSTEM = "apple-generic";
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
};
|
};
|
||||||
|
@ -1653,8 +1652,7 @@
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = chat.rocket.reactnative;
|
PRODUCT_BUNDLE_IDENTIFIER = chat.rocket.reactnative;
|
||||||
PRODUCT_NAME = RocketChatRN;
|
PRODUCT_NAME = RocketChatRN;
|
||||||
PROVISIONING_PROFILE = "c630cec9-82b4-44ed-a9c1-922232f9dd1f";
|
PROVISIONING_PROFILE_SPECIFIER = "match AppStore chat.rocket.reactnative 1552925104";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "match AppStore chat.rocket.reactnative";
|
|
||||||
VERSIONING_SYSTEM = "apple-generic";
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
|
|
Loading…
Reference in New Issue