diff --git a/components/app-text.js b/components/app-text.js index 079d9c7a3b165f1c8e8e6c164e02be2f1c1293af..829e8b345a24f9792d20e255b851085913c7e4fc 100644 --- a/components/app-text.js +++ b/components/app-text.js @@ -1,16 +1,20 @@ import React from 'react' import { Text } from 'react-native' import styles from "../styles" +import Link from './link' export default function AppText(props) { + // we parse for links in case the text contains any return ( - <Text - style={[styles.appText, props.style]} - onPress={props.onPress} - numberOfLines={props.numberOfLines} - > - {props.children} - </Text> + <Link> + <Text + style={[styles.appText, props.style]} + onPress={props.onPress} + numberOfLines={props.numberOfLines} + > + {props.children} + </Text> + </Link> ) } diff --git a/components/cycle-day/symptoms/info-symptom.js b/components/cycle-day/symptoms/info-symptom.js index 8ebe17005c2382a004b598a990589d21490936c5..951ed47b140941fe3a5350854b87990b205a1705 100644 --- a/components/cycle-day/symptoms/info-symptom.js +++ b/components/cycle-day/symptoms/info-symptom.js @@ -1,11 +1,9 @@ import React, { Component } from 'react' import { ScrollView } from 'react-native' -import Hyperlink from 'react-native-hyperlink' import AppText from '../../app-text' import labels from '../../../i18n/en/symptom-info.js' import FramedSegment from '../../framed-segment' import styles from '../../../styles/index' -import replace from '../../helpers/replace-url-with-text' export default class InfoSymptom extends Component { render() { @@ -29,9 +27,7 @@ export default class InfoSymptom extends Component { style={styles.framedSegmentLast} title={labels[currentSymptom].title} > - <Hyperlink linkStyle={styles.link} linkText={replace} linkDefault> - <AppText>{labels[currentSymptom].text}</AppText> - </Hyperlink> + <AppText>{labels[currentSymptom].text}</AppText> </FramedSegment> </ScrollView> ) diff --git a/components/helpers/replace-url-with-text.js b/components/helpers/replace-url-with-text.js deleted file mode 100644 index 69493ebe03d685606f26cfeb73360e15e6d40fc5..0000000000000000000000000000000000000000 --- a/components/helpers/replace-url-with-text.js +++ /dev/null @@ -1,9 +0,0 @@ -import {links} from '../../i18n/en/settings' - -export default function(url) { - const link = Object.values(links).find(link => link.url === url) - if (url === 'mailto:bloodyhealth@mailbox.org') { - console.log(links.email.url === url) - } - return link ? link.text : url -} \ No newline at end of file diff --git a/components/home.js b/components/home.js index 01a74d23fdbb5d511e331191b9bd0d2b88389d9b..e9eb0f4482570ce0552b966598eaa5900e3aa0e8 100644 --- a/components/home.js +++ b/components/home.js @@ -3,163 +3,171 @@ import { ScrollView, View, TouchableHighlight, Dimensions } from 'react-native' import { LocalDate, ChronoUnit } from 'js-joda' import Icon from 'react-native-vector-icons/Entypo' import { secondaryColor, cycleDayColor, periodColor } from '../styles' -import { home as labels, bleedingPrediction as predictLabels, shared } from '../i18n/en/labels' +import { + home as labels, + bleedingPrediction as predictLabels, + shared, +} from '../i18n/en/labels' +import links from '../i18n/en/links' import cycleModule from '../lib/cycle' -import { getCycleDaysSortedByDate, getCycleDay } from '../db' import { getFertilityStatusForDay } from '../lib/sympto-adapter' import styles from '../styles' import AppText from './app-text' import DripHomeIcon from '../assets/drip-home-icons' import Button from './button' +const ShowMoreToggler = ({ isShowingMore, onToggle }) => { + const {height, width} = Dimensions.get('window') + const leftPosition = isShowingMore ? 10 : width - 40 + const style = isShowingMore ? styles.showLess : styles.showMore + const topPosition = height / 2 - styles.header.height - 30 + + return ( + <TouchableHighlight + onPress={onToggle} + style={[style, { top: topPosition, left: leftPosition}]} + > + <View style={{alignItems: 'center'}}> + <AppText>{isShowingMore ? shared.less : shared.more}</AppText> + <Icon name='chevron-thin-down' /> + </View> + </TouchableHighlight> + ) +} + +const IconText = ({ children, wrapperStyles }) => { + return ( + <View style={[styles.homeIconTextWrapper, wrapperStyles]}> + <AppText style={styles.iconText}> + { children } + </AppText> + </View> + ) +} + +const HomeElement = ({ children, onPress, buttonColor, buttonLabel }) => { + return ( + <View + onPress={ onPress } + style={ styles.homeIconElement } + > + { children } + <Button + onPress={ onPress } + backgroundColor={ buttonColor }> + { buttonLabel } + </Button> + </View> + ) +} + export default class Home extends Component { constructor(props) { super(props) - this.getCycleDayNumber = cycleModule().getCycleDayNumber - this.getBleedingPrediction = cycleModule().getPredictedMenses + const { getCycleDayNumber, getPredictedMenses } = cycleModule() + this.getCycleDayNumber = getCycleDayNumber + this.getBleedingPrediction = getPredictedMenses this.todayDateString = LocalDate.now().toString() const prediction = this.getBleedingPrediction() const fertilityStatus = getFertilityStatusForDay(this.todayDateString) this.state = { + isShowingMore: false, cycleDayNumber: this.getCycleDayNumber(this.todayDateString), predictionText: determinePredictionText(prediction), bleedingPredictionRange: getBleedingPredictionRange(prediction), ...fertilityStatus } - - this.cycleDays = getCycleDaysSortedByDate() } passTodayTo(componentName) { - const navigate = this.props.navigate + const { navigate } = this.props navigate(componentName, { date: LocalDate.now().toString() }) } + toggleShowingMore = () => { + this.setState({ isShowingMore: !this.state.isShowingMore }) + } + render() { - const cycleDayMoreText = this.state.cycleDayNumber ? - labels.cycleDayKnown(this.state.cycleDayNumber) - : + const { isShowingMore, cycleDayNumber, phase, status } = this.state + const { navigate } = this.props + const cycleDayMoreText = cycleDayNumber ? + labels.cycleDayKnown(cycleDayNumber) : labels.cycleDayNotEnoughInfo - const {height, width} = Dimensions.get('window') + const { statusText } = this.state + return ( <View flex={1}> <ScrollView> <View style={styles.homeView}> - <View style={styles.homeIconElement}> + + <HomeElement + onPress={ () => this.passTodayTo('CycleDay') } + buttonColor={ cycleDayColor } + buttonLabel={ labels.editToday } + > <View position='absolute'> <DripHomeIcon name="circle" size={80} color={cycleDayColor}/> </View> - <View style={[styles.homeIconTextWrapper, styles.wrapperCycle]}> - <AppText style={styles.iconText}> - {this.state.cycleDayNumber || labels.unknown} - </AppText> - </View> + <IconText wrapperStyles={styles.wrapperCycle}> + {cycleDayNumber || labels.unknown} + </IconText> - { this.state.showMore && + { isShowingMore && <AppText style={styles.paragraph}>{cycleDayMoreText}</AppText> } + </HomeElement> - <Button - onPress={() => this.passTodayTo('CycleDay')} - backgroundColor={cycleDayColor}> - {labels.editToday} - </Button> - - </View> - - <View style={styles.homeIconElement}> + <HomeElement + onPress={ () => this.passTodayTo('BleedingEditView') } + buttonColor={ periodColor } + buttonLabel={ labels.trackPeriod } + > <View position='absolute'> <DripHomeIcon name="drop" size={105} color={periodColor} /> </View> - <View style={[styles.homeIconTextWrapper, styles.wrapperDrop]}> - <AppText style={styles.iconText}> - {this.state.bleedingPredictionRange} - </AppText> - </View> - {this.state.showMore && + <IconText wrapperStyles={styles.wrapperDrop}> + {this.state.bleedingPredictionRange} + </IconText> + + { isShowingMore && <AppText style={styles.paragraph}> {this.state.predictionText} </AppText> } + </HomeElement> - <Button - onPress={() => { - const today = LocalDate.now().toString() - const cycleDay = getCycleDay(today) - const props = {date: today} - if (cycleDay) props.cycleDay = cycleDay - this.props.navigate('BleedingEditView', props) - }} - backgroundColor={periodColor}> - {labels.trackPeriod} - </Button> - - </View> - - <View style={styles.homeIconElement}> + <HomeElement + onPress={ () => navigate('Chart') } + buttonColor={ secondaryColor } + buttonLabel={ labels.checkFertility } + > <View style={styles.homeCircle} position='absolute' /> - <View style={[styles.homeIconTextWrapper, styles.wrapperCircle]}> - <AppText style={styles.iconText}> - {this.state.phase ? - this.state.phase.toString() - : - labels.unknown - } - </AppText> - </View> - {this.state.phase && - <AppText> - {`${labels.phase(this.state.phase)} (${this.state.status})`} - </AppText> + + <IconText wrapperStyles={styles.wrapperCircle}> + { phase ? phase.toString() : labels.unknown } + </IconText> + + { phase && + <AppText>{`${labels.phase(phase)} (${status})`}</AppText> } - {this.state.showMore && - <AppText styles={styles.paragraph}> - {this.state.statusText} - </AppText> + { isShowingMore && + <View> + <AppText styles={styles.paragraph}> + { `${statusText} ${links.moreAboutNfp.url}` } + </AppText> + </View> } - - <Button - onPress={() => this.props.navigate('Chart')} - backgroundColor={secondaryColor}> - {labels.checkFertility} - </Button> - </View> + </HomeElement> </View> - </ScrollView> - - {!this.state.showMore && - <TouchableHighlight - onPress={() => this.setState({showMore: true})} - style={[styles.showMore, { - top: height / 2 - styles.header.height - 30, - left: width - 40 - }]} - > - <View style={{alignItems: 'center'}}> - <AppText>{shared.more}</AppText> - <Icon name='chevron-thin-down' /> - </View> - </TouchableHighlight> - } - - {this.state.showMore && - <TouchableHighlight - onPress={() => this.setState({showMore: false})} - style={[styles.showLess, { - top: height / 2 - styles.header.height - 30, - left: 10 - }]} - > - <View style={{alignItems: 'center'}}> - <AppText>{shared.less}</AppText> - <Icon name='chevron-thin-down' /> - </View> - </TouchableHighlight> - } + <ShowMoreToggler + isShowingMore={isShowingMore} + onToggle={this.toggleShowingMore} + /> </View> ) } diff --git a/components/license.js b/components/license.js index 8e35b2b933fd569f8a5b4a3e52415344daff11d0..a908c5cc96dba5e90bc815ed6c7ea808d7f8c9b9 100644 --- a/components/license.js +++ b/components/license.js @@ -1,22 +1,18 @@ import React from 'react' import { ScrollView, View, BackHandler } from 'react-native' -import Hyperlink from 'react-native-hyperlink' import AppText from './app-text' import { shared } from '../i18n/en/labels' import settingsLabels from '../i18n/en/settings' import styles,{secondaryColor} from '../styles' import Button from './button' import { saveLicenseFlag } from '../local-storage' -import replace from './helpers/replace-url-with-text' const labels = settingsLabels.license export default function License({setLicense}) { return ( <ScrollView style={styles.licensePage}> - <Hyperlink linkStyle={styles.link} linkText={replace} linkDefault> - <AppText style={styles.framedSegmentTitle}>{labels.title}</AppText> - <AppText>{labels.text}</AppText> - </Hyperlink> + <AppText style={styles.framedSegmentTitle}>{labels.title}</AppText> + <AppText>{labels.text}</AppText> <View style={styles.licenseButtons}> <Button style={styles.licenseButton} diff --git a/components/link.js b/components/link.js new file mode 100644 index 0000000000000000000000000000000000000000..d0b9a8354c07677083f99f193aca751229899e6f --- /dev/null +++ b/components/link.js @@ -0,0 +1,21 @@ +import React from 'react' +import Hyperlink from 'react-native-hyperlink' +import styles from '../styles' +import links from '../i18n/en/links' + +export default function Link(props) { + return ( + <Hyperlink + linkStyle={styles.link} + linkText={replaceUrlWithText} + linkDefault + > + {props.children} + </Hyperlink> + ) +} + +function replaceUrlWithText(url) { + const link = Object.values(links).find(l => l.url === url) + return (link && link.text) || url +} \ No newline at end of file diff --git a/components/settings/about.js b/components/settings/about.js index d31731680b539dd7366b4547c8709bea08e5c455..46abf077f4e5a33735b965392012572da25009ee 100644 --- a/components/settings/about.js +++ b/components/settings/about.js @@ -1,20 +1,16 @@ import React, { Component } from 'react' import { ScrollView } from 'react-native' -import Hyperlink from 'react-native-hyperlink' import AppText from '../app-text' +import labels from '../../i18n/en/settings' +import links from '../../i18n/en/links' import FramedSegment from '../framed-segment' -import styles from '../../styles/index' -import labels, { links } from '../../i18n/en/settings' -import replace from '../helpers/replace-url-with-text' export default class AboutSection extends Component { render() { return ( <ScrollView> <FramedSegment title={labels.aboutSection.title}> - <Hyperlink linkStyle={styles.link} linkText={replace} linkDefault> - <AppText>{labels.aboutSection.text}</AppText> - </Hyperlink> + <AppText>{labels.aboutSection.text}</AppText> </FramedSegment> <FramedSegment title={labels.philosophy.title}> <AppText>{labels.philosophy.text}</AppText> @@ -23,9 +19,7 @@ export default class AboutSection extends Component { <AppText>{labels.credits.note}</AppText> </FramedSegment> <FramedSegment title={labels.website.title}> - <Hyperlink linkStyle={styles.link} linkDefault> - <AppText>{links.website.url}</AppText> - </Hyperlink> + <AppText>{links.website.url}</AppText> </FramedSegment> <FramedSegment title={labels.version.title} last> <AppText>{require('../../package.json').version}</AppText> diff --git a/components/settings/license.js b/components/settings/license.js index ca465091b13ec39c9a0d6cf7a366873ff16a6e4c..709188272c01c3470ee442d4efdf65027f31ee6f 100644 --- a/components/settings/license.js +++ b/components/settings/license.js @@ -1,20 +1,16 @@ import React, { Component } from 'react' import { View, ScrollView } from 'react-native' -import Hyperlink from 'react-native-hyperlink' import AppText from '../app-text' import styles from '../../styles/index' import labels from '../../i18n/en/settings' -import replace from '../helpers/replace-url-with-text' export default class License extends Component { render() { return ( <ScrollView> <View style={styles.framedSegment}> - <Hyperlink linkStyle={styles.link} linkText={replace} linkDefault> - <AppText style={styles.framedSegmentTitle}>{`${labels.license.title} `}</AppText> - <AppText>{`${labels.license.text} `}</AppText> - </Hyperlink> + <AppText style={styles.framedSegmentTitle}>{`${labels.license.title} `}</AppText> + <AppText>{`${labels.license.text} `}</AppText> </View> </ScrollView> ) diff --git a/components/settings/nfp-settings/index.js b/components/settings/nfp-settings/index.js index f24a8b62a997d092621d996e1db2d2591e477af6..f39b26fa3ce394677a99f68d197dc58b43d54321 100644 --- a/components/settings/nfp-settings/index.js +++ b/components/settings/nfp-settings/index.js @@ -2,7 +2,6 @@ import React, { Component } from 'react' import { ScrollView, View } from 'react-native' -import Hyperlink from 'react-native-hyperlink' import styles, { iconStyles } from '../../../styles' import labels from '../../../i18n/en/settings' import AppText from '../../app-text' @@ -10,7 +9,6 @@ import FramedSegment from '../../framed-segment' import TempSlider from './temp-slider' import UseCervixSetting from './use-cervix' import Icon from 'react-native-vector-icons/Entypo' -import replaceUrlWithText from '../../helpers/replace-url-with-text' export default class Settings extends Component { constructor(props) { @@ -33,9 +31,8 @@ export default class Settings extends Component { <Icon name="info-with-circle" style={iconStyles.infoInHeading}/> <AppText style={styles.framedSegmentTitle}>{`${labels.preOvu.title} `}</AppText> </View> - <Hyperlink linkStyle={styles.link} linkText={replaceUrlWithText} linkDefault> - <AppText>{labels.preOvu.note}</AppText> - </Hyperlink> + <AppText>{labels.preOvu.note}</AppText> + <AppText>{labels.preOvu.note}</AppText> </FramedSegment> </ScrollView> ) diff --git a/i18n/en/labels.js b/i18n/en/labels.js index ebd768a7ac190a49719ca6ded740e0d10eb3863b..3324c7df542e9a3071031a4f9afd2f83896ca3f9 100644 --- a/i18n/en/labels.js +++ b/i18n/en/labels.js @@ -96,7 +96,7 @@ export const home = { cycleDayKnown: d => `Your last period started ${getDaysDescriptor(d)}.`, trackPeriod: 'track your period', checkFertility: 'check your fertility', - phase: n => `${['1st', '2nd', '3rd'][n - 1]} cycle phase` + phase: n => `${['1st', '2nd', '3rd'][n - 1]} cycle phase`, } const getDaysDescriptor = cycleDayNumber => { diff --git a/i18n/en/links.js b/i18n/en/links.js new file mode 100644 index 0000000000000000000000000000000000000000..f655bbdca74c3b53112d58d2fdbc3e8215065776 --- /dev/null +++ b/i18n/en/links.js @@ -0,0 +1,21 @@ +export default { + gitlab: { + url: 'https://gitlab.com/bloodyhealth/drip', + text: 'GitLab' + }, + email: { + url: 'mailto:bloodyhealth@mailbox.org', + text: 'email' + }, + wiki: { + url: 'https://gitlab.com/bloodyhealth/drip/wikis/home', + text: 'wiki' + }, + website: { + url: 'https://bloodyhealth.gitlab.io/' + }, + moreAboutNfp: { + url: 'https://gitlab.com/bloodyhealth/drip/wikis/nfp/intro', + text: 'More' + }, +} diff --git a/i18n/en/settings.js b/i18n/en/settings.js index 2f18fcff7d54a9585d287d85373a413f76d9dee2..dcabf61bfa9c47bdbcd65c5f079d8fe90fe7385e 100644 --- a/i18n/en/settings.js +++ b/i18n/en/settings.js @@ -1,21 +1,4 @@ - -export const links = { - gitlab: { - url: 'https://gitlab.com/bloodyhealth/drip', - text: 'GitLab' - }, - email: { - url: 'mailto:bloodyhealth@mailbox.org', - text: 'email' - }, - wiki: { - url: 'https://gitlab.com/bloodyhealth/drip/wikis/', - text: 'wiki' - }, - website: { - url: 'https://bloodyhealth.gitlab.io/' - } -} +import links from './links' export default { menuTitles: { @@ -123,7 +106,7 @@ export default { title: 'drip is an open-source cycle tracking app', text: `Copyright (C) 2019 Bloody Health GbR -This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details: https://www.gnu.org/licenses/gpl-3.0.html +This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details: https://www.gnu.org/licenses/gpl-3.0.html You can contact us by bloodyhealth@mailbox.org.` }, diff --git a/i18n/en/symptom-info.js b/i18n/en/symptom-info.js index 5d940929c69388589eb7c034ccedd957960d46fd..8861325080c3a1fbc4c6ad33b24f63c1d3906d53 100644 --- a/i18n/en/symptom-info.js +++ b/i18n/en/symptom-info.js @@ -1,4 +1,4 @@ -import {links} from './settings' +import links from './links' export const generalInfo = { chartNfp: `On the chart, you can track fertility signs. When both a valid temperature shift and a mucus or cervix shift have been detected, an orange line will be displayed on the chart. This indicates the end of the peri-ovulatory and the beginning of the post-ovulatory phase.`, diff --git a/lib/sympto-adapter.js b/lib/sympto-adapter.js index 980cbfe7c79aa4efd3c71d8791fd5e22104f28f3..858a14ef87a559ecad347eeb61227f716d1d8ba7 100644 --- a/lib/sympto-adapter.js +++ b/lib/sympto-adapter.js @@ -7,7 +7,8 @@ export function getFertilityStatusForDay(dateString) { const status = getCycleStatusForDay(dateString) if (!status) return { status: labels.fertile, - phase: null + phase: null, + statusText: labels.unknown } const phases = Object.keys(status.phases)