diff --git a/components/app.js b/components/app.js
index ed3063d..8112245 100644
--- a/components/app.js
+++ b/components/app.js
@@ -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()
- }
-
- render() {
- const { goBack, restartApp } = this.props
- const { date, currentPage } = this.state
- const { navigate } = this
-
- if (!currentPage) {
- return false
- }
-
- 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 pageProps = {
- date,
- setDate: this.setDate,
- isTemperatureEditView,
- navigate,
- }
-
- return (
-
-
-
-
-
+ useEffect(() => {
+ const backHandler = BackHandler.addEventListener(
+ 'hardwareBackPress',
+ goBack
)
+
+ return () => backHandler.remove()
+ })
+
+ useEffect(() => setupNotifications(setCurrentPage, setDate), [])
+
+ const Page = viewsList[currentPage]
+ const isTemperatureEditView = currentPage === 'TemperatureEditView'
+ const headerProps = { navigate: setCurrentPage }
+ const pageProps = {
+ date,
+ setDate,
+ isTemperatureEditView,
+ navigate: setCurrentPage,
}
+
+ return (
+
+
+
+
+
+ )
+}
+
+App.propTypes = {
+ restartApp: PropTypes.func,
}
const styles = StyleSheet.create({
diff --git a/components/calendar.js b/components/calendar.js
index cb01791..02fee6a 100644
--- a/components/calendar.js
+++ b/components/calendar.js
@@ -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,76 +12,45 @@ import {
todayToCalFormat,
} from './helpers/calendar'
-class CalendarView extends Component {
- static propTypes = {
- setDate: PropTypes.func.isRequired,
- navigate: PropTypes.func.isRequired,
+const CalendarView = ({ setDate, navigate }) => {
+ const bleedingDays = getBleedingDaysSortedByDate()
+ const predictedMenses = cycleModule().getPredictedMenses()
+
+ const passDateToDayView = ({ dateString }) => {
+ setDate(dateString)
+ navigate('CycleDay')
}
- constructor(props) {
- super(props)
- this.bleedingDays = getBleedingDaysSortedByDate()
- const predictedMenses = cycleModule().getPredictedMenses()
- this.state = {
- bleedingDaysInCalFormat: toCalFormat(this.bleedingDays),
- predictedBleedingDaysInCalFormat: predictionToCalFormat(predictedMenses),
- todayInCalFormat: todayToCalFormat(),
- }
+ const markedDates = Object.assign(
+ {},
+ todayToCalFormat(),
+ toCalFormat(bleedingDays),
+ predictionToCalFormat(predictedMenses)
+ )
- 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
- )
-
- return (
-
-
-
- )
- }
+ return (
+
+
+
+ )
}
const styles = StyleSheet.create({
container: { flex: 1 },
})
+CalendarView.propTypes = {
+ setDate: PropTypes.func.isRequired,
+ navigate: PropTypes.func.isRequired,
+}
+
export default CalendarView
diff --git a/components/pages.js b/components/pages.js
index f6dee18..e9f9b6c 100644
--- a/components/pages.js
+++ b/components/pages.js
@@ -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',
diff --git a/components/password-prompt.js b/components/password-prompt.js
index bcf6480..8e8e86a 100644
--- a/components/password-prompt.js
+++ b/components/password-prompt.js
@@ -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)
-
- return (
-
-
-
-
-
-
-
-
-
-
- )
+ const onConfirmDeletion = async () => {
+ Alert.alert(labels.deleteDatabaseTitle, labels.deleteDatabaseExplainer, [
+ cancelButton,
+ { text: labels.deleteData, onPress: onDeleteData },
+ ])
}
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+ >
+ )
+}
+
+PasswordPrompt.propTypes = {
+ enableShowApp: PropTypes.func.isRequired,
}
const styles = StyleSheet.create({
@@ -119,3 +104,5 @@ const styles = StyleSheet.create({
justifyContent: 'space-around',
},
})
+
+export default PasswordPrompt