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