diff --git a/components/app.js b/components/app.js
index cef888e..53aa680 100644
--- a/components/app.js
+++ b/components/app.js
@@ -1,117 +1,74 @@
import React, { Component } from 'react'
import { View, BackHandler } from 'react-native'
+import PropTypes from 'prop-types'
+
+import { LocalDate } from 'js-joda'
import { connect } from 'react-redux'
-import { getDate } from '../slices/date'
+import { getDate, setDate } from '../slices/date'
+import { getNavigation, navigate } from '../slices/navigation'
import Header from './header'
import Menu from './menu'
-import Home from './home'
-import Calendar from './calendar'
-import CycleDay from './cycle-day/cycle-day-overview'
-import symptomViews from './cycle-day/symptoms'
-import Chart from './chart/chart'
-import SettingsMenu from './settings/settings-menu'
-import settingsViews from './settings'
-import Stats from './stats'
-import {headerTitles, menuTitles} from '../i18n/en/labels'
-import setupNotifications from '../lib/notifications'
-import { closeDb } from '../db'
+import { pagesList, isSymptomView, isSettingsView } from './pages'
-const HOME_PAGE = 'Home'
-const CYCLE_DAY_PAGE = 'CycleDay'
-const SETTINGS_MENU_PAGE = 'SettingsMenu'
+import { headerTitles } from '../i18n/en/labels'
+import setupNotifications from '../lib/notifications'
+import { closeDb, getCycleDay } from '../db'
class App extends Component {
+
+ static propTypes = {
+ date: PropTypes.string,
+ navigation: PropTypes.object.isRequired,
+ }
+
constructor(props) {
super(props)
+
+ this.todayDateString = LocalDate.now().toString()
+ props.setDate(this.todayDateString)
+
this.state = {
- currentPage: HOME_PAGE,
- cycleDay: {},
+ cycleDay: getCycleDay(this.todayDateString),
}
- this.backHandler = BackHandler.addEventListener('hardwareBackPress', this.handleBackButtonPress)
- setupNotifications(this.navigate)
+
+ this.backHandler = BackHandler.addEventListener(
+ 'hardwareBackPress',
+ this.handleBackButtonPress
+ )
+
+ setupNotifications(this.props.navigate)
}
componentWillUnmount() {
this.backHandler.remove()
}
- navigate = (pageName, cycleDay) => {
- const { currentPage } = this.state
- // for the back button to work properly, we want to
- // remember two origins: which menu item we came from
- // and from where we navigated to the symptom view (day
- // view or home page)
- if (this.isMenuItem()) {
- this.menuOrigin = currentPage
- }
- if (!this.isSymptomView()) {
- this.originForSymptomView = currentPage
- }
- this.setState({ currentPage: pageName, cycleDay })
- }
-
handleBackButtonPress = () => {
- const { currentPage } = this.state
- if (currentPage === HOME_PAGE) {
+ const { current, prev } = this.props.navigation
+ if (current === 'Home') {
closeDb()
return false
}
- if (this.isSymptomView()) {
- this.navigate(this.originForSymptomView)
- } else if (this.isSettingsView()) {
- this.navigate(SETTINGS_MENU_PAGE)
- } else if (currentPage === CYCLE_DAY_PAGE) {
- this.navigate(this.menuOrigin)
- } else {
- this.navigate(HOME_PAGE)
- }
+ this.props.navigate(prev)
return true
}
- isMenuItem() {
- return Object.keys(menuTitles).includes(this.state.currentPage)
- }
-
- isSymptomView() {
- return Object.keys(symptomViews).includes(this.state.currentPage)
- }
-
- isSettingsView() {
- return Object.keys(settingsViews).includes(this.state.currentPage)
- }
-
- isDefaultView() {
- const { currentPage } = this.state
- return this.isMenuItem(currentPage) || currentPage === SETTINGS_MENU_PAGE
- }
-
render() {
- const { currentPage, cycleDay } = this.state
- const pages = {
- Home,
- Calendar,
- CycleDay,
- Chart,
- SettingsMenu,
- ...settingsViews,
- Stats,
- ...symptomViews
- }
- const Page = pages[currentPage]
+ const { cycleDay } = this.state
+ const currentPage = this.props.navigation.current
+
+ const Page = pagesList[currentPage]
const title = headerTitles[currentPage]
- const hasDefaultHeader =
- !this.isSymptomView() &&
- currentPage !== CYCLE_DAY_PAGE
-
- const isSettingsSubView = this.isSettingsView()
+ const isSymptomEditView = isSymptomView(currentPage)
+ const isSettingsSubView = isSettingsView(currentPage)
+ const isCycleDayView = currentPage === 'CycleDay'
return (
-
- { hasDefaultHeader &&
+ { !isSymptomEditView && !isCycleDayView &&
- {!this.isSymptomView() &&
-
- }
+ { !isSymptomEditView && }
)
}
@@ -135,11 +89,19 @@ class App extends Component {
const mapStateToProps = (state) => {
return({
- date: getDate(state)
+ date: getDate(state),
+ navigation: getNavigation(state)
+ })
+}
+
+const mapDispatchToProps = (dispatch) => {
+ return({
+ setDate: (date) => dispatch(setDate(date)),
+ navigate: (page) => dispatch(navigate(page)),
})
}
export default connect(
mapStateToProps,
- null
+ mapDispatchToProps
)(App)
\ No newline at end of file
diff --git a/components/calendar.js b/components/calendar.js
index faf4bea..9387e0c 100644
--- a/components/calendar.js
+++ b/components/calendar.js
@@ -3,6 +3,7 @@ import { CalendarList } from 'react-native-calendars'
import { connect } from 'react-redux'
import { setDate } from '../slices/date'
+import { navigate } from '../slices/navigation'
import { LocalDate } from 'js-joda'
import { getBleedingDaysSortedByDate } from '../db'
@@ -68,6 +69,7 @@ class CalendarView extends Component {
const mapDispatchToProps = (dispatch) => {
return({
setDate: (date) => dispatch(setDate(date)),
+ navigate: (page) => dispatch(navigate(page)),
})
}
diff --git a/components/chart/day-column.js b/components/chart/day-column.js
index 633f636..fffc1fc 100644
--- a/components/chart/day-column.js
+++ b/components/chart/day-column.js
@@ -3,6 +3,7 @@ import { TouchableOpacity } from 'react-native'
import { connect } from 'react-redux'
import { setDate } from '../../slices/date'
+import { navigate } from '../../slices/navigation'
import { getCycleDay } from '../../db'
@@ -109,6 +110,7 @@ class DayColumn extends Component {
const mapDispatchToProps = (dispatch) => {
return({
setDate: (date) => dispatch(setDate(date)),
+ navigate: (page) => dispatch(navigate(page)),
})
}
diff --git a/components/cycle-day/cycle-day-overview.js b/components/cycle-day/cycle-day-overview.js
index 4df2f28..7415ab2 100644
--- a/components/cycle-day/cycle-day-overview.js
+++ b/components/cycle-day/cycle-day-overview.js
@@ -3,6 +3,7 @@ import { ScrollView, View } from 'react-native'
import { connect } from 'react-redux'
import { getDate, setDate } from '../../slices/date'
+import { navigate } from '../../slices/navigation'
import { LocalDate } from 'js-joda'
import Header from '../header'
@@ -41,11 +42,6 @@ class CycleDayOverView extends Component {
this.updateCycleDay(nextDate)
}
- navigate(symptom) {
- const { cycleDay } = this.state
- this.props.navigate(symptom, cycleDay)
- }
-
render() {
const { cycleDay } = this.state
const { date } = this.props
@@ -89,7 +85,7 @@ class CycleDayOverView extends Component {
key={symptom}
symptom={symptom}
symptomData={symptomData}
- onPress={() => this.navigate(symptomEditView, symptomData)}
+ onPress={() => this.props.navigate(symptomEditView)}
disabled={dateInFuture}
/>)
})
@@ -115,6 +111,7 @@ const mapStateToProps = (state) => {
const mapDispatchToProps = (dispatch) => {
return({
setDate: (date) => dispatch(setDate(date)),
+ navigate: (page) => dispatch(navigate(page)),
})
}
diff --git a/components/home.js b/components/home.js
index 25c46b8..a0c4e27 100644
--- a/components/home.js
+++ b/components/home.js
@@ -3,7 +3,7 @@ import React, { Component } from 'react'
import { ScrollView, View } from 'react-native'
import { connect } from 'react-redux'
-import { setDate } from '../slices/date'
+import { navigate } from '../slices/navigation'
import DripHomeIcon from '../assets/drip-home-icons'
import {
@@ -17,7 +17,6 @@ import styles, { cycleDayColor, periodColor, secondaryColor } from '../styles'
import AppText from './app-text'
import Button from './button'
import { formatDateForShortText } from './helpers/format-date'
-import { getCycleDay } from '../db'
const IconText = ({ children, wrapperStyles }) => {
return (
@@ -71,26 +70,20 @@ class Home extends Component {
}
}
- setTodayDate = () => {
- this.props.setDate(this.todayDateString)
- }
-
navigateToCycleDayView = () => {
- this.setTodayDate()
this.props.navigate('CycleDay')
}
navigateToBleedingEditView = () => {
- this.setTodayDate()
- this.props.navigate(
- 'BleedingEditView',
- getCycleDay(this.todayDateString)
- )
+ this.props.navigate('BleedingEditView')
+ }
+
+ navigateToChart = () => {
+ this.props.navigate('Chart')
}
render() {
const { cycleDayNumber, phase, status } = this.state
- const { navigate } = this.props
const cycleDayMoreText = cycleDayNumber ?
labels.cycleDayKnown(cycleDayNumber) :
labels.cycleDayNotEnoughInfo
@@ -136,7 +129,7 @@ class Home extends Component {
navigate('Chart') }
+ onPress={this.navigateToChart}
buttonColor={ secondaryColor }
buttonLabel={ labels.checkFertility }
>
@@ -164,7 +157,7 @@ class Home extends Component {
const mapDispatchToProps = (dispatch) => {
return({
- setDate: (date) => dispatch(setDate(date)),
+ navigate: (page) => dispatch(navigate(page))
})
}
diff --git a/components/menu.js b/components/menu.js
deleted file mode 100644
index 3664e15..0000000
--- a/components/menu.js
+++ /dev/null
@@ -1,87 +0,0 @@
-import React from 'react'
-import {
- View,
- Text,
- TouchableOpacity
-} from 'react-native'
-
-import settingsViews from './settings'
-
-import { menuTitles } from '../i18n/en/labels'
-
-import styles, { iconStyles, secondaryColor } from '../styles'
-import Icon from 'react-native-vector-icons/MaterialCommunityIcons'
-
-const menuTitlesLowerCase = Object.keys(menuTitles).reduce((acc, curr) => {
- acc[curr] = menuTitles[curr].toLowerCase()
- return acc
-}, {})
-
-const menuItems = [
- {
- labelKey: 'Home',
- icon: 'home',
- component: 'Home',
- },
- {
- labelKey: 'Calendar',
- icon: 'calendar-range',
- component: 'Calendar',
- },
- {
- labelKey: 'Chart',
- icon: 'chart-line',
- component: 'Chart',
- },
- {
- labelKey: 'Stats',
- icon: 'chart-pie',
- component: 'Stats',
- },
- {
- labelKey: 'Settings',
- icon: 'settings',
- component: 'SettingsMenu',
- children: Object.keys(settingsViews),
- }
-]
-
-const MenuItem = ({ icon, labelKey, active, onPress }) => {
- const styleActive = active ? { color: secondaryColor } : null
- return (
-
-
-
- {menuTitlesLowerCase[labelKey]}
-
-
- )
-}
-
-const Menu = ({ currentPage, navigate }) => {
- return (
-
- { menuItems.map(({ icon, labelKey, component, children }) => {
- const isActive = (component === currentPage) ||
- (children && children.indexOf(currentPage) !== -1)
- return (
-
- )
-}
-
-export default Menu
\ No newline at end of file
diff --git a/components/menu/index.js b/components/menu/index.js
new file mode 100644
index 0000000..75dd335
--- /dev/null
+++ b/components/menu/index.js
@@ -0,0 +1,54 @@
+import React from 'react'
+import { View } from 'react-native'
+import PropTypes from 'prop-types'
+
+import MenuItem from './menu-item'
+
+import { connect } from 'react-redux'
+import { getNavigation, navigate } from '../../slices/navigation'
+
+import { menuItems } from './menu-config'
+
+import styles from '../../styles'
+
+const Menu = ({ navigation, navigate }) => {
+ return (
+
+ { menuItems.map(({ icon, labelKey, component, children }) => {
+ const isActive = (component === navigation.current) ||
+ (children && children.indexOf(navigation.current) !== -1)
+ return (
+
+ )
+}
+
+Menu.propTypes = {
+ navigation: PropTypes.object,
+ navigate: PropTypes.func,
+}
+
+const mapStateToProps = (state) => {
+ return({
+ navigation: getNavigation(state),
+ })
+}
+
+const mapDispatchToProps = (dispatch) => {
+ return({
+ navigate: (page) => dispatch(navigate(page)),
+ })
+}
+
+export default connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(Menu)
\ No newline at end of file
diff --git a/components/menu/menu-config.js b/components/menu/menu-config.js
new file mode 100644
index 0000000..e02dfd8
--- /dev/null
+++ b/components/menu/menu-config.js
@@ -0,0 +1,30 @@
+import settingsViews from '../settings'
+
+export const menuItems = [
+ {
+ labelKey: 'Home',
+ icon: 'home',
+ component: 'Home',
+ },
+ {
+ labelKey: 'Calendar',
+ icon: 'calendar-range',
+ component: 'Calendar',
+ },
+ {
+ labelKey: 'Chart',
+ icon: 'chart-line',
+ component: 'Chart',
+ },
+ {
+ labelKey: 'Stats',
+ icon: 'chart-pie',
+ component: 'Stats',
+ },
+ {
+ labelKey: 'Settings',
+ icon: 'settings',
+ component: 'SettingsMenu',
+ children: Object.keys(settingsViews),
+ }
+]
\ No newline at end of file
diff --git a/components/menu/menu-item.js b/components/menu/menu-item.js
new file mode 100644
index 0000000..083dd23
--- /dev/null
+++ b/components/menu/menu-item.js
@@ -0,0 +1,32 @@
+import React from 'react'
+import { Text, TouchableOpacity } from 'react-native'
+
+import styles, { iconStyles, secondaryColor } from '../../styles'
+import Icon from 'react-native-vector-icons/MaterialCommunityIcons'
+
+import { menuTitles } from '../../i18n/en/labels'
+
+const menuTitlesLowerCase = Object.keys(menuTitles).reduce((acc, curr) => {
+ acc[curr] = menuTitles[curr].toLowerCase()
+ return acc
+}, {})
+
+const MenuItem = ({ icon, labelKey, active, onPress }) => {
+ const styleActive = active ? { color: secondaryColor } : null
+ return (
+
+
+
+ {menuTitlesLowerCase[labelKey]}
+
+
+ )
+}
+
+export default MenuItem
\ No newline at end of file
diff --git a/components/pages.js b/components/pages.js
new file mode 100644
index 0000000..533f77b
--- /dev/null
+++ b/components/pages.js
@@ -0,0 +1,25 @@
+import Home from './home'
+import Calendar from './calendar'
+import CycleDay from './cycle-day/cycle-day-overview'
+import symptomViews from './cycle-day/symptoms'
+import Chart from './chart/chart'
+import SettingsMenu from './settings/settings-menu'
+import settingsViews from './settings'
+import Stats from './stats'
+
+export const pagesList = {
+ Home,
+ Calendar,
+ CycleDay,
+ Chart,
+ SettingsMenu,
+ ...settingsViews,
+ Stats,
+ ...symptomViews
+}
+
+export const isSymptomView =
+ (page) => Object.keys(symptomViews).includes(page)
+
+export const isSettingsView =
+ (page) => Object.keys(settingsViews).includes(page)
\ No newline at end of file
diff --git a/components/settings/settings-menu.js b/components/settings/settings-menu.js
index ad4915a..a622a1d 100644
--- a/components/settings/settings-menu.js
+++ b/components/settings/settings-menu.js
@@ -1,10 +1,13 @@
import React from 'react'
-import {
- TouchableOpacity,
- ScrollView,
-} from 'react-native'
+import { TouchableOpacity, ScrollView } from 'react-native'
+import { connect } from 'react-redux'
+
+import { navigate } from '../../slices/navigation'
+
import styles from '../../styles/index'
+
import settingsLabels from '../../i18n/en/settings'
+
import AppText from '../app-text'
const labels = settingsLabels.menuTitles
@@ -18,7 +21,7 @@ const menu = [
{title: labels.license, component: 'License'}
]
-export default function SettingsMenu(props) {
+const SettingsMenu = (props) => {
return (
{ menu.map(menuItem)}
@@ -36,4 +39,15 @@ export default function SettingsMenu(props) {
)
}
-}
\ No newline at end of file
+}
+
+const mapDispatchToProps = (dispatch) => {
+ return({
+ navigate: (page) => dispatch(navigate(page)),
+ })
+}
+
+export default connect(
+ null,
+ mapDispatchToProps
+)(SettingsMenu)
\ No newline at end of file
diff --git a/slices/navigation.js b/slices/navigation.js
new file mode 100644
index 0000000..5de20f1
--- /dev/null
+++ b/slices/navigation.js
@@ -0,0 +1,26 @@
+import { createSlice } from 'redux-starter-kit'
+
+const navigationSlice = createSlice({
+ slice: 'navigation',
+ initialState: {
+ current: 'Home',
+ prev: null,
+ },
+ reducers: {
+ navigate: (state, action) => {
+ return {
+ current: action.payload,
+ prev: state.current
+ }
+ }
+ }
+})
+
+// Extract the action creators object and the reducer
+const { actions, reducer, selectors } = navigationSlice
+// Extract and export each action creator by name
+export const { navigate } = actions
+
+export const { getNavigation } = selectors
+
+export default reducer
diff --git a/store.js b/store.js
index 7aa713d..7887652 100644
--- a/store.js
+++ b/store.js
@@ -1,9 +1,11 @@
import { combineReducers, createStore } from "redux"
import date from "./slices/date"
+import navigation from "./slices/navigation"
const reducer = combineReducers({
- date
+ date,
+ navigation
})
const store = createStore(reducer)