Chore/make function components 3
This commit is contained in:
+23
-63
@@ -1,4 +1,4 @@
|
||||
import React, { Component } from 'react'
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { BackHandler, StyleSheet, View } from 'react-native'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
@@ -7,99 +7,59 @@ import { LocalDate } from '@js-joda/core'
|
||||
import Header from './header'
|
||||
import Menu from './menu'
|
||||
import { viewsList } from './views'
|
||||
import { isSettingsView, pages } from './pages'
|
||||
import { pages } from './pages'
|
||||
|
||||
import { headerTitles } from '../i18n/en/labels'
|
||||
import setupNotifications from '../lib/notifications'
|
||||
import { closeDb } from '../db'
|
||||
|
||||
class App extends Component {
|
||||
static propTypes = {
|
||||
date: PropTypes.string,
|
||||
navigate: PropTypes.func,
|
||||
setDate: PropTypes.func,
|
||||
goBack: PropTypes.func,
|
||||
restartApp: PropTypes.func,
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.backHandler = BackHandler.addEventListener(
|
||||
'hardwareBackPress',
|
||||
this.goBack
|
||||
)
|
||||
|
||||
this.state = {
|
||||
date: LocalDate.now().toString(),
|
||||
currentPage: 'Home',
|
||||
}
|
||||
|
||||
setupNotifications(this.navigate, this.setDate)
|
||||
}
|
||||
|
||||
navigate = (page) => {
|
||||
this.setState({ currentPage: page })
|
||||
}
|
||||
|
||||
setDate = (date) => {
|
||||
this.setState({ date })
|
||||
}
|
||||
|
||||
goBack = () => {
|
||||
const { currentPage } = this.state
|
||||
|
||||
const App = ({ restartApp }) => {
|
||||
const [date, setDate] = useState(LocalDate.now().toString())
|
||||
const [currentPage, setCurrentPage] = useState('Home')
|
||||
const goBack = () => {
|
||||
if (currentPage === 'Home') {
|
||||
closeDb()
|
||||
BackHandler.exitApp()
|
||||
} else {
|
||||
const { parent } = pages.find((p) => p.component === currentPage)
|
||||
this.navigate(parent)
|
||||
|
||||
setCurrentPage(parent)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.backHandler.remove()
|
||||
}
|
||||
useEffect(() => {
|
||||
const backHandler = BackHandler.addEventListener(
|
||||
'hardwareBackPress',
|
||||
goBack
|
||||
)
|
||||
|
||||
render() {
|
||||
const { goBack, restartApp } = this.props
|
||||
const { date, currentPage } = this.state
|
||||
const { navigate } = this
|
||||
return () => backHandler.remove()
|
||||
})
|
||||
|
||||
if (!currentPage) {
|
||||
return false
|
||||
}
|
||||
useEffect(() => setupNotifications(setCurrentPage, setDate), [])
|
||||
|
||||
const Page = viewsList[currentPage]
|
||||
const title = headerTitles[currentPage]
|
||||
|
||||
const isSettingsSubView = isSettingsView(currentPage)
|
||||
const isTemperatureEditView = currentPage === 'TemperatureEditView'
|
||||
|
||||
const headerProps = {
|
||||
title,
|
||||
handleBack: isSettingsSubView ? goBack : null,
|
||||
navigate,
|
||||
}
|
||||
|
||||
const headerProps = { navigate: setCurrentPage }
|
||||
const pageProps = {
|
||||
date,
|
||||
setDate: this.setDate,
|
||||
setDate,
|
||||
isTemperatureEditView,
|
||||
navigate,
|
||||
navigate: setCurrentPage,
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Header {...headerProps} />
|
||||
<Page {...pageProps} restartApp={restartApp} />
|
||||
<Menu currentPage={currentPage} navigate={navigate} />
|
||||
<Menu currentPage={currentPage} navigate={setCurrentPage} />
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
App.propTypes = {
|
||||
restartApp: PropTypes.func,
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
|
||||
+16
-48
@@ -1,11 +1,10 @@
|
||||
import React, { Component } from 'react'
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { StyleSheet, View } from 'react-native'
|
||||
import { CalendarList } from 'react-native-calendars'
|
||||
|
||||
import { getBleedingDaysSortedByDate } from '../db'
|
||||
import cycleModule from '../lib/cycle'
|
||||
import nothingChanged from '../db/db-unchanged'
|
||||
import {
|
||||
calendarTheme,
|
||||
predictionToCalFormat,
|
||||
@@ -13,55 +12,20 @@ import {
|
||||
todayToCalFormat,
|
||||
} from './helpers/calendar'
|
||||
|
||||
class CalendarView extends Component {
|
||||
static propTypes = {
|
||||
setDate: PropTypes.func.isRequired,
|
||||
navigate: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.bleedingDays = getBleedingDaysSortedByDate()
|
||||
const CalendarView = ({ setDate, navigate }) => {
|
||||
const bleedingDays = getBleedingDaysSortedByDate()
|
||||
const predictedMenses = cycleModule().getPredictedMenses()
|
||||
this.state = {
|
||||
bleedingDaysInCalFormat: toCalFormat(this.bleedingDays),
|
||||
predictedBleedingDaysInCalFormat: predictionToCalFormat(predictedMenses),
|
||||
todayInCalFormat: todayToCalFormat(),
|
||||
|
||||
const passDateToDayView = ({ dateString }) => {
|
||||
setDate(dateString)
|
||||
navigate('CycleDay')
|
||||
}
|
||||
|
||||
this.bleedingDays.addListener(this.setStateWithCalFormattedDays)
|
||||
}
|
||||
|
||||
setStateWithCalFormattedDays = (_, changes) => {
|
||||
if (nothingChanged(changes)) return
|
||||
const predictedMenses = cycleModule().getPredictedMenses()
|
||||
this.setState({
|
||||
bleedingDaysInCalFormat: toCalFormat(this.bleedingDays),
|
||||
predictedBleedingDaysInCalFormat: predictionToCalFormat(predictedMenses),
|
||||
todayInCalFormat: todayToCalFormat(),
|
||||
})
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.bleedingDays.removeListener(this.setStateWithCalFormattedDays)
|
||||
}
|
||||
|
||||
passDateToDayView = (result) => {
|
||||
this.props.setDate(result.dateString)
|
||||
this.props.navigate('CycleDay')
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
todayInCalFormat,
|
||||
bleedingDaysInCalFormat,
|
||||
predictedBleedingDaysInCalFormat,
|
||||
} = this.state
|
||||
const markedDates = Object.assign(
|
||||
{},
|
||||
todayInCalFormat,
|
||||
bleedingDaysInCalFormat,
|
||||
predictedBleedingDaysInCalFormat
|
||||
todayToCalFormat(),
|
||||
toCalFormat(bleedingDays),
|
||||
predictionToCalFormat(predictedMenses)
|
||||
)
|
||||
|
||||
return (
|
||||
@@ -69,7 +33,7 @@ class CalendarView extends Component {
|
||||
<CalendarList
|
||||
// If firstDay=1 week starts from Monday. Note that dayNames and dayNamesShort should still start from Sunday.
|
||||
firstDay={1}
|
||||
onDayPress={this.passDateToDayView.bind(this)}
|
||||
onDayPress={passDateToDayView}
|
||||
markedDates={markedDates}
|
||||
markingType="custom"
|
||||
theme={calendarTheme}
|
||||
@@ -79,10 +43,14 @@ class CalendarView extends Component {
|
||||
</View>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: { flex: 1 },
|
||||
})
|
||||
|
||||
CalendarView.propTypes = {
|
||||
setDate: PropTypes.func.isRequired,
|
||||
navigate: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
export default CalendarView
|
||||
|
||||
@@ -3,9 +3,6 @@ import settingsViews from './settings'
|
||||
import settingsLabels from '../i18n/en/settings'
|
||||
const labels = settingsLabels.menuItems
|
||||
|
||||
export const isSettingsView = (page) =>
|
||||
Object.keys(settingsViews).includes(page)
|
||||
|
||||
export const pages = [
|
||||
{
|
||||
component: 'Home',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { Component } from 'react'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Alert, StyleSheet, View } from 'react-native'
|
||||
import nodejs from 'nodejs-mobile-react-native'
|
||||
@@ -15,97 +15,82 @@ import { Containers, Spacing } from '../styles'
|
||||
|
||||
const cancelButton = { text: shared.cancel, style: 'cancel' }
|
||||
|
||||
export default class PasswordPrompt extends Component {
|
||||
static propTypes = {
|
||||
enableShowApp: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = { password: null }
|
||||
|
||||
nodejs.channel.addListener('check-pw', this.passHashToDb, this)
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
nodejs.channel.removeListener('check-pw', this.passHashToDb)
|
||||
}
|
||||
|
||||
onConfirmDeletion = async () => {
|
||||
Alert.alert(labels.deleteDatabaseTitle, labels.deleteDatabaseExplainer, [
|
||||
cancelButton,
|
||||
{ text: labels.deleteData, onPress: this.onDeleteData },
|
||||
])
|
||||
}
|
||||
|
||||
onDeleteData = () => {
|
||||
Alert.alert(labels.areYouSureTitle, labels.areYouSure, [
|
||||
cancelButton,
|
||||
{
|
||||
text: labels.reallyDeleteData,
|
||||
onPress: this.onDeleteDataConfirmation,
|
||||
},
|
||||
])
|
||||
}
|
||||
|
||||
onDeleteDataConfirmation = async () => {
|
||||
await deleteDbAndOpenNew()
|
||||
await saveEncryptionFlag(false)
|
||||
this.props.enableShowApp()
|
||||
}
|
||||
|
||||
passHashToDb = async (hash) => {
|
||||
const PasswordPrompt = ({ enableShowApp }) => {
|
||||
const [password, setPassword] = useState(null)
|
||||
const unlockApp = () => requestHash('check-pw', password)
|
||||
const isPasswordEntered = Boolean(password)
|
||||
const passHashToDb = async (hash) => {
|
||||
const connected = await openDb(hash)
|
||||
|
||||
if (!connected) {
|
||||
Alert.alert(shared.incorrectPassword, shared.incorrectPasswordMessage, [
|
||||
{
|
||||
text: shared.tryAgain,
|
||||
onPress: () => this.setState({ password: null }),
|
||||
onPress: () => setPassword(null),
|
||||
},
|
||||
])
|
||||
return
|
||||
}
|
||||
this.props.enableShowApp()
|
||||
|
||||
enableShowApp()
|
||||
}
|
||||
|
||||
unlockApp = () => {
|
||||
requestHash('check-pw', this.state.password)
|
||||
useEffect(() => {
|
||||
nodejs.channel.addListener('check-pw', passHashToDb, this)
|
||||
|
||||
return () => nodejs.channel.remove('check-pw', passHashToDb)
|
||||
}, [])
|
||||
|
||||
const onDeleteDataConfirmation = async () => {
|
||||
await deleteDbAndOpenNew()
|
||||
await saveEncryptionFlag(false)
|
||||
enableShowApp()
|
||||
}
|
||||
|
||||
setPassword = (password) => {
|
||||
this.setState({ password })
|
||||
const onDeleteData = () => {
|
||||
Alert.alert(labels.areYouSureTitle, labels.areYouSure, [
|
||||
cancelButton,
|
||||
{
|
||||
text: labels.reallyDeleteData,
|
||||
onPress: onDeleteDataConfirmation,
|
||||
},
|
||||
])
|
||||
}
|
||||
|
||||
render() {
|
||||
const { password } = this.state
|
||||
const isPasswordEntered = Boolean(password)
|
||||
const onConfirmDeletion = async () => {
|
||||
Alert.alert(labels.deleteDatabaseTitle, labels.deleteDatabaseExplainer, [
|
||||
cancelButton,
|
||||
{ text: labels.deleteData, onPress: onDeleteData },
|
||||
])
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<>
|
||||
<Header isStatic />
|
||||
<AppPage contentContainerStyle={styles.contentContainer}>
|
||||
<AppTextInput
|
||||
isKeyboardOffset={false}
|
||||
onChangeText={this.setPassword}
|
||||
onChangeText={setPassword}
|
||||
secureTextEntry={true}
|
||||
placeholder={labels.enterPassword}
|
||||
/>
|
||||
<View style={styles.containerButtons}>
|
||||
<Button onPress={this.onConfirmDeletion}>
|
||||
{labels.forgotPassword}
|
||||
</Button>
|
||||
<Button onPress={onConfirmDeletion}>{labels.forgotPassword}</Button>
|
||||
<Button
|
||||
disabled={!isPasswordEntered}
|
||||
isCTA={isPasswordEntered}
|
||||
onPress={this.unlockApp}
|
||||
onPress={unlockApp}
|
||||
>
|
||||
{labels.title}
|
||||
</Button>
|
||||
</View>
|
||||
</AppPage>
|
||||
</React.Fragment>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
PasswordPrompt.propTypes = {
|
||||
enableShowApp: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
@@ -119,3 +104,5 @@ const styles = StyleSheet.create({
|
||||
justifyContent: 'space-around',
|
||||
},
|
||||
})
|
||||
|
||||
export default PasswordPrompt
|
||||
|
||||
Reference in New Issue
Block a user