Compare commits

...

24 Commits

Author SHA1 Message Date
bl00dymarie cd96baec22 Release: 1.2409.26 2024-09-26 19:56:18 +02:00
bl00dymarie 93f412e983 Fix: Clean/clarify condition for bleedingDeleted 2024-09-26 18:19:05 +02:00
tina a5b058486e Fix: updates Cycle days once bleeding value is taken out 2024-09-25 17:21:14 +02:00
tina 95cf6f6001 Fix: Pipeline problems 2024-09-25 16:48:55 +02:00
tina 540d6aee03 Fix: disabled exclude when bleeding value is removed 2024-09-25 16:09:35 +02:00
bl00dymarie 0f7e3fe0ad Fix: Sets isCycleStart to false when bleeding value gets deleted 2024-09-09 18:43:57 +02:00
bl00dymarie c21fd193ce Fix: Ensure that bleeding.value spotting is displayed on symptomBox 2024-09-09 18:25:26 +02:00
bl00dymarie d5dc4ada12 Release: 1.2405.8 2024-05-08 12:10:07 +02:00
bl00dymarie 01582c4e16 Chore: Merge exclude functionality with latest main 2024-04-09 13:12:09 +02:00
Liv e6134b4592 Remove line in bleeding and change wording 2024-04-09 13:00:19 +02:00
Liv 3b34ce7134 Added disk as option and moved toggle 2024-04-09 12:59:07 +02:00
Liv d86f3ec9eb Fix spelling symtom -> symptom 2024-04-09 12:55:13 +02:00
Liv 583c9c032c Fix labels and clean up code 2024-04-09 12:51:43 +02:00
Liv 9fbe99e23b Basic working version that includes period products 2024-04-09 12:51:31 +02:00
bl00dymarie e6c253f2cb Merge branch 'Chore/Update-run-script-for-which-iPhone-simulator' into 'main'
Chore: Update run script for which iPhone simulator

See merge request bloodyhealth/drip!680
2024-04-08 10:47:54 +00:00
bl00dymarie 06fab6d2ca Chore: Update run script for which iPhone simulator 2024-04-08 12:39:36 +02:00
bl00dymarie 35224ba722 Merge branch 'Chore/Update-changelog' into 'main'
Update Changelog for v1.2403.19

See merge request bloodyhealth/drip!674
2024-03-25 14:33:12 +00:00
bl00dymarie 2ca033d6d1 Update internal Nfp wording 2024-03-25 15:03:40 +01:00
bl00dymarie 09edaec721 Merge branch 'Chore/Update-RELEASE' into 'main'
Add extensive list of release steps

See merge request bloodyhealth/drip!677
2024-03-25 13:31:17 +00:00
bl00dymarie 7bbb6eaeab Fix formatting 2024-03-20 19:06:34 +01:00
bl00dymarie 09cb31035c Fix anchor links 2024-03-20 19:02:24 +01:00
bl00dymarie 536017914c Add extensive list of release steps 2024-03-20 18:55:58 +01:00
bl00dymarie b2457f4751 Update changelog for v1.2403.19 after implenting user testing feedback 2024-03-19 14:28:09 +01:00
bl00dymarie fd10a78a40 Update Changelog for v1.2403.11 2024-03-19 14:24:19 +01:00
16 changed files with 563 additions and 158 deletions
+41
View File
@@ -2,6 +2,47 @@
All notable changes to this project will be documented in this file.
## v1.2403.19 Android & iOS
### Changes
- Disables temperature slider if temperature tracking off
- Disables secondary symptom if fertility and or cervix/cervical mucus are off
- Disables temperature reminder if temperature tracking off
- Disabled period reminder if period predictions off
- Return from sympto adapter if fertility off
- Restructure settings menu
- Unify wording to "sympto-thermal method"
- Format decimal to x.0 instead of x.00 used for standard deviation and average cycle in stats
- Use SelectTabGroup for secondary symptom customization
- Android changes after updating React Native to 0.68.3
- Update Android Gradle plugin from 7.0.3 to 7.0.4
- Update NDK to "24.0.8215888" only for M1 users which added support for aarch64
- Update metadata phone screenshots for Fdroid store listing
- Updated dependencies:
- @react-native-community/datetimepicker from 6.3.1 to 7.2.0
- @react-native-async-storage/async-storage from ^1.17.9 to ^1.18.2
- metro-react-native-babel-preset from ^0.66.2 to ^0.67.0
### Adds
- Customization settings can turn on & off:
- Tracking categories
- Period predictions
- Fertility phases calculation
- Home displays text elements depending on customization settings
- Chart displays tracking category elements depending on customization settings
- CycleDay displays tracking category elements and exclude switch depending on customization settings
- Reminder can be disabled depending on customization settings
- Adds disabled and more styling to AppSwitch
- Adds TrackingCategorySwitch
- Adds disabled, more styling and alert to SelectTabGroup
- Adds more marginTop to License page
- Adds info text to Password menu item in Settings
## v1.2401.17 iOS
### Changes
+76 -21
View File
@@ -1,14 +1,31 @@
# How to release a new app version for Android
# How to release
_Note: You need the release-key for Android to bundle a signed release that can be uploaded and published via the Google Play Store. A similar process for Apple requires a certificate to upload and publish the app to the App Store. More documentation on 'How to release a new app version for iOS' coming soon._
drip is developed in React Native for iOS and Android and is released on 4 different platforms:
# Table of Contents
1. [Google Play Store](https://play.google.com/store/apps/details?id=com.drip)
2. [Apple App Store](https://apps.apple.com/us/app/drip/id1584564949)
3. [F-Droid](https://f-droid.org/packages/com.drip/)
4. [drip Website](https://dripapp.org)
1. [version updating](#Version-updating)
2. [android building](#Building-in-Android)
- [APK](#APK)
- [AAB](#AAB)
3. [release sharing](#Share-the-release)
In an ideal world the app version is the same across platforms. In reality this has never been the case.
Releasing a new version is very exciting and brings happy changes like fixing a bug, improving a feature, updating dependencies or adding a new functionality to the app. It is more than just pressing the button "publish new version".
_Note_: You need the release-key for Android to bundle a signed release that can be uploaded and published via the Google Play Store. A similar process for Apple requires a certificate to upload and publish the app to the App Store.
### Release steps
1. [Version updating](#version-updating)
2. [Android builds](#android-builds)
3. [iOS builds](#ios-builds)
4. [User testing](#user-testing)
5. [Changelog](#changelog)
6. [Release notes](#release-notes)
7. [Release tag](#release-tag)
8. [Phone screenshots](#phone-screenshots)
9. [Publishing](#publishing)
10. [Communication](#communication)
11. [Self care](#self-care)
## Version updating
@@ -34,7 +51,7 @@ Update the version number for iOS in `ios/drip/Info.plist` under:
<string>1.2403.19</string>
```
## Building in Android
### Android builds
APK versus AAB
@@ -42,7 +59,7 @@ APK versus AAB
(https://developer.android.com/build/building-cmdline)
### APK
#### APK
To build a release apk file, run the following command:
@@ -64,7 +81,7 @@ _which is a shortcut for:_ `zipalign -v -p 4 ./android/app/build/outputs/apk/rel
It adds a file name `app-release_signed.apk` in the same folder in `./android/app/build/outputs/apk/release/`
### AAB
#### AAB
To build a release aab file, run:
@@ -84,7 +101,7 @@ yarn sign-android-aab-release
_which is a shortcut for:_ `jarsigner -keystore ./android/app/drip-release-key.keystore ./android/app/build/outputs/bundle/release/app-release.aab drip-release-key`
## Building in iOS
### iOS builds
To build an .ipa archive file for an upload to the AppStore you need to go to xCode and select Build -> "Any iOS Device" and under "Product" -> "Archive".
@@ -95,29 +112,63 @@ Once the archiving process has completed you can chose to do the following:
- TestFlight & App Store for when you want to upload it for external testing and/or production release
- TestFlight Internal Only for when you want to upload it for internal testing
## Share the release
## User testing
### Gitlab repository
To enable external testing you need to remember that Google Play and Apple App Store might take up to 1 day for their review process. "External testing" for iOS allows testing drip on Testflight anonymously via a public link. "Open testing" for Android allows testing drip on Google Play as beta tester below the normal production listing.
For a quick and easy way to share an apk to testers who are willing to sideload drip onto their Android phones, do this: Upload a signed apk to the Gitlab repository of the drip website under `/release` https://gitlab.com/bloodyhealth/bloodyhealth.gitlab.io/-/tree/main/release and maybe adapt the name of the apk with a more specific name than "app-release.apk". Now you can simply share a direct link to download your newly bundled apk, e.g. [a download link for v1.2311.14](https://gitlab.com/bloodyhealth/bloodyhealth.gitlab.io/-/blob/main/release/v1.2311.14.apk).
## Changelog
The [changelog](https://gitlab.com/bloodyhealth/drip/-/blob/main/CHANGELOG.md) should reflect the technical / code changes between a previous and the new version. Please update the changelog file with any relevant additions, fixes and changes in the following format:
**v1.yymm.d**
**Changes**
Changing the color of funky button
Updating a library from 1.2.3 to 2.3.4
**Adds**
New feature for calendar
**Fixed**
Small bug in chart
## Release notes
These notes are for the users and curious ones who may want to start using drip. They should be based on the changelog but written in a friendly and easy to understand way. The focus is on the user perspective and the impact of the changes for the user. Behind the scenes and in depth code changes are less relevant.
Google Play limits these notes to 500 characters, whereas Apple's App Store limits these notes to 4.000 characters. In Fdroid there are no release notes.
## Release tag
[Tags](https://gitlab.com/bloodyhealth/drip/-/tags) can mark a specific point in the coding/commmit history and helps us identify the version status of a released app. They are named "iOS-v1.2401.17" or "Release-v1.yymm.d".
## Phone screenshots
If there are visual changes in the app you may want to update the screenshots for the Google Play Store listing, which allows up to 8 and for Apple's App Store, which allows up to 10 screenshots. Keep in mind that both Google Play and Apple have specific resolution requirements. You'll find Google's in Grow -> Store presence -> Main Store Listing -> Phone screenshots and Apple's on the main App Store Connect site. Here is a link for [Apple's screenshot specifications](https://developer.apple.com/help/app-store-connect/reference/screenshot-specifications).
Please also update [phone screenshots for the website](https://gitlab.com/bloodyhealth/bloodyhealth.gitlab.io/-/tree/main/assets) and set links on [/index](https://gitlab.com/bloodyhealth/bloodyhealth.gitlab.io/-/blob/f3da9776b1943ffa32458e74ef86eeca98c1891c/index.html#L47) and [/media](https://gitlab.com/bloodyhealth/bloodyhealth.gitlab.io/-/blob/c7f999bb7ad736345321537cbffa3f4c24eeee6d/media.html#L33) that can then also be attached to a social media post.
## Publishing
### Google Play Console
Upload a signed aab to the [Google Play Console for developers](https://play.google.com/console/) and add it to the "App bundle explorer". This requires a higher versionCode and a different version name compared to previously uploaded aab or apk files.
You can decide if you want the new app version to get released for testing (internal, closed or open) or for production. Keep in mind that any track other than "internal testing" triggers an external review by Google and might take a few hours.
#### Phone screenshots
### Apple App Store Connect
If there are visual changes in the app you may want to update the screenshots for the Google Play Store listing. Keep in mind that Google Play has specific resolution requirements. You'll find them in Grow -> Store presence -> Main Store Listing -> Phone screenshots.
Upload a new version and submit it for review, before it can be published.
### drip website
After a new version has been published on Google Play (or F-Droid) the apk version that is downloadable directly from the [drip website](https://dripapp.org) needs to get updated as well. Therefore you upload a signed apk to the [repository](https://gitlab.com/bloodyhealth/bloodyhealth.gitlab.io/) and adapt the name and link on /index.html.
Last time I checked it was [here](f3da9776b1943ffa32458e74ef86eeca98c1891c/index.html#L114).
After a new version has been published on Google Play (or F-Droid) the apk version that is downloadable directly from the [drip website](https://dripapp.org) needs to get updated as well. Therefore you upload a signed apk to the [repository](https://gitlab.com/bloodyhealth/bloodyhealth.gitlab.io/) as [we did in this commit](https://gitlab.com/bloodyhealth/bloodyhealth.gitlab.io/-/commit/f8c0f90c1ae9f23bf8e1bc311790b85443149a4d), and adapt the name and link on /index.html [as we did in this commit](https://gitlab.com/bloodyhealth/bloodyhealth.gitlab.io/-/commit2f8850ff5fa78615a4f335b625ea4a67d4acf03a) and [this commit](https://gitlab.com/bloodyhealth/bloodyhealth.gitlab.io/-/commit/f3da9776b1943ffa32458e74ef86eeca98c1891c). Last time I checked it was [here](f3da9776b1943ffa32458e74ef86eeca98c1891c/index.html#L114).
#### Phone screenshots
Please also update [phone screenshots here](https://gitlab.com/bloodyhealth/bloodyhealth.gitlab.io/-/tree/main/assets) and set links on [/index](https://gitlab.com/bloodyhealth/bloodyhealth.gitlab.io/-/blob/f3da9776b1943ffa32458e74ef86eeca98c1891c/index.html#L47) and [/media](https://gitlab.com/bloodyhealth/bloodyhealth.gitlab.io/-/blob/c7f999bb7ad736345321537cbffa3f4c24eeee6d/media.html#L33) that can then also be attached to a social media post.
## Communication
You probably want to share the app update by posting on one or more of these platforms:
@@ -126,3 +177,7 @@ You probably want to share the app update by posting on one or more of these pla
- [Ko-fi](https://ko-fi.com/dripapp)
- [Linkedin](https://www.linkedin.com/company/34899684/)
- Different tech, privacy, feminist oriented slacks
## Self care
Congratulations. Take a break, eat some chocolate, go see a live show of your favorite band, masturbate <3!
+2 -2
View File
@@ -134,8 +134,8 @@ android {
applicationId "com.drip"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 33
versionName "1.2403.19"
versionCode 35
versionName "1.2409.26"
ndk {
abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
}
+3 -12
View File
@@ -23,20 +23,14 @@ const SymptomBox = ({
const { t } = useTranslation(null, { keyPrefix: 'symptoms' })
const isSymptomEdited = editedSymptom === symptom
const isSymptomDisabled = isDateInFuture(date) && symptom !== 'note'
const isExcluded = symptomData !== null ? symptomData.exclude : false
const iconColor = isSymptomDisabled ? Colors.greyLight : Colors.grey
const iconName = `drip-icon-${symptom}`
const symptomNameStyle = [
styles.symptomName,
isSymptomDisabled && styles.symptomNameDisabled,
isExcluded && styles.symptomNameExcluded,
]
const textStyle = [
styles.text,
isSymptomDisabled && styles.textDisabled,
isExcluded && styles.textExcluded,
]
const textStyle = [styles.text, isSymptomDisabled && styles.textDisabled]
return (
<>
@@ -63,11 +57,11 @@ const SymptomBox = ({
/>
<View style={styles.textContainer}>
<AppText style={symptomNameStyle}>{t(symptom)}</AppText>
{symptomDataToDisplay && (
{symptomDataToDisplay ? (
<AppText style={textStyle} numberOfLines={4}>
{symptomDataToDisplay}
</AppText>
)}
) : null}
</View>
</TouchableOpacity>
</>
@@ -121,9 +115,6 @@ const styles = StyleSheet.create({
textDisabled: {
color: Colors.greyLight,
},
textExcluded: {
color: Colors.grey,
},
})
export default SymptomBox
+37 -18
View File
@@ -12,7 +12,7 @@ import SelectBoxGroup from './select-box-group'
import SelectTabGroup from './select-tab-group'
import Temperature from './temperature'
import { blank, save, shouldShow, symtomPage } from '../helpers/cycle-day'
import { blank, save, shouldShow, symptomPage } from '../helpers/cycle-day'
import { showToast } from '../helpers/general'
import { fertilityTrackingObservable } from '../../local-storage'
@@ -21,9 +21,10 @@ import info from '../../i18n/en/symptom-info'
import { Colors, Containers, Sizes, Spacing } from '../../styles'
const SymptomEditView = ({ date, onClose, symptom, symptomData }) => {
const symptomConfig = symtomPage[symptom]
const symptomConfig = symptomPage[symptom]
const [data, setData] = useState(symptomData ? symptomData : blank[symptom])
const [shouldShowInfo, setShouldShowInfo] = useState(false)
const isBleeding = symptom === 'bleeding'
const getParsedData = () => JSON.parse(JSON.stringify(data))
const onPressLearnMore = () => setShouldShowInfo(!shouldShowInfo)
const isFertilityTrackingEnabled = fertilityTrackingObservable.value
@@ -117,6 +118,23 @@ const SymptomEditView = ({ date, onClose, symptom, symptomData }) => {
textAlignVertical: 'top',
}
{
/* show exclude AppSwitch for bleeding, mucus, cervix, temperature */
}
{
/* but if fertility is off only for bleeding */
}
const excludeToggle = shouldShow(symptomConfig.excludeText) &&
(isBleeding || isFertilityTrackingEnabled) && (
<Segment style={styles.segmentBorder}>
<AppSwitch
onToggle={onExcludeToggle}
text={symptomPage[symptom].excludeText}
value={data.exclude}
/>
</Segment>
)
return (
<AppModal onClose={onSave}>
<ScrollView
@@ -130,10 +148,16 @@ const SymptomEditView = ({ date, onClose, symptom, symptomData }) => {
save={(value, field) => onSaveTemperature(value, field)}
/>
)}
{/* There should not be a line between the bleeding tab group and the exclude toggle */}
{shouldShow(symptomConfig.selectTabGroups) &&
symtomPage[symptom].selectTabGroups.map((group) => {
symptomPage[symptom].selectTabGroups.map((group) => {
return (
<Segment key={group.key} style={styles.segmentBorder}>
<Segment
key={group.key}
style={styles.segmentBorder}
last={isBleeding}
>
<AppText style={styles.title}>{group.title}</AppText>
<SelectTabGroup
activeButton={data[group.key]}
@@ -143,8 +167,12 @@ const SymptomEditView = ({ date, onClose, symptom, symptomData }) => {
</Segment>
)
})}
{/*for bleeding, we want to move the "exclude" toggle up between the tab and box groups, all other symptoms should still have it at the bottom*/}
{isBleeding && excludeToggle}
{shouldShow(symptomConfig.selectBoxGroups) &&
symtomPage[symptom].selectBoxGroups.map((group) => {
symptomPage[symptom].selectBoxGroups.map((group) => {
const isOtherSelected =
data['other'] !== null &&
data['other'] !== false &&
@@ -169,21 +197,12 @@ const SymptomEditView = ({ date, onClose, symptom, symptomData }) => {
</Segment>
)
})}
{/* show exclude AppSwitch for bleeding, mucus, cervix, temperature */}
{/* but if fertility is off only for bleeding */}
{shouldShow(symptomConfig.excludeText) &&
(symptom === 'bleeding' || isFertilityTrackingEnabled) && (
<Segment style={styles.segmentBorder}>
<AppSwitch
onToggle={onExcludeToggle}
text={symtomPage[symptom].excludeText}
value={data.exclude}
/>
</Segment>
)}
{!isBleeding && excludeToggle}
{shouldShow(symptomConfig.note) && (
<Segment style={styles.segmentBorder}>
<AppText>{symtomPage[symptom].note}</AppText>
<AppText>{symptomPage[symptom].note}</AppText>
<AppTextInput
{...inputProps}
onChangeText={onEditNote}
+95 -9
View File
@@ -23,6 +23,7 @@ const noteDescription = labels.noteExplainer
const painLabels = labels.pain.categories
const sexLabels = labels.sex.categories
const temperatureLabels = labels.temperature
const productLabels = labels.products.categories
const minutes = ChronoUnit.MINUTES
@@ -60,6 +61,15 @@ export const blank = {
bleeding: {
exclude: false,
value: null,
pad: null,
tampon: null,
underwear: null,
cup: null,
softTampon: null,
disk: null,
none: null,
other: null,
note: null,
},
cervix: {
exclude: false,
@@ -125,11 +135,10 @@ export const blank = {
},
}
export const symtomPage = {
export const symptomPage = {
bleeding: {
excludeText: labels.bleeding.exclude.explainer,
note: null,
selectBoxGroups: null,
selectTabGroups: [
{
key: 'value',
@@ -137,6 +146,13 @@ export const symtomPage = {
title: labels.bleeding.heaviness.explainer,
},
],
selectBoxGroups: [
{
key: 'products',
options: productLabels,
title: labels.products.explainer,
},
],
},
cervix: {
excludeText: cervixLabels.excludeExplainer,
@@ -246,10 +262,49 @@ export const symtomPage = {
export const save = {
bleeding: (data, date, shouldDeleteData) => {
const { exclude, value } = data
const isDataEntered = isNumber(value)
const {
value,
pad,
tampon,
underwear,
cup,
softTampon,
disk,
none,
other,
note,
} = data
let { exclude } = data
const isDataEntered = [
'value',
'pad',
'tampon',
'underwear',
'cup',
'softTampon',
'disk',
'none',
'other',
'note',
].some((value) => typeof data[value] === 'number' || data[value] == true)
// covering the case when a bleeding value was entered, "excluded" and saved. But then the bleeding value was deleted again.
exclude = isNumber(value) ? exclude : false
const valuesToSave =
shouldDeleteData || !isDataEntered ? null : { value, exclude }
shouldDeleteData || !isDataEntered
? null
: {
exclude,
value,
pad,
tampon,
underwear,
cup,
softTampon,
disk,
none,
other,
note,
}
saveSymptom('bleeding', date, valuesToSave)
},
@@ -329,10 +384,41 @@ const saveBoxSymptom = (data, date, shouldDeleteData, symptom) => {
}
const label = {
bleeding: ({ value, exclude }) => {
if (isNumber(value)) {
const bleedingLabel = bleedingLabels[value]
return exclude ? `(${bleedingLabel})` : bleedingLabel
bleeding: (bleeding) => {
bleeding = mapRealmObjToJsObj(bleeding)
const bleedingLabel = []
if (
bleeding &&
Object.values({ ...bleeding }).some(
(val) => typeof val === 'number' || val == true
)
) {
Object.keys(bleeding).forEach((key) => {
if (bleeding[key] != null && key === 'value') {
bleedingLabel.push(
bleeding.exclude
? `(${bleedingLabels[bleeding[key]]})`
: bleedingLabels[bleeding[key]]
)
}
if (
bleeding[key] &&
key !== 'other' &&
key !== 'note' &&
key !== 'value' &&
key !== 'exclude'
) {
bleedingLabel.push(bleedingLabels[key] || productLabels[key])
}
if (key === 'other' && bleeding.other) {
let label = productLabels[key]
if (bleeding.note) {
label = `${label} (${bleeding.note})`
}
bleedingLabel.push(label)
}
})
return bleedingLabel.join(', ')
}
},
temperature: ({ value, time, exclude }) => {
+1 -1
View File
@@ -65,6 +65,7 @@ export function getBleedingDaysSortedByDate() {
return db
.objects('CycleDay')
.filtered('bleeding != null')
.filtered('bleeding.value != null')
.sorted('date', true)
}
export function getTemperatureDaysSortedByDate() {
@@ -88,7 +89,6 @@ export function getCycleStartsSortedByDate() {
export function saveSymptom(symptom, date, val) {
let cycleDay = getCycleDay(date)
if (!cycleDay) cycleDay = createCycleDay(date)
db.write(() => {
if (symptom === 'bleeding') {
const mensesDaysAfter = getMensesDaysRightAfter(cycleDay)
+195
View File
@@ -0,0 +1,195 @@
const TemperatureSchema = {
name: 'Temperature',
properties: {
value: 'double',
exclude: 'bool',
time: {
type: 'string',
optional: true,
},
note: {
type: 'string',
optional: true,
},
},
}
const BleedingSchema = {
name: 'Bleeding',
properties: {
value: { type: 'int', optional: true },
exclude: 'bool',
pad: { type: 'bool', optional: true },
tampon: { type: 'bool', optional: true },
underwear: { type: 'bool', optional: true },
cup: { type: 'bool', optional: true },
softTampon: { type: 'bool', optional: true },
disk: { type: 'bool', optional: true },
none: { type: 'bool', optional: true },
other: { type: 'bool', optional: true },
note: { type: 'string', optional: true },
},
}
const MucusSchema = {
name: 'Mucus',
properties: {
feeling: { type: 'int', optional: true },
texture: { type: 'int', optional: true },
value: { type: 'int', optional: true },
exclude: 'bool',
},
}
const CervixSchema = {
name: 'Cervix',
properties: {
opening: { type: 'int', optional: true },
firmness: { type: 'int', optional: true },
position: { type: 'int', optional: true },
exclude: 'bool',
},
}
const NoteSchema = {
name: 'Note',
properties: {
value: 'string',
},
}
const DesireSchema = {
name: 'Desire',
properties: {
value: 'int',
},
}
const SexSchema = {
name: 'Sex',
properties: {
solo: { type: 'bool', optional: true },
partner: { type: 'bool', optional: true },
condom: { type: 'bool', optional: true },
pill: { type: 'bool', optional: true },
iud: { type: 'bool', optional: true },
patch: { type: 'bool', optional: true },
ring: { type: 'bool', optional: true },
implant: { type: 'bool', optional: true },
diaphragm: { type: 'bool', optional: true },
none: { type: 'bool', optional: true },
other: { type: 'bool', optional: true },
note: { type: 'string', optional: true },
},
}
const PainSchema = {
name: 'Pain',
properties: {
cramps: { type: 'bool', optional: true },
ovulationPain: { type: 'bool', optional: true },
headache: { type: 'bool', optional: true },
backache: { type: 'bool', optional: true },
nausea: { type: 'bool', optional: true },
tenderBreasts: { type: 'bool', optional: true },
migraine: { type: 'bool', optional: true },
other: { type: 'bool', optional: true },
note: { type: 'string', optional: true },
},
}
const MoodSchema = {
name: 'Mood',
properties: {
happy: { type: 'bool', optional: true },
sad: { type: 'bool', optional: true },
stressed: { type: 'bool', optional: true },
balanced: { type: 'bool', optional: true },
fine: { type: 'bool', optional: true },
anxious: { type: 'bool', optional: true },
energetic: { type: 'bool', optional: true },
fatigue: { type: 'bool', optional: true },
angry: { type: 'bool', optional: true },
other: { type: 'bool', optional: true },
note: { type: 'string', optional: true },
},
}
const CycleDaySchema = {
name: 'CycleDay',
primaryKey: 'date',
properties: {
date: 'string',
temperature: {
type: 'Temperature',
optional: true,
},
isCycleStart: 'bool',
bleeding: {
type: 'Bleeding',
optional: true,
},
mucus: {
type: 'Mucus',
optional: true,
},
cervix: {
type: 'Cervix',
optional: true,
},
note: {
type: 'Note',
optional: true,
},
desire: {
type: 'Desire',
optional: true,
},
sex: {
type: 'Sex',
optional: true,
},
pain: {
type: 'Pain',
optional: true,
},
mood: {
type: 'Mood',
optional: true,
},
},
}
export default {
schema: [
CycleDaySchema,
TemperatureSchema,
BleedingSchema,
MucusSchema,
CervixSchema,
NoteSchema,
DesireSchema,
SexSchema,
PainSchema,
MoodSchema,
],
schemaVersion: 5,
migration: (oldRealm, newRealm) => {
if (oldRealm.schemaVersion < 5) {
const newObjects = newRealm.objects('Bleeding')
// loop through all objects and assign a default value for new properties
for (let i = 0; i < newObjects.length; i++) {
newObjects[i].pad = false
newObjects[i].tampon = false
newObjects[i].underwear = false
newObjects[i].cup = false
newObjects[i].softTampon = false
newObjects[i].disk = false
newObjects[i].none = false
newObjects[i].other = false
newObjects[i].note = null
}
}
},
}
+2 -1
View File
@@ -3,5 +3,6 @@ import schema1 from './1.js'
import schema2 from './2.js'
import schema3 from './3.js'
import schema4 from './4.js'
import schema5 from './5.js'
export default [schema0, schema1, schema2, schema3, schema4]
export default [schema0, schema1, schema2, schema3, schema4, schema5]
+17 -2
View File
@@ -13,6 +13,21 @@ export const bleeding = {
},
}
export const products = {
categories: {
pad: 'pad',
tampon: 'tampon',
underwear: 'underwear',
cup: 'cup',
softTampon: 'soft tampon',
disk: 'disk',
none: 'none',
other: 'other',
},
header: 'period products',
explainer: 'Did you use period products?',
}
export const cervix = {
subcategories: {
opening: 'opening',
@@ -32,7 +47,7 @@ export const cervix = {
explainer: 'How high up in the vagina is the cervix?',
},
excludeExplainer:
"You can exclude this value if you don't want to use it for fertility detection.",
"You can exclude these values if you don't want to use them for fertility detection.",
}
export const mucus = {
@@ -50,7 +65,7 @@ export const mucus = {
'Looking at and touching your cervical mucus, which describes it best?',
},
excludeExplainer:
"You can exclude this value if you don't want to use it for fertility detection",
"You can exclude these values if you don't want to use them for fertility detection",
}
export const desire = {
+1 -1
View File
@@ -19,7 +19,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.2403.19</string>
<string>1.2409.26</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
+1 -1
View File
@@ -1,4 +1,4 @@
export default function getSensiplanMucus(feeling, texture) {
export default function getNfpMucus(feeling, texture) {
if (typeof feeling != 'number' || typeof texture != 'number') return null
const feelingMapping = {
+10 -8
View File
@@ -1,7 +1,9 @@
export default function ({
val, cycleDay, mensesDaysAfter, checkIsMensesStart
val,
cycleDay,
mensesDaysAfter,
checkIsMensesStart,
}) {
cycleDay.bleeding = val
// if a bleeding value is deleted or excluded, we need to check if there are
@@ -20,15 +22,15 @@ export default function ({
}
function bleedingValueDeletedOrExluded(val) {
const bleedingDeleted = !val
const bleedingDeleted = !val || !(typeof val?.value === 'number')
const bleedingExcluded = val && val.exclude
return bleedingDeleted || bleedingExcluded
}
function maybeClearOldCycleStarts() {
// if we have a new bleeding day, we need to clear the
// menses start marker from all following days of this
// menses that may have been marked as start before
mensesDaysAfter.forEach(day => day.isCycleStart = false)
// if we have a new bleeding day, we need to clear the
// menses start marker from all following days of this
// menses that may have been marked as start before
mensesDaysAfter.forEach((day) => (day.isCycleStart = false))
}
}
}
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "drip.",
"version": "1.2403.19",
"version": "1.2409.26",
"contributors": [
"Julia Friesel <julia.friesel@gmail.com>",
"Marie Kochsiek",
@@ -12,8 +12,8 @@
"scripts": {
"start": "react-native start",
"android": "react-native run-android",
"ios": "react-native run-ios --simulator=\"iPhone 15\"",
"iosSE": "react-native run-ios --simulator=\"iPhone SE (2nd generation)\"",
"ios": "react-native run-ios --simulator=\"iPhone 8 Plus\"",
"ios15": "react-native run-ios --simulator=\"iPhone 15 Plus\"",
"log": "react-native log-android",
"test": "jest test && yarn lint",
"test-watch": "jest --watch test",
+79
View File
@@ -0,0 +1,79 @@
import getNfpMucus from '../lib/nfp-mucus'
describe('getNfpMucus', () => {
test('returns null if there is no value for feeling or texture', () => {
expect(getNfpMucus()).toBeNull()
expect(getNfpMucus(undefined, 3)).toBeNull()
expect(getNfpMucus(2, undefined)).toBeNull()
})
describe('results in t for:', () => {
test('dry feeling and no texture', function () {
const nfpValue = getNfpMucus(0, 0)
expect(nfpValue).toEqual(0)
})
})
describe('results in Ø for:', () => {
test('no feeling and no texture', function () {
const nfpValue = getNfpMucus(1, 0)
expect(nfpValue).toEqual(1)
})
})
describe('results in f for:', () => {
test('wet feeling and no texture', function () {
const nfpValue = getNfpMucus(2, 0)
expect(nfpValue).toEqual(2)
})
})
describe('results in S for:', () => {
test('dry feeling and creamy texture', function () {
const nfpValue = getNfpMucus(0, 1)
expect(nfpValue).toEqual(3)
})
test('no feeling and creamy texture', function () {
const nfpValue = getNfpMucus(1, 1)
expect(nfpValue).toEqual(3)
})
test('wet feeling and creamy texture', function () {
const nfpValue = getNfpMucus(2, 1)
expect(nfpValue).toEqual(3)
})
})
describe('results in +S for:', () => {
test('dry feeling and egg white texture', function () {
const nfpValue = getNfpMucus(0, 2)
expect(nfpValue).toEqual(4)
})
test('no feeling and egg white texture', function () {
const nfpValue = getNfpMucus(1, 2)
expect(nfpValue).toEqual(4)
})
test('wet feeling and egg white texture', function () {
const nfpValue = getNfpMucus(2, 2)
expect(nfpValue).toEqual(4)
})
test('slippery feeling and egg white texture', function () {
const nfpValue = getNfpMucus(3, 2)
expect(nfpValue).toEqual(4)
})
test('slippery feeling and creamy texture', function () {
const nfpValue = getNfpMucus(3, 1)
expect(nfpValue).toEqual(4)
})
test('slippery feeling and no texture', function () {
const nfpValue = getNfpMucus(3, 0)
expect(nfpValue).toEqual(4)
})
})
})
-79
View File
@@ -1,79 +0,0 @@
import getSensiplanMucus from '../lib/nfp-mucus'
describe('getSensiplanMucus', () => {
test('returns null if there is no value for feeling or texture', () => {
expect(getSensiplanMucus()).toBeNull()
expect(getSensiplanMucus(undefined, 3)).toBeNull()
expect(getSensiplanMucus(2, undefined)).toBeNull()
})
describe('results in t for:', () => {
test('dry feeling and no texture', function () {
const sensiplanValue = getSensiplanMucus(0, 0)
expect(sensiplanValue).toEqual(0)
})
})
describe('results in Ø for:', () => {
test('no feeling and no texture', function () {
const sensiplanValue = getSensiplanMucus(1, 0)
expect(sensiplanValue).toEqual(1)
})
})
describe('results in f for:', () => {
test('wet feeling and no texture', function () {
const sensiplanValue = getSensiplanMucus(2, 0)
expect(sensiplanValue).toEqual(2)
})
})
describe('results in S for:', () => {
test('dry feeling and creamy texture', function () {
const sensiplanValue = getSensiplanMucus(0, 1)
expect(sensiplanValue).toEqual(3)
})
test('no feeling and creamy texture', function () {
const sensiplanValue = getSensiplanMucus(1, 1)
expect(sensiplanValue).toEqual(3)
})
test('wet feeling and creamy texture', function () {
const sensiplanValue = getSensiplanMucus(2, 1)
expect(sensiplanValue).toEqual(3)
})
})
describe('results in +S for:', () => {
test('dry feeling and egg white texture', function () {
const sensiplanValue = getSensiplanMucus(0, 2)
expect(sensiplanValue).toEqual(4)
})
test('no feeling and egg white texture', function () {
const sensiplanValue = getSensiplanMucus(1, 2)
expect(sensiplanValue).toEqual(4)
})
test('wet feeling and egg white texture', function () {
const sensiplanValue = getSensiplanMucus(2, 2)
expect(sensiplanValue).toEqual(4)
})
test('slippery feeling and egg white texture', function () {
const sensiplanValue = getSensiplanMucus(3, 2)
expect(sensiplanValue).toEqual(4)
})
test('slippery feeling and creamy texture', function () {
const sensiplanValue = getSensiplanMucus(3, 1)
expect(sensiplanValue).toEqual(4)
})
test('slippery feeling and no texture', function () {
const sensiplanValue = getSensiplanMucus(3, 0)
expect(sensiplanValue).toEqual(4)
})
})
})