Moves navigation to the state

This commit is contained in:
Sofiya Tepikin
2019-09-15 18:59:11 +02:00
parent f444de0b87
commit e479b93638
13 changed files with 254 additions and 202 deletions
+49 -87
View File
@@ -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 (
<View style={{ flex: 1 }}>
{ hasDefaultHeader &&
{ !isSymptomEditView && !isCycleDayView &&
<Header
handleBack={isSettingsSubView ? this.handleBackButtonPress : null}
title={title}
@@ -119,15 +76,12 @@ class App extends Component {
}
<Page
navigate={this.navigate}
cycleDay={cycleDay}
date={this.props.date}
handleBackButtonPress={this.handleBackButtonPress}
/>
{!this.isSymptomView() &&
<Menu navigate={this.navigate} currentPage={currentPage} />
}
{ !isSymptomEditView && <Menu /> }
</View>
)
}
@@ -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)
+2
View File
@@ -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)),
})
}
+2
View File
@@ -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)),
})
}
+3 -6
View File
@@ -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)),
})
}
+8 -15
View File
@@ -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 {
</HomeElement>
<HomeElement
onPress={ () => 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))
})
}
-87
View File
@@ -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 (
<TouchableOpacity
style={styles.menuItem}
onPress={onPress}
>
<Icon name={icon} {...iconStyles.menuIcon} {...styleActive} />
<Text
testID={active ? 'activeMenuItem' : `menuItem${labelKey}`}
style={[styles.menuText, styleActive]}
>
{menuTitlesLowerCase[labelKey]}
</Text>
</TouchableOpacity>
)
}
const Menu = ({ currentPage, navigate }) => {
return (
<View style={styles.menu}>
{ menuItems.map(({ icon, labelKey, component, children }) => {
const isActive = (component === currentPage) ||
(children && children.indexOf(currentPage) !== -1)
return (
<MenuItem
key={labelKey}
labelKey={labelKey}
icon={icon}
active={isActive}
onPress={() => navigate(component)}
/>
)}
)}
</View >
)
}
export default Menu
+54
View File
@@ -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 (
<View style={styles.menu}>
{ menuItems.map(({ icon, labelKey, component, children }) => {
const isActive = (component === navigation.current) ||
(children && children.indexOf(navigation.current) !== -1)
return (
<MenuItem
key={labelKey}
labelKey={labelKey}
icon={icon}
active={isActive}
onPress={() => navigate(component)}
/>
)}
)}
</View >
)
}
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)
+30
View File
@@ -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),
}
]
+32
View File
@@ -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 (
<TouchableOpacity
style={styles.menuItem}
onPress={onPress}
>
<Icon name={icon} {...iconStyles.menuIcon} {...styleActive} />
<Text
testID={active ? 'activeMenuItem' : `menuItem${labelKey}`}
style={[styles.menuText, styleActive]}
>
{menuTitlesLowerCase[labelKey]}
</Text>
</TouchableOpacity>
)
}
export default MenuItem
+25
View File
@@ -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)
+20 -6
View File
@@ -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 (
<ScrollView>
{ menu.map(menuItem)}
@@ -36,4 +39,15 @@ export default function SettingsMenu(props) {
</TouchableOpacity>
)
}
}
}
const mapDispatchToProps = (dispatch) => {
return({
navigate: (page) => dispatch(navigate(page)),
})
}
export default connect(
null,
mapDispatchToProps
)(SettingsMenu)