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