diff --git a/components/settings/common/confirm-with-password.js b/components/settings/common/confirm-with-password.js index 6f518b2..4fceacb 100644 --- a/components/settings/common/confirm-with-password.js +++ b/components/settings/common/confirm-with-password.js @@ -1,4 +1,4 @@ -import React, { Component } from 'react' +import React, { useState, useEffect } from 'react' import PropTypes from 'prop-types' import { Alert, StyleSheet, View } from 'react-native' import nodejs from 'nodejs-mobile-react-native' @@ -11,78 +11,65 @@ import { Containers } from '../../../styles' import settings from '../../../i18n/en/settings' import { shared } from '../../../i18n/en/labels' -export default class ConfirmWithPassword extends Component { - constructor() { - super() +const ConfirmWithPassword = ({ onSuccess, onCancel }) => { + const [password, setPassword] = useState(null) - this.state = { password: null } - nodejs.channel.addListener('password-check', this.checkPassword, this) + const checkPassword = async (hash) => { + try { + await openDb(hash) + onSuccess() + } catch (err) { + onIncorrectPassword() + } } - componentWillUnmount() { - nodejs.channel.removeListener('password-check', this.checkPassword) - } + useEffect(() => { + nodejs.channel.addListener('password-check', checkPassword, this) + return () => { + nodejs.channel.removeListener('password-check', checkPassword) + } + }, []) - resetPasswordInput = () => { - this.setState({ password: null }) - } - - onIncorrectPassword = () => { + const onIncorrectPassword = () => { Alert.alert(shared.incorrectPassword, shared.incorrectPasswordMessage, [ { text: shared.cancel, - onPress: this.props.onCancel, + onPress: onCancel, }, { text: shared.tryAgain, - onPress: this.resetPasswordInput, + onPress: () => setPassword(null), }, ]) } - checkPassword = async (hash) => { - try { - await openDb(hash) - this.props.onSuccess() - } catch (err) { - this.onIncorrectPassword() - } + const initPasswordCheck = () => { + requestHash('password-check', password) } - handlePasswordInput = (password) => { - this.setState({ password }) - } + const labels = settings.passwordSettings + const isPassword = password !== null - initPasswordCheck = () => { - requestHash('password-check', this.state.password) - } - - render() { - const { password } = this.state - const labels = settings.passwordSettings - const isPassword = password !== null - - return ( - - - - - - - - ) - } + return ( + <> + + + + + + + ) } ConfirmWithPassword.propTypes = { @@ -95,3 +82,5 @@ const styles = StyleSheet.create({ ...Containers.rowContainer, }, }) + +export default ConfirmWithPassword diff --git a/components/settings/data-management/delete-data.js b/components/settings/data-management/delete-data.js index d0ddef7..4ba3035 100644 --- a/components/settings/data-management/delete-data.js +++ b/components/settings/data-management/delete-data.js @@ -1,4 +1,4 @@ -import React, { Component } from 'react' +import React, { useState } from 'react' import RNFS from 'react-native-fs' import { Alert } from 'react-native' import PropTypes from 'prop-types' @@ -17,26 +17,21 @@ import { EXPORT_FILE_NAME } from './constants' const exportedFilePath = `${RNFS.DocumentDirectoryPath}/${EXPORT_FILE_NAME}` -export default class DeleteData extends Component { - constructor() { - super() +const DeleteData = ({ onStartDeletion, isDeletingData }) => { + const isPasswordSet = hasEncryptionObservable.value + const [isConfirmingWithPassword, setIsConfirmingWithPassword] = + useState(false) - this.state = { - isPasswordSet: hasEncryptionObservable.value, - isConfirmingWithPassword: false, - } - } - - onAlertConfirmation = () => { - this.props.onStartDeletion() - if (this.state.isPasswordSet) { - this.setState({ isConfirmingWithPassword: true }) + const onAlertConfirmation = () => { + onStartDeletion() + if (isPasswordSet) { + setIsConfirmingWithPassword(true) } else { - this.deleteAppData() + deleteAppData() } } - alertBeforeDeletion = async () => { + const alertBeforeDeletion = async () => { const { question, message, confirmation, errors } = settings.deleteSegment if (isDbEmpty() && !(await RNFS.exists(exportedFilePath))) { alertError(errors.noData) @@ -44,64 +39,61 @@ export default class DeleteData extends Component { Alert.alert(question, message, [ { text: confirmation, - onPress: this.onAlertConfirmation, + onPress: onAlertConfirmation, }, { text: sharedLabels.cancel, style: 'cancel', - onPress: this.cancelConfirmationWithPassword, + onPress: cancelConfirmationWithPassword, }, ]) } } - deleteExportedFile = async () => { + const deleteExportedFile = async () => { if (await RNFS.exists(exportedFilePath)) { await RNFS.unlink(exportedFilePath) } } - deleteAppData = async () => { + const deleteAppData = async () => { const { errors, success } = settings.deleteSegment try { if (!isDbEmpty()) { clearDb() } - await this.deleteExportedFile() + await deleteExportedFile() showToast(success.message) } catch (err) { alertError(errors.couldNotDeleteFile) } - this.cancelConfirmationWithPassword() + cancelConfirmationWithPassword() } - cancelConfirmationWithPassword = () => { - this.setState({ isConfirmingWithPassword: false }) + const cancelConfirmationWithPassword = () => { + setIsConfirmingWithPassword(false) } - render() { - const { isConfirmingWithPassword } = this.state - const { isDeletingData } = this.props - - if (isConfirmingWithPassword && isDeletingData) { - return ( - - ) - } - + if (isConfirmingWithPassword && isDeletingData) { return ( - + ) } + + return ( + + ) } DeleteData.propTypes = { isDeletingData: PropTypes.bool, onStartDeletion: PropTypes.func.isRequired, } + +export default DeleteData diff --git a/components/settings/data-management/index.js b/components/settings/data-management/index.js index b6d3524..d36ad70 100644 --- a/components/settings/data-management/index.js +++ b/components/settings/data-management/index.js @@ -1,4 +1,4 @@ -import React, { Component } from 'react' +import React, { useState } from 'react' import AppLoadingView from '../../common/app-loading' import AppPage from '../../common/app-page' @@ -13,78 +13,56 @@ import DeleteData from './delete-data' import labels from '../../../i18n/en/settings' import { ACTION_DELETE, ACTION_EXPORT, ACTION_IMPORT } from '../../../config' -export default class DataManagement extends Component { - constructor(props) { - super(props) +const DataManagement = () => { + const [isLoading, setIsLoading] = useState(false) + const [currentAction, setCurrentAction] = useState(null) - this.state = { - isLoading: false, - currentAction: null, - } - } - - startLoading = () => { - this.setState({ isLoading: true }) - } - - endLoading = () => { - this.setState({ isLoading: false }) - } - - startImportFlow = async (shouldDeleteExistingData) => { - this.startLoading() + const startImportFlow = async (shouldDeleteExistingData) => { + setIsLoading(true) const fileContent = await getFileContent() if (fileContent) { await importData(shouldDeleteExistingData, fileContent) } - this.endLoading() + setIsLoading(false) } - startExport = () => { - this.setCurrentAction(ACTION_EXPORT) + const startExport = () => { + setCurrentAction(ACTION_EXPORT) openShareDialogAndExport() } - startImport = () => { - this.setCurrentAction(ACTION_IMPORT) - openImportDialog(this.startImportFlow) + const startImport = () => { + setCurrentAction(ACTION_IMPORT) + openImportDialog(startImportFlow) } - setCurrentAction = (action) => { - this.setState({ currentAction: action }) - } + if (isLoading) return - render() { - const { currentAction, isLoading } = this.state - const isDeletingData = currentAction === ACTION_DELETE + const isDeletingData = currentAction === ACTION_DELETE - return ( - - {isLoading && } - {!isLoading && ( - - - {labels.export.segmentExplainer} - - - - {labels.import.segmentExplainer} - - - - {labels.deleteSegment.explainer} - this.setCurrentAction(ACTION_DELETE)} - /> - - - )} - - ) - } + return ( + + + {labels.export.segmentExplainer} + + + + {labels.import.segmentExplainer} + + + + {labels.deleteSegment.explainer} + setCurrentAction(ACTION_DELETE)} + /> + + + ) } + +export default DataManagement diff --git a/components/settings/nfp-settings/index.js b/components/settings/nfp-settings/index.js index 427527f..bf83580 100644 --- a/components/settings/nfp-settings/index.js +++ b/components/settings/nfp-settings/index.js @@ -1,4 +1,4 @@ -import React, { Component } from 'react' +import React, { useState } from 'react' import { Platform, StyleSheet, View } from 'react-native' import AppIcon from '../../common/app-icon' @@ -12,58 +12,53 @@ import { useCervixObservable, saveUseCervix } from '../../../local-storage' import { Colors, Spacing, Typography } from '../../../styles' import labels from '../../../i18n/en/settings' -export default class Settings extends Component { - constructor(props) { - super(props) +const Settings = () => { + const [shouldUseCervix, setShouldUseCervix] = useState( + useCervixObservable.value + ) - this.state = { - shouldUseCervix: useCervixObservable.value, - } - } - - onCervixToggle = (value) => { - this.setState({ shouldUseCervix: value }) + const onCervixToggle = (value) => { + setShouldUseCervix(value) saveUseCervix(value) } - render() { - const { shouldUseCervix } = this.state - const cervixText = shouldUseCervix - ? labels.useCervix.cervixModeOn - : labels.useCervix.cervixModeOff + const cervixText = shouldUseCervix + ? labels.useCervix.cervixModeOn + : labels.useCervix.cervixModeOff - return ( - - - + + + + {/* for iOS disabled temporarily, TODO https://gitlab.com/bloodyhealth/drip/-/issues/545 */} + {Platform.OS !== 'ios' && ( + + {labels.tempScale.segmentExplainer} + + + )} + + + - - {/* for iOS disabled temporarily, TODO https://gitlab.com/bloodyhealth/drip/-/issues/545 */} - {Platform.OS !== 'ios' && ( - - {labels.tempScale.segmentExplainer} - - - )} - - - - {labels.preOvu.title} - - {labels.preOvu.note} - - - ) - } + {labels.preOvu.title} + + {labels.preOvu.note} + + + ) } +export default Settings + const styles = StyleSheet.create({ icon: { marginRight: Spacing.base, diff --git a/components/settings/nfp-settings/temperature-slider.js b/components/settings/nfp-settings/temperature-slider.js index 9795af4..2019d55 100644 --- a/components/settings/nfp-settings/temperature-slider.js +++ b/components/settings/nfp-settings/temperature-slider.js @@ -1,4 +1,4 @@ -import React, { Component } from 'react' +import React, { useState } from 'react' import { StyleSheet, View } from 'react-native' import Slider from '@ptomasroos/react-native-multi-slider' @@ -10,51 +10,43 @@ import { Colors, Sizes } from '../../../styles' import labels from '../../../i18n/en/settings' import { TEMP_MIN, TEMP_MAX, TEMP_SLIDER_STEP } from '../../../config' -export default class TemperatureSlider extends Component { - constructor(props) { - super(props) - - const { min, max } = scaleObservable.value - this.state = { minTemperature: min, maxTemperature: max } - } - - onTemperatureSliderChange = (values) => { - this.setState({ - minTemperature: values[0], - maxTemperature: values[1], - }) +const TemperatureSlider = () => { + const savedValue = scaleObservable.value + const [minTemperature, setMinTemperature] = useState(savedValue.min) + const [maxTemperature, setMaxTemperature] = useState(savedValue.max) + const onTemperatureSliderChange = ([min, max]) => { + setMinTemperature(min) + setMaxTemperature(max) try { - saveTempScale({ min: values[0], max: values[1] }) + saveTempScale({ min, max }) } catch (err) { alertError(labels.tempScale.saveError) } } - render() { - const { minTemperature, maxTemperature } = this.state - - return ( - - - - ) - } + return ( + + + + ) } +export default TemperatureSlider + const styles = StyleSheet.create({ container: { alignItems: 'center',