Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a4e0fa64e9 | |||
| fd6f39fbc4 | |||
| d492a27797 | |||
| 22a451d4e6 | |||
| 86bdb8a1f8 | |||
| 4212906917 |
@@ -18,6 +18,10 @@ const AboutSection = () => {
|
|||||||
<AppPage title={t('title')}>
|
<AppPage title={t('title')}>
|
||||||
<Segment>
|
<Segment>
|
||||||
<AppText>{t('intro.text')}</AppText>
|
<AppText>{t('intro.text')}</AppText>
|
||||||
|
<Button isCTA isSmall onPress={() => Linking.openURL(links.faq.url)}>
|
||||||
|
{t('intro.faq')}
|
||||||
|
</Button>
|
||||||
|
<AppText>{t('intro.contact')}</AppText>
|
||||||
<ButtonRow>
|
<ButtonRow>
|
||||||
{[links.email, links.gitlab, links.website].map((link) => (
|
{[links.email, links.gitlab, links.website].map((link) => (
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { FlatList, StyleSheet, View } from 'react-native'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
// import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
|
import AppModal from '../common/app-modal'
|
||||||
|
import AppText from '../common/app-text'
|
||||||
|
|
||||||
|
import symOccModule from '../../lib/sympto-occurance'
|
||||||
|
import { Spacing, Typography, Colors } from '../../styles'
|
||||||
|
|
||||||
|
// const { t } = useTranslation(null, { keyPrefix: 'stats' })
|
||||||
|
|
||||||
|
const SymptomOccurance = ({ onClose }) => {
|
||||||
|
const cycleDays = symOccModule().getCycleStartsOfLastYear()
|
||||||
|
if (!cycleDays || cycleDays.length === 0) return false
|
||||||
|
console.log('cycle starts:', cycleDays)
|
||||||
|
|
||||||
|
const headacheDays = symOccModule().getPainDaysOfLastYear()
|
||||||
|
console.log('pain', headacheDays)
|
||||||
|
|
||||||
|
const cycleDaysOfPain = symOccModule().getCycleDayForPainDays(
|
||||||
|
cycleDays,
|
||||||
|
headacheDays
|
||||||
|
)
|
||||||
|
console.log('cycle days of pain', cycleDaysOfPain)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AppModal onClose={onClose}>
|
||||||
|
<View>
|
||||||
|
<FlatList
|
||||||
|
ListHeaderComponent={FlatListHeader}
|
||||||
|
contentContainerStyle={styles.container}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</AppModal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
SymptomOccurance.propTypes = {
|
||||||
|
onClose: PropTypes.func,
|
||||||
|
}
|
||||||
|
|
||||||
|
const FlatListHeader = () => (
|
||||||
|
<View style={styles.row}>
|
||||||
|
<View style={styles.accentCell}>
|
||||||
|
<AppText style={styles.header}>
|
||||||
|
{'When did you experience headaches in the last year?'}
|
||||||
|
</AppText>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
divider: {
|
||||||
|
height: 1,
|
||||||
|
width: '100%',
|
||||||
|
backgroundColor: Colors.grey,
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
...Typography.accentOrange,
|
||||||
|
paddingVertical: Spacing.small,
|
||||||
|
},
|
||||||
|
headerDivider: {
|
||||||
|
borderBottomColor: Colors.purple,
|
||||||
|
borderBottomWidth: 2,
|
||||||
|
},
|
||||||
|
row: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
paddingVertical: Spacing.tiny,
|
||||||
|
backgroundColor: 'white',
|
||||||
|
},
|
||||||
|
cell: {
|
||||||
|
flex: 2,
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
accentCell: {
|
||||||
|
flex: 3,
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
minHeight: '40%',
|
||||||
|
minWidth: '95%',
|
||||||
|
paddingHorizontal: Spacing.base,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default SymptomOccurance
|
||||||
@@ -8,6 +8,7 @@ import Button from '../common/button'
|
|||||||
import Footnote from '../common/Footnote'
|
import Footnote from '../common/Footnote'
|
||||||
import StatsOverview from './StatsOverview'
|
import StatsOverview from './StatsOverview'
|
||||||
import PeriodDetailsModal from './PeriodDetailsModal'
|
import PeriodDetailsModal from './PeriodDetailsModal'
|
||||||
|
import SymptomOccurance from './SymptomOccurance'
|
||||||
|
|
||||||
import cycleModule from '../../lib/cycle'
|
import cycleModule from '../../lib/cycle'
|
||||||
import { getCycleLengthStats as getCycleInfo } from '../../lib/cycle-length'
|
import { getCycleLengthStats as getCycleInfo } from '../../lib/cycle-length'
|
||||||
@@ -19,6 +20,8 @@ const image = require('../../assets/cycle-icon.png')
|
|||||||
|
|
||||||
const Stats = () => {
|
const Stats = () => {
|
||||||
const [isStatsVisible, setIsStatsVisible] = useState(false)
|
const [isStatsVisible, setIsStatsVisible] = useState(false)
|
||||||
|
const [isSymptomOccuranceVisible, setIsSymptomOccuranceVisible] =
|
||||||
|
useState(false)
|
||||||
|
|
||||||
const { t } = useTranslation(null, { keyPrefix: 'stats' })
|
const { t } = useTranslation(null, { keyPrefix: 'stats' })
|
||||||
|
|
||||||
@@ -83,6 +86,14 @@ const Stats = () => {
|
|||||||
{isStatsVisible && (
|
{isStatsVisible && (
|
||||||
<PeriodDetailsModal onClose={() => setIsStatsVisible(false)} />
|
<PeriodDetailsModal onClose={() => setIsStatsVisible(false)} />
|
||||||
)}
|
)}
|
||||||
|
<Button isCTA onPress={() => setIsSymptomOccuranceVisible(true)}>
|
||||||
|
{t('showSymptomOccurance')}
|
||||||
|
</Button>
|
||||||
|
{isSymptomOccuranceVisible && (
|
||||||
|
<SymptomOccurance
|
||||||
|
onClose={() => setIsSymptomOccuranceVisible(false)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<Footnote>{t('footnote')}</Footnote>
|
<Footnote>{t('footnote')}</Footnote>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -73,6 +73,9 @@ export function getTemperatureDaysSortedByDate() {
|
|||||||
.filtered('temperature != null')
|
.filtered('temperature != null')
|
||||||
.sorted('date', true)
|
.sorted('date', true)
|
||||||
}
|
}
|
||||||
|
export function getPainDaysSortedByDate() {
|
||||||
|
return db.objects('CycleDay').filtered('pain != null').sorted('date', true)
|
||||||
|
}
|
||||||
|
|
||||||
export function getCycleDaysSortedByDate() {
|
export function getCycleDaysSortedByDate() {
|
||||||
const cycleDays = db.objects('CycleDay').sorted('date', true)
|
const cycleDays = db.objects('CycleDay').sorted('date', true)
|
||||||
|
|||||||
+5
-2
@@ -45,7 +45,9 @@
|
|||||||
"text": "The drips are developing this app on a volunteer basis. We are always grateful for support. This could mean condriputing to the code, giving feedback, suggesting improvements or features, testing or donating. It helps and motivates us maintaining this app and developing new features. Thank you for your support!"
|
"text": "The drips are developing this app on a volunteer basis. We are always grateful for support. This could mean condriputing to the code, giving feedback, suggesting improvements or features, testing or donating. It helps and motivates us maintaining this app and developing new features. Thank you for your support!"
|
||||||
},
|
},
|
||||||
"intro": {
|
"intro": {
|
||||||
"text": "Please note that your data is stored locally on your phone and not on a server. This means your data cannot be read by anyone else unless they have access to your phone. We want to ensure that you stay in control of your own data. If you are planning to switch or reset your phone, please remember to export your data before doing so. You can reinstall the app afterwards and import your data.\n\nIf you encounter any technical issues, don't hesitate to contact us via email. You can also contribute to the code base on Gitlab and visit our website."
|
"text": "Please note that your data is stored locally on your phone and not on a server. This means your data cannot be read by anyone else unless they have access to your phone. We want to ensure that you stay in control of your own data. If you are planning to switch or reset your phone, please remember to export your data before doing so. You can reinstall the app afterwards and import your data.\n\nIf you encounter any issues, please take a look at our Frequently Asked Questions page.",
|
||||||
|
"faq": "FAQ",
|
||||||
|
"contact": "\nIf your issue is not listed, don't hesitate to contact us via email. You can also contribute to the code base on Gitlab and visit our website."
|
||||||
},
|
},
|
||||||
"philosophy": {
|
"philosophy": {
|
||||||
"title": "Remember to think for yourself",
|
"title": "Remember to think for yourself",
|
||||||
@@ -154,7 +156,8 @@
|
|||||||
"cycleLength": "Cycle length",
|
"cycleLength": "Cycle length",
|
||||||
"bleedingDays": "Bleeding"
|
"bleedingDays": "Bleeding"
|
||||||
},
|
},
|
||||||
"footnote": "Based on the standard deviation of all your tracked periods drip. calculates a range for the starting day of the upcoming 3 periods. The range will be 3 days if your standard deviation is smaller than 1.5 and 5 days if the value is bigger.\n\nThe standard deviation tells you how much the length of your periods vary, 0 means all your periods are exactly the same length and the bigger the value the more the period length varies."
|
"footnote": "Based on the standard deviation of all your tracked periods drip. calculates a range for the starting day of the upcoming 3 periods. The range will be 3 days if your standard deviation is smaller than 1.5 and 5 days if the value is bigger.\n\nThe standard deviation tells you how much the length of your periods vary, 0 means all your periods are exactly the same length and the bigger the value the more the period length varies.",
|
||||||
|
"showSymptomOccurance": "Show details on headaches"
|
||||||
},
|
},
|
||||||
"plurals": {
|
"plurals": {
|
||||||
"day": "{{count}} day",
|
"day": "{{count}} day",
|
||||||
|
|||||||
@@ -39,4 +39,8 @@ export default {
|
|||||||
url: 'https://www.flaticon.com',
|
url: 'https://www.flaticon.com',
|
||||||
text: 'Flaticon',
|
text: 'Flaticon',
|
||||||
},
|
},
|
||||||
|
faq: {
|
||||||
|
url: 'https://dripapp.org/faq',
|
||||||
|
text: 'FAQ',
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
import * as joda from '@js-joda/core'
|
||||||
|
const LocalDate = joda.LocalDate
|
||||||
|
const DAYS = joda.ChronoUnit.DAYS
|
||||||
|
|
||||||
|
export default function config(opts) {
|
||||||
|
let cycleStartsSortedByDate
|
||||||
|
let painSortedByDate
|
||||||
|
|
||||||
|
if (!opts) {
|
||||||
|
// we only want to require (and run) the db module
|
||||||
|
// when not running the tests
|
||||||
|
cycleStartsSortedByDate = require('../db').getCycleStartsSortedByDate()
|
||||||
|
painSortedByDate = require('../db').getPainDaysSortedByDate()
|
||||||
|
// maxCycleLength = 45
|
||||||
|
} else {
|
||||||
|
cycleStartsSortedByDate = opts.cycleStartsSortedByDate || []
|
||||||
|
painSortedByDate = opts.painSortedByDate || []
|
||||||
|
// maxCycleLength = opts.maxCycleLength || 99
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCycleStartsOfLastYear() {
|
||||||
|
const today = LocalDate.parse(new Date().toISOString().slice(0, 10))
|
||||||
|
const firstRelevantDay = today.minusYears(1)
|
||||||
|
const relevantCycles = cycleStartsSortedByDate.filter(({ date }) =>
|
||||||
|
LocalDate.parse(date).isAfter(firstRelevantDay)
|
||||||
|
)
|
||||||
|
return relevantCycles.map(({ date }) => date)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPainDaysOfLastYear() {
|
||||||
|
const today = LocalDate.parse(new Date().toISOString().slice(0, 10))
|
||||||
|
const firstRelevantDay = today.minusYears(1)
|
||||||
|
const relevantPainDays = painSortedByDate.filter(
|
||||||
|
({ date, pain }) =>
|
||||||
|
LocalDate.parse(date).isAfter(firstRelevantDay) && pain.headache
|
||||||
|
)
|
||||||
|
return relevantPainDays.map(({ date }) => date)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCycleDayForPainDays(cycleStarts, painDays) {
|
||||||
|
let i = 0
|
||||||
|
const cycleStartsAsc = cycleStarts.sort().reverse()
|
||||||
|
const painDaysAsc = painDays.sort().reverse()
|
||||||
|
const painCycleDays = painDaysAsc.map((pdate) => {
|
||||||
|
if (LocalDate.parse(pdate).isBefore(LocalDate.parse(cycleStartsAsc[i]))) {
|
||||||
|
// increase index i until cycleStart of this painDay is found
|
||||||
|
for (let j = i + 1; j < cycleStartsAsc.length; j++) {
|
||||||
|
i = j
|
||||||
|
if (
|
||||||
|
!LocalDate.parse(cycleStartsAsc[i]).isAfter(LocalDate.parse(pdate))
|
||||||
|
) {
|
||||||
|
// not(C > P) === C ≤ P
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return LocalDate.parse(cycleStartsAsc[i]).until(
|
||||||
|
LocalDate.parse(pdate),
|
||||||
|
DAYS
|
||||||
|
)
|
||||||
|
})
|
||||||
|
return painCycleDays
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
getCycleStartsOfLastYear,
|
||||||
|
getPainDaysOfLastYear,
|
||||||
|
getCycleDayForPainDays,
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-1
@@ -7,7 +7,8 @@
|
|||||||
"Tina Baumann",
|
"Tina Baumann",
|
||||||
"Sofiya Tepikin",
|
"Sofiya Tepikin",
|
||||||
"Mariya Zadnepryanets",
|
"Mariya Zadnepryanets",
|
||||||
"Lisa Hillebrand"
|
"Lisa Hillebrand",
|
||||||
|
"Martha Dörfler"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "react-native start",
|
"start": "react-native start",
|
||||||
|
|||||||
Reference in New Issue
Block a user