Compare commits

..

49 Commits

Author SHA1 Message Date
bl00dymarie 0f375530c8 Merge branch 'Chore/Release-v1.2311.14-versionCode25' into 'release'
Release: v1.2311.14

See merge request bloodyhealth/drip!629
2023-11-16 15:49:19 +00:00
bl00dymarie 5401789c46 Release: v1.2311.14 2023-11-14 13:08:11 +01:00
bl00dymarie f6944328fb Fix: Middle chart legend improved;
Row height halfed, elements aligned, fontsize partially decreased
2023-11-14 13:06:58 +01:00
bl00dymarie f730b617cb Remove no-unused-var error 2023-11-13 17:00:02 +01:00
Liv 668faf2534 Clean up backgroundColor variable 2023-11-13 17:00:02 +01:00
livi f304fee293 Highlight weekend dates in chart 2023-11-13 17:00:02 +01:00
bl00dymarie 54b08c15bb Merge branch '656-chore-write-changelog-and-release-notes-1' into 'release'
Add Changelog for Release v1.2311.10

See merge request bloodyhealth/drip!628
2023-11-13 11:50:52 +00:00
bl00dymarie 6d51826dd6 Add Changelog for Release v1.2311.10 2023-11-13 12:22:21 +01:00
tintenfisch 5ff680a591 Merge branch '653-bug-grid-labels-for-temperature-chart-cut-out' into 'release'
fix: changes to the temperature chart

See merge request bloodyhealth/drip!626
2023-11-10 16:50:25 +00:00
tintenfisch 2cda2c38e9 fix: changes to the temperature chart 2023-11-10 16:50:25 +00:00
tintenfisch b638a5bd08 Merge branch '372-ui-bug-import-pop-up' into 'release'
fix: change to data import dialog

See merge request bloodyhealth/drip!625
2023-11-10 12:25:46 +00:00
wunderfisch 32cec10a79 change to data import dialog. first have replace option, second delete. like this synchronous to text above. 2023-11-10 12:30:17 +01:00
bl00dymarie 2eca9ba097 Merge branch 'fix/Update-android-1.2310.31-versionNumber21' into 'release'
Fix: Update android to v1.2310.31 for testing

See merge request bloodyhealth/drip!619
2023-11-09 11:10:35 +00:00
bl00dymarie 573de7545e Merge branch 'chore/edit-merge-request-template-to-close-issues' into 'main'
Edit merge request template to close issues automatically

See merge request bloodyhealth/drip!578
2023-11-07 10:04:44 +00:00
bl00dymarie d81dbac60c Chore: Update versionName 1.2310.31, versionCode 21 and cl tool 2023-10-31 13:05:09 +01:00
bl00dymarie 40fb65ca88 Chore: Target Android 13 and Sdk33 2023-10-31 12:53:01 +01:00
bl00dymarie ed7a5a341b Updates jetifier; converts support libraries of all dependencies to AndroidX 2023-10-31 12:37:15 +01:00
tintenfisch ca3cf16ff7 Merge branch 'chore/templates-for-issue-feature-chore' into 'main'
3 templates for issues

See merge request bloodyhealth/drip!616
2023-10-27 15:40:56 +00:00
bl00dymarie fdbfef38f9 Chore: Add permission request for notifications;
- required on Android 13
2023-10-26 16:44:35 +02:00
bl00dymarie 5c13e91fb3 Chore: Update notifications for Android;
- Use new updated fork of react-native-push-notification without google
- Create channel for PushNotifications
- Adapt AndroidManifest.xml
2023-10-26 16:44:35 +02:00
bl00dymarie 2b235f4d10 Address Google Play 64-bit error;
release isn't compliant with Google Play 64-bit requirement error
2023-10-26 16:43:44 +02:00
bl00dymarie bcc7eaada9 Fix hint display on chart 2023-10-26 16:43:44 +02:00
bl00dymarie 714021465e lowercase symptom names as before 2023-10-26 16:43:44 +02:00
Sofiya Tepikin c3e4fd3f03 Revert json conversion 2023-10-26 16:43:44 +02:00
bl00dymarie e7f76bad33 implement feedback suggestions 2023-10-20 11:20:38 +02:00
wunderfisch 9537e377e0 first draft for possible templates to get certain information when writing issues. 2023-10-20 11:20:38 +02:00
Sofiya Tepikin f11eb3d1a1 Merge branch 'fix/outdated-snapshot' into 'main'
Fix outdated snapshot

See merge request bloodyhealth/drip!593
2023-01-19 09:31:57 +00:00
Sofiya Tepikin 5f83464649 Fix outdated snapshot 2023-01-19 10:23:48 +01:00
Sofiya Tepikin 6e2e03f39e Merge branch 'chore/update-babel-dependencies' into 'main'
Chore/update babel dependencies

See merge request bloodyhealth/drip!579
2022-11-13 20:00:23 +00:00
Sofiya Tepikin cc62e24229 Chore/update babel dependencies 2022-11-13 20:00:23 +00:00
Lisa Hillebrand 1907a78dbf Edit merge request template to close issues automatically 2022-11-06 16:20:03 +01:00
Sofiya Tepikin cd24522b4d Merge branch '618/Refactor-import-section-to-use-translation-lib' into 'main'
618 Refactor import section to use translation lib

See merge request bloodyhealth/drip!555
2022-11-06 14:47:05 +00:00
Lisa 446638d6de 618 Refactor import section to use translation lib 2022-11-06 14:47:05 +00:00
Lisa ae23ef2c58 Merge branch '624/Use-translation-library-in-tutorial' into 'main'
624 Use translation library in tutorial

See merge request bloodyhealth/drip!573
2022-11-06 14:43:15 +00:00
Lisa 38b9e8b31f Merge branch 'dependabot-npm_and_yarn-react-i18next-12.0.0' into 'main'
Bump react-i18next from 11.18.3 to 12.0.0

See merge request bloodyhealth/drip!571
2022-10-23 10:11:06 +00:00
Lisa Hillebrand 84d657cabb 614 Uppercase tutorial component 2022-10-23 11:56:32 +02:00
Lisa Hillebrand 4573b93921 624 Use translation library for chart tutorial 2022-10-23 11:54:11 +02:00
Sofiya Tepikin ab88a4c163 Bump react-i18next from 11.18.3 to 12.0.0
Bumps [react-i18next](https://github.com/i18next/react-i18next) from 11.18.3 to 12.0.0.
- [Release notes](https://github.com/i18next/react-i18next/releases)
- [Changelog](https://github.com/i18next/react-i18next/blob/master/CHANGELOG.md)
- [Commits](https://github.com/i18next/react-i18next/compare/v11.18.3...v12.0.0)
2022-10-23 09:06:40 +00:00
Lisa baaf89c04e Merge branch 'dependabot-npm_and_yarn-i18next-22.0.2' into 'main'
Bump i18next from 21.9.0 to 22.0.2

See merge request bloodyhealth/drip!570
2022-10-23 08:13:38 +00:00
Sofiya Tepikin d476f6c143 Bump i18next from 21.9.0 to 22.0.2
Bumps [i18next](https://github.com/i18next/i18next) from 21.9.0 to 22.0.2.
- [Release notes](https://github.com/i18next/i18next/releases)
- [Changelog](https://github.com/i18next/i18next/blob/master/CHANGELOG.md)
- [Commits](https://github.com/i18next/i18next/compare/v21.9.0...v22.0.2)
2022-10-21 09:07:30 +00:00
Sofiya Tepikin b56e0818f3 Merge branch '619/Create-test-utils-for-react-testing-library' into 'main'
619 Create test utils for react testing library

See merge request bloodyhealth/drip!556
2022-10-19 13:17:19 +00:00
Lisa 1b5ffaf5d6 619 Create test utils for react testing library 2022-10-19 13:17:19 +00:00
Sofiya Tepikin f68cc2b49e Merge branch 'fix/exclude-babel-core-from-dependabot' into 'main'
Exclude @babel/core library from dependabot updates

See merge request bloodyhealth/drip!560
2022-10-05 11:31:38 +00:00
Sofiya Tepikin 27d430a465 Exclude @babel/core library from dependabot updates 2022-10-05 13:28:49 +02:00
Sofiya Tepikin 28f52e2cea Merge branch '617/Remove-weblate-reference-from-readme' into 'main'
617 Remove reference to weblate from readme

See merge request bloodyhealth/drip!554
2022-10-05 10:10:46 +00:00
Lisa 3f87f298fb Merge branch '615-Use-translation-library-for-pages' into 'main'
615 Use translation library for pages

Closes #615

See merge request bloodyhealth/drip!530
2022-10-01 08:13:24 +00:00
Lisa Hillebrand 585f32863d 617 Remove reference to weblate from readme 2022-09-30 11:58:11 +02:00
Lisa Hillebrand 51e1c95e71 615 Remove unused page labels 2022-09-30 11:39:16 +02:00
Lisa Hillebrand 36c33c69b7 615 Use translation library for bottom menu 2022-09-30 11:39:00 +02:00
52 changed files with 3051 additions and 3657 deletions
+1
View File
@@ -14,3 +14,4 @@ updates:
- dependency-name: 'react'
- dependency-name: 'react-native'
- dependency-name: 'react-native-push-notifications'
- dependency-name: '@babel/core'
+26
View File
@@ -0,0 +1,26 @@
## oh no a bug 🐛
### Description what has happened
Short overview how the bug manifests.
### which OS + version is your device
[ ] Android _number_
[ ] iOS _number_
### which drip version number are you using
_On your phone go to ➞ menu on the top right ➞ about, scroll to the very bottom and find the version number_
### how did it happen
_what triggered the bug/behavior, always/sometimes, is it reproducible(how)?_
### describe how it looks or add screenshot
feel free to attach a file 📎
### any idea to solve it
💡
+22
View File
@@ -0,0 +1,22 @@
## This has to be done 🪠
### Description what has to be done
Short overview
### is it urgent? ⏳
[ ] Yes
[ ] No
[ ] something in between
_Explain the urgency if possible, e.g. is it a security vulnerability for potentially everyone?_
### which OS
[ ] Android
[ ] iOS
### what shall be the ideal outcome 🎆
_You can e.g. specify here the version number for a library update_
@@ -0,0 +1,19 @@
## Yeah a feature idea 🧩
### what should this feature do or solve? 🪄
Please give a short overview so as many people as possible would be able to understand.
### what is particularly important to the people who would use this feature?
Do you have certain user groups in mind?
### Any idea where it shall be placed in the app?
### is it connected with or dependent on some other feature?
### any idea how it shall look (sketch?)
feel free to attach a file 📎
### what could be difficulties (with other components) 🪆
+1 -1
View File
@@ -1,6 +1,6 @@
## Why this change?
Closes ticket #
Closes #
## Description
+70 -2
View File
@@ -2,12 +2,80 @@
All notable changes to this project will be documented in this file.
## v1.2311.14
### Changes
- Make the app compatible with Android 13
- Update Android's targetSdkVersion to 33
- Update buildToolsVersion to 33.0.2
- Update Android Gradle plugin to 7.0.3
- Update Gradle to 7.3.3
- Update kotlinVersion to 1.3.40
- Chart: Improved readability
- Finer temperature lines and dots
- Enlarge screen space for temperature chart
- A very light grey background color for weekend days on the whole chart screen
- Reminders:
- Use new fork of react-native-push-notification: <https://github.com/github:bl00dymarie/react-native-push-notification> without google services
- Adding channels after breaking changes in react-native-push-notification
- Homescreen: date displayed in new format
- Minor changes in "about" section
- Updated dependencies:
- moment ^2.29.4,
- prop-types ^15.8.1,
- react v17.0.2,
- react-native v0.67.4,
- react-native-calendars ^1.1287.0,
- react-native-document-picker ^8.1.1,
- react-native-fs ^2.20.0,
- react-native-modal-datetime-picker v14.0.0,
- react-native-share ^7.9.0,
- react-native-vector-icons ^9.2.0,
- realm ^10.16.0,
- sympto v3.0.1
### Adds
- Stats: Show period details, including cycle start, cycle length and amount of days with bleeding
- Stats: Explainer text for standard deviation
- Settings: Privacy Policy
- App asks for permissions for notifications right at the start, which allows you to set reminders (this is a new requirement for Android 13)
- Buttons can now be displayed as row
- Added dependencies:
- @js-joda/core ^5.3.0,
- @react-native-async-storage/async-storage ^1.17.9,
- @react-native-community/art ^1.2.0,
- @react-native-community/datetimepicker ^6.3.1,
- @react-native-community/push-notification-ios ^1.11.0,
- i18next ^22.0.2,
- react-i18next ^12.0.0,
- jshashes ^1.0.8,
- react-native-permissions ^3.10.0,
- react-native-push-notification: github:bl00dymarie/react-native-push-notification,
- react-native-simple-toast ^1.1.3,
- react-native-size-matters ^0.4.0,
### Fixed
- Password: Disable setting empty passwords
- After updating the password the app will do a full restart
- Chart: Grid for symptoms
- Chart: Horizontal lines in temperature chart
## Unreleased
- Partially implemented translations with react-i18next
## v1.2102.28
### Changes
- Temperature range is now between 35 - 39°C and its default values are now set to 35.5 - 37.5°C
### Fixed
- Blocks invalid input of temperature value
- Error message for incorrect password on login screen
- Phase text on home screen for last fertile day
@@ -61,7 +129,7 @@ All notable changes to this project will be documented in this file.
### Adds
- Allows chart not to show temperature part, when temperature is not tracked and corresponding refactoring
- Detox support for e2e testing and addition of the e2e tests
- Detox support for e2e testing and addition of the e2e tests
- Introduces Redux global state (date and navigation are stored locally now)
- Introduces clear.sh script to the project automising clearing project caches and packages reinstallation
@@ -80,7 +148,7 @@ All notable changes to this project will be documented in this file.
- Fixed adding notes to the future dates
- Fixed app exiting with error when hitting back button on device
- Fixed Sex symptom showing on y axis of chart even though the contraception method was deleted
- Fixed Sex symptom showing on y axis of chart even though the contraception method was deleted
- Fixed of the clear.sh file name in package.json
- Fixed of navigation from chart to the cycle day overview
- Bug fix for maximum value of mucus not showing on chart
-4
View File
@@ -201,7 +201,3 @@ More information about how the app calculates fertility status and bleeding pred
react-native link
5. You should be able to use the icon now within drip, e.g. in Cycle Day Overview and on the chart.
## Translation
We are using [Weblate](https://weblate.org/) as translation software.
+8 -7
View File
@@ -134,10 +134,10 @@ android {
applicationId "com.drip"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 8
versionName "1.2102.28"
versionCode 25
versionName "1.2311.14"
ndk {
abiFilters "armeabi-v7a", "x86", "arm64-v8a"
abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
}
testBuildType System.getProperty('testBuildType', 'debug') // This will later be used to control the test apk build type
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
@@ -198,7 +198,7 @@ android {
// For each separate APK per architecture, set a unique version code as described here:
// https://developer.android.com/studio/build/configure-apk-splits.html
// Example: versionCode 1 will generate 1001 for armeabi-v7a, 1002 for x86, etc.
def versionCodes = ["armeabi-v7a": 1, "x86": 2]
def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
def abi = output.getFilter(OutputFile.ABI)
if (abi != null) { // null for the universal-debug, universal-release variants
output.versionCodeOverride =
@@ -211,11 +211,12 @@ android {
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
//noinspection GradleDynamicVersion
implementation 'androidx.appcompat:appcompat:1.0.0'
implementation 'androidx.annotation:annotation:1.1.0'
implementation "androidx.appcompat:appcompat:1.0.0"
implementation "androidx.annotation:annotation:1.1.0"
implementation "androidx.work:work-runtime-ktx:2.7.1"
implementation "com.facebook.react:react-native:+" // From node_modules
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
exclude group:'com.facebook.fbjni'
}
+57 -50
View File
@@ -5,74 +5,81 @@
>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<uses-permission android:name="android.permission.USE_EXACT_ALARM" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission tools:node="remove" android:name="android.permission.READ_PHONE_STATE" />
<uses-permission tools:node="remove" android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission tools:node="remove" android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<permission
android:name="${applicationId}.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="${applicationId}.permission.C2D_MESSAGE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<application
android:name=".MainApplication"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:allowBackup="false"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:name=".MainApplication"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustPan"
android:screenOrientation="sensorPortrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:allowBackup="false"
android:theme="@style/AppTheme">
<meta-data
android:name="com.dieam.reactnativepushnotification.notification_foreground"
android:value="false" />
<meta-data
android:name="com.dieam.reactnativepushnotification.notification_color"
android:resource="@color/purple" />
<meta-data
android:name="com.dieam.reactnativepushnotification.default_notification_channel_id"
android:value="..." />
<receiver
android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationActions"
android:exported="false"
tools:ignore="MissingClass" />
<receiver
android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationPublisher"
android:exported="false" />
<receiver
android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationBootEventReceiver"
android:exported="false" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</receiver>
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustPan"
android:screenOrientation="sensorPortrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.drip.provider"
android:grantUriPermissions="true"
android:exported="false">
android:exported="false" >
<meta-data
tools:replace="android:resource"
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
</provider>
<meta-data android:name="com.dieam.reactnativepushnotification.notification_foreground"
android:value="false"/>
<!-- Change the resource name to your App's accent color - or any other color you want -->
<meta-data android:name="com.dieam.reactnativepushnotification.notification_color"
android:resource="@android:color/white"/> <!-- or @android:color/{name} to use a standard color -->
<receiver android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationActions" />
<receiver android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationPublisher" />
<receiver android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationBootEventReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/>
</intent-filter>
</receiver>
<service
android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationListenerService"
android:exported="false" >
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
</application>
</manifest>
@@ -14,4 +14,5 @@
<color name="grey">#A5A5A5</color>
<color name="orange">#F38337</color>
<color name="purple">#3A2671</color>
<color name="turquoiseDark">#69CBC1</color>
</resources>
+8 -11
View File
@@ -5,13 +5,13 @@ buildscript {
google()
mavenCentral()
}
ext.kotlinVersion = "1.4.20"
ext.kotlinVersion = '1.3.40'
dependencies {
classpath("com.android.tools.build:gradle:4.2.2")
classpath('com.android.tools.build:gradle:7.0.3')
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
}
}
@@ -31,7 +31,7 @@ allprojects {
content {
excludeGroup "com.facebook.react"
}
}
}
google()
maven { url 'https://www.jitpack.io' }
maven {
@@ -46,12 +46,9 @@ allprojects {
}
ext {
googlePlayServicesVersion = "+" // default: "+"
firebaseMessagingVersion = "21.1.0" // default: "+"
buildToolsVersion = "30.0.2"
minSdkVersion = 23
compileSdkVersion = 30
targetSdkVersion = 30
buildToolsVersion = "33.0.2"
minSdkVersion = 21
compileSdkVersion = 33
targetSdkVersion = 33
ndkVersion = "21.4.7075529"
}
+3 -2
View File
@@ -1,5 +1,6 @@
#Wed Oct 11 14:45:21 CEST 2023
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
+9 -11
View File
@@ -1,4 +1,4 @@
import React, { useEffect } from 'react'
import React from 'react'
import { ScrollView, StyleSheet, View } from 'react-native'
import PropTypes from 'prop-types'
import moment from 'moment'
@@ -10,7 +10,6 @@ import Footnote from './common/Footnote'
import cycleModule from '../lib/cycle'
import { getFertilityStatusForDay } from '../lib/sympto-adapter'
import setupNotifications from '../lib/notifications'
import {
determinePredictionText,
formatWithOrdinalSuffix,
@@ -20,10 +19,13 @@ import { Colors, Fonts, Sizes, Spacing } from '../styles'
import { LocalDate } from '@js-joda/core'
import { useTranslation } from 'react-i18next'
const Home = ({ navigation }) => {
const Home = ({ navigate, setDate }) => {
const { t } = useTranslation()
useEffect(() => setupNotifications(navigation), [])
function navigateToCycleDayView() {
setDate(todayDateString)
navigate('CycleDay')
}
const todayDateString = LocalDate.now().toString()
const { getCycleDayNumber, getPredictedMenses } = cycleModule()
@@ -31,14 +33,11 @@ const Home = ({ navigation }) => {
const { status, phase, statusText } =
getFertilityStatusForDay(todayDateString)
const prediction = determinePredictionText(getPredictedMenses(), t)
const cycleDayText = cycleDayNumber
? formatWithOrdinalSuffix(cycleDayNumber)
: ''
function navigateToCycleDayView() {
navigation.navigate('CycleDayOverview', { date: todayDateString })
}
return (
<ScrollView
style={styles.container}
@@ -110,9 +109,8 @@ const styles = StyleSheet.create({
})
Home.propTypes = {
navigation: PropTypes.shape({
navigate: PropTypes.func.isRequired,
}).isRequired,
navigate: PropTypes.func,
setDate: PropTypes.func,
}
export default Home
+1 -3
View File
@@ -1,7 +1,7 @@
import React, { useState, useEffect } from 'react'
import { getLicenseFlag, saveEncryptionFlag } from '../local-storage'
import { closeDb, openDb } from '../db'
import { openDb } from '../db'
import App from './app'
import AppLoadingView from './common/app-loading'
@@ -29,8 +29,6 @@ export default function AppWrapper() {
useEffect(() => {
checkIsLicenseAccepted()
checkIsDbEncrypted()
return () => closeDb()
}, [])
if (isLoading) {
+53 -64
View File
@@ -1,82 +1,71 @@
import React from 'react'
import 'react-native-gesture-handler'
import { StyleSheet, Text } from 'react-native'
import { NavigationContainer } from '@react-navigation/native'
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'
import { createStackNavigator } from '@react-navigation/stack'
import React, { useState, useEffect } from 'react'
import { BackHandler, StyleSheet, View } from 'react-native'
import PropTypes from 'prop-types'
import { LocalDate } from '@js-joda/core'
import Home from './Home'
import Chart from './chart/chart'
import CalendarView from './calendar'
import CycleDayOverview from './cycle-day/cycle-day-overview'
import Stats from './stats'
import Icon from './common/menu-icon'
import Header from './header'
import Menu from './menu'
import { viewsList } from './views'
import { pages } from './pages'
import { Colors, Fonts, Sizes } from '../styles'
import setupNotifications from '../lib/notifications'
import { closeDb } from '../db'
const HomeStack = createStackNavigator()
const App = ({ restartApp }) => {
const [date, setDate] = useState(LocalDate.now().toString())
const [currentPage, setCurrentPage] = useState('Home')
const goBack = () => {
if (currentPage === 'Home') {
closeDb()
BackHandler.exitApp()
} else {
const { parent } = pages.find((p) => p.component === currentPage)
setCurrentPage(parent)
}
return true
}
useEffect(() => {
const backHandler = BackHandler.addEventListener(
'hardwareBackPress',
goBack
)
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,
}
function HomeStackScreen() {
return (
<HomeStack.Navigator screenOptions={{ headerShown: false }}>
<HomeStack.Screen name="Home" component={Home} />
<HomeStack.Screen name="CycleDayOverview" component={CycleDayOverview} />
</HomeStack.Navigator>
<View style={styles.container}>
<Header {...headerProps} />
<Page {...pageProps} restartApp={restartApp} />
<Menu currentPage={currentPage} navigate={setCurrentPage} />
</View>
)
}
const Tab = createBottomTabNavigator()
const App = () => {
return (
<NavigationContainer>
<Tab.Navigator
screenOptions={({ route }) => ({
header: ({ navigation }) => <Header navigation={navigation} />,
tabBarIcon: ({ focused }) => {
let icon = 'chart'
if (route.name === 'CalendarStackScreen') {
icon = 'calendar'
} else if (route.name === 'Stats') {
icon = 'statistics'
}
return <Icon name={icon} isActive={focused} />
},
tabBarLabel: ({ color }) => {
return (
<Text style={[styles.text, { color: color }]}>{route.name}</Text>
)
},
tabBarActiveTintColor: Colors.orange,
tabBarInactiveTintColor: Colors.grey,
tabBarStyle: { height: 80 },
})}
>
<Tab.Screen
name="HomeStackScreen"
component={HomeStackScreen}
options={{ tabBarButton: () => null, tabBarVisible: false }}
/>
<Tab.Screen name="Calendar" component={CalendarView} />
<Tab.Screen name="Chart" component={Chart} />
<Tab.Screen name="Stats" component={Stats} />
</Tab.Navigator>
</NavigationContainer>
)
App.propTypes = {
restartApp: PropTypes.func,
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
text: {
fontFamily: Fonts.bold,
fontSize: Sizes.small,
textTransform: 'uppercase',
},
})
export default App
+5 -5
View File
@@ -12,12 +12,13 @@ import {
todayToCalFormat,
} from './helpers/calendar'
const CalendarView = ({ navigation }) => {
const CalendarView = ({ setDate, navigate }) => {
const bleedingDays = getBleedingDaysSortedByDate()
const predictedMenses = cycleModule().getPredictedMenses()
const passDateToDayView = ({ dateString }) => {
navigation.navigate('CycleDayOverview', { date: dateString })
setDate(dateString)
navigate('CycleDay')
}
const markedDates = Object.assign(
@@ -48,9 +49,8 @@ const styles = StyleSheet.create({
})
CalendarView.propTypes = {
navigation: PropTypes.shape({
navigate: PropTypes.func.isRequired,
}).isRequired,
setDate: PropTypes.func.isRequired,
navigate: PropTypes.func.isRequired,
}
export default CalendarView
@@ -6,16 +6,17 @@ import AppText from '../common/app-text'
import CloseIcon from '../common/close-icon'
import { Containers, Spacing } from '../../styles'
import { chart } from '../../i18n/en/labels'
import { useTranslation } from 'react-i18next'
const image = require('../../assets/swipe.png')
const Tutorial = ({ onClose }) => {
const { t } = useTranslation()
return (
<View style={styles.container}>
<Image resizeMode="contain" source={image} style={styles.image} />
<View style={styles.textContainer}>
<AppText>{chart.tutorial}</AppText>
<AppText>{t('chart.tutorial')}</AppText>
</View>
<CloseIcon onClose={onClose} />
</View>
+12 -3
View File
@@ -4,15 +4,19 @@ import { StyleSheet, View } from 'react-native'
import AppText from '../common/app-text'
import { Typography } from '../../styles'
import { Sizes, Typography } from '../../styles'
import { CHART_YAXIS_WIDTH } from '../../config'
import { shared as labels } from '../../i18n/en/labels'
const ChartLegend = ({ height }) => {
return (
<View style={[styles.container, { height }]}>
<AppText style={styles.textBold}>#</AppText>
<AppText style={styles.text}>{labels.date}</AppText>
<View style={[styles.singleLabelContainer, { height: height / 2 }]}>
<AppText style={styles.textBold}>#</AppText>
</View>
<View style={[styles.singleLabelContainer, { height: height / 2 }]}>
<AppText style={styles.text}>{labels.date}</AppText>
</View>
</View>
)
}
@@ -27,8 +31,13 @@ const styles = StyleSheet.create({
justifyContent: 'flex-end',
width: CHART_YAXIS_WIDTH,
},
singleLabelContainer: {
justifyContent: 'space-around',
alignItems: 'center',
},
text: {
...Typography.label,
fontSize: Sizes.footnote,
},
textBold: {
...Typography.labelBold,
+8 -8
View File
@@ -9,7 +9,7 @@ import HorizontalGrid from './horizontal-grid'
import MainGrid from './main-grid'
import NoData from './no-data'
import NoTemperature from './no-temperature'
import Tutorial from './tutorial'
import Tutorial from './Tutorial'
import YAxis from './y-axis'
import { getCycleDaysSortedByDate } from '../../db'
@@ -28,8 +28,8 @@ import { Spacing } from '../../styles'
const getSymptomsFromCycleDays = (cycleDays) =>
SYMPTOMS.filter((symptom) => cycleDays.some((cycleDay) => cycleDay[symptom]))
const CycleChart = ({ navigation }) => {
const [shouldShowHint, setShouldShowHint] = useState(true)
const CycleChart = ({ navigate, setDate }) => {
const [shouldShowHint, setShouldShowHint] = useState(false)
useEffect(() => {
let isMounted = true
@@ -84,8 +84,9 @@ const CycleChart = ({ navigation }) => {
const renderColumn = ({ item }) => {
return (
<DayColumn
setDate={setDate}
dateString={item}
navigation={navigation}
navigate={navigate}
symptomHeight={symptomHeight}
columnHeight={columnHeight}
symptomRowSymptoms={symptomRowSymptoms}
@@ -99,7 +100,7 @@ const CycleChart = ({ navigation }) => {
const hasDataToDisplay = chartSymptoms.length > 0
if (!hasDataToDisplay) {
return <NoData />
return <NoData navigate={navigate} />
}
return (
@@ -134,9 +135,8 @@ const CycleChart = ({ navigation }) => {
}
CycleChart.propTypes = {
navigation: PropTypes.shape({
navigate: PropTypes.func.isRequired,
}).isRequired,
navigate: PropTypes.func,
setDate: PropTypes.func,
}
const styles = StyleSheet.create({
+20 -7
View File
@@ -19,11 +19,20 @@ const CycleDayLabel = ({ height, date }) => {
return (
<View style={[styles.container, { height }]}>
<AppText style={styles.textBold}>{cycleDayLabel}</AppText>
<View style={styles.dateLabel}>
<AppText style={styles.text}>
{isFirstDayOfMonth ? momentDate.format('MMM') : dayOfMonth}
</AppText>
<View style={{ ...styles.labelRow, height: height / 2 }}>
<AppText style={styles.textBold}>{cycleDayLabel}</AppText>
</View>
<View style={{ ...styles.labelRow, height: height / 2 }}>
{isFirstDayOfMonth && (
<AppText style={styles.textFootnote}>
{momentDate.format('MMM')}
</AppText>
)}
{!isFirstDayOfMonth && (
<AppText style={styles.textSmall}>{dayOfMonth}</AppText>
)}
{!isFirstDayOfMonth && (
<AppText style={styles.textLight}>
{getOrdinalSuffix(dayOfMonth)}
@@ -45,17 +54,21 @@ const styles = StyleSheet.create({
justifyContent: 'flex-end',
left: 4,
},
text: {
textSmall: {
...Typography.label,
fontSize: Sizes.small,
},
textFootnote: {
...Typography.label,
fontSize: Sizes.footnote,
},
textBold: {
...Typography.labelBold,
},
textLight: {
...Typography.labelLight,
},
dateLabel: {
labelRow: {
flexDirection: 'row',
justifyContent: 'space-around',
alignItems: 'center',
+13 -6
View File
@@ -1,6 +1,7 @@
import React from 'react'
import PropTypes from 'prop-types'
import { TouchableOpacity } from 'react-native'
import moment from 'moment'
import { getCycleDay } from '../../db'
@@ -19,12 +20,15 @@ const DayColumn = ({
dateString,
chartSymptoms,
columnHeight,
navigation,
setDate,
navigate,
shouldShowTemperatureColumn,
symptomHeight,
symptomRowSymptoms,
xAxisHeight,
}) => {
const momentDate = moment(dateString)
const isWeekend = momentDate.day() == 0 || momentDate.day() == 6
const cycleDayData = getCycleDay(dateString)
let data = {}
@@ -59,8 +63,10 @@ const DayColumn = ({
columnHeight
)
const onDaySelect = (date) =>
navigation.navigate('CycleDayOverview', { date })
const onDaySelect = (date) => {
setDate(date)
navigate('CycleDay')
}
return (
<TouchableOpacity onPress={() => onDaySelect(dateString)} activeOpacity={1}>
@@ -70,6 +76,7 @@ const DayColumn = ({
isVerticalLine={fhmAndLtl.drawFhmLine}
data={data && data.temperature}
columnHeight={columnHeight}
isWeekend={isWeekend}
/>
)}
@@ -89,6 +96,7 @@ const DayColumn = ({
isSymptomDataComplete={
hasSymptomData && isSymptomDataComplete(symptom, dateString)
}
isWeekend={isWeekend}
height={symptomHeight}
/>
)
@@ -101,13 +109,12 @@ DayColumn.propTypes = {
dateString: PropTypes.string.isRequired,
chartSymptoms: PropTypes.array,
columnHeight: PropTypes.number.isRequired,
navigate: PropTypes.func.isRequired,
setDate: PropTypes.func.isRequired,
shouldShowTemperatureColumn: PropTypes.bool,
symptomHeight: PropTypes.number.isRequired,
symptomRowSymptoms: PropTypes.array,
xAxisHeight: PropTypes.number,
navigation: PropTypes.shape({
navigate: PropTypes.func.isRequired,
}).isRequired,
}
export default DayColumn
+7 -6
View File
@@ -7,7 +7,8 @@ import { Colors } from '../../styles'
import {
CHART_COLUMN_WIDTH,
CHART_COLUMN_MIDDLE,
CHART_DOT_RADIUS,
CHART_DOT_RADIUS_SYMPTOM,
CHART_DOT_RADIUS_TEMPERATURE,
CHART_STROKE_WIDTH,
} from '../../config'
@@ -35,9 +36,9 @@ const DotAndLine = ({
}
const dot = new Path()
.moveTo(CHART_COLUMN_MIDDLE, y - CHART_DOT_RADIUS)
.arc(0, CHART_DOT_RADIUS * 2, CHART_DOT_RADIUS)
.arc(0, CHART_DOT_RADIUS * -2, CHART_DOT_RADIUS)
.moveTo(CHART_COLUMN_MIDDLE, y - CHART_DOT_RADIUS_TEMPERATURE)
.arc(0, CHART_DOT_RADIUS_TEMPERATURE * 2, CHART_DOT_RADIUS_TEMPERATURE)
.arc(0, CHART_DOT_RADIUS_TEMPERATURE * -2, CHART_DOT_RADIUS_TEMPERATURE)
const dotColor = exclude ? Colors.turquoise : Colors.turquoiseDark
const lineColorLeft = excludeLeftLine
? Colors.turquoise
@@ -58,13 +59,13 @@ const DotAndLine = ({
d={lineRight}
stroke={lineColorRight}
strokeWidth={CHART_STROKE_WIDTH}
key={y + CHART_DOT_RADIUS}
key={y + CHART_DOT_RADIUS_SYMPTOM}
/>
<Shape
d={dot}
stroke={dotColor}
strokeWidth={CHART_STROKE_WIDTH}
fill="white"
fill={Colors.turquoiseDark}
key="dot"
/>
</React.Fragment>
+24 -7
View File
@@ -5,7 +5,7 @@ import { StyleSheet, View } from 'react-native'
import { Colors, Containers } from '../../styles'
import {
CHART_COLUMN_WIDTH,
CHART_DOT_RADIUS,
CHART_DOT_RADIUS_SYMPTOM,
CHART_GRID_LINE_HORIZONTAL_WIDTH,
} from '../../config'
@@ -15,14 +15,31 @@ const SymptomCell = ({
symptom,
symptomValue,
isSymptomDataComplete,
isWeekend,
}) => {
const shouldDrawDot = symptomValue !== false
// Determine the background color based on isWeekend prop
const backgroundColor = isWeekend ? Colors.greyVeryLight : 'white'
const styleCell =
index !== 0
? [styles.cell, { height, width: CHART_COLUMN_WIDTH }]
: [styles.cell, { height, width: CHART_COLUMN_WIDTH }, styles.topBorder]
? [
styles.cell,
{
height,
width: CHART_COLUMN_WIDTH,
backgroundColor: backgroundColor,
},
]
: [
styles.cell,
{
height,
width: CHART_COLUMN_WIDTH,
backgroundColor: backgroundColor,
},
styles.topBorder,
]
let styleDot
if (shouldDrawDot) {
const styleSymptom = Colors.iconColors[symptom]
const symptomColor = styleSymptom.shades[symptomValue]
@@ -47,11 +64,11 @@ SymptomCell.propTypes = {
symptom: PropTypes.string,
symptomValue: PropTypes.oneOfType([PropTypes.bool, PropTypes.number]),
isSymptomDataComplete: PropTypes.bool,
isWeekend: PropTypes.bool,
}
const styles = StyleSheet.create({
cell: {
backgroundColor: 'white',
borderBottomColor: Colors.grey,
borderBottomWidth: CHART_GRID_LINE_HORIZONTAL_WIDTH,
borderLeftColor: Colors.grey,
@@ -63,8 +80,8 @@ const styles = StyleSheet.create({
borderTopWidth: CHART_GRID_LINE_HORIZONTAL_WIDTH,
},
dot: {
width: CHART_DOT_RADIUS * 2,
height: CHART_DOT_RADIUS * 2,
width: CHART_DOT_RADIUS_SYMPTOM * 2,
height: CHART_DOT_RADIUS_SYMPTOM * 2,
borderRadius: 50,
},
})
+5 -8
View File
@@ -1,6 +1,6 @@
import React from 'react'
import PropTypes from 'prop-types'
import { StyleSheet } from 'react-native'
import { Colors } from '../../styles'
import { Surface, Path } from '@react-native-community/art'
@@ -14,14 +14,16 @@ const TemperatureColumn = ({
isVerticalLine,
data,
columnHeight,
isWeekend,
}) => {
const x = CHART_STROKE_WIDTH / 2
const backgroundColor = isWeekend ? Colors.greyVeryLight : 'white'
return (
<Surface
width={CHART_COLUMN_WIDTH}
height={columnHeight}
style={styles.container}
style={{ backgroundColor: backgroundColor }}
>
<ChartLine path={new Path().lineTo(0, columnHeight)} />
@@ -63,12 +65,7 @@ TemperatureColumn.propTypes = {
isVerticalLine: PropTypes.bool,
data: PropTypes.object,
columnHeight: PropTypes.number,
isWeekend: PropTypes.bool,
}
const styles = StyleSheet.create({
container: {
backgroundColor: 'white',
},
})
export default TemperatureColumn
+5 -2
View File
@@ -8,13 +8,15 @@ import { Sizes } from '../../styles'
import { CHART_TICK_WIDTH } from '../../config'
const Tick = ({ yPosition, height, isBold, shouldShowLabel, label }) => {
const top = yPosition - height / 2
const top = yPosition - height / 2 - 4
const containerStyle = [styles.container, { flexBasis: height, height, top }]
const textStyle = isBold ? styles.textBold : styles.textNormal
if (!shouldShowLabel) return null
return (
<View style={containerStyle}>
<AppText style={textStyle}>{shouldShowLabel && label}</AppText>
<AppText style={textStyle}>{label}</AppText>
</View>
)
}
@@ -36,6 +38,7 @@ const styles = StyleSheet.create({
position: 'absolute',
right: 0,
width: CHART_TICK_WIDTH,
minHeight: Sizes.base + 2,
},
textBold: {
fontSize: Sizes.base,
+7 -10
View File
@@ -12,8 +12,7 @@ import { getData, nextDate, prevDate } from '../helpers/cycle-day'
import { Spacing } from '../../styles'
import { SYMPTOMS } from '../../config'
const CycleDayOverView = ({ route }) => {
const { date, isTemperatureEditView } = route.params
const CycleDayOverView = ({ date, setDate, isTemperatureEditView }) => {
const cycleDay = getCycleDay(date)
const [editedSymptom, setEditedSymptom] = useState(
@@ -21,11 +20,11 @@ const CycleDayOverView = ({ route }) => {
)
const showNextCycleDay = () => {
//setDate(nextDate(date))
setDate(nextDate(date))
}
const showPrevCycleDay = () => {
//setDate(prevDate(date))
setDate(prevDate(date))
}
return (
@@ -58,12 +57,10 @@ const CycleDayOverView = ({ route }) => {
}
CycleDayOverView.propTypes = {
route: PropTypes.shape({
params: PropTypes.shape({
date: PropTypes.string,
isTemperatureEditView: PropTypes.bool,
}),
}),
cycleDay: PropTypes.object,
date: PropTypes.string,
setDate: PropTypes.func,
isTemperatureEditView: PropTypes.bool,
}
const styles = StyleSheet.create({
+4 -6
View File
@@ -7,17 +7,17 @@ import HamburgerMenu from './hamburger-menu'
import { Colors, Containers, Sizes } from '../../styles'
const Header = ({ isStatic, navigation }) => {
const Header = ({ isStatic, navigate }) => {
return (
<View style={styles.header}>
{isStatic ? (
<Logo />
) : (
<>
<TouchableOpacity onPress={() => navigation.navigate('Home')}>
<TouchableOpacity onPress={() => navigate('Home')}>
<Logo />
</TouchableOpacity>
<HamburgerMenu navigate={navigation.navigate} />
<HamburgerMenu navigate={navigate} />
</>
)}
</View>
@@ -26,9 +26,7 @@ const Header = ({ isStatic, navigation }) => {
Header.propTypes = {
isStatic: PropTypes.bool,
navigation: PropTypes.shape({
navigate: PropTypes.func.isRequired,
}).isRequired,
navigate: PropTypes.func,
}
Header.defaultProps = {
+45
View File
@@ -0,0 +1,45 @@
import React from 'react'
import { StyleSheet, View } from 'react-native'
import PropTypes from 'prop-types'
import MenuItem from './menu-item'
import { Containers } from '../../styles'
import { pages } from '../pages'
import { useTranslation } from 'react-i18next'
const Menu = ({ currentPage, navigate }) => {
const menuItems = pages.filter((page) => page.isInMenu)
const { t } = useTranslation(null, { keyPrefix: 'bottomMenu' })
return (
<View style={styles.container}>
{menuItems.map(({ icon, labelKey, component }) => {
return (
<MenuItem
isActive={component === currentPage}
onPress={() => navigate(component)}
icon={icon}
key={labelKey}
label={t(labelKey)}
/>
)
})}
</View>
)
}
Menu.propTypes = {
currentPage: PropTypes.string,
navigate: PropTypes.func,
}
const styles = StyleSheet.create({
container: {
backgroundColor: 'white',
...Containers.rowContainer,
},
})
export default Menu
+3 -15
View File
@@ -1,75 +1,63 @@
import settingsViews from './settings'
import settingsLabels from '../i18n/en/settings'
const labels = settingsLabels.menuItems
export const pages = [
{
component: 'Home',
icon: 'home',
label: 'Home',
},
{
component: 'Calendar',
icon: 'calendar',
isInMenu: true,
label: 'Calendar',
labelKey: 'calendar',
parent: 'Home',
},
{
component: 'Chart',
icon: 'chart',
isInMenu: true,
label: 'Chart',
labelKey: 'chart',
parent: 'Home',
},
{
component: 'Stats',
icon: 'statistics',
isInMenu: true,
label: 'Stats',
labelKey: 'stats',
parent: 'Home',
},
{
children: Object.keys(settingsViews),
component: 'SettingsMenu',
icon: 'settings',
label: 'Settings',
parent: 'Home',
},
{
component: 'Reminders',
label: labels.reminders.name,
parent: 'SettingsMenu',
},
{
component: 'NfpSettings',
label: labels.nfpSettings.name,
parent: 'SettingsMenu',
},
{
component: 'DataManagement',
label: labels.dataManagement.name,
parent: 'SettingsMenu',
},
{
component: 'Password',
label: labels.password.name,
parent: 'SettingsMenu',
},
{
component: 'About',
label: 'About',
parent: 'SettingsMenu',
},
{
component: 'License',
label: 'License',
parent: 'SettingsMenu',
},
{
component: 'PrivacyPolicy',
label: 'PrivacyPolicy',
parent: 'SettingsMenu',
},
{
@@ -6,40 +6,23 @@ import AppText from '../../common/app-text'
import Button from '../../common/button'
import Segment from '../../common/segment'
import { openImportDialog, getFileContent, importData } from './import-dialog'
import openShareDialogAndExport from './export-dialog'
import DeleteData from './delete-data'
import labels from '../../../i18n/en/settings'
import { ACTION_DELETE, ACTION_EXPORT, ACTION_IMPORT } from '../../../config'
import ImportData from './ImportData'
const DataManagement = () => {
const [isLoading, setIsLoading] = useState(false)
const [currentAction, setCurrentAction] = useState(null)
const startImportFlow = async (shouldDeleteExistingData) => {
setIsLoading(true)
const fileContent = await getFileContent()
if (fileContent) {
await importData(shouldDeleteExistingData, fileContent)
}
setIsLoading(false)
}
const [isDeletingData, setIsDeletingData] = useState(false)
const startExport = () => {
setCurrentAction(ACTION_EXPORT)
setIsDeletingData(false)
openShareDialogAndExport()
}
const startImport = () => {
setCurrentAction(ACTION_IMPORT)
openImportDialog(startImportFlow)
}
if (isLoading) return <AppLoadingView />
const isDeletingData = currentAction === ACTION_DELETE
return (
<AppPage>
<Segment title={labels.export.button}>
@@ -48,17 +31,15 @@ const DataManagement = () => {
{labels.export.button}
</Button>
</Segment>
<Segment title={labels.import.button}>
<AppText>{labels.import.segmentExplainer}</AppText>
<Button isCTA onPress={startImport}>
{labels.import.button}
</Button>
</Segment>
<ImportData
resetIsDeletingData={() => setIsDeletingData(false)}
setIsLoading={setIsLoading}
/>
<Segment title={labels.deleteSegment.title} last>
<AppText>{labels.deleteSegment.explainer}</AppText>
<DeleteData
isDeletingData={isDeletingData}
onStartDeletion={() => setCurrentAction(ACTION_DELETE)}
onStartDeletion={() => setIsDeletingData(true)}
/>
</Segment>
</AppPage>
@@ -0,0 +1,97 @@
import React from 'react'
import PropTypes from 'prop-types'
import { Alert } from 'react-native'
import DocumentPicker from 'react-native-document-picker'
import rnfs from 'react-native-fs'
import importCsv from '../../../lib/import-export/import-from-csv'
import alertError from '../common/alert-error'
import Segment from '../../common/segment'
import AppText from '../../common/app-text'
import Button from '../../common/button'
import { useTranslation } from 'react-i18next'
export default function ImportData({ resetIsDeletingData, setIsLoading }) {
const { t } = useTranslation(null, {
keyPrefix: 'hamburgerMenu.settings.data.import',
})
async function startImport(shouldDeleteExistingData) {
setIsLoading(true)
await importData(shouldDeleteExistingData)
setIsLoading(false)
}
async function getFileInfo() {
try {
const fileInfo = await DocumentPicker.pickSingle({
type: [DocumentPicker.types.csv, 'text/comma-separated-values'],
})
return fileInfo
} catch (error) {
if (DocumentPicker.isCancel(error)) return // User cancelled the picker, exit any dialogs or menus and move on
showImportErrorAlert(error)
}
}
async function getFileContent() {
const fileInfo = await getFileInfo()
if (!fileInfo) return null
try {
const fileContent = await rnfs.readFile(fileInfo.uri, 'utf8')
return fileContent
} catch (err) {
return showImportErrorAlert(t('error.couldNotOpenFile'))
}
}
async function importData(shouldDeleteExistingData) {
const fileContent = await getFileContent()
if (!fileContent) return
try {
await importCsv(fileContent, shouldDeleteExistingData)
Alert.alert(t('success.title'), t('success.message'))
} catch (err) {
showImportErrorAlert(err.message)
}
}
function openImportDialog() {
resetIsDeletingData()
Alert.alert(t('dialog.title'), t('dialog.message'), [
{
text: t('dialog.cancel'),
style: 'cancel',
onPress: () => {},
},
{
text: t('dialog.delete'),
onPress: () => startImport(true),
},
{
text: t('dialog.replace'),
onPress: () => startImport(false),
},
])
}
function showImportErrorAlert(message) {
const errorMessage = t('error.noDataImported', { message })
alertError(errorMessage)
}
return (
<Segment title={t('button')}>
<AppText>{t('segmentExplainer')}</AppText>
<Button isCTA onPress={openImportDialog}>
{t('button')}
</Button>
</Segment>
)
}
ImportData.propTypes = {
resetIsDeletingData: PropTypes.func.isRequired,
setIsLoading: PropTypes.func.isRequired,
}
@@ -1,64 +0,0 @@
import { Alert } from 'react-native'
import DocumentPicker from 'react-native-document-picker'
import rnfs from 'react-native-fs'
import importCsv from '../../../lib/import-export/import-from-csv'
import { shared as sharedLabels } from '../../../i18n/en/labels'
import labels from '../../../i18n/en/settings'
import alertError from '../common/alert-error'
export function openImportDialog(onImportData) {
Alert.alert(labels.import.title, labels.import.message, [
{
text: sharedLabels.cancel,
style: 'cancel',
onPress: () => {},
},
{
text: labels.import.replaceOption,
onPress: () => onImportData(false),
},
{
text: labels.import.deleteOption,
onPress: () => onImportData(true),
},
])
}
export async function getFileContent() {
let fileInfo
try {
fileInfo = await DocumentPicker.pickSingle({
type: [DocumentPicker.types.csv, 'text/comma-separated-values'],
})
} catch (error) {
if (DocumentPicker.isCancel(error)) {
// User cancelled the picker, exit any dialogs or menus and move on
return
} else {
importError(error)
}
}
let fileContent
try {
fileContent = await rnfs.readFile(fileInfo.uri, 'utf8')
} catch (err) {
return importError(labels.import.errors.couldNotOpenFile)
}
return fileContent
}
export async function importData(shouldDeleteExistingData, fileContent) {
try {
await importCsv(fileContent, shouldDeleteExistingData)
Alert.alert(sharedLabels.successTitle, labels.import.success.message)
} catch (err) {
importError(err.message)
}
}
function importError(msg) {
const postFixed = `${msg}\n\n${labels.import.errors.postFix}`
alertError(postFixed)
}
+1 -1
View File
@@ -1,6 +1,6 @@
import Reminders from './reminders/reminders'
import NfpSettings from './nfp-settings'
import DataManagement from './data-management'
import DataManagement from './data-management/DataManagement'
import Password from './password'
import About from './About'
import License from './License'
+5 -8
View File
@@ -1,10 +1,6 @@
import { PixelRatio, StatusBar } from 'react-native'
import { scale, verticalScale } from 'react-native-size-matters'
export const ACTION_DELETE = 'delete'
export const ACTION_EXPORT = 'export'
export const ACTION_IMPORT = 'import'
export const SYMPTOMS = [
'bleeding',
'temperature',
@@ -19,12 +15,13 @@ export const SYMPTOMS = [
export const CHART_COLUMN_WIDTH = 32
export const CHART_COLUMN_MIDDLE = CHART_COLUMN_WIDTH / 2
export const CHART_DOT_RADIUS = scale(6)
export const CHART_DOT_RADIUS_SYMPTOM = scale(6)
export const CHART_DOT_RADIUS_TEMPERATURE = scale(4)
export const CHART_GRID_LINE_HORIZONTAL_WIDTH =
PixelRatio.roundToNearestPixel(0.3)
export const CHART_ICON_SIZE = scale(20)
export const CHART_STROKE_WIDTH = scale(3)
export const CHART_SYMPTOM_HEIGHT_RATIO = scale(0.08)
export const CHART_STROKE_WIDTH = scale(1.5)
export const CHART_SYMPTOM_HEIGHT_RATIO = scale(0.06)
export const CHART_XAXIS_HEIGHT_RATIO = scale(0.1)
export const CHART_YAXIS_WIDTH = scale(32)
export const CHART_TICK_WIDTH = scale(44)
@@ -40,7 +37,7 @@ export const HIT_SLOP = {
top: verticalScale(20),
bottom: verticalScale(20),
left: scale(20),
right: scale(20)
right: scale(20),
}
export const STATUSBAR_HEIGHT = StatusBar.currentHeight
+40 -9
View File
@@ -1,15 +1,23 @@
{
"bottomMenu": {
"calendar": "Calendar",
"chart": "Chart",
"stats": "Stats"
},
"chart": {
"tutorial": "You can swipe the chart to view more dates."
},
"cycleDay": {
"symptomBox": {
"bleeding": "Bleeding",
"temperature": "Temperature",
"mucus": "Cervical Mucus",
"cervix": "Cervix",
"note": "Note",
"desire": "Desire",
"sex": "Sex",
"pain": "Pain",
"mood": "Mood"
"bleeding": "bleeding",
"temperature": "temperature",
"mucus": "cervical mucus",
"cervix": "cervix",
"note": "note",
"desire": "desire",
"sex": "sex",
"pain": "pain",
"mood": "mood"
}
},
"labels": {
@@ -80,6 +88,29 @@
}
},
"settings": {
"data": {
"import": {
"button": "Import data",
"dialog": {
"cancel": "Cancel",
"delete": "Import and delete existing",
"message": "There are two options for the import:\n\n1. Keep existing cycle days and replace only the ones in the import file.\n\n2. Delete all existing cycle days and import cycle days from file",
"replace": "Import and replace",
"title": "Keep existing data?"
},
"error": {
"couldNotOpenFile": "Could not open file",
"futureEdit": "Future dates may only contain a note, no other symptoms",
"incorrectColumns": "Expected CSV column titles to be {{incorrectColumns}}",
"noDataImported": "{{message}}\n\nNo data was imported or changed"
},
"segmentExplainer": "Import data in CSV format",
"success": {
"message": "Data successfully imported",
"title": "Success"
}
}
},
"menuItem": {
"dataManagement": {
"name": "Data",
-4
View File
@@ -3,10 +3,6 @@ export const home = {
phase: (n) => `${['1st', '2nd', '3rd'][n - 1]} cycle phase`,
}
export const chart = {
tutorial: 'You can swipe the chart to view more dates.',
}
export const shared = {
cancel: 'Cancel',
save: 'Save',
-36
View File
@@ -1,24 +1,6 @@
import links from './links'
export default {
menuItems: {
reminders: {
name: 'Reminders',
text: 'turn on/off reminders',
},
nfpSettings: {
name: 'NFP settings',
text: 'define how you want to use NFP',
},
dataManagement: {
name: 'Data',
text: 'import, export or delete your data',
},
password: {
name: 'Password',
text: '',
},
},
export: {
errors: {
noData: 'There is no data to export',
@@ -31,24 +13,6 @@ export default {
segmentExplainer:
'Export data in CSV format for backup or so you can use it elsewhere',
},
import: {
button: 'Import data',
title: 'Keep existing data?',
message: `There are two options for the import:
1. Keep existing cycle days and replace only the ones in the import file.
2. Delete all existing cycle days and import cycle days from file.`,
replaceOption: 'Import and replace',
deleteOption: 'Import and delete existing',
errors: {
couldNotOpenFile: 'Could not open file',
postFix: 'No data was imported or changed',
futureEdit: 'Future dates may only contain a note, no other symptoms',
},
success: {
message: 'Data successfully imported',
},
segmentExplainer: 'Import data in CSV format',
},
deleteSegment: {
title: 'Delete app data',
explainer: 'Delete app data from this phone',
-1
View File
@@ -17,7 +17,6 @@ i18n
compatibilityJSON: 'v3', // TODO: migrate json to v4 and afterwards remove it
resources,
fallbackLng: 'en',
debug: true,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
+4
View File
@@ -5,4 +5,8 @@ module.exports = {
transformIgnorePatterns: [
'node_modules/(?!((jest-)?react-native(-.*)?|@react-native(-community)?)/)',
],
watchPlugins: [
'jest-watch-typeahead/filename',
'jest-watch-typeahead/testname',
],
}
+4 -9
View File
@@ -3,8 +3,6 @@ import { getCycleLengthStats } from './cycle-length'
const LocalDate = joda.LocalDate
const DAYS = joda.ChronoUnit.DAYS
const toJSON = (realmObj) => JSON.parse(JSON.stringify(realmObj))
export default function config(opts) {
let bleedingDaysSortedByDate
let cycleStartsSortedByDate
@@ -16,13 +14,10 @@ export default function config(opts) {
if (!opts) {
// we only want to require (and run) the db module
// when not running the tests
bleedingDaysSortedByDate = toJSON(
require('../db').getBleedingDaysSortedByDate()
)
cycleStartsSortedByDate = toJSON(
require('../db').getCycleStartsSortedByDate()
)
cycleDaysSortedByDate = toJSON(require('../db').getCycleDaysSortedByDate())
bleedingDaysSortedByDate = require('../db').getBleedingDaysSortedByDate()
cycleStartsSortedByDate = require('../db').getCycleStartsSortedByDate()
cycleDaysSortedByDate = require('../db').getCycleDaysSortedByDate()
maxBreakInBleeding = 1
maxCycleLength = 99
minCyclesForPrediction = 3
+13 -5
View File
@@ -8,7 +8,7 @@ import {
import getColumnNamesForCsv from './get-csv-column-names'
import replaceWithNullIfAllPropertiesAreNull from './replace-with-null'
import { LocalDate } from '@js-joda/core'
import labels from '../../i18n/en/settings'
import i18next from 'i18next'
export default async function importCsv(csv, deleteFirst) {
const parseFuncs = {
@@ -46,7 +46,10 @@ export default async function importCsv(csv, deleteFirst) {
const cycleDays = await csvParser(config)
.fromString(csv)
.on('header', validateHeaders)
.on('header', (headers) => validateHeaders(headers))
.on('error', (error) => {
throw error
})
//remove symptoms where all fields are null
putNullForEmptySymptoms(cycleDays)
@@ -67,8 +70,11 @@ function validateHeaders(headers) {
return expectedHeaders.indexOf(header) > -1
})
) {
const msg = `Expected CSV column titles to be ${expectedHeaders.join()}`
throw new Error(msg)
throw new Error(
i18next.t('hamburgerMenu.settings.data.import.error.incorrectColumns', {
incorrectColumns: expectedHeaders.join(),
})
)
}
}
@@ -92,7 +98,9 @@ function throwIfFutureData(cycleDays) {
day.date > today &&
Object.keys(day).some((symptom) => symptom != 'date' && symptom != 'note')
) {
throw new Error(labels.import.errors.futureEdit)
throw new Error(
i18next.t('hamburgerMenu.settings.data.import.error.futureEdit')
)
}
}
}
+3 -4
View File
@@ -1,17 +1,16 @@
export default function (feeling, texture) {
export default function getSensiplanMucus(feeling, texture) {
if (typeof feeling != 'number' || typeof texture != 'number') return null
const feelingMapping = {
0: 0,
1: 1,
2: 2,
3: 4
3: 4,
}
const textureMapping = {
0: 0,
1: 3,
2: 4
2: 4,
}
const nfpFeelingValue = feelingMapping[feeling]
const nfpTextureValue = textureMapping[texture]
+26 -11
View File
@@ -1,8 +1,10 @@
import { Platform } from 'react-native'
import {
tempReminderObservable,
periodReminderObservable,
} from '../local-storage'
import Notification from 'react-native-push-notification'
import * as PN from 'react-native-push-notification'
import { requestNotifications } from 'react-native-permissions'
import Moment from 'moment'
import { LocalDate } from '@js-joda/core'
@@ -11,21 +13,31 @@ import { getBleedingDaysSortedByDate } from '../db'
import cycleModule from './cycle'
import nothingChanged from '../db/db-unchanged'
export default function setupNotifications(navigation) {
Notification.configure({
export default function setupNotifications(navigate, setDate) {
requestNotifications()
const PushNotification = Platform.OS === 'ios' ? PN : PN.default
PushNotification.createChannel({
channelId: 'drip-channel-id', // (required)
channelName: 'drip reminder', // (required)
playSound: false, // (optional) default: true
})
PushNotification.configure({
onNotification: (notification) => {
const date = LocalDate.now().toString()
// https://github.com/zo0r/react-native-push-notification/issues/966#issuecomment-479069106
if (notification.data?.id === '1' || notification.id === '1') {
navigation.navigate('TemperatureEditView', { date })
const todayDate = LocalDate.now().toString()
setDate(todayDate)
navigate('TemperatureEditView')
} else {
navigation.navigate('Home', { date })
navigate('Home')
}
},
})
tempReminderObservable((reminder) => {
Notification.cancelLocalNotifications({ id: '1' })
PushNotification.cancelLocalNotification({ id: '1' })
if (reminder.enabled) {
const [hours, minutes] = reminder.time.split(':')
let target = new Moment()
@@ -37,31 +49,33 @@ export default function setupNotifications(navigation) {
target = target.add(1, 'd')
}
Notification.localNotificationSchedule({
PushNotification.localNotificationSchedule({
id: '1',
userInfo: { id: '1' },
message: labels.tempReminder.notification,
date: target.toDate(),
vibrate: false,
repeatType: 'day',
channelId: 'drip-channel-id',
})
}
}, false)
periodReminderObservable((reminder) => {
Notification.cancelLocalNotifications({ id: '2' })
PushNotification.cancelLocalNotification({ id: '2' })
if (reminder.enabled) setupPeriodReminder()
}, false)
getBleedingDaysSortedByDate().addListener((_, changes) => {
// the listener fires on setup, so we check if there were actually any changes
if (nothingChanged(changes)) return
Notification.cancelLocalNotifications({ id: '2' })
PushNotification.cancelLocalNotification({ id: '2' })
if (periodReminderObservable.value.enabled) setupPeriodReminder()
})
}
function setupPeriodReminder() {
const PushNotification = Platform.OS === 'ios' ? PN : PN.default
const bleedingPrediction = cycleModule().getPredictedMenses()
if (bleedingPrediction.length > 0) {
const predictedBleedingStart = Moment(
@@ -79,12 +93,13 @@ function setupPeriodReminder() {
// period is likely to start in 3 to 3 + (length of prediction - 1) days
const daysToEndOfPrediction = bleedingPrediction[0].length + 2
Notification.localNotificationSchedule({
PushNotification.localNotificationSchedule({
id: '2',
userInfo: { id: '2' },
message: labels.periodReminder.notification(daysToEndOfPrediction),
date: reminderDate.toDate(),
vibrate: false,
channelId: 'drip-channel-id',
})
}
}
+13 -19
View File
@@ -1,6 +1,6 @@
{
"name": "drip.",
"version": "1.2208.11",
"version": "1.2311.14",
"contributors": [
"Julia Friesel <julia.friesel@gmail.com>",
"Marie Kochsiek",
@@ -34,29 +34,23 @@
"@react-native-async-storage/async-storage": "^1.17.9",
"@react-native-community/art": "^1.2.0",
"@react-native-community/datetimepicker": "^6.3.1",
"@react-native-community/push-notification-ios": "^1.8.0",
"@react-navigation/bottom-tabs": "^6.4.0",
"@react-navigation/native": "^6.0.13",
"@react-navigation/native-stack": "^6.9.0",
"@react-navigation/stack": "^6.3.1",
"@react-native-community/push-notification-ios": "^1.11.0",
"csvtojson": "^2.0.8",
"i18next": "^21.9.0",
"i18next": "^22.0.2",
"jshashes": "^1.0.8",
"moment": "^2.29.4",
"object-path": "^0.11.4",
"obv": "0.0.1",
"prop-types": "^15.8.1",
"react": "17.0.2",
"react-i18next": "^11.18.3",
"react-i18next": "^12.0.0",
"react-native": "0.67.4",
"react-native-calendars": "^1.1287.0",
"react-native-document-picker": "^8.1.1",
"react-native-fs": "^2.20.0",
"react-native-gesture-handler": "^2.6.2",
"react-native-modal-datetime-picker": "14.0.0",
"react-native-push-notification": "3.2.1",
"react-native-safe-area-context": "^4.3.4",
"react-native-screens": "^3.17.0",
"react-native-permissions": "^3.10.0",
"react-native-push-notification": "github:bl00dymarie/react-native-push-notification",
"react-native-share": "^7.9.0",
"react-native-simple-toast": "^1.1.3",
"react-native-size-matters": "^0.4.0",
@@ -65,18 +59,18 @@
"sympto": "3.0.1"
},
"devDependencies": {
"@babel/core": "^7.12.9",
"@babel/core": "^7.20.2",
"@babel/eslint-parser": "^7.19.1",
"@babel/preset-react": "^7.16.0",
"@babel/preset-react": "^7.18.6",
"@babel/runtime": "^7.12.5",
"@testing-library/jest-native": "^4.0.12",
"@testing-library/react-native": "^11.1.0",
"basic-changelog": "gitlab:bloodyhealth/basic-changelog",
"eslint": "7.14.0",
"eslint-plugin-react": "^7.8.2",
"eslint": "^7.32.0",
"eslint-plugin-react": "^7.31.10",
"husky": "^8.0.0",
"jest": "^28.1.3",
"jetifier": "^1.6.6",
"jest": "^29.1.2",
"jest-watch-typeahead": "^2.2.0",
"jetifier": "^2.0.0",
"metro-react-native-babel-preset": "^0.66.2",
"prettier": "2.4.0",
"pretty-quick": "^3.1.1",
+18 -10
View File
@@ -1,7 +1,14 @@
const redColor = '#c3000d'
export const shadesOfRed = ['#e7999e', '#db666d', '#cf323d', '#c3000d'] // light to dark
const violetColor = '#6a7b98'
const shadesOfViolet = ['#e3e7ed', '#c8cfdc', '#acb8cb', '#91a0ba', '#7689a9', violetColor] // light to dark
const shadesOfViolet = [
'#e3e7ed',
'#c8cfdc',
'#acb8cb',
'#91a0ba',
'#7689a9',
violetColor,
] // light to dark
const yellowColor = '#dbb40c'
const shadesOfYellow = ['#f0e19d', '#e9d26d', '#e2c33c', yellowColor] // light to dark
const magentaColor = '#6f2565'
@@ -16,6 +23,7 @@ export default {
greyDark: '#555',
grey: '#888',
greyLight: '#CCC',
greyVeryLight: '#F4F4F4',
orange: '#F38337',
purple: '#3A2671',
purpleLight: '#938EB2',
@@ -23,37 +31,37 @@ export default {
turquoise: '#CFECEA',
turquoiseLight: '#E9F2ED',
iconColors: {
'bleeding': {
bleeding: {
color: redColor,
shades: shadesOfRed,
},
'mucus': {
mucus: {
color: violetColor,
shades: shadesOfViolet,
},
'cervix': {
cervix: {
color: yellowColor,
shades: shadesOfYellow,
},
'sex': {
sex: {
color: magentaColor,
shades: shadesOfMagenta,
},
'desire': {
desire: {
color: pinkColor,
shades: shadesOfPink,
},
'pain': {
pain: {
color: lightGreenColor,
shades: [lightGreenColor],
},
'mood': {
mood: {
color: orangeColor,
shades: [orangeColor],
},
'note': {
note: {
color: mintColor,
shades: [mintColor],
},
},
}
}
+5 -13
View File
@@ -1,27 +1,19 @@
import React from 'react'
import { render, screen, fireEvent } from '@testing-library/react-native'
import AcceptLicense from '../components/AcceptLicense'
import { saveLicenseFlag } from '../local-storage'
import { render, screen, fireEvent } from './test-utils'
jest.mock('../local-storage', () => ({
saveLicenseFlag: jest.fn(() => Promise.resolve()),
}))
jest.mock('react-i18next', () => ({
useTranslation: () => ({
t: (str, options) => {
return str + (options ? JSON.stringify(options) : '')
},
}),
}))
describe('AcceptLicense', () => {
test('On clicking OK button, the license is accepted', async () => {
test('should accept license when clicking ok button', async () => {
const mockedSetLicense = jest.fn()
render(<AcceptLicense setLicense={mockedSetLicense} />)
const okButton = screen.getByText('ok', { exact: false })
const okButton = screen.getByText('OK')
fireEvent(okButton, 'click')
@@ -29,9 +21,9 @@ describe('AcceptLicense', () => {
expect(mockedSetLicense).toHaveBeenCalled()
})
test('There is a Cancel button', async () => {
test('should render cancel button', async () => {
render(<AcceptLicense setLicense={jest.fn()} />)
screen.getByText('cancel', { exact: false })
screen.getByText('Cancel')
})
})
+3 -11
View File
@@ -1,24 +1,16 @@
import React from 'react'
import { render, screen } from '@testing-library/react-native'
import License from '../components/settings/License'
jest.mock('react-i18next', () => ({
useTranslation: () => ({
t: (str, options) => {
return str + (options ? JSON.stringify(options) : '')
},
}),
}))
import { render, screen } from './test-utils'
describe('License screen', () => {
test('It should have a correct year', async () => {
test('should display license text with correct year', async () => {
render(<License />)
const year = new Date().getFullYear().toString()
screen.getByText(year, { exact: false })
})
test('It should match the snapshot', async () => {
test('should match the snapshot', async () => {
const licenseScreen = render(<License />)
expect(licenseScreen).toMatchSnapshot()
+15 -13
View File
@@ -1,9 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`License screen It should match the snapshot 1`] = `
exports[`License screen should match the snapshot 1`] = `
<View
style={
Object {
{
"backgroundColor": "#E9F2ED",
"flex": 1,
}
@@ -11,8 +11,8 @@ exports[`License screen It should match the snapshot 1`] = `
>
<RCTScrollView
contentContainerStyle={
Array [
Object {
[
{
"backgroundColor": "#E9F2ED",
"flexGrow": 1,
},
@@ -23,13 +23,13 @@ exports[`License screen It should match the snapshot 1`] = `
<View>
<Text
style={
Array [
Object {
[
{
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
Object {
{
"alignSelf": "center",
"color": "#3A2671",
"fontFamily": "Jost-Bold",
@@ -41,11 +41,11 @@ exports[`License screen It should match the snapshot 1`] = `
]
}
>
title
drip. an open-source cycle tracking app
</Text>
<View
style={
Object {
{
"marginBottom": 34.285714285714285,
"marginHorizontal": 34.285714285714285,
}
@@ -53,8 +53,8 @@ exports[`License screen It should match the snapshot 1`] = `
>
<Text
style={
Array [
Object {
[
{
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
@@ -63,12 +63,14 @@ exports[`License screen It should match the snapshot 1`] = `
]
}
>
text{"currentYear":2022}
Copyright (C) 2023 Heart of Code e.V.
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:
</Text>
<Text
onPress={[Function]}
style={
Object {
{
"color": "#3A2671",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
@@ -3,7 +3,7 @@
exports[`Footnote component when children are present, renders them 1`] = `
<View
style={
Object {
{
"alignContent": "flex-start",
"flexDirection": "row",
"marginBottom": 8.571428571428571,
@@ -13,13 +13,13 @@ exports[`Footnote component when children are present, renders them 1`] = `
>
<Text
style={
Array [
Object {
[
{
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
Object {
{
"color": "#F38337",
},
]
@@ -29,18 +29,18 @@ exports[`Footnote component when children are present, renders them 1`] = `
</Text>
<Text
linkStyle={
Object {
{
"color": "white",
}
}
style={
Array [
Object {
[
{
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
Object {
{
"color": "#555",
"paddingLeft": 21.428571428571427,
},
+10
View File
@@ -0,0 +1,10 @@
import { render } from '@testing-library/react-native'
import '../i18n/i18n'
const customRender = (ui, options) => render(ui, { ...options })
// re-export everything
export * from '@testing-library/react-native'
// override render method
export { customRender as render }
-6
View File
@@ -50,14 +50,8 @@ module.exports = () => {
}
const pkgJSON = JSON.parse(fs.readFileSync('./package.json'))
const pkgLockJSON = JSON.parse(fs.readFileSync('./package-lock.json'))
pkgJSON.version = nextVersion
pkgLockJSON.version = nextVersion
fs.writeFileSync('./package.json', JSON.stringify(pkgJSON, null, 2))
fs.writeFileSync(
'./package-lock.json',
JSON.stringify(pkgLockJSON, null, 2)
)
await ReactNativeVersion.version(
{
+2338 -3158
View File
File diff suppressed because it is too large Load Diff