diff --git a/App/App.js b/App/App.js
index 017eb645f..152f7b8fb 100644
--- a/App/App.js
+++ b/App/App.js
@@ -2,7 +2,7 @@ import React, { Component } from 'react'
import { Provider } from 'react-redux'
import { PersistGate } from 'redux-persist/lib/integration/react'
import createStore from 'App/Stores'
-import ExampleScreen from './Containers/Example/ExampleScreen'
+import RootScreen from './Containers/Root/RootScreen'
const { store, persistor } = createStore()
@@ -21,7 +21,7 @@ export default class App extends Component {
* @see https://github.com/rt2zz/redux-persist/blob/master/docs/PersistGate.md
*/}
-
+
)
diff --git a/App/Containers/Root/RootScreen.js b/App/Containers/Root/RootScreen.js
new file mode 100644
index 000000000..83264b80f
--- /dev/null
+++ b/App/Containers/Root/RootScreen.js
@@ -0,0 +1,60 @@
+import React, { Component } from 'react'
+import { createStackNavigator } from 'react-navigation'
+import NavigationService from 'App/Services/NavigationService'
+import { View } from 'react-native'
+import styles from './RootScreenStyle'
+import ExampleScreen from 'App/Containers/Example/ExampleScreen'
+import SplashScreen from 'App/Containers/SplashScreen/SplashScreen'
+import { connect } from 'react-redux'
+import StartupActions from 'App/Stores/Startup/Actions'
+
+/**
+ * The root screen contains the application's navigation.
+ *
+ * @see https://reactnavigation.org/docs/en/hello-react-navigation.html#creating-a-stack-navigator
+ */
+const AppNav = createStackNavigator(
+ {
+ // Create the application routes here (the key is the route name, the value is the target screen)
+ // See https://reactnavigation.org/docs/en/stack-navigator.html#routeconfigs
+ SplashScreen: SplashScreen,
+ MainScreen: ExampleScreen,
+ },
+ {
+ // By default the application will show the splash screen
+ initialRouteName: 'SplashScreen',
+ // See https://reactnavigation.org/docs/en/stack-navigator.html#stacknavigatorconfig
+ headerMode: 'none',
+ }
+)
+
+class RootScreen extends Component {
+ componentDidMount() {
+ // Run the startup saga when the application is starting
+ this.props.startup()
+ }
+
+ render() {
+ return (
+
+ {
+ NavigationService.setTopLevelNavigator(navigatorRef)
+ }}
+ />
+
+ )
+ }
+}
+
+const mapStateToProps = (state) => ({})
+
+const mapDispatchToProps = (dispatch) => ({
+ startup: () => dispatch(StartupActions.startup()),
+})
+
+export default connect(
+ mapStateToProps,
+ mapDispatchToProps
+)(RootScreen)
diff --git a/App/Containers/Root/RootScreenStyle.js b/App/Containers/Root/RootScreenStyle.js
new file mode 100644
index 000000000..90faa255c
--- /dev/null
+++ b/App/Containers/Root/RootScreenStyle.js
@@ -0,0 +1,8 @@
+import { StyleSheet } from 'react-native'
+import ApplicationStyles from 'App/Theme/ApplicationStyles'
+
+export default StyleSheet.create({
+ container: {
+ ...ApplicationStyles.screen.container,
+ },
+})
diff --git a/App/Containers/SplashScreen/SplashScreen.js b/App/Containers/SplashScreen/SplashScreen.js
new file mode 100644
index 000000000..cb8a51d84
--- /dev/null
+++ b/App/Containers/SplashScreen/SplashScreen.js
@@ -0,0 +1,15 @@
+import React from 'react'
+import { Text, View } from 'react-native'
+import styles from './SplashScreenStyle'
+
+export default class SplashScreen extends React.Component {
+ render() {
+ return (
+
+
+ LOGO
+
+
+ )
+ }
+}
diff --git a/App/Containers/SplashScreen/SplashScreenStyle.js b/App/Containers/SplashScreen/SplashScreenStyle.js
new file mode 100644
index 000000000..fe3077e13
--- /dev/null
+++ b/App/Containers/SplashScreen/SplashScreenStyle.js
@@ -0,0 +1,21 @@
+import { StyleSheet } from 'react-native'
+import Colors from 'App/Theme/Colors'
+import ApplicationStyles from 'App/Theme/ApplicationStyles'
+
+export default StyleSheet.create({
+ container: {
+ ...ApplicationStyles.screen.container,
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'center',
+ backgroundColor: Colors.primary,
+ },
+ logo: {
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'center',
+ height: 70,
+ width: 70,
+ backgroundColor: 'white',
+ },
+})
diff --git a/App/Sagas/ExampleSaga.js b/App/Sagas/ExampleSaga.js
index 431994778..6ffa9c88b 100644
--- a/App/Sagas/ExampleSaga.js
+++ b/App/Sagas/ExampleSaga.js
@@ -1,6 +1,6 @@
import { put, call } from 'redux-saga/effects'
import ExampleActions from 'App/Stores/Example/Actions'
-import { WeatherService } from 'App/Service/WeatherService'
+import { WeatherService } from 'App/Services/WeatherService'
/**
* A saga can contain multiple functions.
diff --git a/App/Sagas/StartupSaga.js b/App/Sagas/StartupSaga.js
new file mode 100644
index 000000000..abcfbefaa
--- /dev/null
+++ b/App/Sagas/StartupSaga.js
@@ -0,0 +1,18 @@
+import { put } from 'redux-saga/effects'
+import ExampleActions from 'App/Stores/Example/Actions'
+import NavigationService from 'App/Services/NavigationService'
+
+/**
+ * The startup saga is the place to define behavior to execute when the application starts.
+ */
+export function* startup() {
+ // Dispatch a redux action using `put()`
+ // @see https://redux-saga.js.org/docs/basics/DispatchingActions.html
+ yield put(ExampleActions.fetchTemperature())
+
+ // Add more operations you need to do at startup here
+ // ...
+
+ // When those operations are finished we redirect to the main screen
+ NavigationService.navigateAndReset('MainScreen')
+}
diff --git a/App/Sagas/index.js b/App/Sagas/index.js
index 28348f01d..198086b81 100644
--- a/App/Sagas/index.js
+++ b/App/Sagas/index.js
@@ -1,12 +1,16 @@
import { takeLatest } from 'redux-saga/effects'
import { ExampleTypes } from 'App/Stores/Example/Actions'
+import { StartupTypes } from 'App/Stores/Startup/Actions'
import { fetchTemperature } from './ExampleSaga'
+import { startup } from './StartupSaga'
export default function* root() {
yield [
/**
* @see https://redux-saga.js.org/docs/basics/UsingSagaHelpers.html
*/
+ // Run the startup saga when the application starts
+ takeLatest(StartupTypes.STARTUP, startup),
// Call `fetchTemperature()` when a `FETCH_TEMPERATURE` action is triggered
takeLatest(ExampleTypes.FETCH_TEMPERATURE, fetchTemperature),
]
diff --git a/App/Service/README.md b/App/Service/README.md
deleted file mode 100644
index cb1a72c83..000000000
--- a/App/Service/README.md
+++ /dev/null
@@ -1 +0,0 @@
-This directory contains services that are meant to connect the application to other APIs, for example
diff --git a/App/Services/NavigationService.js b/App/Services/NavigationService.js
new file mode 100644
index 000000000..73b387024
--- /dev/null
+++ b/App/Services/NavigationService.js
@@ -0,0 +1,61 @@
+import { NavigationActions, StackActions } from 'react-navigation'
+
+/**
+ * The navigation is implemented as a service so that it can be used outside of components, for example in sagas.
+ *
+ * @see https://reactnavigation.org/docs/en/navigating-without-navigation-prop.html
+ */
+
+let navigator
+
+/**
+ * This function is called when the RootScreen is created to set the navigator instance to use.
+ */
+function setTopLevelNavigator(navigatorRef) {
+ navigator = navigatorRef
+}
+
+/**
+ * Call this function when you want to navigate to a specific route.
+ *
+ * @param routeName The name of the route to navigate to. Routes are defined in RootScreen using createStackNavigator()
+ * @param params Route parameters.
+ */
+function navigate(routeName, params) {
+ navigator.dispatch(
+ NavigationActions.navigate({
+ routeName,
+ params,
+ })
+ )
+}
+
+/**
+ * Call this function when you want to navigate to a specific route AND reset the navigation history.
+ *
+ * That means the user cannot go back. This is useful for example to redirect from a splashscreen to
+ * the main screen: the user should not be able to go back to the splashscreen.
+ *
+ * @param routeName The name of the route to navigate to. Routes are defined in RootScreen using createStackNavigator()
+ * @param params Route parameters.
+ */
+function navigateAndReset(routeName, params) {
+ navigator.dispatch(
+ StackActions.reset({
+ index: 0,
+ key: null,
+ actions: [
+ NavigationActions.navigate({
+ routeName,
+ params,
+ }),
+ ],
+ })
+ )
+}
+
+export default {
+ navigate,
+ navigateAndReset,
+ setTopLevelNavigator,
+}
diff --git a/App/Services/README.md b/App/Services/README.md
new file mode 100644
index 000000000..4a91eb443
--- /dev/null
+++ b/App/Services/README.md
@@ -0,0 +1 @@
+This directory contains application services, for example services to connect the application to APIs.
diff --git a/App/Service/WeatherService.js b/App/Services/WeatherService.js
similarity index 100%
rename from App/Service/WeatherService.js
rename to App/Services/WeatherService.js
diff --git a/App/Stores/Startup/Actions.js b/App/Stores/Startup/Actions.js
new file mode 100644
index 000000000..91e248210
--- /dev/null
+++ b/App/Stores/Startup/Actions.js
@@ -0,0 +1,8 @@
+import { createActions } from 'reduxsauce'
+
+const { Types, Creators } = createActions({
+ startup: null,
+})
+
+export const StartupTypes = Types
+export default Creators
diff --git a/README.md b/README.md
index 25b30d14d..25896470a 100644
--- a/README.md
+++ b/README.md
@@ -16,6 +16,7 @@ The boilerplate contains:
- [reduxsauce](https://github.com/infinitered/reduxsauce) (v0.7) to facilitate using Redux
- [apisauce](https://github.com/infinitered/apisauce) (v0.15) to make [axios](https://github.com/axios/axios) even better
- [prettier](https://prettier.io/) and [eslint](https://eslint.org/) preconfigured for React Native
+- [react-native-navigation](https://reactnavigation.org) (v2.12) with a [`NavigationService`](App/Services/NavigationService.js) for the application's navigation
## Updates
@@ -29,6 +30,7 @@ The boilerplate will follow new React-Native releases as soon as libraries and t
- [`App/Images`](App/Images): images used by the application
- [`App/Stores`](App/Stores): redux [actions, reducers and stores](https://redux.js.org/basics)
- [`App/Sagas`](App/Sagas): redux sagas
+- [`App/Services`](App/Services): application services
- [`App/Theme`](App/Theme): base styles for the application
For more information on each directory, click the link and read the directory's README.
diff --git a/package.json b/package.json
index 4167b4468..3cfc7d950 100644
--- a/package.json
+++ b/package.json
@@ -17,6 +17,7 @@
"prop-types": "^15.6.1",
"react": "16.3.1",
"react-native": "0.55.4",
+ "react-navigation": "^2.12.1",
"react-redux": "^5.0.7",
"redux": "^3.7.2",
"redux-persist": "^5.9.1",
diff --git a/yarn.lock b/yarn.lock
index 6b228bf9d..09560f33c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1572,6 +1572,10 @@ circular-json@^0.3.1:
version "0.3.3"
resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66"
+clamp@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/clamp/-/clamp-1.0.1.tgz#66a0e64011816e37196828fdc8c8c147312c8634"
+
class-utils@^0.3.5:
version "0.3.6"
resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463"
@@ -1761,6 +1765,13 @@ create-react-class@^15.6.3:
loose-envify "^1.3.1"
object-assign "^4.1.1"
+create-react-context@^0.2.1:
+ version "0.2.3"
+ resolved "https://registry.yarnpkg.com/create-react-context/-/create-react-context-0.2.3.tgz#9ec140a6914a22ef04b8b09b7771de89567cb6f3"
+ dependencies:
+ fbjs "^0.8.0"
+ gud "^1.0.0"
+
cross-spawn@^5.0.1, cross-spawn@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
@@ -2413,7 +2424,7 @@ fbjs-scripts@^0.8.1:
semver "^5.1.0"
through2 "^2.0.0"
-fbjs@^0.8.14, fbjs@^0.8.16, fbjs@^0.8.9:
+fbjs@^0.8.0, fbjs@^0.8.14, fbjs@^0.8.16, fbjs@^0.8.9:
version "0.8.17"
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd"
dependencies:
@@ -2688,6 +2699,10 @@ growly@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
+gud@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/gud/-/gud-1.0.0.tgz#a489581b17e6a70beca9abe3ae57de7a499852c0"
+
handlebars@^4.0.3:
version "4.0.11"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.11.tgz#630a35dfe0294bc281edae6ffc5d329fc7982dcc"
@@ -2764,7 +2779,7 @@ has@^1.0.1, has@^1.0.3:
dependencies:
function-bind "^1.1.1"
-hoist-non-react-statics@^2.5.0:
+hoist-non-react-statics@^2.2.0, hoist-non-react-statics@^2.3.1, hoist-non-react-statics@^2.5.0:
version "2.5.5"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47"
@@ -3102,6 +3117,10 @@ is-windows@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
+isarray@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
+
isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
@@ -4493,6 +4512,12 @@ path-parse@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1"
+path-to-regexp@^1.7.0:
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d"
+ dependencies:
+ isarray "0.0.1"
+
path-type@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441"
@@ -4669,6 +4694,13 @@ qs@~6.5.1:
version "6.5.2"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
+query-string@^6.1.0:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.1.0.tgz#01e7d69f6a0940dac67a937d6c6325647aa4532a"
+ dependencies:
+ decode-uri-component "^0.2.0"
+ strict-uri-encode "^2.0.0"
+
ramda@^0.24.1:
version "0.24.1"
resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.24.1.tgz#c3b7755197f35b8dc3502228262c4c91ddb6b857"
@@ -4723,6 +4755,26 @@ react-is@^16.3.1:
version "16.4.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.4.1.tgz#d624c4650d2c65dbd52c72622bbf389435d9776e"
+react-lifecycles-compat@^3, react-lifecycles-compat@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
+
+react-native-dismiss-keyboard@1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/react-native-dismiss-keyboard/-/react-native-dismiss-keyboard-1.0.0.tgz#32886242b3f2317e121f3aeb9b0a585e2b879b49"
+
+react-native-drawer-layout-polyfill@^1.3.2:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/react-native-drawer-layout-polyfill/-/react-native-drawer-layout-polyfill-1.3.2.tgz#192c84d7a5a6b8a6d2be2c7daa5e4164518d0cc7"
+ dependencies:
+ react-native-drawer-layout "1.3.2"
+
+react-native-drawer-layout@1.3.2:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/react-native-drawer-layout/-/react-native-drawer-layout-1.3.2.tgz#b9740d7663a1dc4f88a61b9c6d93d2d948ea426e"
+ dependencies:
+ react-native-dismiss-keyboard "1.0.0"
+
react-native-rename@^2.2.2:
version "2.2.2"
resolved "https://registry.yarnpkg.com/react-native-rename/-/react-native-rename-2.2.2.tgz#edbe16eea9d2d0e61b7b2c62ec4c2da585384f24"
@@ -4733,6 +4785,30 @@ react-native-rename@^2.2.2:
node-replace "^0.3.3"
shelljs "^0.7.7"
+react-native-safe-area-view@^0.7.0:
+ version "0.7.0"
+ resolved "https://registry.yarnpkg.com/react-native-safe-area-view/-/react-native-safe-area-view-0.7.0.tgz#38f5ab9368d6ef9e5d18ab64212938af3ec39421"
+ dependencies:
+ hoist-non-react-statics "^2.3.1"
+
+react-native-safe-area-view@^0.9.0:
+ version "0.9.0"
+ resolved "https://registry.yarnpkg.com/react-native-safe-area-view/-/react-native-safe-area-view-0.9.0.tgz#10ece2ecac70e7005a5b0b3f06c8833060e6d21f"
+ dependencies:
+ hoist-non-react-statics "^2.3.1"
+
+react-native-tab-view@^0.0.77:
+ version "0.0.77"
+ resolved "https://registry.yarnpkg.com/react-native-tab-view/-/react-native-tab-view-0.0.77.tgz#11ceb8e7c23100d07e628dc151b57797524d00d4"
+ dependencies:
+ prop-types "^15.6.0"
+
+react-native-tab-view@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/react-native-tab-view/-/react-native-tab-view-1.0.2.tgz#66e0bc6d38a227ed2b212e3a256b7902f6ce02ed"
+ dependencies:
+ prop-types "^15.6.1"
+
react-native@0.55.4:
version "0.55.4"
resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.55.4.tgz#eecffada3750a928e2ddd07cf11d857ae9751c30"
@@ -4797,6 +4873,48 @@ react-native@0.55.4:
xmldoc "^0.4.0"
yargs "^9.0.0"
+react-navigation-deprecated-tab-navigator@1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/react-navigation-deprecated-tab-navigator/-/react-navigation-deprecated-tab-navigator-1.3.0.tgz#015dcae1e977b984ca7e99245261c15439026bb7"
+ dependencies:
+ react-native-tab-view "^0.0.77"
+
+react-navigation-drawer@0.5.0:
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/react-navigation-drawer/-/react-navigation-drawer-0.5.0.tgz#d91b6a6ec65c34ba78c00f814b1e6508922cc9ec"
+ dependencies:
+ react-native-drawer-layout-polyfill "^1.3.2"
+
+react-navigation-stack@0.2.3:
+ version "0.2.3"
+ resolved "https://registry.yarnpkg.com/react-navigation-stack/-/react-navigation-stack-0.2.3.tgz#9d1e2524aa1d178302c938948b8ece49d713f12b"
+
+react-navigation-tabs@0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/react-navigation-tabs/-/react-navigation-tabs-0.6.0.tgz#2f526194f4360e56c2702e736887449acc2080dc"
+ dependencies:
+ hoist-non-react-statics "^2.5.0"
+ prop-types "^15.6.1"
+ react-lifecycles-compat "^3.0.4"
+ react-native-safe-area-view "^0.7.0"
+ react-native-tab-view "^1.0.0"
+
+react-navigation@^2.12.1:
+ version "2.12.1"
+ resolved "https://registry.yarnpkg.com/react-navigation/-/react-navigation-2.12.1.tgz#17122a4162cd5a65d79814385cd61c2c0cb7ebb5"
+ dependencies:
+ clamp "^1.0.1"
+ create-react-context "^0.2.1"
+ hoist-non-react-statics "^2.2.0"
+ path-to-regexp "^1.7.0"
+ query-string "^6.1.0"
+ react-lifecycles-compat "^3"
+ react-native-safe-area-view "^0.9.0"
+ react-navigation-deprecated-tab-navigator "1.3.0"
+ react-navigation-drawer "0.5.0"
+ react-navigation-stack "0.2.3"
+ react-navigation-tabs "0.6.0"
+
react-proxy@^1.1.7:
version "1.1.8"
resolved "https://registry.yarnpkg.com/react-proxy/-/react-proxy-1.1.8.tgz#9dbfd9d927528c3aa9f444e4558c37830ab8c26a"
@@ -5460,6 +5578,10 @@ stream-buffers@~2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-2.2.0.tgz#91d5f5130d1cef96dcfa7f726945188741d09ee4"
+strict-uri-encode@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546"
+
string-length@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed"