diff --git a/Example/Example.js b/Example/Example.js index a47186c63..c17ccee7d 100644 --- a/Example/Example.js +++ b/Example/Example.js @@ -1,5 +1,5 @@ import React from 'react'; -import { StyleSheet, Text, View } from 'react-native'; +import { Platform, StyleSheet, Text, View } from 'react-native'; import Launch from './components/Launch'; import Register from './components/Register'; import Login from './components/Login'; @@ -59,10 +59,14 @@ const getSceneStyle = () => ({ shadowRadius: 3, }); +// on Android, the URI prefix typically contains a host in addition to scheme +const prefix = Platform.OS === 'android' ? 'mychat://mychat/' : 'mychat://'; + const Example = () => ( #import +#import @implementation AppDelegate @@ -34,4 +35,12 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( return YES; } +// Add this above the `@end`: +- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url + sourceApplication:(NSString *)sourceApplication annotation:(id)annotation +{ + return [RCTLinkingManager application:application openURL:url + sourceApplication:sourceApplication annotation:annotation]; +} + @end diff --git a/Example/ios/Example/Info.plist b/Example/ios/Example/Info.plist index 0bf27a6a6..b418e454a 100644 --- a/Example/ios/Example/Info.plist +++ b/Example/ios/Example/Info.plist @@ -41,7 +41,6 @@ NSLocationWhenInUseUsageDescription NSAppTransportSecurity - NSExceptionDomains @@ -52,5 +51,16 @@ + CFBundleURLTypes + + + CFBundleURLName + org.rnrf + CFBundleURLSchemes + + mychat + + + diff --git a/dist/Router.js b/dist/Router.js index d45b7fadd..c36025951 100644 --- a/dist/Router.js +++ b/dist/Router.js @@ -1,4 +1,4 @@ -Object.defineProperty(exports,"__esModule",{value:true});var _extends=Object.assign||function(target){for(var i=1;i=0)continue;if(!Object.prototype.hasOwnProperty.call(obj,i))continue;target[i]=obj[i];}return target;}function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor)){throw new TypeError("Cannot call a class as a function");}}function _possibleConstructorReturn(self,call){if(!self){throw new ReferenceError("this hasn't been initialised - super() hasn't been called");}return call&&(typeof call==="object"||typeof call==="function")?call:self;}function _inherits(subClass,superClass){if(typeof superClass!=="function"&&superClass!==null){throw new TypeError("Super expression must either be null or a function, not "+typeof superClass);}subClass.prototype=Object.create(superClass&&superClass.prototype,{constructor:{value:subClass,enumerable:false,writable:true,configurable:true}});if(superClass)Object.setPrototypeOf?Object.setPrototypeOf(subClass,superClass):subClass.__proto__=superClass;}var -App=(0,_native.observer)(_class=(_temp2=_class2=function(_React$Component){_inherits(App,_React$Component);function App(){var _ref;var _temp,_this,_ret;_classCallCheck(this,App);for(var _len=arguments.length,args=Array(_len),_key=0;_key<_len;_key++){args[_key]=arguments[_key];}return _ret=(_temp=(_this=_possibleConstructorReturn(this,(_ref=App.__proto__||Object.getPrototypeOf(App)).call.apply(_ref,[this].concat(args))),_this),_this. +App=(0,_native.observer)(_class=function(_React$Component){_inherits(App,_React$Component);function App(){var _ref;var _temp,_this,_ret;_classCallCheck(this,App);for(var _len=arguments.length,args=Array(_len),_key=0;_key<_len;_key++){args[_key]=arguments[_key];}return _ret=(_temp=(_this=_possibleConstructorReturn(this,(_ref=App.__proto__||Object.getPrototypeOf(App)).call.apply(_ref,[this].concat(args))),_this),_this. @@ -16,24 +16,43 @@ App=(0,_native.observer)(_class=(_temp2=_class2=function(_React$Component){_inhe +onBackPress=function(){ +_navigationStore2.default.pop(); +return _navigationStore2.default.currentScene!==_navigationStore2.default.prevScene; +},_this. + + -onBackPress=function(){ -_navigationStore2.default.pop(); -return _navigationStore2.default.currentScene!==_navigationStore2.default.prevScene; -},_temp),_possibleConstructorReturn(_this,_ret);}_createClass(App,[{key:'componentDidMount',value:function componentDidMount(){_reactNative.BackHandler.addEventListener('hardwareBackPress',this.props.backAndroidHandler||this.onBackPress);}},{key:'componentWillUnmount',value:function componentWillUnmount(){_reactNative.BackHandler.removeEventListener('hardwareBackPress',this.props.backAndroidHandler||this.onBackPress);}},{key:'render',value:function render() + + + + + + +_handleOpenURL=function(url){ +var parsedUrl=_this._urlToPathAndParams(url); +if(parsedUrl){var +path=parsedUrl.path,params=parsedUrl.params; +var action=_navigationStore2.default.router.getActionForPathAndParams(path,params); +console.log('HANDLE URL:',url,action,path,params); +if(action){ +_navigationStore2.default.dispatch(action); +} +} +},_temp),_possibleConstructorReturn(_this,_ret);}_createClass(App,[{key:'componentDidMount',value:function componentDidMount(){var _this2=this;_reactNative.BackHandler.addEventListener('hardwareBackPress',this.props.backAndroidHandler||this.onBackPress);_reactNative.Linking.addEventListener('url',function(_ref2){var url=_ref2.url;_this2._handleOpenURL(url);});}},{key:'componentWillUnmount',value:function componentWillUnmount(){_reactNative.BackHandler.removeEventListener('hardwareBackPress',this.props.backAndroidHandler||this.onBackPress);}},{key:'_urlToPathAndParams',value:function _urlToPathAndParams(url){var params={};var delimiter=this.props.uriPrefix||'://';var path=url.split(delimiter)[1];if(!path){path=url;}return{path:path,params:params};}},{key:'render',value:function render() { var AppNavigator=this.props.navigator; return( -_react2.default.createElement(AppNavigator,{navigation:(0,_reactNavigation.addNavigationHelpers)({dispatch:_navigationStore2.default.dispatch,state:_navigationStore2.default.state}),__source:{fileName:_jsxFileName,lineNumber:31}})); +_react2.default.createElement(AppNavigator,{navigation:(0,_reactNavigation.addNavigationHelpers)({dispatch:_navigationStore2.default.dispatch,state:_navigationStore2.default.state}),__source:{fileName:_jsxFileName,lineNumber:50}})); -}}]);return App;}(_react2.default.Component),_class2.propTypes={navigator:_propTypes2.default.func,backAndroidHandler:_propTypes2.default.func},_temp2))||_class; +}}]);return App;}(_react2.default.Component))||_class; -var Router=function Router(_ref2){var createReducer=_ref2.createReducer,sceneStyle=_ref2.sceneStyle,scenes=_ref2.scenes,navigator=_ref2.navigator,getSceneStyle=_ref2.getSceneStyle,children=_ref2.children,state=_ref2.state,dispatch=_ref2.dispatch,_ref2$wrapBy=_ref2.wrapBy,wrapBy=_ref2$wrapBy===undefined?function(props){return props;}:_ref2$wrapBy,props=_objectWithoutProperties(_ref2,['createReducer','sceneStyle','scenes','navigator','getSceneStyle','children','state','dispatch','wrapBy']); +var Router=function Router(_ref3){var createReducer=_ref3.createReducer,uriPrefix=_ref3.uriPrefix,sceneStyle=_ref3.sceneStyle,scenes=_ref3.scenes,navigator=_ref3.navigator,getSceneStyle=_ref3.getSceneStyle,children=_ref3.children,state=_ref3.state,dispatch=_ref3.dispatch,_ref3$wrapBy=_ref3.wrapBy,wrapBy=_ref3$wrapBy===undefined?function(props){return props;}:_ref3$wrapBy,props=_objectWithoutProperties(_ref3,['createReducer','uriPrefix','sceneStyle','scenes','navigator','getSceneStyle','children','state','dispatch','wrapBy']); var data=_extends({},props); if(getSceneStyle){ data.cardStyle=getSceneStyle(props); @@ -47,9 +66,9 @@ if(dispatch&&state){ _navigationStore2.default.setState(state); _navigationStore2.default.dispatch=dispatch; -return _react2.default.createElement(AppNavigator,{navigation:(0,_reactNavigation.addNavigationHelpers)({dispatch:dispatch,state:state}),__source:{fileName:_jsxFileName,lineNumber:50}}); +return _react2.default.createElement(AppNavigator,{navigation:(0,_reactNavigation.addNavigationHelpers)({dispatch:dispatch,state:state}),uriPrefix:uriPrefix,__source:{fileName:_jsxFileName,lineNumber:69}}); } -return _react2.default.createElement(App,_extends({},props,{navigator:AppNavigator,__source:{fileName:_jsxFileName,lineNumber:52}})); +return _react2.default.createElement(App,_extends({},props,{navigator:AppNavigator,uriPrefix:uriPrefix,__source:{fileName:_jsxFileName,lineNumber:71}})); }; Router.propTypes={ createReducer:_propTypes2.default.func, @@ -59,8 +78,9 @@ scenes:_propTypes2.default.func, navigator:_propTypes2.default.func, wrapBy:_propTypes2.default.func, getSceneStyle:_propTypes2.default.func, -sceneStyle:_reactNative.ViewPropTypes.style, -children:_propTypes2.default.element};exports.default= +sceneStyle:ViewPropTypes.style, +children:_propTypes2.default.element, +uriPrefix:_propTypes2.default.string};exports.default= Router; \ No newline at end of file diff --git a/dist/navigationStore.js b/dist/navigationStore.js index cf90c17b1..cf0cbc812 100644 --- a/dist/navigationStore.js +++ b/dist/navigationStore.js @@ -441,7 +441,7 @@ _child){ var key=_child.key||'key'+counter++; var init=key===children[0].key; (0,_Util.assert)(reservedKeys.indexOf(key)===-1,'Scene name cannot be reserved word: '+_child.key);var _child$props= -_child.props,component=_child$props.component,_child$props$type=_child$props.type,type=_child$props$type===undefined?tabs||drawer?'jump':'push':_child$props$type,onEnter=_child$props.onEnter,onExit=_child$props.onExit,on=_child$props.on,failure=_child$props.failure,success=_child$props.success,wrap=_child$props.wrap,props=_objectWithoutProperties(_child$props,['component','type','onEnter','onExit','on','failure','success','wrap']); +_child.props,component=_child$props.component,_child$props$type=_child$props.type,type=_child$props$type===undefined?tabs||drawer?'jump':'push':_child$props$type,onEnter=_child$props.onEnter,path=_child$props.path,onExit=_child$props.onExit,on=_child$props.on,failure=_child$props.failure,success=_child$props.success,wrap=_child$props.wrap,props=_objectWithoutProperties(_child$props,['component','type','onEnter','path','onExit','on','failure','success','wrap']); if(!_this2.states[key]){ _this2.states[key]={}; } @@ -461,6 +461,7 @@ failure:function(args){console.log('Transition to state='+failure);_this2[failur } var screen={ +path:path, screen:createWrapper(component,wrapBy,_this2)||_this2.processScene(_child,commonProps,clones)||lightbox&&_reactNative.View, navigationOptions:createNavigationOptions(_extends({},commonProps,getProperties(component),_child.props,{init:init,component:component}))}; @@ -472,6 +473,7 @@ wrapNavBar=false; } if(component&&wrapNavBar){ res[key]={ +path:path, screen:_this2.processScene({key:key,props:{children:{key:'_'+key,props:_extends({},_child.props,{wrap:false})}}},commonProps,clones,wrapBy), navigationOptions:createNavigationOptions(_extends({},commonProps,_child.props))}; @@ -508,8 +510,8 @@ if(lightbox){ return(0,_LightboxNavigator2.default)(res,_extends({mode:mode,initialRouteParams:initialRouteParams,initialRouteName:initialRouteName},commonProps,{navigationOptions:createNavigationOptions(commonProps)})); }else if(tabs){ if(!tabBarComponent){ -tabBarComponent=tabBarPosition==='top'?function(props){return _react2.default.createElement(_reactNavigation.TabBarTop,_extends({},props,commonProps,{__source:{fileName:_jsxFileName,lineNumber:511}}));}: -function(props){return _react2.default.createElement(_reactNavigation.TabBarBottom,_extends({},props,commonProps,{__source:{fileName:_jsxFileName,lineNumber:512}}));}; +tabBarComponent=tabBarPosition==='top'?function(props){return _react2.default.createElement(_reactNavigation.TabBarTop,_extends({},props,commonProps,{__source:{fileName:_jsxFileName,lineNumber:513}}));}: +function(props){return _react2.default.createElement(_reactNavigation.TabBarBottom,_extends({},props,commonProps,{__source:{fileName:_jsxFileName,lineNumber:514}}));}; } if(!tabBarPosition){ tabBarPosition=_reactNative.Platform.OS==='android'?'top':'bottom'; diff --git a/src/Router.js b/src/Router.js index 2de6b1f6d..23a9e7251 100644 --- a/src/Router.js +++ b/src/Router.js @@ -1,30 +1,49 @@ import React from 'react'; import { observer } from 'mobx-react/native'; -import { ViewPropTypes, BackHandler } from 'react-native'; +import { BackHandler, Linking, ViewPropTypes } from 'react-native'; import navigationStore from './navigationStore'; import PropTypes from 'prop-types'; import { addNavigationHelpers } from 'react-navigation'; @observer class App extends React.Component { - static propTypes = { - navigator: PropTypes.func, - backAndroidHandler: PropTypes.func, - }; - componentDidMount() { BackHandler.addEventListener('hardwareBackPress', this.props.backAndroidHandler || this.onBackPress); + Linking.addEventListener('url', ({ url }: { url: string }) => { + this._handleOpenURL(url); + }); } - componentWillUnmount() { BackHandler.removeEventListener('hardwareBackPress', this.props.backAndroidHandler || this.onBackPress); } - onBackPress = () => { navigationStore.pop(); return navigationStore.currentScene !== navigationStore.prevScene; }; + _urlToPathAndParams(url: string) { + const params = {}; + const delimiter = this.props.uriPrefix || '://'; + let path = url.split(delimiter)[1]; + if (!path) { + path = url; + } + return { + path, + params, + }; + } + _handleOpenURL = (url: string) => { + const parsedUrl = this._urlToPathAndParams(url); + if (parsedUrl) { + const { path, params } = parsedUrl; + const action = navigationStore.router.getActionForPathAndParams(path, params); + console.log('HANDLE URL:', url, action, path, params); + if (action) { + navigationStore.dispatch(action); + } + } + }; render() { const AppNavigator = this.props.navigator; return ( @@ -33,7 +52,13 @@ class App extends React.Component { } } -const Router = ({ createReducer, sceneStyle, scenes, navigator, getSceneStyle, children, state, dispatch, wrapBy = props => props, ...props }) => { +App.propTypes = { + navigator: PropTypes.func, + backAndroidHandler: PropTypes.func, + uriPrefix: PropTypes.string, +}; + +const Router = ({ createReducer, uriPrefix, sceneStyle, scenes, navigator, getSceneStyle, children, state, dispatch, wrapBy = props => props, ...props }) => { const data = { ...props }; if (getSceneStyle) { data.cardStyle = getSceneStyle(props); @@ -47,9 +72,9 @@ const Router = ({ createReducer, sceneStyle, scenes, navigator, getSceneStyle, c // set external state and dispatch navigationStore.setState(state); navigationStore.dispatch = dispatch; - return ; + return ; } - return ; + return ; }; Router.propTypes = { createReducer: PropTypes.func, @@ -61,6 +86,7 @@ Router.propTypes = { getSceneStyle: PropTypes.func, sceneStyle: ViewPropTypes.style, children: PropTypes.element, + uriPrefix: PropTypes.string, }; export default Router; diff --git a/src/navigationStore.js b/src/navigationStore.js index f87e633d7..965d38f04 100644 --- a/src/navigationStore.js +++ b/src/navigationStore.js @@ -441,7 +441,7 @@ class NavigationStore { const key = child.key || `key${counter++}`; const init = key === children[0].key; assert(reservedKeys.indexOf(key) === -1, `Scene name cannot be reserved word: ${child.key}`); - const { component, type = tabs || drawer ? 'jump' : 'push', onEnter, onExit, on, failure, success, wrap, ...props } = child.props; + const { component, type = tabs || drawer ? 'jump' : 'push', onEnter, path, onExit, on, failure, success, wrap, ...props } = child.props; if (!this.states[key]) { this.states[key] = {}; } @@ -459,8 +459,9 @@ class NavigationStore { this.states[key].failure = failure instanceof Function ? failure : args => { console.log(`Transition to state=${failure}`); this[failure](args); }; } - // console.log(`KEY ${key} DRAWER ${drawer} TABS ${tabs} WRAP ${wrap}`, JSON.stringify(commonProps)); + // console.log(`KEY ${key} PATH ${path} DRAWER ${drawer} TABS ${tabs} WRAP ${wrap}`, JSON.stringify(commonProps)); const screen = { + path, screen: createWrapper(component, wrapBy, this) || this.processScene(child, commonProps, clones) || (lightbox && View), navigationOptions: createNavigationOptions({ ...commonProps, ...getProperties(component), ...child.props, init, component }), }; @@ -472,6 +473,7 @@ class NavigationStore { } if (component && wrapNavBar) { res[key] = { + path, screen: this.processScene({ key, props: { children: { key: `_${key}`, props: { ...child.props, wrap: false } } } }, commonProps, clones, wrapBy), navigationOptions: createNavigationOptions({ ...commonProps, ...child.props }), };