Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9b9db80949 | |||
| 6cfbcd0408 | |||
| 93608ca8ef |
@@ -14,4 +14,3 @@ updates:
|
|||||||
- dependency-name: 'react'
|
- dependency-name: 'react'
|
||||||
- dependency-name: 'react-native'
|
- dependency-name: 'react-native'
|
||||||
- dependency-name: 'react-native-push-notifications'
|
- dependency-name: 'react-native-push-notifications'
|
||||||
- dependency-name: '@babel/core'
|
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
## 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
|
|
||||||
|
|
||||||
💡
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
## 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_
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
## 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,6 +1,6 @@
|
|||||||
## Why this change?
|
## Why this change?
|
||||||
|
|
||||||
Closes #
|
Closes ticket #
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|
||||||
|
|||||||
@@ -2,80 +2,12 @@
|
|||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
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
|
## v1.2102.28
|
||||||
|
|
||||||
### Changes
|
### Changes
|
||||||
|
|
||||||
- Temperature range is now between 35 - 39°C and its default values are now set to 35.5 - 37.5°C
|
- Temperature range is now between 35 - 39°C and its default values are now set to 35.5 - 37.5°C
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Blocks invalid input of temperature value
|
- Blocks invalid input of temperature value
|
||||||
- Error message for incorrect password on login screen
|
- Error message for incorrect password on login screen
|
||||||
- Phase text on home screen for last fertile day
|
- Phase text on home screen for last fertile day
|
||||||
|
|||||||
@@ -201,3 +201,7 @@ More information about how the app calculates fertility status and bleeding pred
|
|||||||
react-native link
|
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.
|
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.
|
||||||
|
|||||||
@@ -134,10 +134,10 @@ android {
|
|||||||
applicationId "com.drip"
|
applicationId "com.drip"
|
||||||
minSdkVersion rootProject.ext.minSdkVersion
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
versionCode 25
|
versionCode 8
|
||||||
versionName "1.2311.14"
|
versionName "1.2102.28"
|
||||||
ndk {
|
ndk {
|
||||||
abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
|
abiFilters "armeabi-v7a", "x86", "arm64-v8a"
|
||||||
}
|
}
|
||||||
testBuildType System.getProperty('testBuildType', 'debug') // This will later be used to control the test apk build type
|
testBuildType System.getProperty('testBuildType', 'debug') // This will later be used to control the test apk build type
|
||||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||||
@@ -198,7 +198,7 @@ android {
|
|||||||
// For each separate APK per architecture, set a unique version code as described here:
|
// For each separate APK per architecture, set a unique version code as described here:
|
||||||
// https://developer.android.com/studio/build/configure-apk-splits.html
|
// https://developer.android.com/studio/build/configure-apk-splits.html
|
||||||
// Example: versionCode 1 will generate 1001 for armeabi-v7a, 1002 for x86, etc.
|
// Example: versionCode 1 will generate 1001 for armeabi-v7a, 1002 for x86, etc.
|
||||||
def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
|
def versionCodes = ["armeabi-v7a": 1, "x86": 2]
|
||||||
def abi = output.getFilter(OutputFile.ABI)
|
def abi = output.getFilter(OutputFile.ABI)
|
||||||
if (abi != null) { // null for the universal-debug, universal-release variants
|
if (abi != null) { // null for the universal-debug, universal-release variants
|
||||||
output.versionCodeOverride =
|
output.versionCodeOverride =
|
||||||
@@ -211,12 +211,11 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||||
//noinspection GradleDynamicVersion
|
//noinspection GradleDynamicVersion
|
||||||
implementation "androidx.appcompat:appcompat:1.0.0"
|
implementation 'androidx.appcompat:appcompat:1.0.0'
|
||||||
implementation "androidx.annotation:annotation:1.1.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 "com.facebook.react:react-native:+" // From node_modules
|
||||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
|
|
||||||
|
|
||||||
|
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
|
||||||
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
|
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
|
||||||
exclude group:'com.facebook.fbjni'
|
exclude group:'com.facebook.fbjni'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,81 +5,74 @@
|
|||||||
>
|
>
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<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.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.READ_PHONE_STATE" />
|
||||||
<uses-permission tools:node="remove" android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<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 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
|
<permission
|
||||||
android:name="${applicationId}.permission.C2D_MESSAGE"
|
android:name="${applicationId}.permission.C2D_MESSAGE"
|
||||||
android:protectionLevel="signature" />
|
android:protectionLevel="signature" />
|
||||||
|
|
||||||
|
<uses-permission android:name="${applicationId}.permission.C2D_MESSAGE" />
|
||||||
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
||||||
|
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".MainApplication"
|
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:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:launchMode="singleTask"
|
||||||
android:allowBackup="false"
|
android:windowSoftInputMode="adjustPan"
|
||||||
android:theme="@style/AppTheme">
|
android:screenOrientation="sensorPortrait">
|
||||||
|
<intent-filter>
|
||||||
<meta-data
|
<action android:name="android.intent.action.MAIN" />
|
||||||
android:name="com.dieam.reactnativepushnotification.notification_foreground"
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
android:value="false" />
|
</intent-filter>
|
||||||
<meta-data
|
</activity>
|
||||||
android:name="com.dieam.reactnativepushnotification.notification_color"
|
<provider
|
||||||
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:name="androidx.core.content.FileProvider"
|
||||||
android:authorities="com.drip.provider"
|
android:authorities="com.drip.provider"
|
||||||
android:grantUriPermissions="true"
|
android:grantUriPermissions="true"
|
||||||
android:exported="false" >
|
android:exported="false">
|
||||||
<meta-data
|
<meta-data
|
||||||
tools:replace="android:resource"
|
tools:replace="android:resource"
|
||||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
android:resource="@xml/filepaths" />
|
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>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
@@ -14,5 +14,4 @@
|
|||||||
<color name="grey">#A5A5A5</color>
|
<color name="grey">#A5A5A5</color>
|
||||||
<color name="orange">#F38337</color>
|
<color name="orange">#F38337</color>
|
||||||
<color name="purple">#3A2671</color>
|
<color name="purple">#3A2671</color>
|
||||||
<color name="turquoiseDark">#69CBC1</color>
|
|
||||||
</resources>
|
</resources>
|
||||||
+10
-7
@@ -5,13 +5,13 @@ buildscript {
|
|||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
ext.kotlinVersion = '1.3.40'
|
ext.kotlinVersion = "1.3.10"
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath('com.android.tools.build:gradle:7.0.3')
|
classpath("com.android.tools.build:gradle:4.2.2")
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
// in the individual module build.gradle files
|
// in the individual module build.gradle files
|
||||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,9 +46,12 @@ allprojects {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
buildToolsVersion = "33.0.2"
|
googlePlayServicesVersion = "+" // default: "+"
|
||||||
minSdkVersion = 21
|
firebaseMessagingVersion = "21.1.0" // default: "+"
|
||||||
compileSdkVersion = 33
|
|
||||||
targetSdkVersion = 33
|
buildToolsVersion = "30.0.2"
|
||||||
|
minSdkVersion = 23
|
||||||
|
compileSdkVersion = 30
|
||||||
|
targetSdkVersion = 30
|
||||||
ndkVersion = "21.4.7075529"
|
ndkVersion = "21.4.7075529"
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-3
@@ -1,6 +1,5 @@
|
|||||||
#Wed Oct 11 14:45:21 CEST 2023
|
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
|
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStorePath=wrapper/dists
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
|||||||
@@ -4,19 +4,15 @@ import { StyleSheet, View } from 'react-native'
|
|||||||
|
|
||||||
import AppText from '../common/app-text'
|
import AppText from '../common/app-text'
|
||||||
|
|
||||||
import { Sizes, Typography } from '../../styles'
|
import { Typography } from '../../styles'
|
||||||
import { CHART_YAXIS_WIDTH } from '../../config'
|
import { CHART_YAXIS_WIDTH } from '../../config'
|
||||||
import { shared as labels } from '../../i18n/en/labels'
|
import { shared as labels } from '../../i18n/en/labels'
|
||||||
|
|
||||||
const ChartLegend = ({ height }) => {
|
const ChartLegend = ({ height }) => {
|
||||||
return (
|
return (
|
||||||
<View style={[styles.container, { height }]}>
|
<View style={[styles.container, { height }]}>
|
||||||
<View style={[styles.singleLabelContainer, { height: height / 2 }]}>
|
<AppText style={styles.textBold}>#</AppText>
|
||||||
<AppText style={styles.textBold}>#</AppText>
|
<AppText style={styles.text}>{labels.date}</AppText>
|
||||||
</View>
|
|
||||||
<View style={[styles.singleLabelContainer, { height: height / 2 }]}>
|
|
||||||
<AppText style={styles.text}>{labels.date}</AppText>
|
|
||||||
</View>
|
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -31,13 +27,8 @@ const styles = StyleSheet.create({
|
|||||||
justifyContent: 'flex-end',
|
justifyContent: 'flex-end',
|
||||||
width: CHART_YAXIS_WIDTH,
|
width: CHART_YAXIS_WIDTH,
|
||||||
},
|
},
|
||||||
singleLabelContainer: {
|
|
||||||
justifyContent: 'space-around',
|
|
||||||
alignItems: 'center',
|
|
||||||
},
|
|
||||||
text: {
|
text: {
|
||||||
...Typography.label,
|
...Typography.label,
|
||||||
fontSize: Sizes.footnote,
|
|
||||||
},
|
},
|
||||||
textBold: {
|
textBold: {
|
||||||
...Typography.labelBold,
|
...Typography.labelBold,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import HorizontalGrid from './horizontal-grid'
|
|||||||
import MainGrid from './main-grid'
|
import MainGrid from './main-grid'
|
||||||
import NoData from './no-data'
|
import NoData from './no-data'
|
||||||
import NoTemperature from './no-temperature'
|
import NoTemperature from './no-temperature'
|
||||||
import Tutorial from './Tutorial'
|
import Tutorial from './tutorial'
|
||||||
import YAxis from './y-axis'
|
import YAxis from './y-axis'
|
||||||
|
|
||||||
import { getCycleDaysSortedByDate } from '../../db'
|
import { getCycleDaysSortedByDate } from '../../db'
|
||||||
@@ -29,7 +29,7 @@ const getSymptomsFromCycleDays = (cycleDays) =>
|
|||||||
SYMPTOMS.filter((symptom) => cycleDays.some((cycleDay) => cycleDay[symptom]))
|
SYMPTOMS.filter((symptom) => cycleDays.some((cycleDay) => cycleDay[symptom]))
|
||||||
|
|
||||||
const CycleChart = ({ navigate, setDate }) => {
|
const CycleChart = ({ navigate, setDate }) => {
|
||||||
const [shouldShowHint, setShouldShowHint] = useState(false)
|
const [shouldShowHint, setShouldShowHint] = useState(true)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let isMounted = true
|
let isMounted = true
|
||||||
|
|||||||
@@ -19,20 +19,11 @@ const CycleDayLabel = ({ height, date }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.container, { height }]}>
|
<View style={[styles.container, { height }]}>
|
||||||
<View style={{ ...styles.labelRow, height: height / 2 }}>
|
<AppText style={styles.textBold}>{cycleDayLabel}</AppText>
|
||||||
<AppText style={styles.textBold}>{cycleDayLabel}</AppText>
|
<View style={styles.dateLabel}>
|
||||||
</View>
|
<AppText style={styles.text}>
|
||||||
|
{isFirstDayOfMonth ? momentDate.format('MMM') : dayOfMonth}
|
||||||
<View style={{ ...styles.labelRow, height: height / 2 }}>
|
</AppText>
|
||||||
{isFirstDayOfMonth && (
|
|
||||||
<AppText style={styles.textFootnote}>
|
|
||||||
{momentDate.format('MMM')}
|
|
||||||
</AppText>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!isFirstDayOfMonth && (
|
|
||||||
<AppText style={styles.textSmall}>{dayOfMonth}</AppText>
|
|
||||||
)}
|
|
||||||
{!isFirstDayOfMonth && (
|
{!isFirstDayOfMonth && (
|
||||||
<AppText style={styles.textLight}>
|
<AppText style={styles.textLight}>
|
||||||
{getOrdinalSuffix(dayOfMonth)}
|
{getOrdinalSuffix(dayOfMonth)}
|
||||||
@@ -54,21 +45,17 @@ const styles = StyleSheet.create({
|
|||||||
justifyContent: 'flex-end',
|
justifyContent: 'flex-end',
|
||||||
left: 4,
|
left: 4,
|
||||||
},
|
},
|
||||||
textSmall: {
|
text: {
|
||||||
...Typography.label,
|
...Typography.label,
|
||||||
fontSize: Sizes.small,
|
fontSize: Sizes.small,
|
||||||
},
|
},
|
||||||
textFootnote: {
|
|
||||||
...Typography.label,
|
|
||||||
fontSize: Sizes.footnote,
|
|
||||||
},
|
|
||||||
textBold: {
|
textBold: {
|
||||||
...Typography.labelBold,
|
...Typography.labelBold,
|
||||||
},
|
},
|
||||||
textLight: {
|
textLight: {
|
||||||
...Typography.labelLight,
|
...Typography.labelLight,
|
||||||
},
|
},
|
||||||
labelRow: {
|
dateLabel: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
justifyContent: 'space-around',
|
justifyContent: 'space-around',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { TouchableOpacity } from 'react-native'
|
import { TouchableOpacity } from 'react-native'
|
||||||
import moment from 'moment'
|
|
||||||
|
|
||||||
import { getCycleDay } from '../../db'
|
import { getCycleDay } from '../../db'
|
||||||
|
|
||||||
@@ -27,8 +26,6 @@ const DayColumn = ({
|
|||||||
symptomRowSymptoms,
|
symptomRowSymptoms,
|
||||||
xAxisHeight,
|
xAxisHeight,
|
||||||
}) => {
|
}) => {
|
||||||
const momentDate = moment(dateString)
|
|
||||||
const isWeekend = momentDate.day() == 0 || momentDate.day() == 6
|
|
||||||
const cycleDayData = getCycleDay(dateString)
|
const cycleDayData = getCycleDay(dateString)
|
||||||
let data = {}
|
let data = {}
|
||||||
|
|
||||||
@@ -76,7 +73,6 @@ const DayColumn = ({
|
|||||||
isVerticalLine={fhmAndLtl.drawFhmLine}
|
isVerticalLine={fhmAndLtl.drawFhmLine}
|
||||||
data={data && data.temperature}
|
data={data && data.temperature}
|
||||||
columnHeight={columnHeight}
|
columnHeight={columnHeight}
|
||||||
isWeekend={isWeekend}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -96,7 +92,6 @@ const DayColumn = ({
|
|||||||
isSymptomDataComplete={
|
isSymptomDataComplete={
|
||||||
hasSymptomData && isSymptomDataComplete(symptom, dateString)
|
hasSymptomData && isSymptomDataComplete(symptom, dateString)
|
||||||
}
|
}
|
||||||
isWeekend={isWeekend}
|
|
||||||
height={symptomHeight}
|
height={symptomHeight}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ import { Colors } from '../../styles'
|
|||||||
import {
|
import {
|
||||||
CHART_COLUMN_WIDTH,
|
CHART_COLUMN_WIDTH,
|
||||||
CHART_COLUMN_MIDDLE,
|
CHART_COLUMN_MIDDLE,
|
||||||
CHART_DOT_RADIUS_SYMPTOM,
|
CHART_DOT_RADIUS,
|
||||||
CHART_DOT_RADIUS_TEMPERATURE,
|
|
||||||
CHART_STROKE_WIDTH,
|
CHART_STROKE_WIDTH,
|
||||||
} from '../../config'
|
} from '../../config'
|
||||||
|
|
||||||
@@ -36,9 +35,9 @@ const DotAndLine = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const dot = new Path()
|
const dot = new Path()
|
||||||
.moveTo(CHART_COLUMN_MIDDLE, y - CHART_DOT_RADIUS_TEMPERATURE)
|
.moveTo(CHART_COLUMN_MIDDLE, y - CHART_DOT_RADIUS)
|
||||||
.arc(0, CHART_DOT_RADIUS_TEMPERATURE * 2, CHART_DOT_RADIUS_TEMPERATURE)
|
.arc(0, CHART_DOT_RADIUS * 2, CHART_DOT_RADIUS)
|
||||||
.arc(0, CHART_DOT_RADIUS_TEMPERATURE * -2, CHART_DOT_RADIUS_TEMPERATURE)
|
.arc(0, CHART_DOT_RADIUS * -2, CHART_DOT_RADIUS)
|
||||||
const dotColor = exclude ? Colors.turquoise : Colors.turquoiseDark
|
const dotColor = exclude ? Colors.turquoise : Colors.turquoiseDark
|
||||||
const lineColorLeft = excludeLeftLine
|
const lineColorLeft = excludeLeftLine
|
||||||
? Colors.turquoise
|
? Colors.turquoise
|
||||||
@@ -59,13 +58,13 @@ const DotAndLine = ({
|
|||||||
d={lineRight}
|
d={lineRight}
|
||||||
stroke={lineColorRight}
|
stroke={lineColorRight}
|
||||||
strokeWidth={CHART_STROKE_WIDTH}
|
strokeWidth={CHART_STROKE_WIDTH}
|
||||||
key={y + CHART_DOT_RADIUS_SYMPTOM}
|
key={y + CHART_DOT_RADIUS}
|
||||||
/>
|
/>
|
||||||
<Shape
|
<Shape
|
||||||
d={dot}
|
d={dot}
|
||||||
stroke={dotColor}
|
stroke={dotColor}
|
||||||
strokeWidth={CHART_STROKE_WIDTH}
|
strokeWidth={CHART_STROKE_WIDTH}
|
||||||
fill={Colors.turquoiseDark}
|
fill="white"
|
||||||
key="dot"
|
key="dot"
|
||||||
/>
|
/>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { StyleSheet, View } from 'react-native'
|
|||||||
import { Colors, Containers } from '../../styles'
|
import { Colors, Containers } from '../../styles'
|
||||||
import {
|
import {
|
||||||
CHART_COLUMN_WIDTH,
|
CHART_COLUMN_WIDTH,
|
||||||
CHART_DOT_RADIUS_SYMPTOM,
|
CHART_DOT_RADIUS,
|
||||||
CHART_GRID_LINE_HORIZONTAL_WIDTH,
|
CHART_GRID_LINE_HORIZONTAL_WIDTH,
|
||||||
} from '../../config'
|
} from '../../config'
|
||||||
|
|
||||||
@@ -15,31 +15,14 @@ const SymptomCell = ({
|
|||||||
symptom,
|
symptom,
|
||||||
symptomValue,
|
symptomValue,
|
||||||
isSymptomDataComplete,
|
isSymptomDataComplete,
|
||||||
isWeekend,
|
|
||||||
}) => {
|
}) => {
|
||||||
const shouldDrawDot = symptomValue !== false
|
const shouldDrawDot = symptomValue !== false
|
||||||
// Determine the background color based on isWeekend prop
|
|
||||||
const backgroundColor = isWeekend ? Colors.greyVeryLight : 'white'
|
|
||||||
const styleCell =
|
const styleCell =
|
||||||
index !== 0
|
index !== 0
|
||||||
? [
|
? [styles.cell, { height, width: CHART_COLUMN_WIDTH }]
|
||||||
styles.cell,
|
: [styles.cell, { height, width: CHART_COLUMN_WIDTH }, styles.topBorder]
|
||||||
{
|
|
||||||
height,
|
|
||||||
width: CHART_COLUMN_WIDTH,
|
|
||||||
backgroundColor: backgroundColor,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
: [
|
|
||||||
styles.cell,
|
|
||||||
{
|
|
||||||
height,
|
|
||||||
width: CHART_COLUMN_WIDTH,
|
|
||||||
backgroundColor: backgroundColor,
|
|
||||||
},
|
|
||||||
styles.topBorder,
|
|
||||||
]
|
|
||||||
let styleDot
|
let styleDot
|
||||||
|
|
||||||
if (shouldDrawDot) {
|
if (shouldDrawDot) {
|
||||||
const styleSymptom = Colors.iconColors[symptom]
|
const styleSymptom = Colors.iconColors[symptom]
|
||||||
const symptomColor = styleSymptom.shades[symptomValue]
|
const symptomColor = styleSymptom.shades[symptomValue]
|
||||||
@@ -64,11 +47,11 @@ SymptomCell.propTypes = {
|
|||||||
symptom: PropTypes.string,
|
symptom: PropTypes.string,
|
||||||
symptomValue: PropTypes.oneOfType([PropTypes.bool, PropTypes.number]),
|
symptomValue: PropTypes.oneOfType([PropTypes.bool, PropTypes.number]),
|
||||||
isSymptomDataComplete: PropTypes.bool,
|
isSymptomDataComplete: PropTypes.bool,
|
||||||
isWeekend: PropTypes.bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
cell: {
|
cell: {
|
||||||
|
backgroundColor: 'white',
|
||||||
borderBottomColor: Colors.grey,
|
borderBottomColor: Colors.grey,
|
||||||
borderBottomWidth: CHART_GRID_LINE_HORIZONTAL_WIDTH,
|
borderBottomWidth: CHART_GRID_LINE_HORIZONTAL_WIDTH,
|
||||||
borderLeftColor: Colors.grey,
|
borderLeftColor: Colors.grey,
|
||||||
@@ -80,8 +63,8 @@ const styles = StyleSheet.create({
|
|||||||
borderTopWidth: CHART_GRID_LINE_HORIZONTAL_WIDTH,
|
borderTopWidth: CHART_GRID_LINE_HORIZONTAL_WIDTH,
|
||||||
},
|
},
|
||||||
dot: {
|
dot: {
|
||||||
width: CHART_DOT_RADIUS_SYMPTOM * 2,
|
width: CHART_DOT_RADIUS * 2,
|
||||||
height: CHART_DOT_RADIUS_SYMPTOM * 2,
|
height: CHART_DOT_RADIUS * 2,
|
||||||
borderRadius: 50,
|
borderRadius: 50,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { Colors } from '../../styles'
|
import { StyleSheet } from 'react-native'
|
||||||
|
|
||||||
import { Surface, Path } from '@react-native-community/art'
|
import { Surface, Path } from '@react-native-community/art'
|
||||||
|
|
||||||
@@ -14,16 +14,14 @@ const TemperatureColumn = ({
|
|||||||
isVerticalLine,
|
isVerticalLine,
|
||||||
data,
|
data,
|
||||||
columnHeight,
|
columnHeight,
|
||||||
isWeekend,
|
|
||||||
}) => {
|
}) => {
|
||||||
const x = CHART_STROKE_WIDTH / 2
|
const x = CHART_STROKE_WIDTH / 2
|
||||||
|
|
||||||
const backgroundColor = isWeekend ? Colors.greyVeryLight : 'white'
|
|
||||||
return (
|
return (
|
||||||
<Surface
|
<Surface
|
||||||
width={CHART_COLUMN_WIDTH}
|
width={CHART_COLUMN_WIDTH}
|
||||||
height={columnHeight}
|
height={columnHeight}
|
||||||
style={{ backgroundColor: backgroundColor }}
|
style={styles.container}
|
||||||
>
|
>
|
||||||
<ChartLine path={new Path().lineTo(0, columnHeight)} />
|
<ChartLine path={new Path().lineTo(0, columnHeight)} />
|
||||||
|
|
||||||
@@ -65,7 +63,12 @@ TemperatureColumn.propTypes = {
|
|||||||
isVerticalLine: PropTypes.bool,
|
isVerticalLine: PropTypes.bool,
|
||||||
data: PropTypes.object,
|
data: PropTypes.object,
|
||||||
columnHeight: PropTypes.number,
|
columnHeight: PropTypes.number,
|
||||||
isWeekend: PropTypes.bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
backgroundColor: 'white',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
export default TemperatureColumn
|
export default TemperatureColumn
|
||||||
|
|||||||
@@ -6,17 +6,16 @@ import AppText from '../common/app-text'
|
|||||||
import CloseIcon from '../common/close-icon'
|
import CloseIcon from '../common/close-icon'
|
||||||
|
|
||||||
import { Containers, Spacing } from '../../styles'
|
import { Containers, Spacing } from '../../styles'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { chart } from '../../i18n/en/labels'
|
||||||
|
|
||||||
const image = require('../../assets/swipe.png')
|
const image = require('../../assets/swipe.png')
|
||||||
|
|
||||||
const Tutorial = ({ onClose }) => {
|
const Tutorial = ({ onClose }) => {
|
||||||
const { t } = useTranslation()
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<Image resizeMode="contain" source={image} style={styles.image} />
|
<Image resizeMode="contain" source={image} style={styles.image} />
|
||||||
<View style={styles.textContainer}>
|
<View style={styles.textContainer}>
|
||||||
<AppText>{t('chart.tutorial')}</AppText>
|
<AppText>{chart.tutorial}</AppText>
|
||||||
</View>
|
</View>
|
||||||
<CloseIcon onClose={onClose} />
|
<CloseIcon onClose={onClose} />
|
||||||
</View>
|
</View>
|
||||||
@@ -3,6 +3,7 @@ import { LocalDate } from '@js-joda/core'
|
|||||||
import { scaleObservable, unitObservable } from '../../local-storage'
|
import { scaleObservable, unitObservable } from '../../local-storage'
|
||||||
import { getCycleStatusForDay } from '../../lib/sympto-adapter'
|
import { getCycleStatusForDay } from '../../lib/sympto-adapter'
|
||||||
import { getCycleDay, getAmountOfCycleDays } from '../../db'
|
import { getCycleDay, getAmountOfCycleDays } from '../../db'
|
||||||
|
import { Sizes } from '../../styles'
|
||||||
|
|
||||||
//YAxis helpers
|
//YAxis helpers
|
||||||
|
|
||||||
@@ -50,15 +51,19 @@ export function getTickList(columnHeight) {
|
|||||||
const label = tick.toFixed(1)
|
const label = tick.toFixed(1)
|
||||||
let shouldShowLabel
|
let shouldShowLabel
|
||||||
|
|
||||||
// when temp range <= 2, units === 0.1 we show temp values with step 0.2
|
// when units === 0.1 and tick height is big enough, we show temp values with step 0.2
|
||||||
// when temp range > 2, units === 0.5 we show temp values with step 0.5
|
// when units === 0.5 and tick height is not big enough, we show temp values with step 1
|
||||||
|
// otherwise we show temp values with step 0.5
|
||||||
|
|
||||||
if (unit === 0.1) {
|
switch (true) {
|
||||||
// show label with step 0.2
|
case unit === 0.1 && tickHeight > Sizes.base + 2:
|
||||||
shouldShowLabel = !((label * 10) % 2)
|
shouldShowLabel = !((label * 10) % 2)
|
||||||
} else {
|
break
|
||||||
// show label with step 0.5
|
case unit === 0.5 && tickHeight <= Sizes.base + 2:
|
||||||
shouldShowLabel = !((label * 10) % 5)
|
shouldShowLabel = !((label * 10) % 10)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
shouldShowLabel = !((label * 10) % 5)
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't show label, if first or last tick
|
// don't show label, if first or last tick
|
||||||
|
|||||||
@@ -6,23 +6,20 @@ import MenuItem from './menu-item'
|
|||||||
|
|
||||||
import { Containers } from '../../styles'
|
import { Containers } from '../../styles'
|
||||||
import { pages } from '../pages'
|
import { pages } from '../pages'
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
|
|
||||||
const Menu = ({ currentPage, navigate }) => {
|
const Menu = ({ currentPage, navigate }) => {
|
||||||
const menuItems = pages.filter((page) => page.isInMenu)
|
const menuItems = pages.filter((page) => page.isInMenu)
|
||||||
|
|
||||||
const { t } = useTranslation(null, { keyPrefix: 'bottomMenu' })
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
{menuItems.map(({ icon, labelKey, component }) => {
|
{menuItems.map(({ icon, label, component }) => {
|
||||||
return (
|
return (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
isActive={component === currentPage}
|
isActive={component === currentPage}
|
||||||
onPress={() => navigate(component)}
|
onPress={() => navigate(component)}
|
||||||
icon={icon}
|
icon={icon}
|
||||||
key={labelKey}
|
key={label}
|
||||||
label={t(labelKey)}
|
label={label}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
|||||||
+15
-3
@@ -1,63 +1,75 @@
|
|||||||
import settingsViews from './settings'
|
import settingsViews from './settings'
|
||||||
|
|
||||||
|
import settingsLabels from '../i18n/en/settings'
|
||||||
|
const labels = settingsLabels.menuItems
|
||||||
|
|
||||||
export const pages = [
|
export const pages = [
|
||||||
{
|
{
|
||||||
component: 'Home',
|
component: 'Home',
|
||||||
icon: 'home',
|
icon: 'home',
|
||||||
|
label: 'Home',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: 'Calendar',
|
component: 'Calendar',
|
||||||
icon: 'calendar',
|
icon: 'calendar',
|
||||||
isInMenu: true,
|
isInMenu: true,
|
||||||
labelKey: 'calendar',
|
label: 'Calendar',
|
||||||
parent: 'Home',
|
parent: 'Home',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: 'Chart',
|
component: 'Chart',
|
||||||
icon: 'chart',
|
icon: 'chart',
|
||||||
isInMenu: true,
|
isInMenu: true,
|
||||||
labelKey: 'chart',
|
label: 'Chart',
|
||||||
parent: 'Home',
|
parent: 'Home',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: 'Stats',
|
component: 'Stats',
|
||||||
icon: 'statistics',
|
icon: 'statistics',
|
||||||
isInMenu: true,
|
isInMenu: true,
|
||||||
labelKey: 'stats',
|
label: 'Stats',
|
||||||
parent: 'Home',
|
parent: 'Home',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
children: Object.keys(settingsViews),
|
children: Object.keys(settingsViews),
|
||||||
component: 'SettingsMenu',
|
component: 'SettingsMenu',
|
||||||
icon: 'settings',
|
icon: 'settings',
|
||||||
|
label: 'Settings',
|
||||||
parent: 'Home',
|
parent: 'Home',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: 'Reminders',
|
component: 'Reminders',
|
||||||
|
label: labels.reminders.name,
|
||||||
parent: 'SettingsMenu',
|
parent: 'SettingsMenu',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: 'NfpSettings',
|
component: 'NfpSettings',
|
||||||
|
label: labels.nfpSettings.name,
|
||||||
parent: 'SettingsMenu',
|
parent: 'SettingsMenu',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: 'DataManagement',
|
component: 'DataManagement',
|
||||||
|
label: labels.dataManagement.name,
|
||||||
parent: 'SettingsMenu',
|
parent: 'SettingsMenu',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: 'Password',
|
component: 'Password',
|
||||||
|
label: labels.password.name,
|
||||||
parent: 'SettingsMenu',
|
parent: 'SettingsMenu',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: 'About',
|
component: 'About',
|
||||||
|
label: 'About',
|
||||||
parent: 'SettingsMenu',
|
parent: 'SettingsMenu',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: 'License',
|
component: 'License',
|
||||||
|
label: 'License',
|
||||||
parent: 'SettingsMenu',
|
parent: 'SettingsMenu',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: 'PrivacyPolicy',
|
component: 'PrivacyPolicy',
|
||||||
|
label: 'PrivacyPolicy',
|
||||||
parent: 'SettingsMenu',
|
parent: 'SettingsMenu',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,97 +0,0 @@
|
|||||||
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,
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
+27
-8
@@ -6,23 +6,40 @@ import AppText from '../../common/app-text'
|
|||||||
import Button from '../../common/button'
|
import Button from '../../common/button'
|
||||||
import Segment from '../../common/segment'
|
import Segment from '../../common/segment'
|
||||||
|
|
||||||
|
import { openImportDialog, getFileContent, importData } from './import-dialog'
|
||||||
import openShareDialogAndExport from './export-dialog'
|
import openShareDialogAndExport from './export-dialog'
|
||||||
import DeleteData from './delete-data'
|
import DeleteData from './delete-data'
|
||||||
|
|
||||||
import labels from '../../../i18n/en/settings'
|
import labels from '../../../i18n/en/settings'
|
||||||
import ImportData from './ImportData'
|
import { ACTION_DELETE, ACTION_EXPORT, ACTION_IMPORT } from '../../../config'
|
||||||
|
|
||||||
const DataManagement = () => {
|
const DataManagement = () => {
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
const [isDeletingData, setIsDeletingData] = 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 startExport = () => {
|
const startExport = () => {
|
||||||
setIsDeletingData(false)
|
setCurrentAction(ACTION_EXPORT)
|
||||||
openShareDialogAndExport()
|
openShareDialogAndExport()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const startImport = () => {
|
||||||
|
setCurrentAction(ACTION_IMPORT)
|
||||||
|
openImportDialog(startImportFlow)
|
||||||
|
}
|
||||||
|
|
||||||
if (isLoading) return <AppLoadingView />
|
if (isLoading) return <AppLoadingView />
|
||||||
|
|
||||||
|
const isDeletingData = currentAction === ACTION_DELETE
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppPage>
|
<AppPage>
|
||||||
<Segment title={labels.export.button}>
|
<Segment title={labels.export.button}>
|
||||||
@@ -31,15 +48,17 @@ const DataManagement = () => {
|
|||||||
{labels.export.button}
|
{labels.export.button}
|
||||||
</Button>
|
</Button>
|
||||||
</Segment>
|
</Segment>
|
||||||
<ImportData
|
<Segment title={labels.import.button}>
|
||||||
resetIsDeletingData={() => setIsDeletingData(false)}
|
<AppText>{labels.import.segmentExplainer}</AppText>
|
||||||
setIsLoading={setIsLoading}
|
<Button isCTA onPress={startImport}>
|
||||||
/>
|
{labels.import.button}
|
||||||
|
</Button>
|
||||||
|
</Segment>
|
||||||
<Segment title={labels.deleteSegment.title} last>
|
<Segment title={labels.deleteSegment.title} last>
|
||||||
<AppText>{labels.deleteSegment.explainer}</AppText>
|
<AppText>{labels.deleteSegment.explainer}</AppText>
|
||||||
<DeleteData
|
<DeleteData
|
||||||
isDeletingData={isDeletingData}
|
isDeletingData={isDeletingData}
|
||||||
onStartDeletion={() => setIsDeletingData(true)}
|
onStartDeletion={() => setCurrentAction(ACTION_DELETE)}
|
||||||
/>
|
/>
|
||||||
</Segment>
|
</Segment>
|
||||||
</AppPage>
|
</AppPage>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import Reminders from './reminders/reminders'
|
import Reminders from './reminders/reminders'
|
||||||
import NfpSettings from './nfp-settings'
|
import NfpSettings from './nfp-settings'
|
||||||
import DataManagement from './data-management/DataManagement'
|
import DataManagement from './data-management'
|
||||||
import Password from './password'
|
import Password from './password'
|
||||||
import About from './About'
|
import About from './About'
|
||||||
import License from './License'
|
import License from './License'
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import { PixelRatio, StatusBar } from 'react-native'
|
import { PixelRatio, StatusBar } from 'react-native'
|
||||||
import { scale, verticalScale } from 'react-native-size-matters'
|
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 = [
|
export const SYMPTOMS = [
|
||||||
'bleeding',
|
'bleeding',
|
||||||
'temperature',
|
'temperature',
|
||||||
@@ -15,13 +19,12 @@ export const SYMPTOMS = [
|
|||||||
|
|
||||||
export const CHART_COLUMN_WIDTH = 32
|
export const CHART_COLUMN_WIDTH = 32
|
||||||
export const CHART_COLUMN_MIDDLE = CHART_COLUMN_WIDTH / 2
|
export const CHART_COLUMN_MIDDLE = CHART_COLUMN_WIDTH / 2
|
||||||
export const CHART_DOT_RADIUS_SYMPTOM = scale(6)
|
export const CHART_DOT_RADIUS = scale(6)
|
||||||
export const CHART_DOT_RADIUS_TEMPERATURE = scale(4)
|
|
||||||
export const CHART_GRID_LINE_HORIZONTAL_WIDTH =
|
export const CHART_GRID_LINE_HORIZONTAL_WIDTH =
|
||||||
PixelRatio.roundToNearestPixel(0.3)
|
PixelRatio.roundToNearestPixel(0.3)
|
||||||
export const CHART_ICON_SIZE = scale(20)
|
export const CHART_ICON_SIZE = scale(20)
|
||||||
export const CHART_STROKE_WIDTH = scale(1.5)
|
export const CHART_STROKE_WIDTH = scale(3)
|
||||||
export const CHART_SYMPTOM_HEIGHT_RATIO = scale(0.06)
|
export const CHART_SYMPTOM_HEIGHT_RATIO = scale(0.08)
|
||||||
export const CHART_XAXIS_HEIGHT_RATIO = scale(0.1)
|
export const CHART_XAXIS_HEIGHT_RATIO = scale(0.1)
|
||||||
export const CHART_YAXIS_WIDTH = scale(32)
|
export const CHART_YAXIS_WIDTH = scale(32)
|
||||||
export const CHART_TICK_WIDTH = scale(44)
|
export const CHART_TICK_WIDTH = scale(44)
|
||||||
@@ -37,7 +40,7 @@ export const HIT_SLOP = {
|
|||||||
top: verticalScale(20),
|
top: verticalScale(20),
|
||||||
bottom: verticalScale(20),
|
bottom: verticalScale(20),
|
||||||
left: scale(20),
|
left: scale(20),
|
||||||
right: scale(20),
|
right: scale(20)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const STATUSBAR_HEIGHT = StatusBar.currentHeight
|
export const STATUSBAR_HEIGHT = StatusBar.currentHeight
|
||||||
|
|||||||
+9
-40
@@ -1,23 +1,15 @@
|
|||||||
{
|
{
|
||||||
"bottomMenu": {
|
|
||||||
"calendar": "Calendar",
|
|
||||||
"chart": "Chart",
|
|
||||||
"stats": "Stats"
|
|
||||||
},
|
|
||||||
"chart": {
|
|
||||||
"tutorial": "You can swipe the chart to view more dates."
|
|
||||||
},
|
|
||||||
"cycleDay": {
|
"cycleDay": {
|
||||||
"symptomBox": {
|
"symptomBox": {
|
||||||
"bleeding": "bleeding",
|
"bleeding": "Bleeding",
|
||||||
"temperature": "temperature",
|
"temperature": "Temperature",
|
||||||
"mucus": "cervical mucus",
|
"mucus": "Cervical Mucus",
|
||||||
"cervix": "cervix",
|
"cervix": "Cervix",
|
||||||
"note": "note",
|
"note": "Note",
|
||||||
"desire": "desire",
|
"desire": "Desire",
|
||||||
"sex": "sex",
|
"sex": "Sex",
|
||||||
"pain": "pain",
|
"pain": "Pain",
|
||||||
"mood": "mood"
|
"mood": "Mood"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
@@ -88,29 +80,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"settings": {
|
"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": {
|
"menuItem": {
|
||||||
"dataManagement": {
|
"dataManagement": {
|
||||||
"name": "Data",
|
"name": "Data",
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ export const home = {
|
|||||||
phase: (n) => `${['1st', '2nd', '3rd'][n - 1]} cycle phase`,
|
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 = {
|
export const shared = {
|
||||||
cancel: 'Cancel',
|
cancel: 'Cancel',
|
||||||
save: 'Save',
|
save: 'Save',
|
||||||
|
|||||||
@@ -1,6 +1,24 @@
|
|||||||
import links from './links'
|
import links from './links'
|
||||||
|
|
||||||
export default {
|
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: {
|
export: {
|
||||||
errors: {
|
errors: {
|
||||||
noData: 'There is no data to export',
|
noData: 'There is no data to export',
|
||||||
@@ -13,6 +31,24 @@ export default {
|
|||||||
segmentExplainer:
|
segmentExplainer:
|
||||||
'Export data in CSV format for backup or so you can use it elsewhere',
|
'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: {
|
deleteSegment: {
|
||||||
title: 'Delete app data',
|
title: 'Delete app data',
|
||||||
explainer: 'Delete app data from this phone',
|
explainer: 'Delete app data from this phone',
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ i18n
|
|||||||
compatibilityJSON: 'v3', // TODO: migrate json to v4 and afterwards remove it
|
compatibilityJSON: 'v3', // TODO: migrate json to v4 and afterwards remove it
|
||||||
resources,
|
resources,
|
||||||
fallbackLng: 'en',
|
fallbackLng: 'en',
|
||||||
|
debug: true,
|
||||||
|
|
||||||
interpolation: {
|
interpolation: {
|
||||||
escapeValue: false, // not needed for react as it escapes by default
|
escapeValue: false, // not needed for react as it escapes by default
|
||||||
|
|||||||
@@ -5,8 +5,4 @@ module.exports = {
|
|||||||
transformIgnorePatterns: [
|
transformIgnorePatterns: [
|
||||||
'node_modules/(?!((jest-)?react-native(-.*)?|@react-native(-community)?)/)',
|
'node_modules/(?!((jest-)?react-native(-.*)?|@react-native(-community)?)/)',
|
||||||
],
|
],
|
||||||
watchPlugins: [
|
|
||||||
'jest-watch-typeahead/filename',
|
|
||||||
'jest-watch-typeahead/testname',
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-4
@@ -3,6 +3,8 @@ import { getCycleLengthStats } from './cycle-length'
|
|||||||
const LocalDate = joda.LocalDate
|
const LocalDate = joda.LocalDate
|
||||||
const DAYS = joda.ChronoUnit.DAYS
|
const DAYS = joda.ChronoUnit.DAYS
|
||||||
|
|
||||||
|
const toJSON = (realmObj) => JSON.parse(JSON.stringify(realmObj))
|
||||||
|
|
||||||
export default function config(opts) {
|
export default function config(opts) {
|
||||||
let bleedingDaysSortedByDate
|
let bleedingDaysSortedByDate
|
||||||
let cycleStartsSortedByDate
|
let cycleStartsSortedByDate
|
||||||
@@ -14,10 +16,13 @@ export default function config(opts) {
|
|||||||
if (!opts) {
|
if (!opts) {
|
||||||
// we only want to require (and run) the db module
|
// we only want to require (and run) the db module
|
||||||
// when not running the tests
|
// when not running the tests
|
||||||
bleedingDaysSortedByDate = require('../db').getBleedingDaysSortedByDate()
|
bleedingDaysSortedByDate = toJSON(
|
||||||
cycleStartsSortedByDate = require('../db').getCycleStartsSortedByDate()
|
require('../db').getBleedingDaysSortedByDate()
|
||||||
cycleDaysSortedByDate = require('../db').getCycleDaysSortedByDate()
|
)
|
||||||
|
cycleStartsSortedByDate = toJSON(
|
||||||
|
require('../db').getCycleStartsSortedByDate()
|
||||||
|
)
|
||||||
|
cycleDaysSortedByDate = toJSON(require('../db').getCycleDaysSortedByDate())
|
||||||
maxBreakInBleeding = 1
|
maxBreakInBleeding = 1
|
||||||
maxCycleLength = 99
|
maxCycleLength = 99
|
||||||
minCyclesForPrediction = 3
|
minCyclesForPrediction = 3
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
import getColumnNamesForCsv from './get-csv-column-names'
|
import getColumnNamesForCsv from './get-csv-column-names'
|
||||||
import replaceWithNullIfAllPropertiesAreNull from './replace-with-null'
|
import replaceWithNullIfAllPropertiesAreNull from './replace-with-null'
|
||||||
import { LocalDate } from '@js-joda/core'
|
import { LocalDate } from '@js-joda/core'
|
||||||
import i18next from 'i18next'
|
import labels from '../../i18n/en/settings'
|
||||||
|
|
||||||
export default async function importCsv(csv, deleteFirst) {
|
export default async function importCsv(csv, deleteFirst) {
|
||||||
const parseFuncs = {
|
const parseFuncs = {
|
||||||
@@ -46,10 +46,7 @@ export default async function importCsv(csv, deleteFirst) {
|
|||||||
|
|
||||||
const cycleDays = await csvParser(config)
|
const cycleDays = await csvParser(config)
|
||||||
.fromString(csv)
|
.fromString(csv)
|
||||||
.on('header', (headers) => validateHeaders(headers))
|
.on('header', validateHeaders)
|
||||||
.on('error', (error) => {
|
|
||||||
throw error
|
|
||||||
})
|
|
||||||
|
|
||||||
//remove symptoms where all fields are null
|
//remove symptoms where all fields are null
|
||||||
putNullForEmptySymptoms(cycleDays)
|
putNullForEmptySymptoms(cycleDays)
|
||||||
@@ -70,11 +67,8 @@ function validateHeaders(headers) {
|
|||||||
return expectedHeaders.indexOf(header) > -1
|
return expectedHeaders.indexOf(header) > -1
|
||||||
})
|
})
|
||||||
) {
|
) {
|
||||||
throw new Error(
|
const msg = `Expected CSV column titles to be ${expectedHeaders.join()}`
|
||||||
i18next.t('hamburgerMenu.settings.data.import.error.incorrectColumns', {
|
throw new Error(msg)
|
||||||
incorrectColumns: expectedHeaders.join(),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,9 +92,7 @@ function throwIfFutureData(cycleDays) {
|
|||||||
day.date > today &&
|
day.date > today &&
|
||||||
Object.keys(day).some((symptom) => symptom != 'date' && symptom != 'note')
|
Object.keys(day).some((symptom) => symptom != 'date' && symptom != 'note')
|
||||||
) {
|
) {
|
||||||
throw new Error(
|
throw new Error(labels.import.errors.futureEdit)
|
||||||
i18next.t('hamburgerMenu.settings.data.import.error.futureEdit')
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-3
@@ -1,16 +1,17 @@
|
|||||||
export default function getSensiplanMucus(feeling, texture) {
|
export default function (feeling, texture) {
|
||||||
|
|
||||||
if (typeof feeling != 'number' || typeof texture != 'number') return null
|
if (typeof feeling != 'number' || typeof texture != 'number') return null
|
||||||
|
|
||||||
const feelingMapping = {
|
const feelingMapping = {
|
||||||
0: 0,
|
0: 0,
|
||||||
1: 1,
|
1: 1,
|
||||||
2: 2,
|
2: 2,
|
||||||
3: 4,
|
3: 4
|
||||||
}
|
}
|
||||||
const textureMapping = {
|
const textureMapping = {
|
||||||
0: 0,
|
0: 0,
|
||||||
1: 3,
|
1: 3,
|
||||||
2: 4,
|
2: 4
|
||||||
}
|
}
|
||||||
const nfpFeelingValue = feelingMapping[feeling]
|
const nfpFeelingValue = feelingMapping[feeling]
|
||||||
const nfpTextureValue = textureMapping[texture]
|
const nfpTextureValue = textureMapping[texture]
|
||||||
|
|||||||
+7
-21
@@ -1,10 +1,8 @@
|
|||||||
import { Platform } from 'react-native'
|
|
||||||
import {
|
import {
|
||||||
tempReminderObservable,
|
tempReminderObservable,
|
||||||
periodReminderObservable,
|
periodReminderObservable,
|
||||||
} from '../local-storage'
|
} from '../local-storage'
|
||||||
import * as PN from 'react-native-push-notification'
|
import Notification from 'react-native-push-notification'
|
||||||
import { requestNotifications } from 'react-native-permissions'
|
|
||||||
import Moment from 'moment'
|
import Moment from 'moment'
|
||||||
import { LocalDate } from '@js-joda/core'
|
import { LocalDate } from '@js-joda/core'
|
||||||
|
|
||||||
@@ -14,16 +12,7 @@ import cycleModule from './cycle'
|
|||||||
import nothingChanged from '../db/db-unchanged'
|
import nothingChanged from '../db/db-unchanged'
|
||||||
|
|
||||||
export default function setupNotifications(navigate, setDate) {
|
export default function setupNotifications(navigate, setDate) {
|
||||||
requestNotifications()
|
Notification.configure({
|
||||||
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) => {
|
onNotification: (notification) => {
|
||||||
// https://github.com/zo0r/react-native-push-notification/issues/966#issuecomment-479069106
|
// https://github.com/zo0r/react-native-push-notification/issues/966#issuecomment-479069106
|
||||||
if (notification.data?.id === '1' || notification.id === '1') {
|
if (notification.data?.id === '1' || notification.id === '1') {
|
||||||
@@ -37,7 +26,7 @@ export default function setupNotifications(navigate, setDate) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
tempReminderObservable((reminder) => {
|
tempReminderObservable((reminder) => {
|
||||||
PushNotification.cancelLocalNotification({ id: '1' })
|
Notification.cancelLocalNotifications({ id: '1' })
|
||||||
if (reminder.enabled) {
|
if (reminder.enabled) {
|
||||||
const [hours, minutes] = reminder.time.split(':')
|
const [hours, minutes] = reminder.time.split(':')
|
||||||
let target = new Moment()
|
let target = new Moment()
|
||||||
@@ -49,33 +38,31 @@ export default function setupNotifications(navigate, setDate) {
|
|||||||
target = target.add(1, 'd')
|
target = target.add(1, 'd')
|
||||||
}
|
}
|
||||||
|
|
||||||
PushNotification.localNotificationSchedule({
|
Notification.localNotificationSchedule({
|
||||||
id: '1',
|
id: '1',
|
||||||
userInfo: { id: '1' },
|
userInfo: { id: '1' },
|
||||||
message: labels.tempReminder.notification,
|
message: labels.tempReminder.notification,
|
||||||
date: target.toDate(),
|
date: target.toDate(),
|
||||||
vibrate: false,
|
vibrate: false,
|
||||||
repeatType: 'day',
|
repeatType: 'day',
|
||||||
channelId: 'drip-channel-id',
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}, false)
|
}, false)
|
||||||
|
|
||||||
periodReminderObservable((reminder) => {
|
periodReminderObservable((reminder) => {
|
||||||
PushNotification.cancelLocalNotification({ id: '2' })
|
Notification.cancelLocalNotifications({ id: '2' })
|
||||||
if (reminder.enabled) setupPeriodReminder()
|
if (reminder.enabled) setupPeriodReminder()
|
||||||
}, false)
|
}, false)
|
||||||
|
|
||||||
getBleedingDaysSortedByDate().addListener((_, changes) => {
|
getBleedingDaysSortedByDate().addListener((_, changes) => {
|
||||||
// the listener fires on setup, so we check if there were actually any changes
|
// the listener fires on setup, so we check if there were actually any changes
|
||||||
if (nothingChanged(changes)) return
|
if (nothingChanged(changes)) return
|
||||||
PushNotification.cancelLocalNotification({ id: '2' })
|
Notification.cancelLocalNotifications({ id: '2' })
|
||||||
if (periodReminderObservable.value.enabled) setupPeriodReminder()
|
if (periodReminderObservable.value.enabled) setupPeriodReminder()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupPeriodReminder() {
|
function setupPeriodReminder() {
|
||||||
const PushNotification = Platform.OS === 'ios' ? PN : PN.default
|
|
||||||
const bleedingPrediction = cycleModule().getPredictedMenses()
|
const bleedingPrediction = cycleModule().getPredictedMenses()
|
||||||
if (bleedingPrediction.length > 0) {
|
if (bleedingPrediction.length > 0) {
|
||||||
const predictedBleedingStart = Moment(
|
const predictedBleedingStart = Moment(
|
||||||
@@ -93,13 +80,12 @@ function setupPeriodReminder() {
|
|||||||
// period is likely to start in 3 to 3 + (length of prediction - 1) days
|
// period is likely to start in 3 to 3 + (length of prediction - 1) days
|
||||||
const daysToEndOfPrediction = bleedingPrediction[0].length + 2
|
const daysToEndOfPrediction = bleedingPrediction[0].length + 2
|
||||||
|
|
||||||
PushNotification.localNotificationSchedule({
|
Notification.localNotificationSchedule({
|
||||||
id: '2',
|
id: '2',
|
||||||
userInfo: { id: '2' },
|
userInfo: { id: '2' },
|
||||||
message: labels.periodReminder.notification(daysToEndOfPrediction),
|
message: labels.periodReminder.notification(daysToEndOfPrediction),
|
||||||
date: reminderDate.toDate(),
|
date: reminderDate.toDate(),
|
||||||
vibrate: false,
|
vibrate: false,
|
||||||
channelId: 'drip-channel-id',
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-4
@@ -12,10 +12,16 @@ export const unitObservable = Observable()
|
|||||||
unitObservable.set(TEMP_SCALE_UNITS)
|
unitObservable.set(TEMP_SCALE_UNITS)
|
||||||
scaleObservable((scale) => {
|
scaleObservable((scale) => {
|
||||||
const scaleRange = scale.max - scale.min
|
const scaleRange = scale.max - scale.min
|
||||||
if (scaleRange <= 1.5) {
|
|
||||||
unitObservable.set(0.1)
|
switch (true) {
|
||||||
} else {
|
case scaleRange <= 1:
|
||||||
unitObservable.set(0.5)
|
unitObservable.set(0.1)
|
||||||
|
break
|
||||||
|
case scaleRange > 1 && scaleRange <= 2:
|
||||||
|
unitObservable.set(0.2)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
unitObservable.set(0.5)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
+12
-13
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "drip.",
|
"name": "drip.",
|
||||||
"version": "1.2311.14",
|
"version": "1.2208.11",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
"Julia Friesel <julia.friesel@gmail.com>",
|
"Julia Friesel <julia.friesel@gmail.com>",
|
||||||
"Marie Kochsiek",
|
"Marie Kochsiek",
|
||||||
@@ -34,23 +34,22 @@
|
|||||||
"@react-native-async-storage/async-storage": "^1.17.9",
|
"@react-native-async-storage/async-storage": "^1.17.9",
|
||||||
"@react-native-community/art": "^1.2.0",
|
"@react-native-community/art": "^1.2.0",
|
||||||
"@react-native-community/datetimepicker": "^6.3.1",
|
"@react-native-community/datetimepicker": "^6.3.1",
|
||||||
"@react-native-community/push-notification-ios": "^1.11.0",
|
"@react-native-community/push-notification-ios": "^1.8.0",
|
||||||
"csvtojson": "^2.0.8",
|
"csvtojson": "^2.0.8",
|
||||||
"i18next": "^22.0.2",
|
"i18next": "^21.9.0",
|
||||||
"jshashes": "^1.0.8",
|
"jshashes": "^1.0.8",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"object-path": "^0.11.4",
|
"object-path": "^0.11.4",
|
||||||
"obv": "0.0.1",
|
"obv": "0.0.1",
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
"react": "17.0.2",
|
"react": "17.0.2",
|
||||||
"react-i18next": "^12.0.0",
|
"react-i18next": "^11.18.3",
|
||||||
"react-native": "0.67.4",
|
"react-native": "0.67.4",
|
||||||
"react-native-calendars": "^1.1287.0",
|
"react-native-calendars": "^1.1287.0",
|
||||||
"react-native-document-picker": "^8.1.1",
|
"react-native-document-picker": "^8.1.1",
|
||||||
"react-native-fs": "^2.20.0",
|
"react-native-fs": "^2.20.0",
|
||||||
"react-native-modal-datetime-picker": "14.0.0",
|
"react-native-modal-datetime-picker": "14.0.0",
|
||||||
"react-native-permissions": "^3.10.0",
|
"react-native-push-notification": "3.2.1",
|
||||||
"react-native-push-notification": "github:bl00dymarie/react-native-push-notification",
|
|
||||||
"react-native-share": "^7.9.0",
|
"react-native-share": "^7.9.0",
|
||||||
"react-native-simple-toast": "^1.1.3",
|
"react-native-simple-toast": "^1.1.3",
|
||||||
"react-native-size-matters": "^0.4.0",
|
"react-native-size-matters": "^0.4.0",
|
||||||
@@ -59,18 +58,18 @@
|
|||||||
"sympto": "3.0.1"
|
"sympto": "3.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.20.2",
|
"@babel/core": "^7.12.9",
|
||||||
"@babel/eslint-parser": "^7.19.1",
|
"@babel/eslint-parser": "^7.19.1",
|
||||||
"@babel/preset-react": "^7.18.6",
|
"@babel/preset-react": "^7.16.0",
|
||||||
"@babel/runtime": "^7.12.5",
|
"@babel/runtime": "^7.12.5",
|
||||||
"@testing-library/jest-native": "^4.0.12",
|
"@testing-library/jest-native": "^4.0.12",
|
||||||
"@testing-library/react-native": "^11.1.0",
|
"@testing-library/react-native": "^11.1.0",
|
||||||
"eslint": "^7.32.0",
|
"basic-changelog": "gitlab:bloodyhealth/basic-changelog",
|
||||||
"eslint-plugin-react": "^7.31.10",
|
"eslint": "7.14.0",
|
||||||
|
"eslint-plugin-react": "^7.8.2",
|
||||||
"husky": "^8.0.0",
|
"husky": "^8.0.0",
|
||||||
"jest": "^29.1.2",
|
"jest": "^28.1.3",
|
||||||
"jest-watch-typeahead": "^2.2.0",
|
"jetifier": "^1.6.6",
|
||||||
"jetifier": "^2.0.0",
|
|
||||||
"metro-react-native-babel-preset": "^0.66.2",
|
"metro-react-native-babel-preset": "^0.66.2",
|
||||||
"prettier": "2.4.0",
|
"prettier": "2.4.0",
|
||||||
"pretty-quick": "^3.1.1",
|
"pretty-quick": "^3.1.1",
|
||||||
|
|||||||
+9
-17
@@ -1,14 +1,7 @@
|
|||||||
const redColor = '#c3000d'
|
const redColor = '#c3000d'
|
||||||
export const shadesOfRed = ['#e7999e', '#db666d', '#cf323d', '#c3000d'] // light to dark
|
export const shadesOfRed = ['#e7999e', '#db666d', '#cf323d', '#c3000d'] // light to dark
|
||||||
const violetColor = '#6a7b98'
|
const violetColor = '#6a7b98'
|
||||||
const shadesOfViolet = [
|
const shadesOfViolet = ['#e3e7ed', '#c8cfdc', '#acb8cb', '#91a0ba', '#7689a9', violetColor] // light to dark
|
||||||
'#e3e7ed',
|
|
||||||
'#c8cfdc',
|
|
||||||
'#acb8cb',
|
|
||||||
'#91a0ba',
|
|
||||||
'#7689a9',
|
|
||||||
violetColor,
|
|
||||||
] // light to dark
|
|
||||||
const yellowColor = '#dbb40c'
|
const yellowColor = '#dbb40c'
|
||||||
const shadesOfYellow = ['#f0e19d', '#e9d26d', '#e2c33c', yellowColor] // light to dark
|
const shadesOfYellow = ['#f0e19d', '#e9d26d', '#e2c33c', yellowColor] // light to dark
|
||||||
const magentaColor = '#6f2565'
|
const magentaColor = '#6f2565'
|
||||||
@@ -23,7 +16,6 @@ export default {
|
|||||||
greyDark: '#555',
|
greyDark: '#555',
|
||||||
grey: '#888',
|
grey: '#888',
|
||||||
greyLight: '#CCC',
|
greyLight: '#CCC',
|
||||||
greyVeryLight: '#F4F4F4',
|
|
||||||
orange: '#F38337',
|
orange: '#F38337',
|
||||||
purple: '#3A2671',
|
purple: '#3A2671',
|
||||||
purpleLight: '#938EB2',
|
purpleLight: '#938EB2',
|
||||||
@@ -31,35 +23,35 @@ export default {
|
|||||||
turquoise: '#CFECEA',
|
turquoise: '#CFECEA',
|
||||||
turquoiseLight: '#E9F2ED',
|
turquoiseLight: '#E9F2ED',
|
||||||
iconColors: {
|
iconColors: {
|
||||||
bleeding: {
|
'bleeding': {
|
||||||
color: redColor,
|
color: redColor,
|
||||||
shades: shadesOfRed,
|
shades: shadesOfRed,
|
||||||
},
|
},
|
||||||
mucus: {
|
'mucus': {
|
||||||
color: violetColor,
|
color: violetColor,
|
||||||
shades: shadesOfViolet,
|
shades: shadesOfViolet,
|
||||||
},
|
},
|
||||||
cervix: {
|
'cervix': {
|
||||||
color: yellowColor,
|
color: yellowColor,
|
||||||
shades: shadesOfYellow,
|
shades: shadesOfYellow,
|
||||||
},
|
},
|
||||||
sex: {
|
'sex': {
|
||||||
color: magentaColor,
|
color: magentaColor,
|
||||||
shades: shadesOfMagenta,
|
shades: shadesOfMagenta,
|
||||||
},
|
},
|
||||||
desire: {
|
'desire': {
|
||||||
color: pinkColor,
|
color: pinkColor,
|
||||||
shades: shadesOfPink,
|
shades: shadesOfPink,
|
||||||
},
|
},
|
||||||
pain: {
|
'pain': {
|
||||||
color: lightGreenColor,
|
color: lightGreenColor,
|
||||||
shades: [lightGreenColor],
|
shades: [lightGreenColor],
|
||||||
},
|
},
|
||||||
mood: {
|
'mood': {
|
||||||
color: orangeColor,
|
color: orangeColor,
|
||||||
shades: [orangeColor],
|
shades: [orangeColor],
|
||||||
},
|
},
|
||||||
note: {
|
'note': {
|
||||||
color: mintColor,
|
color: mintColor,
|
||||||
shades: [mintColor],
|
shades: [mintColor],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,19 +1,27 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import { render, screen, fireEvent } from '@testing-library/react-native'
|
||||||
import AcceptLicense from '../components/AcceptLicense'
|
import AcceptLicense from '../components/AcceptLicense'
|
||||||
|
|
||||||
import { saveLicenseFlag } from '../local-storage'
|
import { saveLicenseFlag } from '../local-storage'
|
||||||
import { render, screen, fireEvent } from './test-utils'
|
|
||||||
|
|
||||||
jest.mock('../local-storage', () => ({
|
jest.mock('../local-storage', () => ({
|
||||||
saveLicenseFlag: jest.fn(() => Promise.resolve()),
|
saveLicenseFlag: jest.fn(() => Promise.resolve()),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
jest.mock('react-i18next', () => ({
|
||||||
|
useTranslation: () => ({
|
||||||
|
t: (str, options) => {
|
||||||
|
return str + (options ? JSON.stringify(options) : '')
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}))
|
||||||
|
|
||||||
describe('AcceptLicense', () => {
|
describe('AcceptLicense', () => {
|
||||||
test('should accept license when clicking ok button', async () => {
|
test('On clicking OK button, the license is accepted', async () => {
|
||||||
const mockedSetLicense = jest.fn()
|
const mockedSetLicense = jest.fn()
|
||||||
render(<AcceptLicense setLicense={mockedSetLicense} />)
|
render(<AcceptLicense setLicense={mockedSetLicense} />)
|
||||||
|
|
||||||
const okButton = screen.getByText('OK')
|
const okButton = screen.getByText('ok', { exact: false })
|
||||||
|
|
||||||
fireEvent(okButton, 'click')
|
fireEvent(okButton, 'click')
|
||||||
|
|
||||||
@@ -21,9 +29,9 @@ describe('AcceptLicense', () => {
|
|||||||
expect(mockedSetLicense).toHaveBeenCalled()
|
expect(mockedSetLicense).toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should render cancel button', async () => {
|
test('There is a Cancel button', async () => {
|
||||||
render(<AcceptLicense setLicense={jest.fn()} />)
|
render(<AcceptLicense setLicense={jest.fn()} />)
|
||||||
|
|
||||||
screen.getByText('Cancel')
|
screen.getByText('cancel', { exact: false })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
+11
-3
@@ -1,16 +1,24 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import { render, screen } from '@testing-library/react-native'
|
||||||
import License from '../components/settings/License'
|
import License from '../components/settings/License'
|
||||||
import { render, screen } from './test-utils'
|
|
||||||
|
jest.mock('react-i18next', () => ({
|
||||||
|
useTranslation: () => ({
|
||||||
|
t: (str, options) => {
|
||||||
|
return str + (options ? JSON.stringify(options) : '')
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}))
|
||||||
|
|
||||||
describe('License screen', () => {
|
describe('License screen', () => {
|
||||||
test('should display license text with correct year', async () => {
|
test('It should have a correct year', async () => {
|
||||||
render(<License />)
|
render(<License />)
|
||||||
const year = new Date().getFullYear().toString()
|
const year = new Date().getFullYear().toString()
|
||||||
|
|
||||||
screen.getByText(year, { exact: false })
|
screen.getByText(year, { exact: false })
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should match the snapshot', async () => {
|
test('It should match the snapshot', async () => {
|
||||||
const licenseScreen = render(<License />)
|
const licenseScreen = render(<License />)
|
||||||
|
|
||||||
expect(licenseScreen).toMatchSnapshot()
|
expect(licenseScreen).toMatchSnapshot()
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`License screen should match the snapshot 1`] = `
|
exports[`License screen It should match the snapshot 1`] = `
|
||||||
<View
|
<View
|
||||||
style={
|
style={
|
||||||
{
|
Object {
|
||||||
"backgroundColor": "#E9F2ED",
|
"backgroundColor": "#E9F2ED",
|
||||||
"flex": 1,
|
"flex": 1,
|
||||||
}
|
}
|
||||||
@@ -11,8 +11,8 @@ exports[`License screen should match the snapshot 1`] = `
|
|||||||
>
|
>
|
||||||
<RCTScrollView
|
<RCTScrollView
|
||||||
contentContainerStyle={
|
contentContainerStyle={
|
||||||
[
|
Array [
|
||||||
{
|
Object {
|
||||||
"backgroundColor": "#E9F2ED",
|
"backgroundColor": "#E9F2ED",
|
||||||
"flexGrow": 1,
|
"flexGrow": 1,
|
||||||
},
|
},
|
||||||
@@ -23,13 +23,13 @@ exports[`License screen should match the snapshot 1`] = `
|
|||||||
<View>
|
<View>
|
||||||
<Text
|
<Text
|
||||||
style={
|
style={
|
||||||
[
|
Array [
|
||||||
{
|
Object {
|
||||||
"color": "#555",
|
"color": "#555",
|
||||||
"fontFamily": "Jost-Book",
|
"fontFamily": "Jost-Book",
|
||||||
"fontSize": 34.285714285714285,
|
"fontSize": 34.285714285714285,
|
||||||
},
|
},
|
||||||
{
|
Object {
|
||||||
"alignSelf": "center",
|
"alignSelf": "center",
|
||||||
"color": "#3A2671",
|
"color": "#3A2671",
|
||||||
"fontFamily": "Jost-Bold",
|
"fontFamily": "Jost-Bold",
|
||||||
@@ -41,11 +41,11 @@ exports[`License screen should match the snapshot 1`] = `
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
drip. an open-source cycle tracking app
|
title
|
||||||
</Text>
|
</Text>
|
||||||
<View
|
<View
|
||||||
style={
|
style={
|
||||||
{
|
Object {
|
||||||
"marginBottom": 34.285714285714285,
|
"marginBottom": 34.285714285714285,
|
||||||
"marginHorizontal": 34.285714285714285,
|
"marginHorizontal": 34.285714285714285,
|
||||||
}
|
}
|
||||||
@@ -53,8 +53,8 @@ exports[`License screen should match the snapshot 1`] = `
|
|||||||
>
|
>
|
||||||
<Text
|
<Text
|
||||||
style={
|
style={
|
||||||
[
|
Array [
|
||||||
{
|
Object {
|
||||||
"color": "#555",
|
"color": "#555",
|
||||||
"fontFamily": "Jost-Book",
|
"fontFamily": "Jost-Book",
|
||||||
"fontSize": 34.285714285714285,
|
"fontSize": 34.285714285714285,
|
||||||
@@ -63,14 +63,12 @@ exports[`License screen should match the snapshot 1`] = `
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Copyright (C) 2023 Heart of Code e.V.
|
text{"currentYear":2022}
|
||||||
|
|
||||||
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>
|
||||||
<Text
|
<Text
|
||||||
onPress={[Function]}
|
onPress={[Function]}
|
||||||
style={
|
style={
|
||||||
{
|
Object {
|
||||||
"color": "#3A2671",
|
"color": "#3A2671",
|
||||||
"fontFamily": "Jost-Book",
|
"fontFamily": "Jost-Book",
|
||||||
"fontSize": 34.285714285714285,
|
"fontSize": 34.285714285714285,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
exports[`Footnote component when children are present, renders them 1`] = `
|
exports[`Footnote component when children are present, renders them 1`] = `
|
||||||
<View
|
<View
|
||||||
style={
|
style={
|
||||||
{
|
Object {
|
||||||
"alignContent": "flex-start",
|
"alignContent": "flex-start",
|
||||||
"flexDirection": "row",
|
"flexDirection": "row",
|
||||||
"marginBottom": 8.571428571428571,
|
"marginBottom": 8.571428571428571,
|
||||||
@@ -13,13 +13,13 @@ exports[`Footnote component when children are present, renders them 1`] = `
|
|||||||
>
|
>
|
||||||
<Text
|
<Text
|
||||||
style={
|
style={
|
||||||
[
|
Array [
|
||||||
{
|
Object {
|
||||||
"color": "#555",
|
"color": "#555",
|
||||||
"fontFamily": "Jost-Book",
|
"fontFamily": "Jost-Book",
|
||||||
"fontSize": 34.285714285714285,
|
"fontSize": 34.285714285714285,
|
||||||
},
|
},
|
||||||
{
|
Object {
|
||||||
"color": "#F38337",
|
"color": "#F38337",
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@@ -29,18 +29,18 @@ exports[`Footnote component when children are present, renders them 1`] = `
|
|||||||
</Text>
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
linkStyle={
|
linkStyle={
|
||||||
{
|
Object {
|
||||||
"color": "white",
|
"color": "white",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
style={
|
style={
|
||||||
[
|
Array [
|
||||||
{
|
Object {
|
||||||
"color": "#555",
|
"color": "#555",
|
||||||
"fontFamily": "Jost-Book",
|
"fontFamily": "Jost-Book",
|
||||||
"fontSize": 34.285714285714285,
|
"fontSize": 34.285714285714285,
|
||||||
},
|
},
|
||||||
{
|
Object {
|
||||||
"color": "#555",
|
"color": "#555",
|
||||||
"paddingLeft": 21.428571428571427,
|
"paddingLeft": 21.428571428571427,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
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 }
|
|
||||||
@@ -50,8 +50,14 @@ module.exports = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const pkgJSON = JSON.parse(fs.readFileSync('./package.json'))
|
const pkgJSON = JSON.parse(fs.readFileSync('./package.json'))
|
||||||
|
const pkgLockJSON = JSON.parse(fs.readFileSync('./package-lock.json'))
|
||||||
pkgJSON.version = nextVersion
|
pkgJSON.version = nextVersion
|
||||||
|
pkgLockJSON.version = nextVersion
|
||||||
fs.writeFileSync('./package.json', JSON.stringify(pkgJSON, null, 2))
|
fs.writeFileSync('./package.json', JSON.stringify(pkgJSON, null, 2))
|
||||||
|
fs.writeFileSync(
|
||||||
|
'./package-lock.json',
|
||||||
|
JSON.stringify(pkgLockJSON, null, 2)
|
||||||
|
)
|
||||||
|
|
||||||
await ReactNativeVersion.version(
|
await ReactNativeVersion.version(
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user